.NET Core加解密實戰系列之——RSA非對稱加密演算法
阿新 • • 發佈:2020-06-12
![](https://img2020.cnblogs.com/blog/2029875/202006/2029875-20200612165254059-1754283874.png)
---
[TOC]
## 簡介
加解密現狀,編寫此專案的背景:
- 需要考慮系統環境相容性問題(Linux、Windows)
- 語言互通問題(如C#、Java)
- 網上資料版本不一、不全面
- .NET官方庫密碼演算法提供不全面,很難針對其他語言(Java)進行適配
本系列文章主要介紹如何結合BouncyCastle在 .NET Core 中使用非對稱加密演算法、編碼演算法、雜湊演算法、對稱加密演算法、國密演算法等一系列演算法,內容篇幅程式碼居多(加解密演算法相關的原理知識網上有很多,因此不過多介紹)。如有錯誤之處,還請大家批評指正。
本系列程式碼專案地址:https://github.com/fuluteam/ICH.BouncyCastle.git
## 功能依賴
BouncyCastle(https://www.bouncycastle.org/csharp) 是一個開放原始碼的輕量級密碼術包;它支援大量的密碼術演算法,它提供了很多.NET Core標準庫沒有的演算法。
支援.NET 4,.NET Standard 1.0-2.0,WP,Silverlight,MonoAndroid,Xamarin.iOS,.NET Core
| 功能 | 依賴 |
| :-- | :-- |
| Portable.BouncyCastle | [Portable.BouncyCastle • 1.8.5](https://www.nuget.org/packages/Portable.BouncyCastle/1.8.5) |
## 生成RSA祕鑰
### PKCS1格式
```csharp
///
/// PKCS1(非Java適用)
///
/// 金鑰長度”一般只是指模值的位長度。目前主流可選值:1024、2048、3072、4096...
/// PEM格式
///
public RSAKeyParameter Pkcs1(int keySize, bool format=false)
{
var keyGenerator = GeneratorUtilities.GetKeyPairGenerator("RSA");
keyGenerator.Init(new KeyGenerationParameters(new SecureRandom(), keySize));
var keyPair = keyGenerator.GenerateKeyPair();
var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
if (!format)
{
return new RSAKeyParameter
{
PrivateKey = Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded()),
PublicKey = Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded())
};
}
var rsaKey = new RSAKeyParameter();
using (var sw = new StringWriter())
{
var pWrt = new PemWriter(sw);
pWrt.WriteObject(keyPair.Private);
pWrt.Writer.Close();
rsaKey.PrivateKey = sw.ToString();
}
using (var sw = new StringWriter())
{
var pWrt = new PemWriter(sw);
pWrt.WriteObject(keyPair.Public);
pWrt.Writer.Close();
rsaKey.PublicKey = sw.ToString();
}
return rsaKey;
}
```
### PKCS8格式
```csharp
///
/// PKCS8(JAVA適用)
///
/// 金鑰長度”一般只是指模值的位長度。目前主流可選值:1024、2048、3072、4096...
/// PEM格式
///
public RSAKeyParameter Pkcs8(int keySize, bool format=false)
{
var keyGenerator = GeneratorUtilities.GetKeyPairGenerator("RSA");
keyGenerator.Init(new KeyGenerationParameters(new SecureRandom(), keySize));
var keyPair = keyGenerator.GenerateKeyPair();
var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
if (!format)
{
return new RSAKeyParameter
{
PrivateKey = Base64.ToBase64String(privateKeyInfo.GetEncoded()),
PublicKey = Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded())
};
}
var rsaKey = new RSAKeyParameter();
using (var sw = new StringWriter())
{
var pWrt = new PemWriter(sw);
var pkcs8 = new Pkcs8Generator(keyPair.Private);
pWrt.WriteObject(pkcs8);
pWrt.Writer.Close();
rsaKey.PrivateKey = sw.ToString();
}
using (var sw = new StringWriter())
{
var pWrt = new PemWriter(sw);
pWrt.WriteObject(keyPair.Public);
pWrt.Writer.Close();
rsaKey.PublicKey = sw.ToString();
}
return rsaKey;
}
```
## 私鑰操作
### PKCS1與PKCS8格式互轉
==僅私鑰有PKCS1和PKCS8之分,公鑰無格式區別。==
```csharp
///
/// Pkcs1>>Pkcs8
///
/// Pkcs1私鑰
/// 是否轉PEM格式
///
public static string PrivateKeyPkcs1ToPkcs8(string privateKey, bool format = false)
{
var akp = RSAUtilities.GetAsymmetricKeyParameterFormPrivateKey(privateKey);
if (format)
{
var sw = new StringWriter();
var pWrt = new PemWriter(sw);
var pkcs8 = new Pkcs8Generator(akp);
pWrt.WriteObject(pkcs8);
pWrt.Writer.Close();
return sw.ToString();
}
else
{
var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp);
return Base64.ToBase64String(privateKeyInfo.GetEncoded());
}
}
```
```csharp
///
/// Pkcs8>>Pkcs1
///
/// Pkcs8私鑰
/// 是否轉PEM格式
///
public static string PrivateKeyPkcs8ToPkcs1(string privateKey, bool format = false)
{
var akp = RSAUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(privateKey);
if (format)
{
var sw = new StringWriter();
var pWrt = new PemWriter(sw);
pWrt.WriteObject(akp);
pWrt.Writer.Close();
return sw.ToString();
}
else
{
var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp);
return Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded());
}
}
```
### PKCS1與PKCS8私鑰中提取公鑰
```csharp
///
/// 從Pkcs1私鑰中提取公鑰
///
/// Pkcs1私鑰
///
public static string GetPublicKeyFromPrivateKeyPkcs1(string privateKey)
{
var instance = RsaPrivateKeyStructure.GetInstance(Base64.Decode(privateKey));
var publicParameter = (AsymmetricKeyParameter)new RsaKeyParameters(false, instance.Modulus,instance.PublicExponent);
var privateParameter = (AsymmetricKeyParameter)new RsaPrivateCrtKeyParameters(instance.Modulus,instance.PublicExponent, instance.PrivateExponent, instance.Prime1, instance.Prime2, instance.Exponent1,instance.Exponent2, instance.Coefficient);
var keyPair = new AsymmetricCipherKeyPair(publicParameter, privateParameter);
var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
return Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded());
}
```
```csharp
///
/// 從Pkcs8私鑰中提取公鑰
///
/// Pkcs8私鑰
///
public static string GetPublicKeyFromPrivateKeyPkcs8(string privateKey)
{
var privateKeyInfo = PrivateKeyInfo.GetInstance(Asn1Object.FromByteArray(Base64.Decode(privateKey)));
privateKey = Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded());
var instance = RsaPrivateKeyStructure.GetInstance(Base64.Decode(privateKey));
var publicParameter = (AsymmetricKeyParameter)new RsaKeyParameters(false, instance.Modulus,instance.PublicExponent);
var privateParameter = (AsymmetricKeyParameter)new RsaPrivateCrtKeyParameters(instance.Modulus,instance.PublicExponent, instance.PrivateExponent, instance.Prime1, instance.Prime2, instance.Exponent1,instance.Exponent2, instance.Coefficient);
var keyPair = new AsymmetricCipherKeyPair(publicParameter, privateParameter);
var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
return Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded());
}
```
## PEM操作
### PEM格式金鑰讀取
```csharp
public static string ReadPkcs1PrivateKey(string text)
{
if (!text.StartsWith("-----BEGIN RSA PRIVATE KEY-----"))
{
return text;
}
using (var reader = new StringReader(text))
{
var pr = new PemReader(reader);
var keyPair = pr.ReadObject() as AsymmetricCipherKeyPair;
pr.Reader.Close();
var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair?.Private);
return Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded());
}
}
public static string ReadPkcs8PrivateKey(string text)
{
if (!text.StartsWith("-----BEGIN PRIVATE KEY-----"))
{
return text;
}
using (var reader = new StringReader(text))
{
var pr = new PemReader(reader);
var akp = pr.ReadObject() as AsymmetricKeyParameter; ;
pr.Reader.Close();
return Base64.ToBase64String(PrivateKeyInfoFactory.CreatePrivateKeyInfo(akp).GetEncoded());
}
}
public static string ReadPublicKey(string text)
{
if (!text.StartsWith("-----BEGIN PUBLIC KEY-----"))
{
return text;
}
using (var reader = new StringReader(text))
{
var pr = new PemReader(reader);
var keyPair = pr.ReadObject() as AsymmetricCipherKeyPair;
pr.Reader.Close();
var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair?.Public);
returnBase64.ToBase64String(subjectPublicKeyIno.GetEncoded());
}
}
```
### PEM格式金鑰寫入
```csharp
public static string WritePkcs1PrivateKey(string privateKey)
{
if (privateKey.StartsWith("-----BEGIN RSA PRIVATE KEY-----"))
{
return privateKey;
}
var akp = RSAUtilities.GetAsymmetricKeyParameterFormPrivateKey(privateKey);
using (var sw = new StringWriter())
{
var pWrt = new PemWriter(sw);
pWrt.WriteObject(akp);
pWrt.Writer.Close();
return sw.ToString();
}
}
public static string WritePkcs8PrivateKey(string privateKey)
{
if (privateKey.StartsWith("-----BEGIN PRIVATE KEY-----"))
{
return privateKey;
}
var akp = RSAUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(privateKey);
using (var sw = new StringWriter())
{
var pWrt = new PemWriter(sw);
var pkcs8 = new Pkcs8Generator(akp);
pWrt.WriteObject(pkcs8);
pWrt.Writer.Close();
return sw.ToString();
}
}
public static string WritePublicKey(string publicKey)
{
if (publicKey.StartsWith("-----BEGIN PUBLIC KEY-----"))
{
return publicKey;
}
var akp = RSAUtilities.GetAsymmetricKeyParameterFormPublicKey(publicKey);
using (var sw = new StringWriter())
{
var pWrt = new PemWriter(sw);
pWrt.WriteObject(akp);
pWrt.Writer.Close();
return sw.ToString();
}
}
```
## RSA加解密
### 獲取非對稱祕鑰引數(AsymmetricKeyParameter)
```csharp
///
/// -----BEGIN RSA PRIVATE KEY-----
/// ...
/// -----END RSA PRIVATE KEY-----
///
/// Pkcs1格式私鑰
///
public static AsymmetricKeyParameter GetAsymmetricKeyParameterFormPrivateKey(string privateKey)
{
if (string.IsNullOrEmpty(privateKey))
{
throw new ArgumentNullException(nameof(privateKey));
}
var instance = RsaPrivateKeyStructure.GetInstance(Base64.Decode(privateKey));
return new RsaPrivateCrtKeyParameters(instance.Modulus, instance.PublicExponent, instance.PrivateExponent,instance.Prime1, instance.Prime2, instance.Exponent1, instance.Exponent2, instance.Coefficient);
}
///
/// -----BEGIN PRIVATE KEY-----
/// ...
/// -----END PRIVATE KEY-----
///
/// Pkcs8格式私鑰
///
public static AsymmetricKeyParameter GetAsymmetricKeyParameterFormAsn1PrivateKey(string privateKey)
{
return PrivateKeyFactory.CreateKey(Base64.Decode(privateKey));
}
///
/// -----BEGIN PUBLIC KEY-----
/// ...
/// -----END PUBLIC KEY-----
///
/// 公鑰
///
public static AsymmetricKeyParameter GetAsymmetricKeyParameterFormPublicKey(string publicKey)
{
if (string.IsNullOrEmpty(publicKey))
{
throw new ArgumentNullException(nameof(publicKey));
}
return PublicKeyFactory.CreateKey(Base64.Decode(publicKey));
}
```
### RSA加解與解密
```csharp
///
/// RSA加密
///
/// 未加密資料位元組陣列
/// 非對稱金鑰引數
/// 密文演算法
/// 已加密資料位元組陣列
public static byte[] Encrypt(byte[] data, AsymmetricKeyParameter parameters, string algorithm)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
if (parameters == null)
{
throw new ArgumentNullException(nameof(parameters));
}
if (string.IsNullOrEmpty(algorithm))
{
throw new ArgumentNullException(nameof(algorithm));
}
var bufferedCipher = CipherUtilities.GetCipher(algorithm);
bufferedCipher.Init(true, parameters);
return bufferedCipher.DoFinal(data);
}
///
/// RSA解密
///
/// 已加密資料位元組陣列
/// 非對稱金鑰引數
/// 密文演算法
/// 未加密資料位元組陣列
public static byte[] Decrypt(byte[] data, AsymmetricKeyParameter parameters, string algorithm)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
if (parameters == null)
{
throw new ArgumentNullException(nameof(parameters));
}
if (string.IsNullOrEmpty(algorithm))
{
throw new ArgumentNullException(nameof(algorithm));
}
var bufferedCipher = CipherUtilities.GetCipher(algorithm);
bufferedCipher.Init(false, parameters);
return bufferedCipher.DoFinal(data);
}
///
/// RSA加密——Base64
///
/// 未加密字串
/// 非對稱金鑰引數
/// 密文演算法
/// 已加密Base64字串
public static string EncryptToBase64(string data, AsymmetricKeyParameter parameters, string algorithm)
{
return Base64.ToBase64String(Encrypt(Encoding.UTF8.GetBytes(data), parameters, algorithm));
}
///
/// RSA解密——Base64
///
/// 已加密Base64字串
/// 非對稱金鑰引數
/// 密文演算法
/// 未加密字串
public static string DecryptFromBase64(string data, AsymmetricKeyParameter parameters, string algorithm)
{
return Encoding.UTF8.GetString(Decrypt(Base64.Decode(data), parameters, algorithm));
}
///
/// RSA加密——十六進位制
///
/// 未加密字串
/// 非對稱金鑰引數
/// 密文演算法
/// 已加密十六進位制字串
public static string EncryptToHex(string data, AsymmetricKeyParameter parameters, string algorithm)
{
return Hex.ToHexString(Encrypt(Encoding.UTF8.GetBytes(data), parameters, algorithm));
}
///
/// RSA解密——十六進位制
///
/// 已加密十六進位制字串
/// 非對稱金鑰引數
/// 密文演算法
/// 未加密字串
public static string DecryptFromHex(string data, AsymmetricKeyParameter parameters, string algorithm)
{
return Encoding.UTF8.GetString(Decrypt(Hex.Decode(data), parameters, algorithm));
}
```
### RSA密文演算法
```csharp
public const string RSA_NONE_NoPadding = "RSA/NONE/NoPadding";
public const string RSA_NONE_PKCS1Padding = "RSA/NONE/PKCS1Padding";
public const string RSA_NONE_OAEPPadding = "RSA/NONE/OAEPPadding";
public const string RSA_NONE_OAEPWithSHA1AndMGF1Padding = "RSA/NONE/OAEPWithSHA1AndMGF1Padding";
public const string RSA_NONE_OAEPWithSHA224AndMGF1Padding = "RSA/NONE/OAEPWithSHA224AndMGF1Padding";
public const string RSA_NONE_OAEPWithSHA256AndMGF1Padding = "RSA/NONE/OAEPWithSHA256AndMGF1Padding";
public const string RSA_NONE_OAEPWithSHA384AndMGF1Padding = "RSA/NONE/OAEPWithSHA384AndMGF1Padding";
public const string RSA_NONE_OAEPWithMD5AndMGF1Padding = "RSA/NONE/OAEPWithMD5AndMGF1Padding";
public const string RSA_ECB_NoPadding = "RSA/ECB/NoPadding";
public const string RSA_ECB_PKCS1Padding = "RSA/ECB/PKCS1Padding";
public const string RSA_ECB_OAEPPadding = "RSA/ECB/OAEPPadding";
public const string RSA_ECB_OAEPWithSHA1AndMGF1Padding = "RSA/ECB/OAEPWithSHA1AndMGF1Padding";
public const string RSA_ECB_OAEPWithSHA224AndMGF1Padding = "RSA/ECB/OAEPWithSHA224AndMGF1Padding";
public const string RSA_ECB_OAEPWithSHA256AndMGF1Padding = "RSA/ECB/OAEPWithSHA256AndMGF1Padding";
public const string RSA_ECB_OAEPWithSHA384AndMGF1Padding = "RSA/ECB/OAEPWithSHA384AndMGF1Padding";
public const string RSA_ECB_OAEPWithMD5AndMGF1Padding = "RSA/ECB/OAEPWithMD5AndMGF1Padding";
......
```
## 編碼演算法
大家要明白,不管是對稱演算法還是非對稱演算法,其輸入與輸出均是位元組陣列,通常我們要結合編碼演算法對加密之後或解密之前的資料,進行編碼操作。
### BouncyCastle提供的Base64編碼演算法
```csharp
namespace Org.BouncyCastle.Utilities.Encoders
{
public sealed class Base64
{
//
public static byte[] Decode(byte[] data);
//
public static byte[] Decode(string data);
//
public static int Decode(string data, Stream outStream);
//
public static byte[] Encode(byte[] data);
//
public static byte[] Encode(byte[] data, int off, int length);
//
public static int Encode(byte[] data, Stream outStream);
//
public static int Encode(byte[] data, int off, int length, Stream outStream);
public static string ToBase64String(byte[] data);
public static string ToBase64String(byte[] data, int off, int length);
}
}
```
### BouncyCastle提供的Hex十六進位制編碼演算法
```csharp
namespace Org.BouncyCastle.Utilities.Encoders
{
//
// 摘要:
// Class to decode and encode Hex.
public sealed class Hex
{
//
public static byte[] Decode(byte[] data);
//
public static byte[] Decode(string data);
//
public static int Decode(string data, Stream outStream);
//
public static byte[] Encode(byte[] data);
//
public static byte[] Encode(byte[] data, int off, int length);
//
public static int Encode(byte[] data, Stream outStream);
//
public static int Encode(byte[] data, int off, int length, Stream outStream);
public static string ToHexString(byte[] data);
public static string ToHexString(byte[] data, int off, int length);
}
}
```
## RSA加解密示例
```csharp
private static void RSA_ECB_PKCS1Padding()
{
var data = "hello rsa";
Console.WriteLine($"加密原文:{data}");
// rsa pkcs8 private key encrypt
//algorithm rsa/ecb/pkcs1padding
var pkcs8data = RSA.EncryptToBase64(data, RSAUtilities.GetAsymmetricKeyParameterFormAsn1PrivateKey(pkcs8_1024_private_key),CipherAlgorithms.RSA_ECB_PKCS1Padding);
Console.WriteLine("金鑰格式:pkcs8,密文演算法:rsa/ecb/pkcs1padding,加密結果");
Console.WriteLine(pkcs8data);
//rsa pkcs1 private key encrypt
//algorithm rsa/ecb/pkcs1padding
var pkcs1data = RSA.EncryptToBase64(data, RSAUtilities.GetAsymmetricKeyParameterFormPrivateKey(pkcs1_1024_private_key),CipherAlgorithms.RSA_ECB_PKCS1Padding);
Console.WriteLine($"金鑰格式:pkcs1,密文演算法:rsa/ecb/pkcs1padding");
Console.WriteLine(pkcs1data);
Console.WriteLine($"加密結果比對是否一致:{pkcs8data.Equals(pkcs1data)}");
var _1024_public_key = RSAKeyConverter.GetPublicKeyFromPrivateKeyPkcs1(pkcs1_1024_private_key);
Console.WriteLine($"從pkcs1私鑰中提取公鑰:");
Console.WriteLine(_1024_public_key);
Console.WriteLine("使用公鑰解密資料:");
//rsa public key decrypt
//algorithm rsa/ecb/pkcs1padding
Console.WriteLine(RSA.DecryptFromBase64(pkcs1data, RSAUtilities.GetAsymmetricKeyParameterFormPublicKey(_1024_public_key),CipherAlgorithms.RSA_ECB_PKCS1Padding));
Console.WriteLine();
}
```
![image](https://images.cnblogs.com/cnblogs_com/ynbt/1775471/o_200528052757sample.jpg)
## 下期預告
下一篇將介紹雜湊演算法(HMACSHA1、HMACSHA256、SHA1、SHA1WithRSA、SHA256、SHA256WithRSA),敬請期待...
![](https://img2020.cnblogs.com/blog/2029875/202006/2029875-20200612165240184-1675495