C# 使用应用RSA和ECC进行数字签名和签名验证
文章目录
- C# 使用应用RSA和ECC进行数字签名和签名验证
- 基本概念
- RSA 算法示例
- 1. 密钥生成与保存
- 2. 加密与解密
- 3. 数字签名与验证
- 椭圆曲线数字签名算法 (ECDSA) 示例
- 1. 密钥生成与保存
- 2. 数字签名与验证
- 主程序示例 (Program.cs)
- 运行环境要求
- 关于数字证书
C# 使用应用RSA和ECC进行数字签名和签名验证
开发中需要用到数字签名和验签,这篇博客提供使用RSA和ECDSA(椭圆曲线数字签名算法,也是椭圆曲线加密的常见应用)进行数字签名和签名验证的C#程序示例。同时,也会包含密钥生成和保存的例程。
基本概念
- RSA 通常用于加密少量数据(如对称密钥)和数字签名。直接使用RSA加密大量数据效率较低,更常见的是使用RSA加密一个随机生成的对称密钥,然后用对称密钥加密实际数据。
- 椭圆曲线算法 (ECC) 在相同的安全级别下,密钥长度比RSA短得多,因此在性能和存储方面有优势。它主要用于数字签名 (ECDSA) 和密钥协商 (ECDH),也可以进行加密(通过结合ECDH和对称加密)。
- 密钥保存:在实际应用中,私钥的保存至关重要。通常会使用更安全的方式,如Windows证书存储、硬件安全模块 (HSM) 或加密存储在文件中。这里提供的文件保存示例仅用于演示目的,不应直接用于生产环境。
- 异常处理:为了代码简洁,示例中的异常处理可能不够完善,实际应用中应加强。
RSA 算法示例
RSA算法在.NET中主要通过RSACryptoServiceProvider
(旧版) 或 RSA
(推荐,跨平台) 类实现。这里我们使用 RSA
类。
1. 密钥生成与保存
using System.Security.Cryptography;namespace RsaEcdsaCryption
{public class RsaHelper{// 生成RSA密钥对并保存到文件public static void GenerateAndSaveRsaKeys(string privateKeyPath, string publicKeyPath){using (RSA rsa = RSA.Create()){// 设置密钥长度 (例如,2048位)rsa.KeySize = 2048;// 导出私钥 (包含所有参数)string privateKeyXml = rsa.ToXmlString(true);File.WriteAllText(privateKeyPath, privateKeyXml);// 导出公钥 (只包含模数和指数)string publicKeyXml = rsa.ToXmlString(false);File.WriteAllText(publicKeyPath, publicKeyXml);Console.WriteLine($"RSA 私钥已保存到: {privateKeyPath}");Console.WriteLine($"RSA 公钥已保存到: {publicKeyPath}");}}// 从文件加载RSA私钥public static RSA LoadRsaPrivateKey(string privateKeyPath){RSA rsa = RSA.Create();string privateKeyXml = File.ReadAllText(privateKeyPath);rsa.FromXmlString(privateKeyXml); return rsa;}// 从文件加载RSA公钥public static RSA LoadRsaPublicKey(string publicKeyPath){RSA rsa = RSA.Create();string publicKeyXml = File.ReadAllText(publicKeyPath);rsa.FromXmlString(publicKeyXml);return rsa;}}
}
2. 加密与解密
RSA加密通常是公钥加密,私钥解密。
using System.Security.Cryptography;
using System.Text;namespace RsaEcdsaCryption
{public class RsaEncryptionDecryption{// 使用RSA公钥加密数据public static byte[] Encrypt(byte[] data, RSA publicKeyRsa){// OEAP填充模式推荐用于加密,提供更好的安全性return publicKeyRsa.Encrypt(data, RSAEncryptionPadding.OaepSHA256);}// 使用RSA私钥解密数据public static byte[] Decrypt(byte[] encryptedData, RSA privateKeyRsa){// OEAP填充模式推荐用于解密return privateKeyRsa.Decrypt(encryptedData, RSAEncryptionPadding.OaepSHA256);}public static void RunEncryptionDecryptionExample(RSA publicKey, RSA privateKey){string originalText = "这是一段要使用RSA加密的秘密消息。";byte[] originalBytes = Encoding.UTF8.GetBytes(originalText);Console.WriteLine($"\n原始消息: {originalText}");// 加密byte[] encryptedBytes = Encrypt(originalBytes, publicKey);Console.WriteLine($"加密后 (Base64): {Convert.ToBase64String(encryptedBytes)}");// 解密byte[] decryptedBytes = Decrypt(encryptedBytes, privateKey);string decryptedText = Encoding.UTF8.GetString(decryptedBytes);Console.WriteLine($"解密后: {decryptedText}");Console.WriteLine($"加密/解密 {(originalText == decryptedText ? "成功" : "失败")}");}}
}
3. 数字签名与验证
RSA签名通常使用私钥签名,公钥验证。
using System.Security.Cryptography;
using System.Text;public class RsaSignature
{// 使用RSA私钥对数据进行签名public static byte[] SignData(byte[] data, RSA privateKeyRsa){// 选择哈希算法 (例如,SHA256) 和填充模式 (PSS推荐用于签名)return privateKeyRsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);}// 使用RSA公钥验证签名public static bool VerifySignature(byte[] data, byte[] signature, RSA publicKeyRsa){// 选择哈希算法和填充模式与签名时一致return publicKeyRsa.VerifyData(data, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pss);}public static void RunSignatureExample(RSA publicKey, RSA privateKey){string messageToSign = "这是一段要进行数字签名的消息。";byte[] messageBytes = Encoding.UTF8.GetBytes(messageToSign);Console.WriteLine($"\n待签名消息: {messageToSign}");// 签名byte[] signature = SignData(messageBytes, privateKey);Console.WriteLine($"生成签名 (Base64): {Convert.ToBase64String(signature)}");// 验证签名bool isValid = VerifySignature(messageBytes, signature, publicKey);Console.WriteLine($"签名验证结果: {(isValid ? "有效" : "无效")}");// 尝试篡改数据后验证签名Console.WriteLine("\n尝试篡改数据后验证签名...");byte[] tamperedMessageBytes = Encoding.UTF8.GetBytes("这是一段被篡改的消息。");bool isTamperedValid = VerifySignature(tamperedMessageBytes, signature, publicKey);Console.WriteLine($"篡改后签名验证结果: {(isTamperedValid ? "有效" : "无效")}");}
}
椭圆曲线数字签名算法 (ECDSA) 示例
ECDSA在.NET中主要通过 ECDsa
类实现。
1. 密钥生成与保存
ECDSA密钥通常是公私钥对,公钥可以从私钥派生。
using System.Security.Cryptography;public class EcdsaHelper
{// 生成ECDSA密钥对并保存到文件public static void GenerateAndSaveEcdsaKeys(string privateKeyPath, string publicKeyPath){// 选择一个命名曲线,例如 P-256 (secp256r1)using (ECDsa ecdsa = ECDsa.Create(ECCurve.NamedCurves.nistP256)){// 导出私钥(PKCS#8格式,PEM编码)string privateKeyPem = ecdsa.ExportPkcs8PrivateKeyPem();File.WriteAllText(privateKeyPath, privateKeyPem);// 导出公钥(SubjectPublicKeyInfo格式,PEM编码)string publicKeyPem = ecdsa.ExportSubjectPublicKeyInfoPem();File.WriteAllText(publicKeyPath, publicKeyPem);Console.WriteLine($"ECDSA 私钥已保存到: {privateKeyPath}");Console.WriteLine($"ECDSA 公钥已保存到: {publicKeyPath}");}}// 从文件加载ECDSA私钥public static ECDsa LoadEcdsaPrivateKey(string privateKeyPath){ECDsa ecdsa = ECDsa.Create();string privateKeyPem = File.ReadAllText(privateKeyPath);ecdsa.ImportFromPem(privateKeyPem); // 或 ImportPkcs8PrivateKeyPemreturn ecdsa;}// 从文件加载ECDSA公钥public static ECDsa LoadEcdsaPublicKey(string publicKeyPath){ECDsa ecdsa = ECDsa.Create();string publicKeyPem = File.ReadAllText(publicKeyPath);ecdsa.ImportFromPem(publicKeyPem); // 或 ImportSubjectPublicKeyInfoPemreturn ecdsa;}
}
2. 数字签名与验证
ECDSA主要用于签名,不支持直接的加密/解密操作(需要结合ECDH和对称加密才能实现)。
using System.Security.Cryptography;
using System.Text;public class EcdsaSignature
{// 使用ECDSA私钥对数据进行签名public static byte[] SignData(byte[] data, ECDsa privateKeyEcdsa){// 选择哈希算法 (例如,SHA256)return privateKeyEcdsa.SignData(data, HashAlgorithmName.SHA256);}// 使用ECDSA公钥验证签名public static bool VerifySignature(byte[] data, byte[] signature, ECDsa publicKeyEcdsa){// 选择哈希算法与签名时一致return publicKeyEcdsa.VerifyData(data, signature, HashAlgorithmName.SHA256);}public static void RunSignatureExample(ECDsa publicKey, ECDsa privateKey){string messageToSign = "这是一段要使用ECDSA进行数字签名的消息。";byte[] messageBytes = Encoding.UTF8.GetBytes(messageToSign);Console.WriteLine($"\n待签名消息: {messageToSign}");// 签名byte[] signature = SignData(messageBytes, privateKey);Console.WriteLine($"生成签名 (Base64): {Convert.ToBase64String(signature)}");// 验证签名bool isValid = VerifySignature(messageBytes, signature, publicKey);Console.WriteLine($"签名验证结果: {(isValid ? "有效" : "无效")}");// 尝试篡改数据后验证签名Console.WriteLine("\n尝试篡改数据后验证签名...");byte[] tamperedMessageBytes = Encoding.UTF8.GetBytes("这是一段被篡改的消息。");bool isTamperedValid = VerifySignature(tamperedMessageBytes, signature, publicKey);Console.WriteLine($"篡改后签名验证结果: {(isTamperedValid ? "有效" : "无效")}");}
}
主程序示例 (Program.cs)
将上述类集成到Program.cs
中,演示如何使用它们。
using System;
using System.IO;
using System.Security.Cryptography;public class Program
{public static void Main(string[] args){// 定义密钥文件路径string rsaPrivateKeyPath = "rsa_private_key.xml";string rsaPublicKeyPath = "rsa_public_key.xml";string ecdsaPrivateKeyPath = "ecdsa_private_key.pem";string ecdsaPublicKeyPath = "ecdsa_public_key.pem";Console.WriteLine("--- RSA 示例 ---");// RSA 密钥生成与保存RsaHelper.GenerateAndSaveRsaKeys(rsaPrivateKeyPath, rsaPublicKeyPath);RSA rsaPublicKey = RsaHelper.LoadRsaPublicKey(rsaPublicKeyPath);RSA rsaPrivateKey = RsaHelper.LoadRsaPrivateKey(rsaPrivateKeyPath);// RSA 加密与解密RsaEncryptionDecryption.RunEncryptionDecryptionExample(rsaPublicKey, rsaPrivateKey);// RSA 数字签名与验证RsaSignature.RunSignatureExample(rsaPublicKey, rsaPrivateKey);Console.WriteLine("\n--- ECDSA 示例 ---");// ECDSA 密钥生成与保存EcdsaHelper.GenerateAndSaveEcdsaKeys(ecdsaPrivateKeyPath, ecdsaPublicKeyPath);ECDsa ecdsaPublicKey = EcdsaHelper.LoadEcdsaPublicKey(ecdsaPublicKeyPath);ECDsa ecdsaPrivateKey = EcdsaHelper.LoadEcdsaPrivateKey(ecdsaPrivateKeyPath);// ECDSA 数字签名与验证EcdsaSignature.RunSignatureExample(ecdsaPublicKey, ecdsaPrivateKey);// 清理生成的密钥文件 (可选)// File.Delete(rsaPrivateKeyPath);// File.Delete(rsaPublicKeyPath);// File.Delete(ecdsaPrivateKeyPath);// File.Delete(ecdsaPublicKeyPath);Console.WriteLine("\n所有示例运行完毕。");Console.ReadKey();}
}
运行环境要求
- .NET Core 3.1 或更高版本 / .NET 5.0 或更高版本:
RSA.Create()
,ECDsa.Create(ECCurve.NamedCurves.nistP256)
,ExportPkcs8PrivateKeyPem()
,ExportSubjectPublicKeyInfoPem()
,ImportFromPem()
等方法需要较新的.NET版本支持。如果您使用的是较旧的.NET Framework,可能需要调整为RSACryptoServiceProvider
和ECDiffieHellmanCng
/ECDsaCng
,但它们的使用方式有所不同,且通常更推荐使用新的API。
关于数字证书
数字证书是公钥基础设施 (PKI) 的核心组成部分,它将公钥与实体的身份绑定在一起,并由可信的第三方(证书颁发机构 C A)进行签名。上述示例只是生成了原始的公钥和私钥文件,并没有涉及证书的生成和使用。
如果需要处理数字证书,C# 提供了 X509Certificate2
类来加载、创建和管理证书。通常涉及:
- 加载证书:从文件 (
.pfx
,.cer
) 或证书存储区加载。 - 提取公钥:从
X509Certificate2
对象中获取RSA
或ECDsa
公钥。 - 使用私钥:如果证书包含私钥,可以直接用它进行签名或解密。
示例:
// 假设你有一个带有私钥的PFX文件
// string certPath = "myCert.pfx";
// string certPassword = "myPassword";// using (X509Certificate2 cert = new X509Certificate2(certPath, certPassword))
// {
// // 获取公钥用于验证
// RSA rsaPublicKeyFromCert = cert.GetRSAPublicKey(); // 或 GetECDsaPublicKey()
// // 获取私钥用于签名
// RSA rsaPrivateKeyFromCert = cert.GetRSAPrivateKey(); // 或 GetECDsaPrivateKey()// // 然后就可以用这些密钥进行签名/验证或加密/解密
// }
程序运行效果如下: