当 HTTP 请求协议中包含敏感数据时,出于防止数据有泄露风险的目的,往往要求对敏感数据进行加密后再发起请求。
本篇文章记录其中一种实现方式,即 RSA 加密。
关于其它实现方式,欢迎大家在评论区留言讨论。
RSA介绍
RSA一种非对称加密方式,包含公钥和私钥。
数据接收方需将公钥发给数据请求方,数据请求方使用公钥对数据进行加密后再传输,数据接收方在收到数据后使用私钥进行解密。
公钥只能用于加密,不能解密数据。私钥是不公开的,即使第三方拦截到请求数据也不能解密出原始数据。所以能很安全的保护数据。
PEM 格式的私钥和公钥
RSA 的公钥和私有有多种表示方式,常见是 PEM 和 DER。
此处介绍 PEM。公钥格式如下:
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPKlGv4g8dyCvF1X1JHvoXEHG9vNepEf
xhAc6804DwmfFmedE0OgXeFO1ccFcOBEwPeWW9oAoEAIvzIVMohjvFkCAwEAAQ==
-----END PUBLIC KEY-----
私钥格式如下:
-----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEA8qUa/iDx3IK8XVfU
ke+hcQcb2816kR/GEBzrzTgPCZ8WZ50TQ6Bd4U7VxwVw4ETA95Zb2gCgQAi/MhUy
iGO8WQIDAQABAkBa5Op1pBW2CutQ9CPW/zM0yfrvnct6LbLeOldCqutHKKxOVWTJ
MStzogSGOM5neX927Fnkkg1Y1Mp0EkbdmuRhAiEA/zebGX3Q0pIErjVgOD6tbshq
vRpuseRQuujzpCyGyuUCIQDzY6DBymuT2iEsDOMNCl6gUzXfWUZfcqiHVP18whPw
ZQIhAN8M/Ru5TxFJlpeehJqDhNYGiOr+t/H3Resk/xLj62ZVAiAA5R/lNopYu9ed
Ytq+5IfejQ/Ip3pjxXcrMDvSZqBOJQIhAIaT3ppTbm0zoZ79MZftlNhwA+MfoHWa
I8EiU9p6wgpo
-----END PRIVATE KEY-----
后端逻辑
后端将生成的公钥和私钥存储到数据库中,并提供接口用于获取公钥,此处主要介绍在 .NET8 中基于 PEM 格式的公钥和私钥实现 RSA 加解密。
创建 RSAExtension.cs 扩展类,如下:
using System.Text.RegularExpressions;
namespace System.Security.Cryptography;
/// <summary>
/// RSA 扩展类
/// </summary>
public static class RSAExtension
{
/// <summary>
/// 通过 PEM 格式的私钥字符串初始化 <see cref="RSA"/> 对象
/// </summary>
/// <param name="rsa"></param>
/// <param name="pemKey"></param>
public static void FromPrivatePemString(this RSA rsa, string pemKey)
{
var pattern = @"-----BEGIN PRIVATE KEY-----|-----END PRIVATE KEY-----|\r|\n";
pemKey = Regex.Replace(pemKey, pattern, string.Empty).Trim();
var bytes = Convert.FromBase64String(pemKey);
rsa.ImportPkcs8PrivateKey(bytes, out _);
}
/// <summary>
/// 通过 PEM 格式的公钥字符串初始化 <see cref="RSA"/> 对象
/// </summary>
/// <param name="rsa"></param>
/// <param name="pemKey"></param>
public static void FromPublicPemString(this RSA rsa, string pemKey)
{
var pattern = @"-----BEGIN PUBLIC KEY-----|-----END PUBLIC KEY-----|\r|\n";
pemKey = Regex.Replace(pemKey, pattern, string.Empty).Trim();
var bytes = Convert.FromBase64String(pemKey);
rsa.ImportSubjectPublicKeyInfo(bytes, out _);
}
}
创建 RSAStringExtensions.cs 扩展类,如下:
using System.Security.Cryptography;
namespace System;
/// <summary>
/// RSA 字符串扩展类
/// </summary>
public static class RSAStringExtensions
{
/// <summary>
/// RSA 解密,使用 PEM 格式的私钥
/// </summary>
/// <param name="s"></param>
/// <param name="pemPrivateKey"></param>
/// <returns></returns>
public static string RSADecryptFromPemString(this string s, string pemPrivateKey)
=> RSADecrypt(s, rsa => rsa.FromPrivatePemString(pemPrivateKey));
/// <summary>
/// RSA 解密
/// </summary>
/// <param name="s"></param>
/// <param name="rsaInitAction"></param>
/// <returns></returns>
public static string RSADecrypt(this string s, Action<RSA> rsaInitAction)
{
using var rsa = RSA.Create();
rsaInitAction(rsa);
var input = Convert.FromBase64String(s);
var bytes = rsa.Decrypt(input, RSAEncryptionPadding.Pkcs1);
return Encoding.UTF8.GetString(bytes);
}
/// <summary>
/// RSA 加密,使用 PEM 格式的私钥
/// </summary>
/// <param name="s"></param>
/// <param name="pemPrivateKey"></param>
/// <returns></returns>
public static string RSAEncryptFromPrivatePemString(this string s, string pemPrivateKey)
=> RSAEncrypt(s, rsa => rsa.FromPrivatePemString(pemPrivateKey));
/// <summary>
/// RSA 加密,使用 PEM 格式的公钥
/// </summary>
/// <param name="s"></param>
/// <param name="pemPublicKey"></param>
/// <returns></returns>
public static string RSAEncryptFromPublicPemString(this string s, string pemPublicKey)
=> RSAEncrypt(s, rsa => rsa.FromPublicPemString(pemPublicKey));
/// <summary>
/// RSA 加密
/// </summary>
/// <param name="s"></param>
/// <param name="rsaInitAction"></param>
/// <returns></returns>
public static string RSAEncrypt(this string s, Action<RSA> rsaInitAction)
{
using var rsa = RSA.Create();
rsaInitAction(rsa);
var input = Encoding.UTF8.GetBytes(s);
var bytes = rsa.Encrypt(input, RSAEncryptionPadding.Pkcs1);
return Convert.ToBase64String(bytes);
}
}
接下来对上述实现代码进行测试,编写如下测试代码:
public class RSATests
{
[Fact]
public void Test_RSAEncrypt_Decrypt()
{
string publicPemKey = @"-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoKjT23zzOPM+vuBulGY2
aAAunrL+HVEVJcHqjvujBNyINnMBNtfCiSFGtMb06j/dB+/x0xaOChLIu5n/oCco
8MCUfevvF0OQ3sER8ZcGX17mnDYAPa+LQT/TQwlvXq+ahPXSpvM+FG2ecSOhNbtI
W2XrtkEOLnsCi0YSzaFnVNSx4kK6fbE6/GCj4e7hPomfrFK5UQJK4MQDPPsFUtTr
H4zt7Cc5+QU5YOXiroZZhJF9/2c+Qc8pjtaC7GzjOvkEmjheOuWACZJhjfiYppjx
hmUEwQmj2FOle6nlXGw7IBNvyGm9BluLCHBizC+yYYrwUdp+CghSAYD6ZSeYVItG
RQIDAQAB
-----END PUBLIC KEY-----";
string privatePemKey = @"-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCgqNPbfPM48z6+
4G6UZjZoAC6esv4dURUlweqO+6ME3Ig2cwE218KJIUa0xvTqP90H7/HTFo4KEsi7
mf+gJyjwwJR96+8XQ5DewRHxlwZfXuacNgA9r4tBP9NDCW9er5qE9dKm8z4UbZ5x
I6E1u0hbZeu2QQ4uewKLRhLNoWdU1LHiQrp9sTr8YKPh7uE+iZ+sUrlRAkrgxAM8
+wVS1OsfjO3sJzn5BTlg5eKuhlmEkX3/Zz5BzymO1oLsbOM6+QSaOF465YAJkmGN
+JimmPGGZQTBCaPYU6V7qeVcbDsgE2/Iab0GW4sIcGLML7JhivBR2n4KCFIBgPpl
J5hUi0ZFAgMBAAECggEAG6RwrzcUchwIE7dxzz625bYCT3PknQ6yYMi4ntlU9yUq
mpO3BcShyA5dqAUDZrqOQStbpEIhNkJ9Hyoj/V4/gQzhf7j0bkBF0M3PVI3znr0p
WH9CE1r8zsE4jDQ3YUc9gtr435jCealStYy31BLhJDipEbanxf+MNIJeAdtfeAzK
pH4UisoG2JIbQ0l/X7P4v80VF+vDgAG1O6ZfJjZxGvD7Xg4l4hSf4+Bn3/vXcwC5
bx6UjC2AlBynThEh6t2XaS5ViW2Bdumm4KvsDRHBf4ie5yNVifVUBkN1LyFuQ8eq
VmEiiwa3VoeppY8LiLmdHZMHR+DkAJgoxkSh761lEQKBgQDTqCsdMXSjifZzcBYS
Iqe+AzKrlSZcNQQfYffBJ+z7tTwDnRVsPizpYQ9urNlvKfLalJfN5JrfgkqnOp58
1pbL1gqbW7b3XWqpt9Zg/4fFsDBT6AMTy5HKq+0qogpM6C6R0eFKpK7PZF7qatrW
IWfdAU071XypAi/3z1Mg0qshJwKBgQDCUYFjKH02fb6mA9scFyzKmXbCVjh7tSqo
QHWJFLYvf1ZPxKMa0wjxc8xg7VcFFDx11T33ESXVOkmrUhSB6Ibbtidu93NN0QuG
kDxDngkfF/8lo04EQqJ6vcSkgtY6vuNdsIdWC1/aICP97/tiqHRPiqsd0HdpZR0I
y9TTT8MoswKBgCI4emAJ8vXOBI69fJQKDlzmX3bmcoFOARCh4lDUh7haoQ1khGYv
10sUak5WnAEhIya8B58+atvgUPmgvKcqes8SGDzuIzLzY591HbApIGaSCf5G8Nhu
fW/rRDk0Yb31fDIPYQGUhr75V29HP4KHfCd7YmVeKv2YYPEMEECqTwjZAoGBAI7k
otIJFzK3+zdXxIt+m0iM26EDCFwoGCtL42KfoW2CJTVZc1upbn0n21Y3kfNBM0iv
O/bwXDLVvxDFtiDlni35YDwgqWDC5cfZTwemMiz0V4MU+Kz3V0YNQIp9UA45Czd9
NbhXevALDIyTKfgdyhIn9bdh8j3FxjkOsv2FPQ0zAoGBAKSS8JPrC78mgKPMrd7m
K4zFc+rVh6yLKUMJekRB8qz1KKPaTlH/pkIPyJSRAKsI+1dyGrk2Mwr8LHWyvsE0
7TA864n/x40d/otElOYOGmQxbKcIRuShcw+VvFScpmpxcupIWrI68/YotW5shl9k
NHcMl+TIlG+YVDeGqs1RXXFT
-----END PRIVATE KEY-----";
var expected = "123456";
var encrypedText = expected.RSAEncryptFromPublicPemString(publicPemKey);
var actual = encrypedText.RSADecryptFromPemString(privatePemKey);
Assert.Equal(expected, actual);
}
}
完结!
感谢阅读!
欢迎留言!