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内部使用。

  • 安全注意:处理私钥时应避免泄漏,操作完成后应清理内存,生产环境尽量使用安全的密钥存储机制。

 

posted @ 2025-07-21 14:28  曲琦  阅读(145)  评论(0)    收藏  举报