软件编程
位置:首页>> 软件编程>> C#编程>> c# 实现RSA非对称加密算法

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

作者:没有星星的夏季  发布时间:2021-10-15 10:54:06 

标签:c#,rsa,加密算法

公钥与私钥

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

公钥与私钥的生成有多种方式,可以通过程序生成(下文具体实现),可以通过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
       /// <summary>
       /// 将秘钥保存到Pem文件
       /// </summary>
       /// <param name="isPrivateKey"></param>
       /// <param name="buffer"></param>
       /// <param name="pemFileName"></param>
       /// <returns></returns>
       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();
               }
           }
       }
       /// <summary>
       /// 从Pem文件中读取秘钥
       /// </summary>
       /// <param name="pemFileName"></param>
       /// <returns></returns>
       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;
               }
           }
       }

/// <summary>
       /// 从xml中读取秘钥
       /// </summary>
       /// <param name="xml"></param>
       /// <param name="isPrivateKey"></param>
       /// <param name="usePkcs8"></param>
       /// <returns></returns>
       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();
           }
       }
       /// <summary>
       /// 将秘钥保存到xml中
       /// </summary>
       /// <param name="buffer"></param>
       /// <param name="isPrivateKey"></param>
       /// <param name="usePkcs8"></param>
       /// <returns></returns>
       public static string WriteToXml(byte[] buffer, bool isPrivateKey, bool usePkcs8)
       {
           using (var rsa = CreateRSACryptoServiceProvider(buffer, isPrivateKey, usePkcs8))
           {
               return rsa.ToXmlString(isPrivateKey);
           }
       }

/// <summary>
       /// 获取RSA非对称加密的Key
       /// </summary>
       /// <param name="publicKey"></param>
       /// <param name="privateKey"></param>
       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();//私钥
               }
           }
       }
       /// <summary>
       /// 从Pfx文件获取RSA非对称加密的Key
       /// </summary>
       /// <param name="pfxFileName"></param>
       /// <param name="publicKey"></param>
       /// <param name="privateKey"></param>
       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();
       }
       /// <summary>
       /// 从Crt文件中读取公钥
       /// </summary>
       /// <param name="crtFileName"></param>
       /// <param name="password"></param>
       /// <returns></returns>
       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

/// <summary>
       /// Pkcs1转Pkcs8
       /// </summary>
       /// <param name="isPrivateKey"></param>
       /// <param name="buffer"></param>
       /// <returns></returns>
       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();
               }
           }
       }
       /// <summary>
       /// Pkcs8转Pkcs1
       /// </summary>
       /// <param name="isPrivateKey"></param>
       /// <param name="buffer"></param>
       /// <returns></returns>
       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

/// <summary>
       /// 获取一个RSACryptoServiceProvider
       /// </summary>
       /// <param name="isPrivateKey"></param>
       /// <param name="buffer"></param>
       /// <param name="usePkcs8"></param>
       /// <returns></returns>
       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;
       }

/// <summary>
       /// RSA公钥加密
       /// </summary>
       /// <param name="value">待加密的明文</param>
       /// <param name="publicKey">公钥</param>
       /// <param name="usePkcs8">是否使用pkcs8填充</param>
       /// <returns></returns>
       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();
           }
       }
       /// <summary>
       /// RSA私钥解密
       /// </summary>
       /// <param name="value">密文</param>
       /// <param name="privateKey">私钥</param>
       /// <param name="usePkcs8">是否使用pkcs8填充</param>
       /// <returns></returns>
       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 < buffer.Length; i++)
               {
                   buffer[i] = (byte)Convert.ToInt32(value.Substring(i * 2, 2), 16);
               }
               buffer = rsa.Decrypt(buffer, false);
               return Encoding.UTF8.GetString(buffer);
           }
       }
       /// <summary>
       /// RSA私钥生成签名
       /// </summary>
       /// <param name="value">原始值</param>
       /// <param name="publicKey">公钥</param>
       /// <param name="halg">签名hash算法:SHA,SHA1,MD5,SHA256,SHA384,SHA512</param>
       /// <param name="usePkcs8">是否使用pkcs8填充</param>
       /// <returns></returns>
       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();
           }
       }
       /// <summary>
       /// RSA公钥验证签名
       /// </summary>
       /// <param name="value">原始值</param>
       /// <param name="publicKey">公钥</param>
       /// <param name="signature">签名</param>
       /// <param name="halg">签名hash算法:SHA,SHA1,MD5,SHA256,SHA384,SHA512</param>
       /// <param name="usePkcs8">是否使用pkcs8填充</param>
       /// <returns></returns>
       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 < buffer.Length; i++)
               {
                   buffer[i] = (byte)Convert.ToInt32(signature.Substring(i * 2, 2), 16);
               }
               return rsa.VerifyData(Encoding.UTF8.GetBytes(value), HashAlgorithm.Create(halg), buffer);
           }
       }
       #endregion
   }
}

可以使用生成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}");
           }
       }
   }

来源:https://www.cnblogs.com/shanfeng1000/p/14840055.html

0
投稿

猜你喜欢

  • 本文为大家分享了java多线程的简单实现及线程池实例,供大家参考,具体内容如下一、多线程的两种实现方式1、继承Thread类的多线程/**
  • using 指令有两个用途: 允许在命名空间中使用类型,以便您不必限定在该命名空间中使用的类型。 为命名空间创建别名。 using
  • 在类中自定义的&ldquo;函数&rdquo;称为&ldquo;方法&rdquo;,由于C#是完全面向对象的
  • C#之继承继承、封装和多态是面向对象编程的重要特性。其成员被继承的类叫基类也称父类,继承其成员的类叫派生类也称子类。派生类隐式获得基类的除构
  • dart 是一个面向对象的语言;面向对象有继承封装多态dart的所有东西都是对象,所有的对象都是继承与object类一个类通常是由属性和方法
  • 本文实例为大家分享了Java实现24点小游戏的具体代码,供大家参考,具体内容如下程序设计要求:24点游戏是经典的纸牌益智游戏。常见游戏规则:
  • SpringMVC接收到请求和数据后,进行一些了的处理,当然这个处理可以是转发给Service,Service层再调用Dao层完成的,不管怎
  • 前言我们在很多博客中都有发现,Seata AT模式里面的全局锁其实是行锁,这也是Seata AT模式和XA模式在锁粒度上的最大区别。我们可以
  • Spingboot JPA CriteriaBuilder获取指定字段废话不说直接贴代码public class ActivityVO im
  • 1. matlab的lp2lp函数的作用去归一化 H(s) 的分母2. matlab的lp2lp函数的使用方法[z, p, k]=butta
  • 我们先来看看公众号发放现金红包的效果:需要调用商户平台的接口,接口发放规则如下:1.发送频率限制——默认1800/min 2.发送个数上限—
  • String 对象是不可改变的。每次使用 System.String 类中的方法之一时,都要在内存中创建一个新的字符串对象,这就需要为该新对
  • 目录:1.list中添加,获取,删除元素;2.list中是否包含某个元素;3.list中根据索引将元素数值改变(替换);4.list中查看(
  • 经过很多查看在巨人的肩膀上写完这篇博客,如有雷同纯属巧合,虽然自己也查了些文章才总结的,但是站在巨人肩膀上不敢搞原创!学习使用一些插件,可以
  • 本文实例为大家分享了JavaMail实现带附件的邮件发送的具体代码,供大家参考,具体内容如下发送纯文本的邮件package com.haiw
  • 中断线程线程的thread.interrupt()方法是中断线程,将会设置该线程的中断状态位,即设置为true,中断的结果线程是死亡、还是等
  • 项目里用到@within时,出现了一些问题,使用@target就可以解决,但又会出现一些新的问题,因此本文探讨了在spring中,使用@wi
  • 静态代理第一种实现(基于接口):1》接口public interface Hello { void say(String msg);}2》目
  •  Java 定时器在JAVA中实现定时器功能要用的二个类是Timer,TimerTaskTimer类是用来执行任务的类,它接受一个
  • 本文实例为大家分享了tryAcquire()、addWaiter()、acquireQueued()的用法 ,供大家参考,具体内容如下try
手机版 软件编程 asp之家 www.aspxhome.com