c# RSA非對(duì)稱加解密及XML&PEM格式互換方案
最近因考慮接口安全問(wèn)題,有實(shí)現(xiàn)給WEB API實(shí)現(xiàn)統(tǒng)一的參數(shù)鑒權(quán)功能,以防止請(qǐng)求參數(shù)被篡改或重復(fù)執(zhí)行,參數(shù)鑒權(quán)方法基本與常見(jiàn)的鑒權(quán)思路相同,采用(timestamp+sign),而我為了防止timestamp被更改,sign算法(timestamp+相關(guān)參數(shù)排序、格式化后拼接再M(fèi)D5)也因?yàn)樵谇岸耸遣话踩?,故?duì)timestamp采取使用非對(duì)稱加解密,以盡可能的保證生成的sign不易被破解或替換;
RSA加解密(即:非對(duì)稱加解密)
生成公鑰、私鑰對(duì)方法(C#),生成出來(lái)后默認(rèn)都是XML格式:
public static Tuple<string, string> GeneratePublicAndPrivateKeyPair() { using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { string publicKey = rsa.ToXmlString(false); // 公鑰 string privateKey = rsa.ToXmlString(true); // 私鑰 return Tuple.Create(publicKey, privateKey); } }
使用公鑰加密:(支持分段加密,普通單次加密可能會(huì)因?yàn)閮?nèi)容過(guò)長(zhǎng)而報(bào)錯(cuò))
public static string RSAEncrypt(string publicKey, string rawInput) { if (string.IsNullOrEmpty(rawInput)) { return string.Empty; } if (string.IsNullOrWhiteSpace(publicKey)) { throw new ArgumentException("Invalid Public Key"); } using (var rsaProvider = new RSACryptoServiceProvider()) { var inputBytes = Encoding.UTF8.GetBytes(rawInput);//有含義的字符串轉(zhuǎn)化為字節(jié)流 rsaProvider.FromXmlString(publicKey);//載入公鑰 int bufferSize = (rsaProvider.KeySize / 8) - 11;//單塊最大長(zhǎng)度 var buffer = new byte[bufferSize]; using (MemoryStream inputStream = new MemoryStream(inputBytes), outputStream = new MemoryStream()) { while (true) { //分段加密 int readSize = inputStream.Read(buffer, 0, bufferSize); if (readSize <= 0) { break; } var temp = new byte[readSize]; Array.Copy(buffer, 0, temp, 0, readSize); var encryptedBytes = rsaProvider.Encrypt(temp, false); outputStream.Write(encryptedBytes, 0, encryptedBytes.Length); } return Convert.ToBase64String(outputStream.ToArray());//轉(zhuǎn)化為字節(jié)流方便傳輸 } } }
使用私鑰解密:(支持分段解密,普通單次解密可能會(huì)因?yàn)槊芪倪^(guò)長(zhǎng)而報(bào)錯(cuò))
public static string RSADecrypt(string privateKey,string encryptedInput) { if (string.IsNullOrEmpty(encryptedInput)) { return string.Empty; } if (string.IsNullOrWhiteSpace(privateKey)) { throw new ArgumentException("Invalid Private Key"); } using (var rsaProvider = new RSACryptoServiceProvider()) { var inputBytes = Convert.FromBase64String(encryptedInput); rsaProvider.FromXmlString(privateKey); int bufferSize = rsaProvider.KeySize / 8; var buffer = new byte[bufferSize]; using (MemoryStream inputStream = new MemoryStream(inputBytes), outputStream = new MemoryStream()) { while (true) { int readSize = inputStream.Read(buffer, 0, bufferSize); if (readSize <= 0) { break; } var temp = new byte[readSize]; Array.Copy(buffer, 0, temp, 0, readSize); var rawBytes = rsaProvider.Decrypt(temp, false); outputStream.Write(rawBytes, 0, rawBytes.Length); } return Encoding.UTF8.GetString(outputStream.ToArray()); } } }
如果都是C#項(xiàng)目可能如上兩個(gè)方法就可以了,但如果需要與WEB前端、JAVA等其它編程語(yǔ)言協(xié)同交互處理時(shí)(比如:WEB前端用公鑰加密,后端C#私鑰解密),則可能因?yàn)楣€與私鑰的格式不相同而導(dǎo)致無(wú)法正常的進(jìn)行對(duì)接【前端、JAVA 等語(yǔ)言使用的是PEM格式的,而C#使用的是XML格式】,網(wǎng)上查XML轉(zhuǎn)PEM格式方案時(shí),都是復(fù)制自:https://www.cnblogs.com/micenote/p/7862989.html 這篇文章,但其實(shí)這篇文章也只是寫了私鑰XML轉(zhuǎn)PEM格式,并沒(méi)有說(shuō)明公鑰XML如何轉(zhuǎn)PEM格式,而且只寫了支持從文件中獲取內(nèi)容再轉(zhuǎn)換,方案不全,但是給了我思路,我經(jīng)過(guò)各種驗(yàn)證,最終實(shí)現(xiàn)了比較友好的PEM與XML格式的相互轉(zhuǎn)換方式,且經(jīng)過(guò)單元測(cè)試驗(yàn)證通過(guò),在此分享給大家。
如下是完整的XML與PEM格式轉(zhuǎn)換器類代碼;(注意需引入BouncyCastle nuget包)
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Math; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Threading.Tasks; namespace Zuowj.Common { /// <summary> /// RSA公鑰、私鑰對(duì)格式(XML與PEM)轉(zhuǎn)換器 /// author:zuowenjun /// date:2020-12-29 /// </summary> public static class RsaKeysFormatConverter { /// <summary> /// XML公鑰轉(zhuǎn)成Pem公鑰 /// </summary> /// <param name="xmlPublicKey"></param> /// <returns></returns> public static string XmlPublicKeyToPem(string xmlPublicKey) { RSAParameters rsaParam; using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.FromXmlString(xmlPublicKey); rsaParam = rsa.ExportParameters(false); } RsaKeyParameters param = new RsaKeyParameters(false, new BigInteger(1, rsaParam.Modulus), new BigInteger(1, rsaParam.Exponent)); string pemPublicKeyStr = null; using (var ms = new MemoryStream()) { using (var sw = new StreamWriter(ms)) { var pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(sw); pemWriter.WriteObject(param); sw.Flush(); byte[] buffer = new byte[ms.Length]; ms.Position = 0; ms.Read(buffer, 0, (int)ms.Length); pemPublicKeyStr = Encoding.UTF8.GetString(buffer); } } return pemPublicKeyStr; } /// <summary> /// Pem公鑰轉(zhuǎn)成XML公鑰 /// </summary> /// <param name="pemPublicKeyStr"></param> /// <returns></returns> public static string PemPublicKeyToXml(string pemPublicKeyStr) { RsaKeyParameters pemPublicKey; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(pemPublicKeyStr))) { using (var sr = new StreamReader(ms)) { var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(sr); pemPublicKey = (RsaKeyParameters)pemReader.ReadObject(); } } var p = new RSAParameters { Modulus = pemPublicKey.Modulus.ToByteArrayUnsigned(), Exponent = pemPublicKey.Exponent.ToByteArrayUnsigned() }; string xmlPublicKeyStr; using (var rsa = new RSACryptoServiceProvider()) { rsa.ImportParameters(p); xmlPublicKeyStr = rsa.ToXmlString(false); } return xmlPublicKeyStr; } /// <summary> /// XML私鑰轉(zhuǎn)成PEM私鑰 /// </summary> /// <param name="xmlPrivateKey"></param> /// <returns></returns> public static string XmlPrivateKeyToPem(string xmlPrivateKey) { RSAParameters rsaParam; using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.FromXmlString(xmlPrivateKey); rsaParam = rsa.ExportParameters(true); } var param = new RsaPrivateCrtKeyParameters( new BigInteger(1, rsaParam.Modulus), new BigInteger(1, rsaParam.Exponent), new BigInteger(1, rsaParam.D), new BigInteger(1, rsaParam.P), new BigInteger(1, rsaParam.Q), new BigInteger(1, rsaParam.DP), new BigInteger(1, rsaParam.DQ), new BigInteger(1, rsaParam.InverseQ)); string pemPrivateKeyStr = null; using (var ms = new MemoryStream()) { using (var sw = new StreamWriter(ms)) { var pemWriter = new Org.BouncyCastle.OpenSsl.PemWriter(sw); pemWriter.WriteObject(param); sw.Flush(); byte[] buffer = new byte[ms.Length]; ms.Position = 0; ms.Read(buffer, 0, (int)ms.Length); pemPrivateKeyStr = Encoding.UTF8.GetString(buffer); } } return pemPrivateKeyStr; } /// <summary> /// Pem私鑰轉(zhuǎn)成XML私鑰 /// </summary> /// <param name="pemPrivateKeyStr"></param> /// <returns></returns> public static string PemPrivateKeyToXml(string pemPrivateKeyStr) { RsaPrivateCrtKeyParameters pemPrivateKey; using (var ms = new MemoryStream(Encoding.UTF8.GetBytes(pemPrivateKeyStr))) { using (var sr = new StreamReader(ms)) { var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(sr); var keyPair = (AsymmetricCipherKeyPair)pemReader.ReadObject(); pemPrivateKey = (RsaPrivateCrtKeyParameters)keyPair.Private; } } var p = new RSAParameters { Modulus = pemPrivateKey.Modulus.ToByteArrayUnsigned(), Exponent = pemPrivateKey.PublicExponent.ToByteArrayUnsigned(), D = pemPrivateKey.Exponent.ToByteArrayUnsigned(), P = pemPrivateKey.P.ToByteArrayUnsigned(), Q = pemPrivateKey.Q.ToByteArrayUnsigned(), DP = pemPrivateKey.DP.ToByteArrayUnsigned(), DQ = pemPrivateKey.DQ.ToByteArrayUnsigned(), InverseQ = pemPrivateKey.QInv.ToByteArrayUnsigned(), }; string xmlPrivateKeyStr; using (var rsa = new RSACryptoServiceProvider()) { rsa.ImportParameters(p); xmlPrivateKeyStr = rsa.ToXmlString(true); } return xmlPrivateKeyStr; } } }
如下是單元測(cè)試代碼:
//公鑰(XML、PEM格式互)測(cè)試 string srcPublicKey = “具體的XML Public Key”; string pemPublicKeyStr= RsaKeysFormatConverter.XmlPublicKeyToPem(publicKey); string xmlPublicKeyStr= RsaKeysFormatConverter.PemPublicKeyToXml(pemPublicKeyStr); Assert.AreEqual(srcPublicKey, xmlPublicKeyStr);
//私鑰(XML、PEM格式互)測(cè)試 string srcPrivateKey = “具體的XML Private Key”; string pemPrivateKeyStr = RsaKeysFormatConverter.XmlPrivateKeyToPem(srcPrivateKey); string xmlPrivateKeyStr = RsaKeysFormatConverter.PemPrivateKeyToXml(pemPrivateKeyStr); Assert.AreEqual(privateKey,xmlPrivateKeyStr)
當(dāng)然也可以不用這么費(fèi)勁自己實(shí)現(xiàn)格式轉(zhuǎn)換,可以使用在線網(wǎng)站直接轉(zhuǎn)換:https://the-x.cn/certificate/XmlToPem.aspx ,另外也有一篇文章實(shí)現(xiàn)了類似的功能,但生成的PEM格式并非完整的格式,缺少注釋頭尾:https://www.cnblogs.com/datous/p/RSAKeyConvert.html
以上就是c# RSA非對(duì)稱加解密及XML&PEM格式互換方案的詳細(xì)內(nèi)容,更多關(guān)于c# RSA非對(duì)稱加解密的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
WPF實(shí)現(xiàn)雷達(dá)圖(仿英雄聯(lián)盟)的示例代碼
這篇文章主要介紹了如何利用WPF實(shí)現(xiàn)雷達(dá)圖(仿英雄聯(lián)盟)的繪制,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)或工作有一定幫助,需要的可以參考一下2022-07-07C#的Socket實(shí)現(xiàn)UDP協(xié)議通信示例代碼
本篇文章主要介紹了C#的Socket實(shí)現(xiàn)UDP協(xié)議通信示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01C#批量刪除Excel重復(fù)項(xiàng)的實(shí)現(xiàn)方法
當(dāng)從不同來(lái)源導(dǎo)入Excel數(shù)據(jù)時(shí),可能存在重復(fù)的記錄,為了確保數(shù)據(jù)的準(zhǔn)確性,通常需要?jiǎng)h除這些重復(fù)的行,本文將提供一個(gè)使用C# 快速查找并刪除Excel重復(fù)項(xiàng)的免費(fèi)解決方案,需要的朋友可以參考下2024-04-04C# 創(chuàng)建控制臺(tái)應(yīng)用程序
這篇文章主要介紹了C# 創(chuàng)建控制臺(tái)應(yīng)用程序,在學(xué)習(xí)C#語(yǔ)言的時(shí)候,首先要學(xué)習(xí)控制臺(tái)的應(yīng)用程序,這樣才能專注于語(yǔ)言的學(xué)習(xí),減少學(xué)習(xí)的梯度,也有利于輸出自己需要輸出的內(nèi)容,一定要先使用控制臺(tái)的應(yīng)用程序的方式,下面就和小編一起學(xué)習(xí)該內(nèi)容吧2021-10-10c# 免費(fèi)組件html轉(zhuǎn)pdf的實(shí)現(xiàn)過(guò)程
這篇文章主要介紹了c# 免費(fèi)組件html轉(zhuǎn)pdf的實(shí)現(xiàn)過(guò)程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06