最新要闻

广告

手机

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案

家电

AES之CryptoJS加密与C#解密

来源:博客园


(资料图片仅供参考)

曾经以为ChatGpt 应该是无所不知道,无所不能的,但是就这个C# 解密用了两天时间来搞,gpt给的代码一直有各种bug,最后还是要靠搜索引擎Bing的帮助才找到了答案AES加密之CryptoJS与Java C#互相加密解密_梁金堂的博客-CSDN博客这个文章提供了我查找的方向Port crypto-js AES functions to C# - TruongTX Blog这个文章给了我解决的方案下面是完整的代码示例
///         /// C# 版本的 cryptojs.AES.decrypt(encryptedString, passphrase).toString(cryptojs.enc.Utf8)        /// 使用 AES 加密时,需要传入一个 Key 和一个随机的 IV - 初始化向量(IV 用于为加密过程添加随机性)        /// 在 crypto-js 中,如果你将一个口令传递给 "encrypt" 函数,例如 cryptojs.AES.encrypt(message, passphrase).toString(),Key 和 IV 将会自动生成        /// 为了从口令中派生出 Key,它使用 OpenSSL 兼容的派生函数 EVP_BytesToKey。该模式生成一个新的 8 字节随机 salt,并与口令一起使用来生成 Key 和 IV。更多信息请参见         /// 使用 cryptojs.AES.encrypt(message, passphrase).toString() 加密的结果是一个 Base64 编码的密文,其中包含字符串 "Salted__",后面跟着 8 字节的 salt 和实际的密文。这意味着,前 8 个字节是 "Salted__" 字符串,接下来的 8 个字节是 salt,剩下的字节是实际的密文。        /// 以下是解密 cryptojs 生成的 Base64 字符串的步骤:        /// - 将 Base64 字符串转换回一个字节数组        /// - 忽略前 8 个字节(为字符串 "Salted__")        /// - 取接下来的 8 个字节作为 salt        /// - 取剩下的字节作为密文        /// - 使用实用程序函数  生成 Key 和 IV        /// - 将密文、Key 和 IV 输入 AES 解密器以获得最终的字符串        ///         /// 待解密的 Base64 字符串        /// 口令,用于生成 Key 和 IV        /// 解密后的字符串        public static string Cryptojs_AesDecrypt(string encryptedString, string passphrase)        {            // 检查参数是否为 null 或空字符串            if (string.IsNullOrEmpty(encryptedString))            {                throw new ArgumentException("加密字符串不能为空。", nameof(encryptedString));            }            if (string.IsNullOrEmpty(passphrase))            {                throw new ArgumentException("口令不能为空。", nameof(passphrase));            }            // 将 Base64 编码的字符串转换为字节数组            var encryptedBytes = Convert.FromBase64String(encryptedString);            // 获取盐值和密文            var salt = encryptedBytes[8..16];            var cipherText = encryptedBytes[16..];            // 从口令和盐值派生密钥和 IV            const int iterations = 1; // CryptoJS 默认使用 1 次迭代            var passphraseBytes = Encoding.UTF8.GetBytes(passphrase);            DeriveKeyAndIv(passphraseBytes, salt, iterations, out var key, out var iv);            // 创建 AES 解密器            using var aes = Aes.Create();            aes.Key = key;            aes.IV = iv;            aes.KeySize = 256;            aes.Padding = PaddingMode.PKCS7;            aes.Mode = CipherMode.CBC;            // 创建解密器            var decryptor = aes.CreateDecryptor(key, iv);            // 解密密文            using var msDecrypt = new MemoryStream(cipherText);            using var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read);            using var srDecrypt = new StreamReader(csDecrypt);            // 从解密流中读取解密后的字节,并将它们放入一个字符串中。            return srDecrypt.ReadToEnd();        }        ///         /// 使用C#编写等效的OpenSSL EVP_BytesToKey方法        /// 从输入的口令和盐生成密钥和初始化向量(IV)        ///         /// 口令字节数组        /// 盐字节数组        /// 哈希迭代次数        /// 输出32字节密钥        /// 输出16字节初始化向量(IV)        private static void DeriveKeyAndIv(byte[] passphrase, byte[] salt, int iterations, out byte[] key, out byte[] iv)        {            // 用于保存哈希值            var hashList = new List();            // 计算第一个哈希值            var preHashLength = passphrase.Length + (salt?.Length ?? 0);            var preHash = new byte[preHashLength];            // 将口令复制到 preHash 数组            Buffer.BlockCopy(passphrase, 0, preHash, 0, passphrase.Length);            if (salt != null)            {                // 将盐值复制到 preHash 数组                Buffer.BlockCopy(salt, 0, preHash, passphrase.Length, salt.Length);             }            // 创建 MD5 哈希对象            var hash = MD5.Create();            // 计算哈希值            var currentHash = hash.ComputeHash(preHash);            // 迭代计算哈希值            for (var i = 1; i < iterations; i++)            {                currentHash = hash.ComputeHash(currentHash);            }            // 将第一个哈希值加入哈希值列表            hashList.AddRange(currentHash);            #region  用于32字节密钥和16字节IV            // 如果哈希值不足48字节,则进行更多的哈希计算            while (hashList.Count < 48)            {                preHashLength = currentHash.Length + passphrase.Length + (salt?.Length ?? 0);                preHash = new byte[preHashLength];                // 将上一次的哈希值复制到 preHash 数组                Buffer.BlockCopy(currentHash, 0, preHash, 0, currentHash.Length);                // 将口令复制到 preHash 数组                Buffer.BlockCopy(passphrase, 0, preHash, currentHash.Length, passphrase.Length);                if (salt != null)                {                    // 将盐值复制到 preHash 数组                    Buffer.BlockCopy(salt, 0, preHash, currentHash.Length + passphrase.Length, salt.Length);                }                // 计算哈希值                currentHash = hash.ComputeHash(preHash);                // 迭代计算哈希值                for (var i = 1; i < iterations; i++)                {                    currentHash = hash.ComputeHash(currentHash);                }                // 将计算得到的哈希值加入哈希值列表                hashList.AddRange(currentHash);            }            #endregion 用于32字节密钥和16字节IV            #region  从哈希值列表中提取密钥和 IV            // 清除哈希对象            hash.Clear();            // 用于保存密钥的字节数组            key = new byte[32];            // 用于保存 IV 的字节数组            iv = new byte[16];            // 将前32个字节复制到密钥数组            hashList.CopyTo(0, key, 0, 32);            // 将后16个字节复制到 IV 数组            hashList.CopyTo(32, iv, 0, 16);            #endregion  从哈希值列表中提取密钥和 IV        }

关键词: