c# RSA非对称加解密及XML&PEM格式互换方案
最近因考虑接口安全问题,有实现给WEBAPI实现统一的参数鉴权功能,以防止请求参数被篡改或重复执行,参数鉴权方法基本与常见的鉴权思路相同,采用(timestamp+sign),而我为了防止timestamp被更改,sign算法(timestamp+相关参数排序、格式化后拼接再MD5)也因为在前端是不安全的,故对timestamp采取使用非对称加解密,以尽可能的保证生成的sign不易被破解或替换;
RSA加解密(即:非对称加解密)
生成公钥、私钥对方法(C#),生成出来后默认都是XML格式:
publicstaticTupleGeneratePublicAndPrivateKeyPair() { using(RSACryptoServiceProviderrsa=newRSACryptoServiceProvider()) { stringpublicKey=rsa.ToXmlString(false);//公钥 stringprivateKey=rsa.ToXmlString(true);//私钥 returnTuple.Create(publicKey,privateKey); } }
使用公钥加密:(支持分段加密,普通单次加密可能会因为内容过长而报错)
publicstaticstringRSAEncrypt(stringpublicKey,stringrawInput)
{
if(string.IsNullOrEmpty(rawInput))
{
returnstring.Empty;
}
if(string.IsNullOrWhiteSpace(publicKey))
{
thrownewArgumentException("InvalidPublicKey");
}
using(varrsaProvider=newRSACryptoServiceProvider())
{
varinputBytes=Encoding.UTF8.GetBytes(rawInput);//有含义的字符串转化为字节流
rsaProvider.FromXmlString(publicKey);//载入公钥
intbufferSize=(rsaProvider.KeySize/8)-11;//单块最大长度
varbuffer=newbyte[bufferSize];
using(MemoryStreaminputStream=newMemoryStream(inputBytes),
outputStream=newMemoryStream())
{
while(true)
{//分段加密
intreadSize=inputStream.Read(buffer,0,bufferSize);
if(readSize<=0)
{
break;
}
vartemp=newbyte[readSize];
Array.Copy(buffer,0,temp,0,readSize);
varencryptedBytes=rsaProvider.Encrypt(temp,false);
outputStream.Write(encryptedBytes,0,encryptedBytes.Length);
}
returnConvert.ToBase64String(outputStream.ToArray());//转化为字节流方便传输
}
}
}
使用私钥解密:(支持分段解密,普通单次解密可能会因为密文过长而报错)
publicstaticstringRSADecrypt(stringprivateKey,stringencryptedInput)
{
if(string.IsNullOrEmpty(encryptedInput))
{
returnstring.Empty;
}
if(string.IsNullOrWhiteSpace(privateKey))
{
thrownewArgumentException("InvalidPrivateKey");
}
using(varrsaProvider=newRSACryptoServiceProvider())
{
varinputBytes=Convert.FromBase64String(encryptedInput);
rsaProvider.FromXmlString(privateKey);
intbufferSize=rsaProvider.KeySize/8;
varbuffer=newbyte[bufferSize];
using(MemoryStreaminputStream=newMemoryStream(inputBytes),
outputStream=newMemoryStream())
{
while(true)
{
intreadSize=inputStream.Read(buffer,0,bufferSize);
if(readSize<=0)
{
break;
}
vartemp=newbyte[readSize];
Array.Copy(buffer,0,temp,0,readSize);
varrawBytes=rsaProvider.Decrypt(temp,false);
outputStream.Write(rawBytes,0,rawBytes.Length);
}
returnEncoding.UTF8.GetString(outputStream.ToArray());
}
}
}
如果都是C#项目可能如上两个方法就可以了,但如果需要与WEB前端、JAVA等其它编程语言协同交互处理时(比如:WEB前端用公钥加密,后端C#私钥解密),则可能因为公钥与私钥的格式不相同而导致无法正常的进行对接【前端、JAVA等语言使用的是PEM格式的,而C#使用的是XML格式】,网上查XML转PEM格式方案时,都是复制自:https://www.cnblogs.com/micenote/p/7862989.html这篇文章,但其实这篇文章也只是写了私钥XML转PEM格式,并没有说明公钥XML如何转PEM格式,而且只写了支持从文件中获取内容再转换,方案不全,但是给了我思路,我经过各种验证,最终实现了比较友好的PEM与XML格式的相互转换方式,且经过单元测试验证通过,在此分享给大家。
如下是完整的XML与PEM格式转换器类代码;(注意需引入BouncyCastlenuget包)
usingOrg.BouncyCastle.Crypto;
usingOrg.BouncyCastle.Crypto.Parameters;
usingOrg.BouncyCastle.Math;
usingSystem;
usingSystem.Collections.Generic;
usingSystem.IO;
usingSystem.Linq;
usingSystem.Security.Cryptography;
usingSystem.Text;
usingSystem.Threading.Tasks;
namespaceZuowj.Common
{
///
///RSA公钥、私钥对格式(XML与PEM)转换器
///author:zuowenjun
///date:2020-12-29
///
publicstaticclassRsaKeysFormatConverter
{
///
///XML公钥转成Pem公钥
///
///
///
publicstaticstringXmlPublicKeyToPem(stringxmlPublicKey)
{
RSAParametersrsaParam;
using(RSACryptoServiceProviderrsa=newRSACryptoServiceProvider())
{
rsa.FromXmlString(xmlPublicKey);
rsaParam=rsa.ExportParameters(false);
}
RsaKeyParametersparam=newRsaKeyParameters(false,newBigInteger(1,rsaParam.Modulus),newBigInteger(1,rsaParam.Exponent));
stringpemPublicKeyStr=null;
using(varms=newMemoryStream())
{
using(varsw=newStreamWriter(ms))
{
varpemWriter=newOrg.BouncyCastle.OpenSsl.PemWriter(sw);
pemWriter.WriteObject(param);
sw.Flush();
byte[]buffer=newbyte[ms.Length];
ms.Position=0;
ms.Read(buffer,0,(int)ms.Length);
pemPublicKeyStr=Encoding.UTF8.GetString(buffer);
}
}
returnpemPublicKeyStr;
}
///
///Pem公钥转成XML公钥
///
///
///
publicstaticstringPemPublicKeyToXml(stringpemPublicKeyStr)
{
RsaKeyParameterspemPublicKey;
using(varms=newMemoryStream(Encoding.UTF8.GetBytes(pemPublicKeyStr)))
{
using(varsr=newStreamReader(ms))
{
varpemReader=newOrg.BouncyCastle.OpenSsl.PemReader(sr);
pemPublicKey=(RsaKeyParameters)pemReader.ReadObject();
}
}
varp=newRSAParameters
{
Modulus=pemPublicKey.Modulus.ToByteArrayUnsigned(),
Exponent=pemPublicKey.Exponent.ToByteArrayUnsigned()
};
stringxmlPublicKeyStr;
using(varrsa=newRSACryptoServiceProvider())
{
rsa.ImportParameters(p);
xmlPublicKeyStr=rsa.ToXmlString(false);
}
returnxmlPublicKeyStr;
}
///
///XML私钥转成PEM私钥
///
///
///
publicstaticstringXmlPrivateKeyToPem(stringxmlPrivateKey)
{
RSAParametersrsaParam;
using(RSACryptoServiceProviderrsa=newRSACryptoServiceProvider())
{
rsa.FromXmlString(xmlPrivateKey);
rsaParam=rsa.ExportParameters(true);
}
varparam=newRsaPrivateCrtKeyParameters(
newBigInteger(1,rsaParam.Modulus),newBigInteger(1,rsaParam.Exponent),newBigInteger(1,rsaParam.D),
newBigInteger(1,rsaParam.P),newBigInteger(1,rsaParam.Q),newBigInteger(1,rsaParam.DP),newBigInteger(1,rsaParam.DQ),
newBigInteger(1,rsaParam.InverseQ));
stringpemPrivateKeyStr=null;
using(varms=newMemoryStream())
{
using(varsw=newStreamWriter(ms))
{
varpemWriter=newOrg.BouncyCastle.OpenSsl.PemWriter(sw);
pemWriter.WriteObject(param);
sw.Flush();
byte[]buffer=newbyte[ms.Length];
ms.Position=0;
ms.Read(buffer,0,(int)ms.Length);
pemPrivateKeyStr=Encoding.UTF8.GetString(buffer);
}
}
returnpemPrivateKeyStr;
}
///
///Pem私钥转成XML私钥
///
///
///
publicstaticstringPemPrivateKeyToXml(stringpemPrivateKeyStr)
{
RsaPrivateCrtKeyParameterspemPrivateKey;
using(varms=newMemoryStream(Encoding.UTF8.GetBytes(pemPrivateKeyStr)))
{
using(varsr=newStreamReader(ms))
{
varpemReader=newOrg.BouncyCastle.OpenSsl.PemReader(sr);
varkeyPair=(AsymmetricCipherKeyPair)pemReader.ReadObject();
pemPrivateKey=(RsaPrivateCrtKeyParameters)keyPair.Private;
}
}
varp=newRSAParameters
{
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(),
};
stringxmlPrivateKeyStr;
using(varrsa=newRSACryptoServiceProvider())
{
rsa.ImportParameters(p);
xmlPrivateKeyStr=rsa.ToXmlString(true);
}
returnxmlPrivateKeyStr;
}
}
}
如下是单元测试代码:
//公钥(XML、PEM格式互)测试 stringsrcPublicKey=“具体的XMLPublicKey”; stringpemPublicKeyStr=RsaKeysFormatConverter.XmlPublicKeyToPem(publicKey); stringxmlPublicKeyStr=RsaKeysFormatConverter.PemPublicKeyToXml(pemPublicKeyStr); Assert.AreEqual(srcPublicKey,xmlPublicKeyStr);
//私钥(XML、PEM格式互)测试 stringsrcPrivateKey=“具体的XMLPrivateKey”; stringpemPrivateKeyStr=RsaKeysFormatConverter.XmlPrivateKeyToPem(srcPrivateKey); stringxmlPrivateKeyStr=RsaKeysFormatConverter.PemPrivateKeyToXml(pemPrivateKeyStr); Assert.AreEqual(privateKey,xmlPrivateKeyStr)
当然也可以不用这么费劲自己实现格式转换,可以使用在线网站直接转换:https://the-x.cn/certificate/XmlToPem.aspx,另外也有一篇文章实现了类似的功能,但生成的PEM格式并非完整的格式,缺少注释头尾:https://www.cnblogs.com/datous/p/RSAKeyConvert.html
以上就是c#RSA非对称加解密及XML&PEM格式互换方案的详细内容,更多关于c#RSA非对称加解密的资料请关注毛票票其它相关文章!