Search code examples
c#encryptionopensslpbkdf2

Decrypt AES-256-CBC with PBKDF2 from OpenSSL in C#


I need to decrypt a string that was encrypted with OpenSSL as following:

openssl rand -out secret.key -hex 192
openssl aes-256-cbc -pbkdf2 -in file_to_encrypt -out encrypted_file -pass file:secret.key

And i can't get it to work in C#

  public void OnStartup()
  {
    using var rsa = RSA.Create();
    var privateKeyContent = File.ReadAllText("/cert/customer.pem");
    rsa.ImportFromPem(privateKeyContent);
    var encryptedSecret = File.ReadAllBytes("license/secret.key.enc");
    var decrypted = rsa.Decrypt(encryptedSecret, RSAEncryptionPadding.Pkcs1);
    _logger.LogInformation(Encoding.UTF8.GetString(decrypted));
    var bytes = File.ReadAllBytes("license/license.json.enc");
    var license = DecryptAesCbc(bytes, decrypted);
    _logger.LogInformation(license);
  }

  public string DecryptAesCbc(byte[] cipheredData, byte[] passphrase)
  {
    string decrypted = null;
    using (var ms = new MemoryStream(cipheredData))
    {
      // Get salt
      var salt = new byte[8];
      ms.Seek(8, SeekOrigin.Begin);
      ms.Read(salt, 0, 8);
      _logger.LogInformation("Salt: {Salt}", string.Concat(Array.ConvertAll(salt, x => x.ToString("X2"))));

      // Derive key and IV
      var pbkdf2 = new Rfc2898DeriveBytes(passphrase, salt, 10000, HashAlgorithmName.SHA256);
      byte[] key = pbkdf2.GetBytes(32);
      byte[] iv = pbkdf2.GetBytes(16);
      _logger.LogInformation("Key: {Key}", string.Concat(Array.ConvertAll(key, x => x.ToString("X2"))));
      _logger.LogInformation("IV: {IV}", string.Concat(Array.ConvertAll(iv, x => x.ToString("X2"))));

      using Aes aes = Aes.Create();
      aes.KeySize = 256;
      aes.Padding = PaddingMode.PKCS7;
      aes.Mode = CipherMode.CBC;
      aes.Key = key;
      aes.IV = iv;

      // Decrypt
      ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);
                using var cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read);
      using var sr = new StreamReader(cs, Encoding.UTF8);
      decrypted = sr.ReadToEnd();
      }
      return decrypted;
    }

With this code i receive an exception at decrypted = sr.ReadToEnd() Padding is invalid and cannot be removed.

The secret is encrypted via RSA and decrypted, the result is the same as the the decrypted file, so this should be working.

Im very thankful for help


Solution

  • Solution:

    public static string DecryptLicense(byte[] cipherData, byte[] passphrase)
    {
      string decrypted = null;
      using (var ms = new MemoryStream(cipherData))
      {
        // Get salt
        var salt = new byte[8];
        ms.Seek(8, SeekOrigin.Begin);
        ms.Read(salt, 0, 8);
    
        // Derive key and IV
        var pbkdf2 = new Rfc2898DeriveBytes(passphrase, salt, 10000, HashAlgorithmName.SHA256);
        byte[] key = pbkdf2.GetBytes(32);
        byte[] iv = pbkdf2.GetBytes(16);
    
        using Aes aes = Aes.Create();
        aes.KeySize = 256;
        aes.Padding = PaddingMode.PKCS7;
        aes.Mode = CipherMode.CBC;
        aes.Key = key;
        aes.IV = iv;
    
        // Decrypt
        ICryptoTransform decipher = aes.CreateDecryptor(aes.Key, aes.IV);
        using var cs = new CryptoStream(ms, decipher, CryptoStreamMode.Read);
        using var sr = new StreamReader(cs, Encoding.UTF8);
        decrypted = sr.ReadToEnd();
      }
    
      return decrypted;
    }
    

    and generate the passphrase with: openssl rand 192 | openssl enc -A -base64 -out secret.key

    The problem was a passphrase which was generated with newlines and openssl uses just the first line from a passphrase file, but C# uses the whole file to derive the keys. In order to prevent that, i now generate a file with no linebreaks.