c# 實(shí)現(xiàn)RSA非對稱加密算法
公鑰與私鑰
公鑰與私鑰是成對的,一般的,我們認(rèn)為的是公鑰加密、私鑰解密、私鑰簽名、公鑰驗(yàn)證,有人說成私鑰加密,公鑰解密時(shí)不對的。
公鑰與私鑰的生成有多種方式,可以通過程序生成(下文具體實(shí)現(xiàn)),可以通過openssl工具:
# 生成一個(gè)私鑰,推薦使用1024位的秘鑰,秘鑰以pem格式保存到-out參數(shù)指定的文件中,采用PKCS1格式
openssl genrsa -out rsa.pem 1024
# 生成與私鑰對應(yīng)的公鑰,生成的是Subject Public Key,一般配合PKCS8格式私鑰使用
openssl rsa -in rsa.pem -pubout -out rsa.pub
RSA生成公鑰與私鑰一般有兩種格式:PKCS1和PKCS8,上面的命令生成的秘鑰是PKCS1格式的,而公鑰是Subject Public Key,一般配合PKCS8格式私鑰使用,所以就可能會涉及到PKCS1和PKCS8之間的轉(zhuǎn)換:
# PKCS1格式私鑰轉(zhuǎn)換為PKCS8格式私鑰,私鑰直接輸出到-out參數(shù)指定的文件中
openssl pkcs8 -topk8 -inform PEM -in rsa.pem -outform pem -nocrypt -out rsa_pkcs8.pem
# PKCS8格式私鑰轉(zhuǎn)換為PKCS1格式私鑰,私鑰直接輸出到-out參數(shù)指定的文件中
openssl rsa -in rsa_pkcs8.pem -out rsa_pkcs1.pem
# PKCS1格式公鑰轉(zhuǎn)換為PKCS8格式公鑰,轉(zhuǎn)換后的內(nèi)容直接輸出
openssl rsa -pubin -in rsa.pub -RSAPublicKey_out
# PKCS8格式公鑰轉(zhuǎn)換為PKCS1格式公鑰,轉(zhuǎn)換后的內(nèi)容直接輸出
openssl rsa -RSAPublicKey_in -pubout -in rsa.pub
現(xiàn)實(shí)中,我們往往從pem、crt、pfx文件獲取公私和私鑰,crt、pfx的制作可以參考:簡單的制作ssl證書,并在nginx和IIS中使用,或者使用現(xiàn)成的:https://pan.baidu.com/s/1MJ5YmuZiLBnf-DfNR_6D7A (提取碼:c6tj),密碼都是:123456
C#實(shí)現(xiàn)
為了方便讀取pem、crt、pfx文件中的公私和私鑰,這里我使用了第三方的包:Portable.BouncyCastle,可以使用NuGet安裝:Install-Package Portable.BouncyCastle
接著,這里我封裝了一個(gè)RsaHelper輔助類來實(shí)現(xiàn)各種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填充方式導(dǎo)出
publicKey = rsa.ExportSubjectPublicKeyInfo();//公鑰
privateKey = rsa.ExportPkcs8PrivateKey();//私鑰
}
else
{
//使用pkcs1填充方式導(dǎo)出
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轉(zhuǎn)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轉(zhuǎn)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>
/// 獲取一個(gè)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格式輸出數(shù)據(jù)
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))
{
//轉(zhuǎn)換hex格式數(shù)據(jù)為byte數(shù)組
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格式輸出數(shù)據(jù)
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公鑰驗(yàn)證簽名
/// </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))
{
//轉(zhuǎn)換hex格式數(shù)據(jù)為byte數(shù)組
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文件不包含私鑰,因此需要單獨(dú)獲取私鑰:
//從crt文件讀取,filePath是文件目錄
publicKey = RsaHelper.ReadPublicKeyFromCrt(Path.Combine(filePath, "demo.crt"), "");
privateKey = RsaHelper.ReadFromPem(Path.Combine(filePath, "demo.key"));
pfx文件中包含了公鑰和私鑰,可以很方便就讀取到:
//從demo.pfx文件讀?。╠emo.pfx采用的是pkcs1),filePath是文件目錄
RsaHelper.ReadFromPfx(Path.Combine(filePath, "demo.pfx"), "123456", out publicKey, out privateKey);
有時(shí)候我們還可能需要進(jìn)行秘鑰的轉(zhuǎn)換:
// Pkcs8格式公鑰轉(zhuǎn)換為Pkcs1格式公鑰
publicKey = RsaHelper.Pkcs8ToPkcs1(false, publicKey);
// Pkcs8格式私鑰轉(zhuǎn)換為Pkcs1格式私鑰
privateKey = RsaHelper.Pkcs8ToPkcs1(true, privateKey);
// Pkcs1格式公鑰轉(zhuǎn)換為Pkcs8格式公鑰
publicKey = RsaHelper.Pkcs1ToPkcs8(false, publicKey);
// Pkcs1格式私鑰轉(zhuǎn)換為Pkcs8格式私鑰
privateKey = RsaHelper.Pkcs1ToPkcs8(true, privateKey);
有了公鑰和私鑰,接下就就能實(shí)現(xiàn)加密、解密、簽名、驗(yàn)證簽名等操作了:
string encryptText = RsaHelper.RsaEncrypt(text, publicKey, usePKCS8);
Console.WriteLine($"【{text}】經(jīng)過【RSA】加密后:{encryptText}");
string decryptText = RsaHelper.RsaDecrypt(encryptText, privateKey, usePKCS8);
Console.WriteLine($"【{encryptText}】經(jīng)過【RSA】解密后:{decryptText}");
string signature = RsaHelper.Sign(text, privateKey, "MD5", usePKCS8);
Console.WriteLine($"【{text}】經(jīng)過【RSA】簽名后:{signature}");
bool result = RsaHelper.Verify(text, publicKey, signature, "MD5", usePKCS8);
Console.WriteLine($"【{text}】的簽名【{signature}】經(jīng)過【RSA】驗(yàn)證后結(jié)果是:{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文件讀?。╠emo.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格式公鑰轉(zhuǎn)換為Pkcs1格式公鑰
publicKey = RsaHelper.Pkcs8ToPkcs1(false, publicKey);
// Pkcs8格式私鑰轉(zhuǎn)換為Pkcs1格式私鑰
privateKey = RsaHelper.Pkcs8ToPkcs1(true, privateKey);
// Pkcs1格式公鑰轉(zhuǎn)換為Pkcs8格式公鑰
publicKey = RsaHelper.Pkcs1ToPkcs8(false, publicKey);
// Pkcs1格式私鑰轉(zhuǎn)換為Pkcs8格式私鑰
privateKey = RsaHelper.Pkcs1ToPkcs8(true, privateKey);
string encryptText = RsaHelper.RsaEncrypt(text, publicKey, usePKCS8);
Console.WriteLine($"【{text}】經(jīng)過【RSA】加密后:{encryptText}");
string decryptText = RsaHelper.RsaDecrypt(encryptText, privateKey, usePKCS8);
Console.WriteLine($"【{encryptText}】經(jīng)過【RSA】解密后:{decryptText}");
string signature = RsaHelper.Sign(text, privateKey, "MD5", usePKCS8);
Console.WriteLine($"【{text}】經(jīng)過【RSA】簽名后:{signature}");
bool result = RsaHelper.Verify(text, publicKey, signature, "MD5", usePKCS8);
Console.WriteLine($"【{text}】的簽名【{signature}】經(jīng)過【RSA】驗(yàn)證后結(jié)果是:{result}");
}
}
}
以上就是c# 實(shí)現(xiàn)RSA非對稱加密算法的詳細(xì)內(nèi)容,更多關(guān)于c# RSA非對稱加密算法的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C#實(shí)現(xiàn)把圖片轉(zhuǎn)換成二進(jìn)制以及把二進(jìn)制轉(zhuǎn)換成圖片的方法示例
這篇文章主要介紹了C#實(shí)現(xiàn)把圖片轉(zhuǎn)換成二進(jìn)制以及把二進(jìn)制轉(zhuǎn)換成圖片的方法,結(jié)合具體實(shí)例形式分析了基于C#的圖片與二進(jìn)制相互轉(zhuǎn)換以及圖片保存到數(shù)據(jù)庫的相關(guān)操作技巧,需要的朋友可以參考下2017-06-06
C#中Parallel類For、ForEach和Invoke使用介紹
這篇文章介紹了C#中Parallel類For、ForEach和Invoke的使用方法,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04
利用C#/VB.NET實(shí)現(xiàn)將PDF轉(zhuǎn)為Word
眾所周知,PDF 文檔支持特長文件,集成度和安全可靠性都較高,可有效防止他人對 PDF 內(nèi)容進(jìn)行更改,所以在工作中深受大家喜愛。本文將分為兩部分介紹如何以編程的方式將 PDF 轉(zhuǎn)換為 Word,需要的可以參考一下2022-12-12
C#中使用Interlocked進(jìn)行原子操作的技巧
使用.NET提供的Interlocked類可以對一些數(shù)據(jù)進(jìn)行原子操作,看起來似乎跟lock鎖一樣,但它并不是lock鎖,它的原子操作是基于CPU本身的,非阻塞的,所以要比lock的效率高2016-12-12
C# 9.0新特性——擴(kuò)展方法GetEnumerator支持foreach循環(huán)
這篇文章主要介紹了C# 9.0新特性——擴(kuò)展方法GetEnumerator支持foreach循環(huán)的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)c# 9.0,感興趣的朋友可以了解下2020-11-11

