c# 实现RSA非对称加密算法

RSA解决了对称加密的一个不足,比如AES算法加密和解密时使用的是同一个秘钥,因此这个秘钥不能公开,因此对于需要公开秘钥的场合,我们需要在加密和解密过程中使用不同的秘钥,加密使用的公钥可以公开,解密使用的私钥要保密,这就是非对称加密的好处。 

  公钥与私钥

  公钥与私钥是成对的,一般的,我们认为的是公钥加密、私钥解密、私钥签名、公钥验证,有人说成私钥加密,公钥解密时不对的。

  公钥与私钥的生成有多种方式,可以通过程序生成(下文具体实现),可以通过openssl工具:  

 # 生成一个私钥,推荐使用1024位的秘钥,秘钥以pem格式保存到-out参数指定的文件中,采用PKCS1格式 openssl genrsa -out rsa.pem 1024 # 生成与私钥对应的公钥,生成的是Subject Public Key,一般配合PKCS8格式私钥使用 openssl rsa -in rsa.pem -pubout -out rsa.pub  

  RSA生成公钥与私钥一般有两种格式:PKCS1和PKCS8,上面的命令生成的秘钥是PKCS1格式的,而公钥是Subject Public Key,一般配合PKCS8格式私钥使用,所以就可能会涉及到PKCS1和PKCS8之间的转换:

 # PKCS1格式私钥转换为PKCS8格式私钥,私钥直接输出到-out参数指定的文件中 openssl pkcs8 -topk8 -inform PEM -in rsa.pem -outform pem -nocrypt -out rsa_pkcs8.pem # PKCS8格式私钥转换为PKCS1格式私钥,私钥直接输出到-out参数指定的文件中 openssl rsa -in rsa_pkcs8.pem -out rsa_pkcs1.pem # PKCS1格式公钥转换为PKCS8格式公钥,转换后的内容直接输出 openssl rsa -pubin -in rsa.pub -RSAPublicKey_out # PKCS8格式公钥转换为PKCS1格式公钥,转换后的内容直接输出 openssl rsa -RSAPublicKey_in -pubout -in rsa.pub

  现实中,我们往往从pem、crt、pfx文件获取公私和私钥,crt、pfx的制作可以参考:简单的制作ssl证书,并在nginx和IIS中使用,或者使用现成的:https://pan.baidu.com/s/1MJ5YmuZiLBnf-DfNR_6D7A (提取码:c6tj),密码都是:123456

  C#实现

  为了方便读取pem、crt、pfx文件中的公私和私钥,这里我使用了第三方的包:Portable.BouncyCastle,可以使用NuGet安装:Install-Package Portable.BouncyCastle

  接着,这里我封装了一个RsaHelper辅助类来实现各种RSA加密的过程: 

 using Org.BouncyCastle.Utilities.IO.Pem; using System; using System.Collections.Generic; using System.IO; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; namespace ConsoleApp1 { public class RsaHelper { #region key ///  /// 将秘钥保存到Pem文件 ///  ///  ///  ///  ///  public static void WriteToPem(byte[] buffer, bool isPrivateKey, string pemFileName) { PemObject pemObject = new PemObject(isPrivateKey ? "RSA PRIVATE KEY" : "RSA PUBLIC KEY", buffer); if (File.Exists(pemFileName)) { File.Delete(pemFileName); } using (var fileStream = new FileStream(pemFileName, FileMode.OpenOrCreate, FileAccess.Write)) { using (var sw = new StreamWriter(fileStream)) { var writer = new PemWriter(sw); writer.WriteObject(pemObject); sw.Flush(); } } } ///  /// 从Pem文件中读取秘钥 ///  ///  ///  public static byte[] ReadFromPem(string pemFileName) { using (var fileStream = new FileStream(pemFileName, FileMode.Open, FileAccess.Read)) { using (var sw = new StreamReader(fileStream)) { var writer = new PemReader(sw); return writer.ReadPemObject().Content; } } } ///  /// 从xml中读取秘钥 ///  ///  ///  ///  ///  public static byte[] ReadFromXml(string xml, bool isPrivateKey, bool usePkcs8) { using (var rsa = new RSACryptoServiceProvider()) { rsa.FromXmlString(xml); if (isPrivateKey) { return usePkcs8 ? rsa.ExportPkcs8PrivateKey() : rsa.ExportRSAPrivateKey(); } return usePkcs8 ? rsa.ExportSubjectPublicKeyInfo() : rsa.ExportRSAPublicKey(); } } ///  /// 将秘钥保存到xml中 ///  ///  ///  ///  ///  public static string WriteToXml(byte[] buffer, bool isPrivateKey, bool usePkcs8) { using (var rsa = CreateRSACryptoServiceProvider(buffer, isPrivateKey, usePkcs8)) { return rsa.ToXmlString(isPrivateKey); } } ///  /// 获取RSA非对称加密的Key ///  ///  ///  public static void GenerateRsaKey(bool usePKCS8, out byte[] publicKey, out byte[] privateKey) { using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.KeySize = 1024;//1024位 if (usePKCS8) { //使用pkcs8填充方式导出 publicKey = rsa.ExportSubjectPublicKeyInfo();//公钥 privateKey = rsa.ExportPkcs8PrivateKey();//私钥 } else { //使用pkcs1填充方式导出 publicKey = rsa.ExportRSAPublicKey();//公钥 privateKey = rsa.ExportRSAPrivateKey();//私钥 } } } ///  /// 从Pfx文件获取RSA非对称加密的Key ///  ///  ///  ///  public static void ReadFromPfx(string pfxFileName, string password, out byte[] publicKey, out byte[] privateKey) { X509Certificate2 x509Certificate2 = new X509Certificate2(pfxFileName, password, X509KeyStorageFlags.Exportable); publicKey = x509Certificate2.GetRSAPublicKey().ExportRSAPublicKey(); privateKey = x509Certificate2.GetRSAPrivateKey().ExportRSAPrivateKey(); } ///  /// 从Crt文件中读取公钥 ///  ///  ///  ///  public static byte[] ReadPublicKeyFromCrt(string crtFileName, string password) { X509Certificate2 x509Certificate2 = new X509Certificate2(crtFileName, password, X509KeyStorageFlags.Exportable); var publicKey = x509Certificate2.GetRSAPublicKey().ExportRSAPublicKey(); return publicKey; } #endregion #region Pkcs1 and Pkcs8 ///  /// Pkcs1转Pkcs8 ///  ///  ///  ///  public static byte[] Pkcs1ToPkcs8(bool isPrivateKey, byte[] buffer) { using (var rsa = new RSACryptoServiceProvider()) { if (isPrivateKey) { rsa.ImportRSAPrivateKey(buffer, out _); return rsa.ExportPkcs8PrivateKey(); } else { rsa.ImportRSAPublicKey(buffer, out _); return rsa.ExportSubjectPublicKeyInfo(); } } } ///  /// Pkcs8转Pkcs1 ///  ///  ///  ///  public static byte[] Pkcs8ToPkcs1(bool isPrivateKey, byte[] buffer) { using (var rsa = new RSACryptoServiceProvider()) { if (isPrivateKey) { rsa.ImportPkcs8PrivateKey(buffer, out _); return rsa.ExportRSAPrivateKey(); } else { rsa.ImportSubjectPublicKeyInfo(buffer, out _); return rsa.ExportRSAPublicKey(); } } } #endregion #region RSA ///  /// 获取一个RSACryptoServiceProvider ///  ///  ///  ///  ///  public static RSACryptoServiceProvider CreateRSACryptoServiceProvider(byte[] buffer, bool isPrivateKey, bool usePkcs8 = false) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(); if (isPrivateKey) { if (usePkcs8) rsa.ImportPkcs8PrivateKey(buffer, out _); else rsa.ImportRSAPrivateKey(buffer, out _); } else { if (usePkcs8) rsa.ImportSubjectPublicKeyInfo(buffer, out _); else rsa.ImportRSAPublicKey(buffer, out _); } return rsa; } ///  /// RSA公钥加密 ///  /// 待加密的明文 /// 公钥 /// 是否使用pkcs8填充 ///  public static string RsaEncrypt(string value, byte[] publicKey, bool usePkcs8 = false) { if (string.IsNullOrEmpty(value)) return value; using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(publicKey, false, usePkcs8)) { var buffer = Encoding.UTF8.GetBytes(value); buffer = rsa.Encrypt(buffer, false); //使用hex格式输出数据 StringBuilder result = new StringBuilder(); foreach (byte b in buffer) { result.AppendFormat("{0:x2}", b); } return result.ToString(); //或者使用下面的输出 //return BitConverter.ToString(buffer).Replace("-", "").ToLower(); } } ///  /// RSA私钥解密 ///  /// 密文 /// 私钥 /// 是否使用pkcs8填充 ///  public static string RsaDecrypt(string value, byte[] privateKey, bool usePkcs8 = false) { if (string.IsNullOrEmpty(value)) return value; using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(privateKey, true, usePkcs8)) { //转换hex格式数据为byte数组 var buffer = new byte[value.Length / 2]; for (var i = 0; i  /// RSA私钥生成签名 ///  /// 原始值 /// 公钥 /// 签名hash算法:SHA,SHA1,MD5,SHA256,SHA384,SHA512 /// 是否使用pkcs8填充 ///  public static string Sign(string value, byte[] privateKey, string halg = "MD5", bool usePkcs8 = false) { if (string.IsNullOrEmpty(value)) return value; using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(privateKey, true, usePkcs8)) { byte[] buffer = Encoding.UTF8.GetBytes(value); buffer = rsa.SignData(buffer, HashAlgorithm.Create(halg)); //使用hex格式输出数据 StringBuilder result = new StringBuilder(); foreach (byte b in buffer) { result.AppendFormat("{0:x2}", b); } return result.ToString(); //或者使用下面的输出 //return BitConverter.ToString(buffer).Replace("-", "").ToLower(); } } ///  /// RSA公钥验证签名 ///  /// 原始值 /// 公钥 /// 签名 /// 签名hash算法:SHA,SHA1,MD5,SHA256,SHA384,SHA512 /// 是否使用pkcs8填充 ///  public static bool Verify(string value, byte[] publicKey, string signature, string halg = "MD5", bool usePkcs8 = false) { if (string.IsNullOrEmpty(value)) return false; using (RSACryptoServiceProvider rsa = CreateRSACryptoServiceProvider(publicKey, false, usePkcs8)) { //转换hex格式数据为byte数组 var buffer = new byte[signature.Length / 2]; for (var i = 0; i 

  可以使用生成RSA的公私秘钥:  

 //通过程序生成 RsaHelper.GenerateRsaKey(usePKCS8, out publicKey, out privateKey);

  生成秘钥后,需要保存,一般保存到pem文件中:  

 //保存到Pem文件,filePath是文件目录 RsaHelper.WriteToPem(publicKey, false, Path.Combine(filePath, "rsa.pub")); RsaHelper.WriteToPem(privateKey, true, Path.Combine(filePath, "rsa.pem"));

  还以将公钥和私钥输出为xml格式:  

 //保存到xml中 string publicKeyXml = RsaHelper.WriteToXml(publicKey, false, usePKCS8); string privateKeyXml = RsaHelper.WriteToXml(privateKey, true, usePKCS8);

  保存到pem文件和xml中后,也可以从中读取:  

 //从Pem文件获取,filePath是文件目录 publicKey = RsaHelper.ReadFromPem(Path.Combine(filePath, "rsa.pub")); privateKey = RsaHelper.ReadFromPem(Path.Combine(filePath, "rsa.pem")); //从xml中读取 publicKey = RsaHelper.ReadFromXml(publicKeyXml, false, usePKCS8); privateKey = RsaHelper.ReadFromXml(privateKeyXml, true, usePKCS8);

  还可以从crt证书中读取公钥,而crt文件不包含私钥,因此需要单独获取私钥:

 //从crt文件读取,filePath是文件目录 publicKey = RsaHelper.ReadPublicKeyFromCrt(Path.Combine(filePath, "demo.crt"), ""); privateKey = RsaHelper.ReadFromPem(Path.Combine(filePath, "demo.key"));

  pfx文件中包含了公钥和私钥,可以很方便就读取到:  

 //从demo.pfx文件读取(demo.pfx采用的是pkcs1),filePath是文件目录 RsaHelper.ReadFromPfx(Path.Combine(filePath, "demo.pfx"), "123456", out publicKey, out privateKey);

  有时候我们还可能需要进行秘钥的转换:  

 // Pkcs8格式公钥转换为Pkcs1格式公钥 publicKey = RsaHelper.Pkcs8ToPkcs1(false, publicKey); // Pkcs8格式私钥转换为Pkcs1格式私钥 privateKey = RsaHelper.Pkcs8ToPkcs1(true, privateKey); // Pkcs1格式公钥转换为Pkcs8格式公钥 publicKey = RsaHelper.Pkcs1ToPkcs8(false, publicKey); // Pkcs1格式私钥转换为Pkcs8格式私钥 privateKey = RsaHelper.Pkcs1ToPkcs8(true, privateKey);

  有了公钥和私钥,接下就就能实现加密、解密、签名、验证签名等操作了:  

 string encryptText = RsaHelper.RsaEncrypt(text, publicKey, usePKCS8); Console.WriteLine($"【{text}】经过【RSA】加密后:{encryptText}"); string decryptText = RsaHelper.RsaDecrypt(encryptText, privateKey, usePKCS8); Console.WriteLine($"【{encryptText}】经过【RSA】解密后:{decryptText}"); string signature = RsaHelper.Sign(text, privateKey, "MD5", usePKCS8); Console.WriteLine($"【{text}】经过【RSA】签名后:{signature}"); bool result = RsaHelper.Verify(text, publicKey, signature, "MD5", usePKCS8); Console.WriteLine($"【{text}】的签名【{signature}】经过【RSA】验证后结果是:{result}");

  完整的demo代码:  

 using System; using System.IO; namespace ConsoleApp1 { class Program { static void Main(string[] args) { string text = "上山打老虎"; bool usePKCS8 = true;// usePKCS8=true表示是否成PKCS8格式的公私秘钥,否则乘车PKCS1格式的公私秘钥 string filePath = Directory.GetCurrentDirectory(); Console.WriteLine($"文件路径:{filePath}");// 存放pem,crt,pfx等文件的目录 byte[] publicKey, privateKey;// 公钥和私钥 //通过程序生成 RsaHelper.GenerateRsaKey(usePKCS8, out publicKey, out privateKey); //从Pem文件获取,filePath是文件目录 //publicKey = RsaHelper.ReadFromPem(Path.Combine(filePath, "rsa.pub")); //privateKey = RsaHelper.ReadFromPem(Path.Combine(filePath, "rsa.pem")); //从demo.pfx文件读取(demo.pfx采用的是pkcs1),filePath是文件目录 //RsaHelper.ReadFromPfx(Path.Combine(filePath, "demo.pfx"), "123456", out publicKey, out privateKey); //从crt文件读取,filePath是文件目录 //publicKey = RsaHelper.ReadPublicKeyFromCrt(Path.Combine(filePath, "demo.crt"), ""); //privateKey = RsaHelper.ReadFromPem(Path.Combine(filePath, "demo.key")); //保存到Pem文件,filePath是文件目录 RsaHelper.WriteToPem(publicKey, false, Path.Combine(filePath, "rsa.pub")); RsaHelper.WriteToPem(privateKey, true, Path.Combine(filePath, "rsa.pem")); //保存到xml中 string publicKeyXml = RsaHelper.WriteToXml(publicKey, false, usePKCS8); string privateKeyXml = RsaHelper.WriteToXml(privateKey, true, usePKCS8); //从xml中读取 publicKey = RsaHelper.ReadFromXml(publicKeyXml, false, usePKCS8); privateKey = RsaHelper.ReadFromXml(privateKeyXml, true, usePKCS8); // Pkcs8格式公钥转换为Pkcs1格式公钥 publicKey = RsaHelper.Pkcs8ToPkcs1(false, publicKey); // Pkcs8格式私钥转换为Pkcs1格式私钥 privateKey = RsaHelper.Pkcs8ToPkcs1(true, privateKey); // Pkcs1格式公钥转换为Pkcs8格式公钥 publicKey = RsaHelper.Pkcs1ToPkcs8(false, publicKey); // Pkcs1格式私钥转换为Pkcs8格式私钥 privateKey = RsaHelper.Pkcs1ToPkcs8(true, privateKey); string encryptText = RsaHelper.RsaEncrypt(text, publicKey, usePKCS8); Console.WriteLine($"【{text}】经过【RSA】加密后:{encryptText}"); string decryptText = RsaHelper.RsaDecrypt(encryptText, privateKey, usePKCS8); Console.WriteLine($"【{encryptText}】经过【RSA】解密后:{decryptText}"); string signature = RsaHelper.Sign(text, privateKey, "MD5", usePKCS8); Console.WriteLine($"【{text}】经过【RSA】签名后:{signature}"); bool result = RsaHelper.Verify(text, publicKey, signature, "MD5", usePKCS8); Console.WriteLine($"【{text}】的签名【{signature}】经过【RSA】验证后结果是:{result}"); } } }

以上就是c# 实现RSA非对称加密算法的详细内容,更多请关注0133技术站其它相关文章!

赞(0) 打赏
未经允许不得转载:0133技术站首页 » 其他教程