在 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# 使用整个文件来派生密钥。为了防止这种情况,我现在生成一个没有换行符的文件。
我需要解密一个用 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# 使用整个文件来派生密钥。为了防止这种情况,我现在生成一个没有换行符的文件。