Files
2026-08-19 14:11:17 +08:00

296 lines
9.1 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using CryptographyRSA = System.Security.Cryptography.RSA; // 别名避免与当前类名冲突
namespace System
{
/// <summary>
/// RSA操作类(跨平台实现)
/// </summary>
public class RSA
{
// 记录当前实例是否仅包含公钥(替代原来的 rsa.PublicOnly)
private bool publicOnly;
/// <summary>
/// 导出XML格式密钥对,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
/// </summary>
public string ToXML(bool convertToPublic = false)
{
bool includePrivate = !publicOnly && !convertToPublic;
return rsa.ToXmlString(includePrivate);
}
/// <summary>
/// 导出PEM PKCS#1格式密钥对,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
/// </summary>
public string ToPEM_PKCS1(bool convertToPublic = false)
{
if (!publicOnly && !convertToPublic)
{
return rsa.ExportRSAPrivateKeyPem();
}
return rsa.ExportRSAPublicKeyPem();
}
/// <summary>
/// 导出PEM PKCS#8格式密钥对,如果convertToPublic含私钥的RSA将只返回公钥,仅含公钥的RSA不受影响
/// </summary>
public string ToPEM_PKCS8(bool convertToPublic = false)
{
if (!publicOnly && !convertToPublic)
{
return rsa.ExportPkcs8PrivateKeyPem();
}
return rsa.ExportSubjectPublicKeyInfoPem();
}
/// <summary>
/// 加密字符串(utf-8),出错抛异常
/// </summary>
public string Encode(string str)
{
return RSA_Unit.Base64EncodeBytes(Encode(Encoding.UTF8.GetBytes(str)));
}
/// <summary>
/// 加密数据,出错抛异常
/// </summary>
public byte[] Encode(byte[] data)
{
int blockLen = rsa.KeySize / 8 - 11;
if (data.Length <= blockLen)
{
return rsa.Encrypt(data, RSAEncryptionPadding.Pkcs1);
}
using (var dataStream = new MemoryStream(data))
using (var enStream = new MemoryStream())
{
byte[] buffer = new byte[blockLen];
int len = dataStream.Read(buffer, 0, blockLen);
while (len > 0)
{
byte[] block = new byte[len];
Array.Copy(buffer, 0, block, 0, len);
byte[] enBlock = rsa.Encrypt(block, RSAEncryptionPadding.Pkcs1);
enStream.Write(enBlock, 0, enBlock.Length);
len = dataStream.Read(buffer, 0, blockLen);
}
return enStream.ToArray();
}
}
/// <summary>
/// 解密字符串(utf-8),解密异常返回null
/// </summary>
public string DecodeOrNull(string str)
{
if (string.IsNullOrEmpty(str))
return null;
var bytes = RSA_Unit.Base64DecodeBytes(str);
if (bytes == null)
return null;
var decrypted = DecodeOrNull(bytes);
if (decrypted == null)
return null;
return Encoding.UTF8.GetString(decrypted);
}
/// <summary>
/// 解密数据,解密异常返回null
/// </summary>
public byte[] DecodeOrNull(byte[] data)
{
try
{
int blockLen = rsa.KeySize / 8;
if (data.Length <= blockLen)
{
return rsa.Decrypt(data, RSAEncryptionPadding.Pkcs1);
}
using (var dataStream = new MemoryStream(data))
using (var deStream = new MemoryStream())
{
byte[] buffer = new byte[blockLen];
int len = dataStream.Read(buffer, 0, blockLen);
while (len > 0)
{
byte[] block = new byte[len];
Array.Copy(buffer, 0, block, 0, len);
byte[] deBlock = rsa.Decrypt(block, RSAEncryptionPadding.Pkcs1);
deStream.Write(deBlock, 0, deBlock.Length);
len = dataStream.Read(buffer, 0, blockLen);
}
return deStream.ToArray();
}
}
catch
{
return null;
}
}
/// <summary>
/// 对str进行签名,并指定hash算法(如:SHA256)
/// </summary>
public string Sign(string hash, string str)
{
return RSA_Unit.Base64EncodeBytes(Sign(hash, Encoding.UTF8.GetBytes(str)));
}
/// <summary>
/// 对data进行签名,并指定hash算法(如:SHA256)
/// </summary>
public byte[] Sign(string hash, byte[] data)
{
return rsa.SignData(data, new HashAlgorithmName(hash), RSASignaturePadding.Pkcs1);
}
/// <summary>
/// 验证字符串str的签名是否是sgin,并指定hash算法(如:SHA256)
/// </summary>
public bool Verify(string hash, string sgin, string str)
{
var bytes = RSA_Unit.Base64DecodeBytes(sgin);
if (bytes == null)
return false;
return Verify(hash, bytes, Encoding.UTF8.GetBytes(str));
}
/// <summary>
/// 验证data的签名是否是sgin,并指定hash算法(如:SHA256)
/// </summary>
public bool Verify(string hash, byte[] sgin, byte[] data)
{
try
{
return rsa.VerifyData(data, sgin, new HashAlgorithmName(hash), RSASignaturePadding.Pkcs1);
}
catch
{
return false;
}
}
private CryptographyRSA rsa;
public RSA() : this(2048)
{
}
/// <summary>
/// 用指定密钥大小创建一个新的RSA,出错抛异常
/// </summary>
public RSA(int keySize)
{
rsa = CryptographyRSA.Create();
rsa.KeySize = keySize;
publicOnly = false; // 新生成的密钥对包含私钥
}
/// <summary>
/// 通过指定的密钥,创建一个RSA,xml内可以只包含一个公钥或私钥,或都包含,出错抛异常
/// </summary>
public RSA(string xml)
{
rsa = CryptographyRSA.Create();
rsa.FromXmlString(xml);
// 判断是否仅包含公钥
publicOnly = false;
try
{
rsa.ExportParameters(true); // 能导出私钥参数说明含私钥
}
catch (CryptographicException)
{
publicOnly = true;
}
}
/// <summary>
/// 通过一个pem文件创建RSA,pem为公钥或私钥,出错抛异常
/// </summary>
public RSA(string pem, bool noop)
{
rsa = CryptographyRSA.Create();
// 1. 兼容文件路径
string pemContent = pem;
if (!pemContent.Contains("BEGIN"))
{
pemContent = File.ReadAllText(pem);
}
// 2. 提取 Base64 部分(忽略标签)
var match = Regex.Match(pemContent,
@"-----BEGIN [^-]+-----([\s\S]*?)-----END [^-]+-----",
RegexOptions.Singleline);
if (!match.Success)
throw new CryptographicException("无效的 PEM 格式。");
string base64 = match.Groups[1].Value
.Replace("\r", "")
.Replace("\n", "")
.Replace(" ", "")
.Replace("\t", "")
.Trim();
byte[] der = Convert.FromBase64String(base64);
// 3. 依次尝试常见 RSA 密钥格式(顺序可调整)
// 先尝试 PKCS#8 私钥,再尝试 PKCS#1 私钥,然后公钥
try
{
rsa.ImportPkcs8PrivateKey(der, out _);
publicOnly = false;
return;
}
catch (CryptographicException) { }
try
{
rsa.ImportRSAPrivateKey(der, out _);
publicOnly = false;
return;
}
catch (CryptographicException) { }
try
{
rsa.ImportSubjectPublicKeyInfo(der, out _);
publicOnly = true;
return;
}
catch (CryptographicException) { }
try
{
rsa.ImportRSAPublicKey(der, out _);
publicOnly = true;
return;
}
catch (CryptographicException) { }
throw new CryptographicException("无法识别的 RSA 密钥格式。");
}
}
}