You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OnlineMsgServer/Core/RsaService.cs

175 lines
6.1 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System.Security.Cryptography;
using System.Text;
namespace OnlineMsgServer.Core
{
class RsaService
{
//用于服务端加密解密
private static readonly RSA _Rsa = RSA.Create();
private static readonly object _RsaLock = new();
//用于客户端加密
private static readonly RSA _PublicRsa = RSA.Create();
private static readonly object _PublicRsaLock = new();
/// <summary>
/// 用客户端公钥加密
/// </summary>
public static string EncryptForClient(string pkey, string msg)
{
lock (_PublicRsaLock)
{
_PublicRsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(pkey), out _);
// byte[] encrypt = _PublicRsa.Encrypt(Encoding.UTF8.GetBytes(msg), RSAEncryptionPadding.OaepSHA256);
// return Convert.ToBase64String(encrypt);
return RsaEncrypt(_PublicRsa, msg);
}
}
/// <summary>
/// 导入服务端私钥
/// </summary>
public static void LoadRsaPkey(SecurityConfig config)
{
lock (_RsaLock)
{
if (!string.IsNullOrWhiteSpace(config.ServerPrivateKeyBase64))
{
_Rsa.ImportPkcs8PrivateKey(Convert.FromBase64String(config.ServerPrivateKeyBase64), out _);
return;
}
if (!string.IsNullOrWhiteSpace(config.ServerPrivateKeyPath))
{
string pkey = File.ReadAllText(config.ServerPrivateKeyPath).Trim();
_Rsa.ImportPkcs8PrivateKey(Convert.FromBase64String(pkey), out _);
return;
}
if (config.AllowEphemeralServerKey)
{
OnlineMsgServer.Common.Log.Security("server_key_ephemeral", "using in-memory generated private key");
return;
}
throw new InvalidOperationException("服务端私钥未配置。请设置 SERVER_PRIVATE_KEY_B64 或 SERVER_PRIVATE_KEY_PATH。");
}
}
/// <summary>
/// 以base64格式导出公钥字符串
/// </summary>
/// <returns>公钥字符串base64格式</returns>
public static string GetRsaPublickKey()
{
lock (_RsaLock)
{
return Convert.ToBase64String(_Rsa.ExportSubjectPublicKeyInfo());
}
}
/// <summary>
/// 服务端解密 base64编码
/// </summary>
/// <param name="secret">密文</param>
/// <returns> 原文字符串</returns>
public static string Decrypt(string secret)
{
lock (_RsaLock)
{
byte[] secretBytes = Convert.FromBase64String(secret);
int size = secretBytes.Length;
int blockSize = 256;
if (size % blockSize != 0)
{
throw new FormatException("ciphertext length invalid");
}
int blockCount = size / blockSize;
List<byte> decryptList = [];
for (int i = 0; i < blockCount; i++)
{
byte[] block = new byte[blockSize];
Array.Copy(secretBytes, i * blockSize, block, 0, blockSize);
byte[] decryptBlock = _Rsa.Decrypt(block, RSAEncryptionPadding.OaepSHA256);
decryptList.AddRange(decryptBlock);
}
// byte[] decrypt = _Rsa.Decrypt(Convert.FromBase64String(base64),
// RSAEncryptionPadding.OaepSHA256);
return Encoding.UTF8.GetString([.. decryptList]);
}
}
/// <summary>
/// 服务端加密 base64编码
/// </summary>
/// <param name="src">原文字符串</param>
/// <returns>密文</returns>
public static string Encrypt(string src)
{
lock (_RsaLock)
{
return RsaEncrypt(_Rsa, src);
}
}
public static bool VerifySignature(string publicKeyBase64, string src, string signatureBase64)
{
lock (_PublicRsaLock)
{
try
{
_PublicRsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKeyBase64), out _);
byte[] srcBytes = Encoding.UTF8.GetBytes(src);
byte[] signatureBytes = Convert.FromBase64String(signatureBase64);
return _PublicRsa.VerifyData(srcBytes, signatureBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
}
catch
{
return false;
}
}
}
public static bool IsPublicKeyValid(string publicKeyBase64)
{
lock (_PublicRsaLock)
{
try
{
_PublicRsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(publicKeyBase64), out _);
return true;
}
catch
{
return false;
}
}
}
private static string RsaEncrypt(RSA rsa, string src)
{
byte[] srcBytes = Encoding.UTF8.GetBytes(src);
int size = srcBytes.Length;
int blockSize = 190;
if (size == 0)
{
return "";
}
int blockCount = (size + blockSize - 1) / blockSize;
List<byte> encryptList = [];
for (int i = 0; i < blockCount; i++)
{
int len = Math.Min(blockSize, size - i * blockSize);
byte[] block = new byte[len];
Array.Copy(srcBytes, i * blockSize, block, 0, len);
byte[] encryptBlock = rsa.Encrypt(block, RSAEncryptionPadding.OaepSHA256);
encryptList.AddRange(encryptBlock);
}
return Convert.ToBase64String([.. encryptList]);
}
}
}