1、文章回顾
在文章windows使用openssl生成公钥和私钥https://www.luweidong.cn/details/72 中成功使用openssl生成了用于RSA加密和解密的公钥和私钥。
在文章在JavaScript中进行RSA加密和解密https://www.luweidong.cn/details/86 中成功使用JavaScript进行了RSA加密和解密。
如果你习惯使用.net平台,根据以上的文章已经实现了在网页端使用JS进行RSA加密解密的功能,但是还有.net后端呢?只有后端与前端相互配合,才能实现通过RSA加密和解密的整个流程。今天这篇文章就是把这环得圆上。
2、关键代码
示例基于.net6框架。关于RSA的实现函数都放在一个文件中。特别说明一下,该代码只支持windows平台。
密钥生成的关键函数:
#region 密钥生成 /// <summary> /// 取得私钥和公钥 XML 格式,返回数组第一个是私钥,第二个是公钥. /// </summary> /// <param name="size">密钥长度,默认1024,可以为2048</param> /// <returns></returns> public static string[] CreateXmlKey(int size = 1024) { //密钥格式要生成pkcs#1格式的 而不是pkcs#8格式的 RSACryptoServiceProvider sp = new RSACryptoServiceProvider(size); string privateKey = sp.ToXmlString(true);//private key string publicKey = sp.ToXmlString(false);//public key return new string[] { privateKey, publicKey }; } /// <summary> /// 取得私钥和公钥 CspBlob 格式,返回数组第一个是私钥,第二个是公钥. /// </summary> /// <param name="size"></param> /// <returns></returns> public static string[] CreateCspBlobKey(int size = 1024) { //密钥格式要生成pkcs#1格式的 而不是pkcs#8格式的 RSACryptoServiceProvider sp = new RSACryptoServiceProvider(size); string privateKey = System.Convert.ToBase64String(sp.ExportCspBlob(true));//private key string publicKey = System.Convert.ToBase64String(sp.ExportCspBlob(false));//public key return new string[] { privateKey, publicKey }; } /// <summary> /// 导出PEM PKCS#1格式密钥对,返回数组第一个是私钥,第二个是公钥. /// </summary> public static string[] CreateKey_PEM_PKCS1(int size = 1024) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(size); string privateKey = ToPEM(rsa, false, false); string publicKey = ToPEM(rsa, true, false); return new string[] { privateKey, publicKey }; } /// <summary> /// 导出PEM PKCS#8格式密钥对,返回数组第一个是私钥,第二个是公钥. /// </summary> public static string[] CreateKey_PEM_PKCS8(int size = 1024, bool convertToPublic = false) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(size); string privateKey = ToPEM(rsa, false, true); string publicKey = ToPEM(rsa, true, true); return new string[] { privateKey, publicKey }; } #endregion
加密解密函数:
加密块有最大长度限制,如果加密数据的长度超过私钥长度/8-11,会引发长度不正确的异常,所以进行数据的分块加密。
#region 加密和解密 /// <summary> /// RSA加密 /// </summary> /// <param name="Data">原文</param> /// <param name="PublicKeyString">公钥</param> /// <param name="KeyType">密钥类型XML/PEM</param> /// <returns></returns> public static string RSAEncrypt(string Data, string PublicKeyString, string KeyType) { byte[] data = Encoding.GetEncoding("UTF-8").GetBytes(Data); RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); switch (KeyType) { case "XML": rsa.FromXmlString(PublicKeyString); break; case "PEM": rsa = FromPEM(PublicKeyString); break; default: throw new Exception("不支持的密钥类型"); } int MaxBlockSize = rsa.KeySize / 8 - 11; //正常长度 if (data.Length <= MaxBlockSize) { byte[] hashvalueEcy = rsa.Encrypt(data, false); //加密 return System.Convert.ToBase64String(hashvalueEcy); } //长度超过正常值 else { using (MemoryStream PlaiStream = new MemoryStream(data)) using (MemoryStream CrypStream = new MemoryStream()) { Byte[] Buffer = new Byte[MaxBlockSize]; int BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize); while (BlockSize > 0) { Byte[] ToEncrypt = new Byte[BlockSize]; Array.Copy(Buffer, 0, ToEncrypt, 0, BlockSize); Byte[] Cryptograph = rsa.Encrypt(ToEncrypt, false); CrypStream.Write(Cryptograph, 0, Cryptograph.Length); BlockSize = PlaiStream.Read(Buffer, 0, MaxBlockSize); } return System.Convert.ToBase64String(CrypStream.ToArray(), Base64FormattingOptions.None); } } } /// <summary> /// RSA解密 /// </summary> /// <param name="Data">密文</param> /// <param name="PrivateKeyString">私钥</param> /// <param name="KeyType">密钥类型XML/PEM</param> /// <returns></returns> public static string RSADecrypt(string Data, string PrivateKeyString, string KeyType) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); switch (KeyType) { case "XML": rsa.FromXmlString(PrivateKeyString); break; case "PEM": rsa = FromPEM(PrivateKeyString); break; default: throw new Exception("不支持的密钥类型"); } int MaxBlockSize = rsa.KeySize / 8; //解密块最大长度限制 //正常解密 if (Data.Length <= MaxBlockSize) { byte[] hashvalueDcy = rsa.Decrypt(System.Convert.FromBase64String(Data), false);//解密 return Encoding.GetEncoding("UTF-8").GetString(hashvalueDcy); } //分段解密 else { using (MemoryStream CrypStream = new MemoryStream(System.Convert.FromBase64String(Data))) using (MemoryStream PlaiStream = new MemoryStream()) { Byte[] Buffer = new Byte[MaxBlockSize]; int BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize); while (BlockSize > 0) { Byte[] ToDecrypt = new Byte[BlockSize]; Array.Copy(Buffer, 0, ToDecrypt, 0, BlockSize); Byte[] Plaintext = rsa.Decrypt(ToDecrypt, false); PlaiStream.Write(Plaintext, 0, Plaintext.Length); BlockSize = CrypStream.Read(Buffer, 0, MaxBlockSize); } string output = Encoding.GetEncoding("UTF-8").GetString(PlaiStream.ToArray()); return output; } } } #endregion
3、功能测试
在Program中首先生成私钥和公钥分别保存在pri.txt和pub.txt文本中。
//生成公钥和密钥并保存到文本中 string[] a = RSA_Tools.CreateKey_PEM_PKCS8(); string privString = a[0];//私钥 string pubString = a[1];//公钥 File.WriteAllText("pri.txt", privString); File.WriteAllText("pub.txt", pubString); Console.WriteLine($"privateKey:{privString}"); Console.WriteLine($"publicKey:{pubString}");
分别读取私钥和公钥进行加密和解密测试:
string privString = File.ReadAllText("pri.txt"); string pubString = File.ReadAllText("pub.txt"); string text = "123"; Console.WriteLine($"被加密字符:{text}"); string j = RSA_Tools.RSAEncrypt(text, pubString, "PEM"); Console.WriteLine($"加密后字符:{j}"); string b = RSA_Tools.RSADecrypt(j, privString, "PEM"); Console.WriteLine($"解密后字符:{b}");
如图,加密文本“123”,再用加密后的文本解密得到“123,测试成功。

4、JavaScript和.net后端互联互通测试
使用文章在JavaScript中进行RSA加密和解密 中的示例对原始数据进行RSA加密,再把加密后的字符串用.net后端的代码解密,测试是否成功。注意双方使用的公钥和私钥需一致。
简单列出加密解密的流程:
1).net后端生成公钥和私钥,自个保存私钥,把公钥发给前端。(私钥不在网络上传播)
2)前端接收到公钥后,使用公钥加密数据,把加密后的数据返回后端。
3)后端使用私钥解密前端的数据,得到原始数据。
RSA非对称加密和解密的关键就体现于此,私钥是绝不在网络上传播的,公钥是主动公开的。使用公钥加密的数据只有用配对的私钥才能解密!真是神奇,的数学。
这里讲解第二步,JavaScript使用公钥加密代码如下:(注意公钥是第一步中.net后端生成的)
<script type="text/javascript"> $(function () { var encryptor = new JSEncrypt() // 创建加密对象实例 //之前ssl生成的公钥,复制的时候要小心不要有空格 var pubKey = '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1QQRl0HlrVv6kGqhgonD6A9 SU6ZJpnEN+Q0blT/ue6Ndt97WRfxtS' + 'As0QoquTreaDtfC4RRX4o+CU6BTuHLUm+eSvxZS9TzbwoYZq7ObbQAZAY+SYDgAA5PHf1wNN20dGMFFgVS /y0ZWvv1UNa2laEz0I8Vmr5ZlzIn88GkmSiQIDAQAB-----END PUBLIC KEY-----' encryptor.setPublicKey(pubKey)//设置公钥 var rsaPassWord = encryptor.encrypt('123') console.log(rsaPassWord) }); </script>
把原始数据”123“加密后得到”KoO8MxKRr4mu5Z3iG4cpF/se+8Axdd0ydX374CSmFPj+asXCa/bvHcr3NBUpB3YBxi/B9LVzuajgnne 4nYWj8NERS2L5OM2WH11irLLxZNqG98uD9OOx2zO9CjT8VaMWEo0dErvcu VAdZeXpgiD1CJh5sWNKZEioBiVVU/sApzo=“
使用后端解密:
string privString = File.ReadAllText("pri.txt"); string j = "KoO8MxKRr4mu5Z3iG4cpF/se+8Axdd0ydX374CSmFPj+asXCa/bvHcr3NBUpB3YBxi/B9LVzuajgnne4n YWj8NERS2L5OM2WH11irLLxZNqG98uD9OOx2zO9CjT8VaMWEo0dErvcuVAdZeXpgiD1CJh 5sWNKZEioBiVVU/sApzo="; string b = RSA_Tools.RSADecrypt(j, privString, "PEM"); Console.WriteLine($"解密后字符:{b}");

解密得到原始数据”123“。
5、代码下载
至此已经实现了前后端的RSA非对称加密和解密的功能,可以愉快地基于此实现数据的安全传输。以上示例基于服务端生产一对密钥对,其实也可以前端和后端各自生成一对密钥对,并向对方分享各自的公钥。在传输数据前,使用对方的公钥加密数据后再进行传输,安全性将大大提高!
在微信小程序上搜索(密码盾)集成了开心一刻、程序员计算器、密码箱、习惯打卡、还款提醒、倒数纪念日、日记本、备忘录、日程提醒、端口映射、练打字等功能。前后端的数据传输就应用了RSA非对称加密和解密,有兴趣可体验一番!
我已将代码上传,下载码是:D2BC8C3473
下载码是啥?如何下载=》点击查看https://www.luweidong.cn/details/88