C#使用BouncyCastle实现RSA私钥格式转换及加解密操作
RSA加密作为非对称加密的经典算法,广泛应用于数据安全和身份验证中。RSA密钥存在多种格式,常见的有PKCS#1和PKCS#8,且私钥在实际应用中还可能需要转换为XML格式,方便与.NET自带的RSA
类兼容
一、 PKCS#8 PEM 转 XML
public static string ConvertPkcs8PemToXml(string pemPrivateKey) { using (TextReader reader = new StringReader(pemPrivateKey)) { var pemReader = new PemReader(reader); var keyObject = pemReader.ReadObject(); RsaPrivateCrtKeyParameters rsaParams; if (keyObject is AsymmetricCipherKeyPair keyPair) rsaParams = (RsaPrivateCrtKeyParameters)keyPair.Private; else if (keyObject is RsaPrivateCrtKeyParameters keyParameters) rsaParams = keyParameters; else if (keyObject is PrivateKeyInfo privateKeyInfo) rsaParams = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(privateKeyInfo); else throw new ArgumentException("不支持的私钥格式"); var sb = new StringBuilder(); sb.Append("<RSAKeyValue>"); sb.AppendFormat("<Modulus>{0}</Modulus>", Convert.ToBase64String(rsaParams.Modulus.ToByteArrayUnsigned())); sb.AppendFormat("<Exponent>{0}</Exponent>", Convert.ToBase64String(rsaParams.PublicExponent.ToByteArrayUnsigned())); sb.AppendFormat("<P>{0}</P>", Convert.ToBase64String(rsaParams.P.ToByteArrayUnsigned())); sb.AppendFormat("<Q>{0}</Q>", Convert.ToBase64String(rsaParams.Q.ToByteArrayUnsigned())); sb.AppendFormat("<DP>{0}</DP>", Convert.ToBase64String(rsaParams.DP.ToByteArrayUnsigned())); sb.AppendFormat("<DQ>{0}</DQ>", Convert.ToBase64String(rsaParams.DQ.ToByteArrayUnsigned())); sb.AppendFormat("<InverseQ>{0}</InverseQ>", Convert.ToBase64String(rsaParams.QInv.ToByteArrayUnsigned())); sb.AppendFormat("<D>{0}</D>", Convert.ToBase64String(rsaParams.Exponent.ToByteArrayUnsigned())); sb.Append("</RSAKeyValue>"); return sb.ToString(); } }
作用:将私钥的各个关键参数编码成符合.NET RSA.FromXmlString
格式的XML字符串。
二、PKCS#1 PEM 转 XML
/// <summary> /// 从PEM格式私钥字符串转换为XML格式私钥字符串 /// </summary> /// <param name="pemPrivateKey">PEM格式的私钥字符串</param> /// <returns>XML格式私钥字符串</returns> public static string ConvertPemToXml(string pemPrivateKey) { // 读PEM字符串,创建TextReader using (TextReader reader = new StringReader(pemPrivateKey)) { // 用PemReader读取私钥 var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(reader); var keyPair = pemReader.ReadObject() as RsaPrivateCrtKeyParameters; if (keyPair == null) { throw new ArgumentException("PEM格式私钥内容不正确或不是RSA私钥"); } // 构造XML格式字符串 var sb = new StringBuilder(); sb.Append("<RSAKeyValue>"); sb.AppendFormat("<Modulus>{0}</Modulus>", Convert.ToBase64String(keyPair.Modulus.ToByteArrayUnsigned())); sb.AppendFormat("<Exponent>{0}</Exponent>", Convert.ToBase64String(keyPair.PublicExponent.ToByteArrayUnsigned())); sb.AppendFormat("<P>{0}</P>", Convert.ToBase64String(keyPair.P.ToByteArrayUnsigned())); sb.AppendFormat("<Q>{0}</Q>", Convert.ToBase64String(keyPair.Q.ToByteArrayUnsigned())); sb.AppendFormat("<DP>{0}</DP>", Convert.ToBase64String(keyPair.DP.ToByteArrayUnsigned())); sb.AppendFormat("<DQ>{0}</DQ>", Convert.ToBase64String(keyPair.DQ.ToByteArrayUnsigned())); sb.AppendFormat("<InverseQ>{0}</InverseQ>", Convert.ToBase64String(keyPair.QInv.ToByteArrayUnsigned())); sb.AppendFormat("<D>{0}</D>", Convert.ToBase64String(keyPair.Exponent.ToByteArrayUnsigned())); sb.Append("</RSAKeyValue>"); return sb.ToString(); } }
三、PKCS#8 PEM 转 PKCS#1 PEM
public static string ConvertPkcs8ToPkcs1(string pkcs8Pem) { using (TextReader reader = new StringReader(pkcs8Pem)) { var pemReader = new PemReader(reader); var keyObject = pemReader.ReadObject(); RsaPrivateCrtKeyParameters rsaPrivateKey; if (keyObject is AsymmetricCipherKeyPair keyPair) rsaPrivateKey = (RsaPrivateCrtKeyParameters)keyPair.Private; else if (keyObject is RsaPrivateCrtKeyParameters rsaParams) rsaPrivateKey = rsaParams; else if (keyObject is PrivateKeyInfo privateKeyInfo) rsaPrivateKey = (RsaPrivateCrtKeyParameters)PrivateKeyFactory.CreateKey(privateKeyInfo); else throw new ArgumentException("无法识别的私钥格式"); var rsaPrivateKeyStructure = new RsaPrivateKeyStructure( rsaPrivateKey.Modulus, rsaPrivateKey.PublicExponent, rsaPrivateKey.Exponent, rsaPrivateKey.P, rsaPrivateKey.Q, rsaPrivateKey.DP, rsaPrivateKey.DQ, rsaPrivateKey.QInv); var asn1Object = rsaPrivateKeyStructure.ToAsn1Object(); byte[] privateKeyDer = asn1Object.GetEncoded(); StringWriter stringWriter = new StringWriter(); var pemWriter = new PemWriter(stringWriter); pemWriter.WriteObject(new PemObject("RSA PRIVATE KEY", privateKeyDer)); pemWriter.Writer.Flush(); return stringWriter.ToString(); } }
四、RSA解密
public static string Decrypt(string base64CipherText, string privateKeyXml) { byte[] cipherBytes = Convert.FromBase64String(base64CipherText); using (var rsa = RSA.Create()) { rsa.FromXmlString(privateKeyXml); byte[] plainBytes = rsa.Decrypt(cipherBytes, RSAEncryptionPadding.Pkcs1); return Encoding.UTF8.GetString(plainBytes); } }
注意:FromXmlString
是.NET自带RSA接口的扩展方法,接收XML格式私钥。这里假设密文是Base64编码,且加密时使用PKCS#1填充。
五、RSA加密
public static byte[] Encrypt(string publicKeyPem, byte[] dataToEncrypt) { var rsaParameters = GetRSAParametersFromPem(publicKeyPem); using (RSA rsa = RSA.Create()) { rsa.ImportParameters(rsaParameters); return rsa.Encrypt(dataToEncrypt, RSAEncryptionPadding.Pkcs1); } }
实现细节:先用BouncyCastle解析PEM公钥,转换为.NET支持的RSAParameters
结构,然后利用.NET内置RSA
加密。
总结与使用建议
-
私钥格式转换非常实用,特别是在需要与不同平台或库交互时,避免私钥兼容问题。
-
BouncyCastle提供强大的ASN.1和PEM解析能力,弥补了.NET在某些密钥格式解析上的不足。
-
XML格式私钥方便与.NET内置RSA类互操作,但注意这并非标准格式,仅限.NET内部使用。
-
安全注意:处理私钥时应避免泄漏,操作完成后应清理内存,生产环境尽量使用安全的密钥存储机制。