AES之CryptoJS加密与C#解密
曾经以为ChatGpt 应该是无所不知道,无所不能的,但是就这个C# 解密用了两天时间来搞,gpt给的代码一直有各种bug,最后还是要靠搜索引擎Bing的帮助才找到了答案
AES加密之CryptoJS与Java C#互相加密解密_梁金堂的博客-CSDN博客 这个文章提供了我查找的方向
Port crypto-js AES functions to C# - TruongTX Blog 这个文章给了我解决的方案
下面是完整的代码示例
/// <summary>
/// <para>C# 版本的 cryptojs.AES.decrypt(encryptedString, passphrase).toString(cryptojs.enc.Utf8)</para>
/// <para>使用 AES 加密时,需要传入一个 Key 和一个随机的 IV - 初始化向量(IV 用于为加密过程添加随机性)</para>
/// <para>在 crypto-js 中,如果你将一个口令传递给 "encrypt" 函数,例如 <code>cryptojs.AES.encrypt(message, passphrase).toString()</code>,Key 和 IV 将会自动生成</para>
/// <para>为了从口令中派生出 Key,它使用 OpenSSL 兼容的派生函数 EVP_BytesToKey。该模式生成一个新的 8 字节随机 salt,并与口令一起使用来生成 Key 和 IV。更多信息请参见 <see cref="DeriveKeyAndIv"/>。</para>
/// <para>使用 <code>cryptojs.AES.encrypt(message, passphrase).toString()</code> 加密的结果是一个 Base64 编码的密文,其中包含字符串 "Salted__",后面跟着 8 字节的 salt 和实际的密文。这意味着,前 8 个字节是 "Salted__" 字符串,接下来的 8 个字节是 salt,剩下的字节是实际的密文。</para>
/// <para>以下是解密 cryptojs 生成的 Base64 字符串的步骤:</para>
/// <para>- 将 Base64 字符串转换回一个字节数组</para>
/// <para>- 忽略前 8 个字节(为字符串 "Salted__")</para>
/// <para>- 取接下来的 8 个字节作为 salt</para>
/// <para>- 取剩下的字节作为密文</para>
/// <para>- 使用实用程序函数 <see cref="DeriveKeyAndIv"/> 生成 Key 和 IV</para>
/// <para>- 将密文、Key 和 IV 输入 AES 解密器以获得最终的字符串</para>
/// </summary>
/// <param name="encryptedString">待解密的 Base64 字符串</param>
/// <param name="passphrase">口令,用于生成 Key 和 IV</param>
/// <returns>解密后的字符串</returns>
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();
} /// <summary>
/// <para>使用C#编写等效的OpenSSL EVP_BytesToKey方法</para>
/// <para>从输入的口令和盐生成密钥和初始化向量(IV)</para>
/// </summary>
/// <param name="passphrase">口令字节数组</param>
/// <param name="salt">盐字节数组</param>
/// <param name="iterations">哈希迭代次数</param>
/// <param name="key">输出32字节密钥</param>
/// <param name="iv">输出16字节初始化向量(IV)</param>
private static void DeriveKeyAndIv(byte[] passphrase, byte[] salt, int iterations, out byte[] key, out byte[] iv)
{
// 用于保存哈希值
var hashList = new List<byte>(); // 计算第一个哈希值
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 }
AES之CryptoJS加密与C#解密的更多相关文章
- 微信小程序aes前后端加密解密交互
aes前后端加密解密交互 小程序端 1. 首先引入aes.js /** * [description] CryptoJS v3.1.2 * [description] zhuangzhudada so ...
- iOS,一行代码进行RSA、DES 、AES、MD5加密、解密
本文为投稿文章,作者:Flying_Einstein(简书) 加密的Demo,欢迎下载 JAVA端的加密解密,读者可以看我同事的这篇文章:http://www.jianshu.com/p/98569e ...
- golang AES/ECB/PKCS5 加密解密 url-safe-base64
因为项目的需要用到golang的一种特殊的加密解密算法AES/ECB/PKCS5,但是算法并没有包含在标准库中,经过多次失败的尝试,终于解码成功,特此分享: /* 描述 : golang AES/EC ...
- python AES 双向对称加密解密
高级加密标准(Advanced Encryption Standard,AES),在密码学中又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准.这个标准用来替代原先的DES,已经被多方分 ...
- 使用java实现对称加密解密(AES),非对称加密解密(RSA)
对称加密:双方采用同样的秘钥进行加密和解密.特点是速度快,但是安全性没有非对称加密高 非对称加密:接收方生成的公有秘钥公布给发送方,发送方使用该公有秘钥加密之后,发送给接收方,然后接收方使用私有秘钥解 ...
- AES字节数组加密解密流程
AES类时微软MSDN中最常用的加密类,微软官网也有例子,参考链接:https://docs.microsoft.com/zh-cn/dotnet/api/system.security.crypto ...
- Java 前端加密传输后端解密以及验证码功能
目录(?)[-] 加密解密 1 前端js加密概述 2 前后端加密解密 21 引用的js加密库 22 js加密解密 23 Java端加密解密PKCS5Padding与js的Pkcs7一致 验证码 1 概 ...
- AES前后端加密
1.前端代码 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UT ...
- 我的Android进阶之旅------>Android采用AES+RSA的加密机制对http请求进行加密
前言 未加密的抓包截图 加密之后的抓包截图 基本需求及概念 AES算法 AES基本原理及算法流程 AES算法流程 RSA算法 RSA算法基本原理及流程 RSA算法实现流程 AES与RSA相结合数据加密 ...
- Java使用Cipher类实现加密,包括DES,DES3,AES和RSA加密
一.先看一个简单加密,解密实现 1.1 加密 /** * content: 加密内容 * slatKey: 加密的盐,16位字符串 * vectorKey: 加密的向量,16位字符串 */ publi ...
随机推荐
- 如何优化和提高MaxKB回答的质量和准确性?
目前 ChatGPT.GLM等生成式人工智能在文本生成.文本到图像生成等在各行各业的都有着广泛的应用,但是由于大模型训练集基本都是构建于网络公开的数据,对于一些实时性的.非公开的或离线的数据是无法获取 ...
- SearXNG私有化部署与Dify集成
一.概述 SearXNG 是一个免费的互联网元搜索引擎,它聚合了来自各种搜索服务和数据库的结果,但摆脱了隐私追踪 -- 用户行为既不会被引擎跟踪也不会被分析. 功能特性 自托管,可以私有化部署 没有用 ...
- Linux halt命令
若系统的 runlevel 为 0 或 6 ,则Linux halt命令关闭系统,否则以 shutdown 指令(加上 -h 参数)来取代. 使用权限:系统管理者. 语法 halt [-n] [-w] ...
- .NET 平台上的开源模型训练与推理进展
.NET 平台上的开源模型训练与推理进展 作者:痴者工良 博客:https://www.whuanle.cn 电子书仓库:https://github.com/whuanle/cs_pytorch M ...
- 为什么 Java 新生代被划分为 S0、S1 和 Eden 区?
为什么 Java 新生代被划分为 S0.S1 和 Eden 区? 在 Java 的 垃圾回收(GC)机制中,新生代 被进一步划分为 Eden 区 和两个 Survivor 区(S0 和 S1).这种划 ...
- 自己搭建一个https的dns,让不同的浏览器使用不同的DNS,使用相同的域名访问到不同的主机上
我有一个web项目,使用域名访问,需要同时运行线上环境和测试环境,为了防止一些css.js缓存影响,在不同的浏览器里分别访问线上环境和测试环境,比如Chrome浏览器访问测试环境,而Safari浏览器 ...
- 稀疏贝叶斯谱估计及EM算法求解
稀疏贝叶斯 稀疏贝叶斯学习(sparse bayes learning,SBL)最早被提出是作为一种机器学习算法[1].但是在这里我们主要用它来做谱估计,作为求解稀疏重构问题的方法[2].稀疏重构还有 ...
- footer固定在页面底部的几种方法(转载)
几种非常不错的方法,收藏学习:原文见https://blog.csdn.net/m0_37070714/article/details/77587753 方法一:footer高度固定+绝对定位 < ...
- React Native开发鸿蒙Next---富文本浏览
React Native开发鸿蒙Next---富文本浏览 最近在继续开发App剩余的社区功能.地铁的社区相对较为特殊,只有公告/政策规章/操作指南等资讯阅读功能,无法进行交互.对于原先的社区RN,除了 ...
- 【公众号搬运】gap
.markdown-body { line-height: 1.8; font-weight: 400; font-size: 16px; word-spacing: 2px; letter-spac ...