C# RSA 非对称加密算法

2023年4月25日 2014点热度 0人点赞 0条评论
内容目录

基础知识

C# 中又对称加密和非对称加密,其中 RSA 是最常用的非对称加密。

由于 C# 中的加密库,在不同 .NET 版本中改动比较大,因此这里按 .NET 7 以后的版本来说。

首先 RSA 有以下继承关系:

Object -> AsymmetricAlgorithm -> RSA -> RSACryptoServiceProvider

AsymmetricAlgorithm 是非对称加密统一抽象接口,SymmetricAlgorithm 则是对称加密抽象接口。

非对称加密有公钥私钥组成,其中私钥主要用于解密、公钥用于加密

RSA 是抽象类,是所有 RSA 算法的统一抽象,而 RSACryptoServiceProvider 类是 RSA 的一个实现。

要创建一个 RSA 类,可以使用 :

RSA.Create()

或者直接:

new RSACryptoServiceProvider()

直接使用 RSACryptoServiceProvider,可以参考以下示例:

void Main()
{
    var keySize = 2048;

    var rsaCryptoServiceProvider = new RSACryptoServiceProvider(keySize);

    // 打印私钥、公钥
    Convert.ToBase64String(rsaCryptoServiceProvider.ExportPkcs8PrivateKey()).Dump();
    Convert.ToBase64String(rsaCryptoServiceProvider.ExportRSAPublicKey()).Dump();

    var parameters = rsaCryptoServiceProvider.ExportParameters(true);
    // 加密后的字符串
    var cipherText = Encrypt("hello world", parameters);

    // 解密后的字符串
    var plainText = Decrypt(cipherText, parameters);
    Console.WriteLine(plainText);

}

// 加密
public string Encrypt(string data, RSAParameters key)
{

    using (var rsa = new RSACryptoServiceProvider())
    {
        rsa.ImportParameters(key);
        var byteData = Encoding.UTF8.GetBytes(data);
        var encryptData = rsa.Encrypt(byteData, false);
        return Convert.ToBase64String(encryptData);
    }
}

// 解密
public string Decrypt(string cipherText, RSAParameters key)
{

    using (var rsa = new RSACryptoServiceProvider())
    {
        var cipherByteData = Convert.FromBase64String(cipherText);
        rsa.ImportParameters(key);

        var encryptData = rsa.Decrypt(cipherByteData, false);
        return Encoding.UTF8.GetString(encryptData);
    }
}

使用 RSA 类

对于 RSA 公钥私钥的加载,我们可以分为两类,一种是 PKCS1、一种是 PCKS8,对于 PCKS1,可以直接导出导入,对于 PCKS8 则需要麻烦一些。

这里按使用 RSA 来设计帮助类,主要针对 PCKS8,示例代码如下:

/// <summary>
/// Rsa 加解密.
/// </summary>
public static class RsaHelper
{
    /// <summary>
    /// 导出 pck 8 公钥.
    /// </summary>
    /// <param name="rsa"></param>
    /// <returns></returns>
    public static string ExportPublichKeyPck8(this RSA rsa)
    {
        StringBuilder builder = new StringBuilder();
        builder.AppendLine("-----BEGIN PUBLIC KEY-----");
        builder.AppendLine(Convert.ToBase64String(rsa.ExportSubjectPublicKeyInfo(), Base64FormattingOptions.InsertLineBreaks));
        builder.AppendLine("-----END PUBLIC KEY-----");
        return builder.ToString();
    }

    public static void ImportPublichKeyPck8(this RSA rsa, string publicKey)
    {
        publicKey = publicKey
        .Replace("-----BEGIN RSA PUBLIC KEY-----", "")
        .Replace("-----END RSA PUBLIC KEY-----", "")
        .Replace("-----BEGIN PUBLIC KEY-----", "")
        .Replace("-----END PUBLIC KEY-----", "")
            .Replace("\r", "")
            .Replace("\n", "");

        rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKey), out var bytesRead);
    }

    /// <summary>
    /// 加密信息.
    /// </summary>
    /// <param name="rsa"></param>
    /// <param name="message"></param>
    /// <param name="padding">如 <see cref="RSAEncryptionPadding.OaepSHA256"/></param>
    /// <returns></returns>
    public static string Encrypt(this RSA rsa, string message, RSAEncryptionPadding padding)
    {
        var encryptData = rsa.Encrypt(Encoding.UTF8.GetBytes(message), padding);
        return Convert.ToBase64String(encryptData);
    }

    /// <summary>
    /// 解密
    /// </summary>
    /// <param name="rsa"></param>
    /// <param name="message"></param>
    /// <param name="padding"></param>
    /// <returns></returns>
    public static string Decrypt(this RSA rsa, string message, RSAEncryptionPadding padding)
    {
        var cipherByteData = Convert.FromBase64String(message);

        var encryptData = rsa.Decrypt(cipherByteData, padding);
        return Encoding.UTF8.GetString(encryptData);
    }
}

我们可以使用以下代码将 RSA 导出 PCKS8 格式的私钥和公钥,然后分别存储到文件中:

void Main()
{
    using (RSA rsa = RSA.Create())
    {
        rsa.ExportPkcs8PrivateKeyPem().Dump();

        string publicKeyPem = "-----BEGIN PUBLIC KEY-----\n" +
                      Convert.ToBase64String(rsa.ExportSubjectPublicKeyInfo(), Base64FormattingOptions.InsertLineBreaks) +
                      "\n-----END PUBLIC KEY-----";
                      publicKeyPem.Dump();
    }
}

file

-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDS+kP8X+q9qJdu
Rh3sft2WlxZZee4IIA+JXQKic8HSNEOTPB8ZFu8mjnxy16gebQDyOuMF3M00+wip
LLNSfYqQU3yLe/EcKgD9DqBps5CvSmv3PgyKrYgJNlXVcbUq9zGhfZ2dEJTWALhs
tHfMH7+GZEKtcah/sEm6m+Us4W49sIjjeAWrRQl9Fq8+288uoIg4tpYNBGRVi90R
uPnf8a3manbEPHwlPX9rIeskkXRpwoftfHegeK/1EReyuE86co17SJePzwHxiHEv
Ro4UoOkij49IIhiVbmksBlzN4XEgl3YXj0Gtshc5X8Ce6SccNkFoi3KRax/KWmuM
wG8dpT9NAgMBAAECggEBAJfsofJlu6sxcUJ2eWvo+3ZKfEyYceEl/SokcRY8l1Dg
U9z9iUNO8Y3pQxKL20N1qR3Fa9+33YmOT/FLACKhxpshk2j6KmjmkmmoE7mqFcE0
rUSQSQW/6lr/5pVaWWSENxgcVdhZrWPhhuy4lB/IqOmE30L4uqagcqdPRZupBfKj
EjMQzNhV9OwhjhPPPDq9Fek4tdjdPm8wPuQuiDmOmIsB29bRSpwrgzp31kND4jw0
4DCb8TaoJe90F/f1JX5zAHeeToBRHsJFuO2ICLMtNmJWTD1uZIup7vQAivW0xf9F
/oQE+cXsjc5GZmQaGUr03LtWKJBbD8Hc+MsOZynMk50CgYEA1WdmnZ9/7jn1sW3J
h80b10fQxM0tOdP0NySXdP+LShhqCvELzQZgg3aGww5KVR9Zax+LVmsM4GM90f6c
WrAspoNjOQXZxZeOmg71sP6tOKKaSaf0oqUxT3kXeFL5/ubo/hQ6cVyP7qgvojPM
Vc63YIdmLWwFl1aXNxrHbPUzmxcCgYEA/RbiWV3PyTc2yT7cvQzQAd4yLDiaijK0
gvwLzWx9UDrOBGs5DULF4QMOgsCK0rerwvi4FLOuPIdqWqW7ysX+lhn5X9OZGYHv
lNp0GaIcid/KJNBs5TNi6VWMEY0a1v8ic68lMsRvf1xQXTMBA8RQUkR1YrT90LlB
95vhrobgJzsCgYEAzSpm9n1kwgTJGHbjfQMNlDCAHuTfaSxEK0urrRkNsgO8154c
6VULLvih4R95CVNlZV7jWAb9TzE6OwzdBzc/BitlFmpwjs4BlE1zmmGO6dcyHEQ0
JrZIrQ5PKSglHxKix7ts4JXL7veVLA0+kvR1SoGCE4M58OCX6qt9NVyb66sCgYEA
ozrLKZATn1b5ArqEa3mD/nBsM5EeOtuRCJm+kvLRr5j9nmP5G9BhB0qNZU8BOf4z
zT/UmaV5TpiXw3b4s0MXe3+tElzKdWUUPBDYqF+hwFqRaUTztq95r7v45qj3Eori
kXH4r9F5h87mFfX7RY6rryNwAgVxXdjd7vCekY1zrFkCgYAYFqf1ywcnAN8+Sfmj
wRavUjG0T4SSaDjwDpP1dv1B6+WdX32S+2sQHyHUE41GCnhJxFghJqY+2HMrxUz+
tzgmicLGpTrLmXFomz0XhjrJyFdvh0Y0gQChK+14hr9ZluO/a3cgARHnHY20njkl
6BA30QaaarVkSR/NMa1gDEU86Q==
-----END PRIVATE KEY-----
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0vpD/F/qvaiXbkYd7H7dlpcWWXnuCCAP
iV0ConPB0jRDkzwfGRbvJo58cteoHm0A8jrjBdzNNPsIqSyzUn2KkFN8i3vxHCoA/Q6gabOQr0pr
9z4Miq2ICTZV1XG1KvcxoX2dnRCU1gC4bLR3zB+/hmRCrXGof7BJupvlLOFuPbCI43gFq0UJfRav
PtvPLqCIOLaWDQRkVYvdEbj53/Gt5mp2xDx8JT1/ayHrJJF0acKH7Xx3oHiv9REXsrhPOnKNe0iX
j88B8YhxL0aOFKDpIo+PSCIYlW5pLAZczeFxIJd2F49BrbIXOV/AnuknHDZBaItykWsfylprjMBv
HaU/TQIDAQAB
-----END PUBLIC KEY-----

可以这样导出导入 PCKS8 格式的私钥公钥:

var privateKey = RSA.Create();
privateKey.ImportFromPem(File.ReadAllText("./private.key"));
var publicKey = RSA.Create();
publicKey.ImportPublichKeyPck8(File.ReadAllText("./public.key"));

可以打开这里做检查,判断导出的私钥公钥对不对: https://www.bejson.com/enc/rsa/

使用 RSACryptoServiceProvider

继续使用前面生成的两个公钥私钥文件。

编写导入方法:

/// <summary>
/// 导入公钥
/// </summary>
/// <param name="rsa"></param>
/// <param name="publicKey"></param>
/// <returns></returns>
private static RSACryptoServiceProvider ImportPublicKeyFromPEM(RSACryptoServiceProvider rsa, string publicKey)
{
    publicKey = publicKey
    .Replace("-----BEGIN RSA PUBLIC KEY-----", "")
    .Replace("-----END RSA PUBLIC KEY-----", "")
    .Replace("-----BEGIN PUBLIC KEY-----", "")
    .Replace("-----END PUBLIC KEY-----", "")
        .Replace("\r", "")
        .Replace("\n", "");
    rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKey), out var bytesRead);
    return rsa;
}

导入 PCKS8 格式的公钥:

var publicKey = new RSACryptoServiceProvider();
RsaHelper.ImportPublicKeyFromPEM(publicKey, File.ReadAllText("./public.key"));

由于 RSACryptoServiceProvider 实现了 RSA,所以使用方法上也是区别不大。

不过 RSA.Create() 默认是返回 RSAWrapper、RSACng。

痴者工良

高级程序员劝退师

文章评论