.net与Java互通的RSA公钥加密公钥解密(X509格式证书),支持分段加密

发布于 2023-03-30  594 次阅读


介绍

有朋友叫帮忙做有一个RSA加解密的帮助类,后面发现是与JAVA做交互,对方提供了公钥cer文件。由于C#默认RSA只支持公钥加密,私钥解密。而现在需求正好相反,只能使用公钥加密,公钥解密,而且C#与Java并不互通,所以采用第三方类库 BouncyCastle来进行实现。

安装

通过VS自带NuGet搜索BouncyCastle即可安装

加密过程

加密流程为

1.将待加密字符串通过 UTF-8 字符编码转换为二进制数组;
2.将输入二进制数据按 117 字节分组(最后一组大小可小于 117 字节) ;
3.将各分组分别进行 RSA 加密操作后,将加密结果(二进制数组)顺次拼接在一起;
4.对加密后得到的二进制数组进行Base64编码处理后得到加密结果

1.从文件中获取公钥

/// <summary>
/// 从文件中获取公钥
/// </summary>
/// <param name="_data"></param>
/// <returns></returns>
public static PublicKey GetPublicKey()
{
    try
    {
        X509Certificate2 x5092 = new X509Certificate2(CER_PATH);
        return x5092.PublicKey;
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

2.对数据加密过程

/// <summary>
/// 对数据进行加密
/// </summary>
/// <param name="key">加密密钥</param>
/// <param name="data">待加密数据</param>
/// <returns>加密结果</returns>
public static byte[] RsaEncrypt(PublicKey key, byte[] data)
{
    // -- 初始化RSA加密算法
    RSACryptoServiceProvider provider = new RSACryptoServiceProvider();
    provider.ImportParameters(((RSA)key.Key).ExportParameters(false));

    using (MemoryStream bout = new MemoryStream())
    {
        for (int i = 0; i < data.Length; i += MAX_ENCRYPT_BLOCK)
        { // 由于RSA加密一次最多只能从117字节到128字节,需要多次进行
            // 当前循环的加密块大小
            int encryptBlockSize = ((i + MAX_ENCRYPT_BLOCK) <= data.Length) ? MAX_ENCRYPT_BLOCK : (data.Length - i); // 对最后一块数据的逻辑处理                                                                                                         // 加密数据块
            byte[] chunk = new byte[encryptBlockSize];
            Array.Copy(data, i, chunk, 0, encryptBlockSize);
            byte[] encryptedChunk = provider.Encrypt(chunk, false);
            bout.Write(encryptedChunk, 0, encryptedChunk.Length);
        }
        return bout.ToArray();
    }
}

解密过程

解密流程为

1.将加密字符串通过Base64解码为二进制数组
2.将输入二进制数据按 128 字节分组(最后一组大小可小于 128 字节) ;
3.将各分组分别进行 RSA 解密操作后,将解密结果(二进制数组)顺次拼接在一起;
4.通过 UTF-8 字符编码将最终解密结果的二进制数组转换为字符串。

1.从文件中获取公钥 xml格式

/// <summary>
/// 从证书中获取公钥
/// </summary>
/// <param name="cerPath"></param>
/// <returns></returns>
private string GetPublicKeyFromCer(string cerPath)
{
    RSACryptoServiceProvider pubkey = (RSACryptoServiceProvider)GetPublicKey().Key;
    return pubkey.ToXmlString(false);
}

2.将C#格式公钥转成Java格式公钥

/// <summary>
/// 将C#格式公钥转成Java格式公钥
/// </summary>
/// <param name="publicKey"></param>
/// <returns></returns>
public RsaKeyParameters RSAPublicKeyDotNet2Java(string publicKey)
{
    XmlDocument doc = new XmlDocument();
    doc.LoadXml(publicKey);
    BigInteger m = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Modulus")[0].InnerText));
    BigInteger p = new BigInteger(1, Convert.FromBase64String(doc.DocumentElement.GetElementsByTagName("Exponent")[0].InnerText));
    RsaKeyParameters pub = new RsaKeyParameters(false, m, p);
    return pub;
}

3.解密数据

/// <summary>
/// 密文解密
/// </summary>
/// <param name="xmlPublicKey"></param>
/// <param name="strEncryptString"></param>
/// <returns></returns>
public string RSADecryptByPublicKey(string xmlPublicKey, string strEncryptString)
{
    //得到公钥
    RsaKeyParameters keyParams = RSAPublicKeyDotNet2Java(xmlPublicKey);
    //参数与Java中加密解密的参数一致
    IBufferedCipher c = CipherUtilities.GetCipher("RSA/ECB/PKCS1Padding");
    //第一个参数 true-加密,false-解密;第二个参数表示密钥
    c.Init(false, keyParams);
    //对密文进行base64解码
    byte[] dataFromEncrypt = Convert.FromBase64String(strEncryptString);
    //解密
    string result = "";
    for (int i = 0; i < dataFromEncrypt.Length; i += MAX_DECRYPT_BLOCK)
    {
        int encryptBlockSize = ((i + MAX_DECRYPT_BLOCK) <= dataFromEncrypt.Length) ? MAX_DECRYPT_BLOCK : (dataFromEncrypt.Length - i); // 对最后一块数据的逻辑处理
        byte[] chunk = new byte[encryptBlockSize];
        Array.Copy(dataFromEncrypt, i, chunk, 0, encryptBlockSize);
        byte[] encryptedChunk = c.DoFinal(chunk, 0, encryptBlockSize);
        result += Encoding.UTF8.GetString(encryptedChunk);
    }
    return result;
}

其他

MAX_ENCRYPT_BLOCK 定义最大加密长度 为117
MAX_DECRYPT_BLOCK 定义最大解密长度 为128
这里还要注意base64的编码格式,如果编码格式不对,会报Unknown block type错误

啊~~~~~~~~~
最后更新于 2023-03-30