296 lines
9.1 KiB
C#
296 lines
9.1 KiB
C#
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 密钥格式。");
|
||
}
|
||
}
|
||
}
|