如何从c#中使用crypto-js解密AES



我正试图解密一个在c#后端使用AES加密的值。解密部分将在Angular的前端进行(使用crypto-js)我遇到的问题是,我总是得到一个空字符串作为解密的结果。我不知道我做错了什么。我是不是漏掉了什么构型?

我要加密的c#代码是这样的:

//
EncryptAES("XEMFkT92UtR1VJI8kU8XQJALk98GGEFM", "random text to encrypt");
public static string EncryptAES(string passPhrase, string plainText)
{
byte[] iv = Generate256BitsOfRandomEntropy();
byte[] temp;
byte[] array;
using (Aes aes = Aes.Create())
{
byte[] salt = Generate256BitsOfRandomEntropy();
Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(passPhrase, salt, 100);
aes.Key = pdb.GetBytes(32);
aes.KeySize = 256;
aes.Padding = PaddingMode.PKCS7;
aes.Mode = CipherMode.CBC;
aes.IV = iv;
ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);
using (MemoryStream memoryStream = new MemoryStream())
{
using (CryptoStream cryptoStream = new CryptoStream((Stream)memoryStream, encryptor, CryptoStreamMode.Write))
{
using (StreamWriter streamWriter = new StreamWriter((Stream)cryptoStream, Encoding.UTF8))
{
streamWriter.Write(plainText);
}

temp = memoryStream.ToArray();
array = salt.Concat(iv).Concat(temp).ToArray();

cryptoStream.Flush();
encryptor.Dispose();
}
}
}
return Convert.ToBase64String(array);
}

//Random byte[] generator
private static byte[] Generate256BitsOfRandomEntropy()
{
var randomBytes = new byte[16];
using (var rngCsp = new RNGCryptoServiceProvider())
{
rngCsp.GetBytes(randomBytes);
}
return randomBytes;
}
的解密部分。文件是:

//The param "key" will be same as the C# code: XEMFkT92UtR1VJI8kU8XQJALk98GGEFM
//The param "toDecrypt" will the the Base64 returned by the service in C#
decryptAES(key: string, toDecrypt: string) {
var data = Buffer.from(toDecrypt, 'base64');
var salt = data.slice(0, 16); //first 16 bytes to get the salt
var iv = data.slice(16, 32);// next 16 bytes to get the IV
const wordArrayIV = CryptoJS.lib.WordArray.create(Array.from(iv));
const wordArraySalt = CryptoJS.lib.WordArray.create(Array.from(salt))
var keyPBKDF2 = CryptoJS.PBKDF2(key, wordArraySalt, {
keySize: 256 / 32,
iterations: 100
});
var decrypted = CryptoJS.AES.decrypt(toDecrypt, keyPBKDF2, 
{   
mode: CryptoJS.mode.CBC, 
padding: CryptoJS.pad.Pkcs7, 
iv: wordArrayIV
});
//Return empty string 
return decrypted.toString();
}

在c#代码中,不使用PBKDF2派生的密钥,而是使用随机生成的密钥。这是因为在设置密钥大小时,会隐式地生成一个新密钥。
As fix只需删除键大小的设置,即aes.KeySize = 256行(键大小在设置键时隐式设置)。

...
aes.Key = pdb.GetBytes(32);
//aes.KeySize = 256;                // Fix: remove
//aes.Padding = PaddingMode.PKCS7;  // default
//aes.Mode = CipherMode.CBC;        // default
aes.IV = iv;
...

此外,在CryptoJS代码中存在几个问题:首先,缓冲区被错误地转换为WordArrays,因此IV和salt是错误的。
另外,在分离时没有考虑密文,进一步被错误地传递给AES.decrypt()
解密后的数据是十六进制编码,但应该是UTF-8解码。

function decryptAES(key, toDecrypt) {
var data = CryptoJS.enc.Base64.parse(toDecrypt);
var wordArraySalt = CryptoJS.lib.WordArray.create(data.words.slice(0, 4)); // Fix: Array -> WordArray conversion 
var wordArrayIV = CryptoJS.lib.WordArray.create(data.words.slice(4, 8)); // Fix: Array -> WordArray conversion
var wordArrayCt = CryptoJS.lib.WordArray.create(data.words.slice(8)); // Fix: Consider ciphertext
var keyPBKDF2 = CryptoJS.PBKDF2(key, wordArraySalt, {keySize: 256 / 32, iterations: 100});
var decrypted = CryptoJS.AES.decrypt({ciphertext: wordArrayCt}, keyPBKDF2, {iv: wordArrayIV}); // Fix: Pass ciphertext as CipherParams object
return decrypted.toString(CryptoJS.enc.Utf8); // Fix: UTF-8 decode
}
var decrypted = decryptAES('XEMFkT92UtR1VJI8kU8XQJALk98GGEFM', '4YI4unJecVXvvNQVgBsdUwrr7rlwcImDb7t1LT88UO0w8BdFpOp5PLsu6PRJ+eCeKB01rWdVVrGMLj7tOi3KHg==');
console.log(decrypted);
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>

注意上面代码中的密文是用固定的c#代码生成的。


关于漏洞:使用PBKDF2进行密钥派生时,100次迭代计数通常太小。

最新更新