.NET C#解密AES加密数据报错:无效Base64字符串问题排查
我在.NET C#应用中解密AES加密数据时遇到问题:使用Angular的CryptoJS库加密JSON对象,再通过C#的RijndaelManaged类解密,触发错误提示:
输入不是有效的Base-64字符串,因为它包含非Base64字符、填充字符超过两个,或填充字符中存在非法字符
奇怪的是,仅加密较大数据(如JSON对象)时会触发该错误,加密“welcome”这类小文本则正常。已确认两端的key和IV完全一致,怀疑问题与编码、传输或填充规则有关,但无法定位具体原因。
示例:
- 加密
{id: 1, name: "test"}得到结果:+IhryRiKnNFiOWNv3cnWde97CiTZZLtVPMbnIm5ieDg= - 加密“hello”得到结果:
v8FQU3S6MnFlkSK349fZUQ==
Angular端加密代码
const key = CryptoJS.enc.Utf8.parse('4512631236589784'); const iv = CryptoJS.enc.Utf8.parse('4512631236589784'); const data = {id: 1, name: "test"}; const encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(JSON.stringify(data)), key, { keySize: 128 / 8, iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }); console.log('Encrypted : ' + encrypted);
C#端解密代码
public string DecryptData() { try { var token = HttpContext.Request.Query["token"]; var keybytes = Encoding.UTF8.GetBytes("4512631236589784"); var iv = Encoding.UTF8.GetBytes("4512631236589784"); var encrypted = Convert.FromBase64String(token); var decryptedFromJavascript = DecryptStringFromBytes(encrypted, keybytes, iv); return decryptedFromJavascript; } catch (Exception e) { Console.WriteLine(e); return e.Message; } } private string DecryptStringFromBytes(byte[] cipherText, byte[] key, byte[] iv) { // Check arguments. if (cipherText == null || cipherText.Length <= 0) { throw new ArgumentNullException("cipherText"); } if (key == null || key.Length <= 0) { throw new ArgumentNullException("key"); } if (iv == null || iv.Length <= 0) { throw new ArgumentNullException("key"); } // Declare the string used to hold // the decrypted text. string plaintext = null; // Create an RijndaelManaged object // with the specified key and IV. using (var rijAlg = new RijndaelManaged()) { //Settings rijAlg.Mode = CipherMode.CBC; rijAlg.Padding = PaddingMode.PKCS7; rijAlg.FeedbackSize = 128; rijAlg.Key = key; rijAlg.IV = iv; // Create a decrytor to perform the stream transform. var decryptor = rijAlg.CreateDecryptor(rijAlg.Key, rijAlg.IV); try { // Create the streams used for decryption. using(var msDecrypt = new MemoryStream(cipherText)) { using(var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) { using(var srDecrypt = new StreamReader(csDecrypt)) { // Read the decrypted bytes from the decrypting stream // and place them in a string. plaintext = srDecrypt.ReadToEnd(); } } } } catch { plaintext = "keyError"; } } return plaintext; }
问题原因分析
URL传输的Base64字符解析问题:
加密后的Base64字符串包含+、=等特殊字符时,通过URL查询参数传输会被自动解析:+会被替换为空格,长字符串还可能因URL长度限制被截断,导致C#端接收到的字符串不是完整有效的Base64格式。小文本加密结果可能刚好不包含这些特殊字符,或长度足够短,因此能正常传输。CryptoJS结果序列化的隐含问题:
CryptoJS的AES.encrypt返回的是CipherParams对象,直接转为字符串时虽然默认输出Base64,但部分场景下可能存在编码偏差,或在跨环境传输时出现格式丢失。
解决建议
1. Angular端做URL安全处理
明确获取Base64密文并进行URL编码,避免特殊字符被解析:
// 明确获取标准Base64密文 const encryptedBase64 = encrypted.toString(CryptoJS.format.OpenSSL); // 对密文做URL编码 const urlSafeEncrypted = encodeURIComponent(encryptedBase64); console.log('URL Safe Encrypted : ' + urlSafeEncrypted);
2. C#端先解码再解析Base64
接收查询参数后先做URL解码,再转换为字节数组:
var token = HttpContext.Request.Query["token"]; // 解码URL编码后的字符串 var decodedToken = Uri.UnescapeDataString(token); var encrypted = Convert.FromBase64String(decodedToken);
3. 改用POST请求传递密文
如果加密数据较大,建议将密文放在POST请求体中传递,避免URL查询参数的长度限制和字符解析问题。
4. 修复C#端的参数校验错误
DecryptStringFromBytes方法中IV的空校验错误抛出了ArgumentNullException("key"),应修正为:
if (iv == null || iv.Length <= 0) { throw new ArgumentNullException("iv"); }
内容的提问来源于stack exchange,提问作者Himanshu Rahi

