基础知识
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();
}
}
-----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。
文章评论