You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用CryptoJS AES加密的文本无法用C#解密的问题咨询

CryptoJS AES加密后C#解密方案及密钥疑问解答

一、为什么CryptoJS接受非32字符的密钥

CryptoJS的AES.encrypt方法如果传入的是普通字符串(而非符合AES密钥长度的原始字节),会自动触发基于密码的密钥派生逻辑:

  • 生成随机8字节盐值
  • 通过OpenSSL的EVP_BytesToKey算法(哈希用MD5,迭代1次),将你传入的36字符密钥和盐值混合,生成符合AES-128要求的16字节密钥和16字节初始向量(IV)
  • 也就是说,你传入的36字符只是密码,并非直接作为AES密钥使用,这就是它能接受任意长度字符串的原因

二、C#中解密的具体实现

要成功解密,必须完全对齐CryptoJS的加密逻辑,以下是可直接运行的代码:

核心解密工具类

using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Linq;

public static class CryptoJsAesHelper
{
    /// <summary>
    /// 解密CryptoJS AES.encrypt生成的密文
    /// </summary>
    /// <param name="cipherTextBase64">CryptoJS输出的Base64密文</param>
    /// <param name="password">加密时使用的密钥(任意长度字符串)</param>
    /// <returns>解密后的明文</returns>
    public static string Decrypt(string cipherTextBase64, string password)
    {
        byte[] cipherBytes = Convert.FromBase64String(cipherTextBase64);
        
        // 验证密文格式:前8字节必须是"Salted__"
        if (cipherBytes.Length < 16 || !Encoding.ASCII.GetString(cipherBytes, 0, 8).Equals("Salted__"))
        {
            throw new ArgumentException("密文格式不符合CryptoJS AES加密规范");
        }
        
        // 提取盐值和实际密文
        byte[] salt = cipherBytes.Skip(8).Take(8).ToArray();
        byte[] encryptedData = cipherBytes.Skip(16).ToArray();
        
        // 生成AES密钥和IV
        var (aesKey, aesIv) = DeriveKeyAndIv(Encoding.UTF8.GetBytes(password), salt);
        
        // 执行AES解密
        using (Aes aes = Aes.Create())
        {
            aes.Key = aesKey;
            aes.IV = aesIv;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;
            
            using (ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV))
            {
                byte[] decryptedBytes = decryptor.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
                return Encoding.UTF8.GetString(decryptedBytes);
            }
        }
    }

    /// <summary>
    /// 实现OpenSSL的EVP_BytesToKey算法,生成AES密钥和IV
    /// </summary>
    private static (byte[] Key, byte[] Iv) DeriveKeyAndIv(byte[] password, byte[] salt)
    {
        List<byte> keyMaterial = new List<byte>();
        byte[] currentHash = Array.Empty<byte>();
        int requiredLength = 32; // AES-128需要16字节密钥 + 16字节IV

        using (MD5 md5 = MD5.Create())
        {
            while (keyMaterial.Count < requiredLength)
            {
                // 拼接当前哈希、密码、盐值
                byte[] input = new byte[currentHash.Length + password.Length + salt.Length];
                currentHash.CopyTo(input, 0);
                password.CopyTo(input, currentHash.Length);
                salt.CopyTo(input, currentHash.Length + password.Length);
                
                currentHash = md5.ComputeHash(input);
                keyMaterial.AddRange(currentHash);
            }
        }

        return (keyMaterial.Take(16).ToArray(), keyMaterial.Skip(16).Take(16).ToArray());
    }
}

使用示例

// 替换为你的实际密文和密钥
string cryptoJsCipher = "U2FsdGVkX1+..."; // CryptoJS输出的Base64字符串
string originalPassword = "你的36字符密钥";

try
{
    string plainText = CryptoJsAesHelper.Decrypt(cryptoJsCipher, originalPassword);
    Console.WriteLine("解密结果:" + plainText);
}
catch (Exception ex)
{
    Console.WriteLine("解密失败:" + ex.Message);
}

关键注意事项

  • 编码一致性:CryptoJS默认用UTF-8处理明文和密钥,C#代码中必须对应使用Encoding.UTF8,不能用其他编码
  • 加密参数匹配:CryptoJS默认使用AES-CBC模式、PKCS7填充,C#中必须完全匹配这些参数
  • 密文结构:CryptoJS输出的Base64密文解码后,前8字节固定为Salted__,后续8字节是盐值,剩下的才是加密数据,不能跳过盐值处理

内容的提问来源于stack exchange,提问作者Ahmet

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 03:33:31