在 C# 中从 OpenSSL 使用 PBKDF2 解密 AES-256-CBC

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

我需要解密一个用 OpenSSL 加密的字符串,如下所示:

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

我无法让它在 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;
    }

使用此代码,我在 decrypted = sr.ReadToEnd() Padding is invalid and cannot be removed.

收到异常

秘密通过RSA加密并解密,结果与解密文件相同,所以这应该有效。

非常感谢您的帮助

解决方案:

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;
}

并生成密码:openssl rand 192 | openssl enc -A -base64 -out secret.key

问题是密码短语是用换行符生成的,而 openssl 仅使用密码短语文件的第一行,但 C# 使用整个文件来派生密钥。为了防止这种情况,我现在生成一个没有换行符的文件。