.NET Core加解密實戰系列之——使用BouncyCastle製作p12(.pfx)數字證書
阿新 • • 發佈:2020-09-23
## 簡介
加解密現狀,編寫此係列文章的背景:
- 需要考慮系統環境相容性問題(Linux、Windows)
- 語言互通問題(如C#、Java等)(加解密本質上沒有語言之分,所以原則上不存在互通性問題)
- 網上資料版本不一、或不全面
- .NET官方庫密碼演算法提供不全面,很難針對其他語言(Java)進行適配
本系列文章主要介紹如何在 .NET Core 中使用非對稱加密演算法、編碼演算法、訊息摘要演算法、簽名演算法、對稱加密演算法、國密演算法等一系列演算法,如有錯誤之處,還請大家批評指正。
本系列文章旨在引導大家能快速、輕鬆的瞭解接入加解密,乃至自主組合搭配使用BouncyCastle密碼術包中提供的演算法。
本系列程式碼專案地址:[https://github.com/fuluteam/ICH.BouncyCastle.git](https://github.com/fuluteam/ICH.BouncyCastle.git)
上一篇文章《.NET Core加解密實戰系列之——對稱加密演算法》:[https://www.cnblogs.com/fulu/p/13650079.html](https://www.cnblogs.com/fulu/p/13650079.html)
### 功能依賴
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.6](https://www.nuget.org/packages/Portable.BouncyCastle/1.8.6) |
## 前言
在工作中我們難免會接觸對接外部系統(如銀行、支付寶、微信等),對接過程中又無可避免會對資料的加解密和加簽驗籤。一般第三方會提供一個授權證書,讓我們自行解密提取祕鑰。為了讓你拿到證書後不會像我當初一樣一臉懵逼,咱們來看看如何使用C#程式碼製作使用p12證書。
當然,比較常見的,還是推薦大家使用OpenSSL。
OpenSSL是目前最流行的 SSL密碼庫工具,其提供了一個通用、健壯、功能完備的工具套件,用以支援SSL/TLS 協議的實現。
官網:https://www.openssl.org/source/
![image](https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=2594594171,3711109660&fm=26&gp=0.jpg)
## 什麼是p12證書
公鑰加密技術12號標準(Public Key Cryptography Standards #12,PKCS#12)為儲存和傳輸使用者或伺服器私鑰、公鑰和證書指定了一個可移植的格式。它是一種二進位制格式,這些檔案也稱為PFX檔案。
P12證書包含了私鑰、公鑰並且有口令保護,在證書洩露後還有最後一道保障。沒有證書口令無法提取祕鑰。
對PKCS標準感興趣的小夥伴可以參考[百度百科PKCS介紹](https://baike.baidu.com/item/PKCS/1042350)
## 什麼是X.509格式
在密碼學中,X.509是定義公鑰證書格式的標準。X.509證書用於許多Internet協議,包括TLS/SSL,它是HTTPS(用於瀏覽web的安全協議)的基礎。它們也用於離線應用程式,比如電子簽名。一個X.509證書包含一個公鑰和一個標識(主機名、組織或個人),由證書頒發機構簽名或自簽名。當證書由受信任的證書頒發機構簽名時,或者通過其他方法進行驗證時,持有該證書的人可以依賴於它包含的公鑰來與另一方建立安全通訊,或者驗證由相應私鑰數字簽名的文件。
X.509還定義了證書撤銷列表,這是一種分發被簽名機構認為無效的證書資訊的方法,以及認證路徑驗證演算法,該演算法允許證書由中間CA證書籤名,而中間CA證書又由其他證書籤名,最終到達信任錨。
X.509由國際電信聯盟標準化部門(ITU-T)定義,並基於ITU-T的另一個標準ASN.1。
## SSL Certificate (編碼)格式
SSL Certificate實際上就是X.509 Certificate。X.509是一個定義了certificate結構的標準。它在SSL certificate中定義了一個數據域。X.509使用名為 Abstract Syntax Notation One (ASN.1)的通用語言來描述certificate的資料結構。
X.509 certificate 有幾種不同的格式,例如 PEM,DER,PKCS#7 和 PKCS#12。 PEM和PKCS#7格式使用Base64 ASCII編碼,而DER和PKCS#12使用二進位制編碼。certificate檔案基於不同的編碼格式有不同的副檔名。
如下圖就展示了X.509證書的編碼方式和副檔名。
![image](http://note.youdao.com/yws/public/resource/8c2a81e9627c847bab7fc30a28aa7ce7/xmlnote/2F3FA1FD1AEA43ADBDE9878ECBCCED43/21410)
## X.509 證書結構
X.509證書的結構是用ASN.1(Abstract Syntax Notation One:抽象語法標記)來描述其資料結構,並使用ASN1語法進行編碼。
- X.509 v3數字證書的結構如下:
- certificate 證書
- Version Number版本號
- Serial Number序列號
- ID Signature Algorithm ID簽名演算法
- Issuer Name頒發者名稱
- Validity period 有效期
- Not before起始日期
- Not after截至日期
- Subject Name主題名稱
- Subject pbulic Key Info 主題公鑰資訊
- Public Key Algorithm公鑰演算法
- Subject Public Key主題公鑰
- Issuer Unique Identifier (optional)頒發者唯一識別符號(可選)
- Subject Unique Identifier (optional)主題唯一識別符號(可選)
- Extensions (optional) 證書的擴充套件項(可選)
- Certificate Sigature Algorithm證書籤名演算法
- Certificate Signature證書的簽名
## 證書操作
### 證書生成
```cs
///
/// 生成證書
///
/// 證書失效時間
/// 金鑰長度
/// 證書密碼
/// 設定將用於簽署此證書的簽名演算法
/// 設定此證書頒發者的DN
/// 設定此證書使用者的DN
/// 設定證書友好名稱(可選)
/// 證書生效時間
public static void GenerateCertificate(string filename, string password, string signatureAlgorithm, X509Name issuer, X509Name subject, DateTime notBefore, DateTime notAfter, string friendlyName, int keyStrength = 2048)
{
SecureRandom random = new SecureRandom(new CryptoApiRandomGenerator());
var keyGenerationParameters = new KeyGenerationParameters(random, keyStrength);
var keyPairGenerator = new RsaKeyPairGenerator(); //RSA金鑰對生成器
keyPairGenerator.Init(keyGenerationParameters);
var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
ISignatureFactory signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, subjectKeyPair.Private, random);
//the certificate generator
X509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();
var spki = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public);
//設定一些擴充套件欄位
//允許作為一個CA證書(可以頒發下級證書或進行簽名)
certificateGenerator.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(true));
//使用者金鑰識別符號
certificateGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifier(spki));
//授權金鑰識別符號
certificateGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifier(spki));
certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, true, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth));
//證書序列號
BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), random);
certificateGenerator.SetSerialNumber(serialNumber);
certificateGenerator.SetIssuerDN(issuer); //頒發者資訊
certificateGenerator.SetSubjectDN(subject); //使用者資訊
certificateGenerator.SetNotBefore(notBefore); //證書生效時間
certificateGenerator.SetNotAfter(notAfter); //證書失效時間
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
Org.BouncyCastle.X509.X509Certificate certificate = certificateGenerator.Generate(signatureFactory);
//生成cer證書,公鑰證書
//var certificate2 = new X509Certificate2(DotNetUtilities.ToX509Certificate(certificate))
//{
// FriendlyName = friendlyName, //設定友好名稱
//};
////cer公鑰檔案
//var bytes = certificate2.Export(X509ContentType.Cert);
//using (var fs = new FileStream(certPath, FileMode.Create))
//{
// fs.Write(bytes, 0, bytes.Length);
//}
//另一種程式碼生成p12證書的方式(要求使用.net standard 2.1)
//certificate2 =
// certificate2.CopyWithPrivateKey(DotNetUtilities.ToRSA((RsaPrivateCrtKeyParameters)keyPair.Private));
//var bytes2 = certificate2.Export(X509ContentType.Pfx, password);
//using (var fs = new FileStream(pfxPath, FileMode.Create))
//{
// fs.Write(bytes2, 0, bytes2.Length);
//}
var certEntry = new X509CertificateEntry(certificate);
var store = new Pkcs12StoreBuilder().Build();
store.SetCertificateEntry(friendlyName, certEntry); //設定證書
var chain = new X509CertificateEntry[1];
chain[0] = certEntry;
store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(subjectKeyPair.Private), chain); //設定私鑰
using (var fs = File.Create(filename))
{
store.Save(fs, password.ToCharArray(), random); //儲存
};
}
private static void Certificate_Sample()
{
//頒發者DN
var issuer = new X509Name(new ArrayList
{
X509Name.C,
X509Name.O,
X509Name.OU,
X509Name.L,
X509Name.ST
}, new Hashtable
{
[X509Name.C] = "CN",
[X509Name.O] = "Fulu Newwork",
[X509Name.OU] = "Fulu RSA CA 2020",
[X509Name.L] = "Wuhan",
[X509Name.ST] = "Hubei",
});
//使用者DN
var subject = new X509Name(new ArrayList
{
X509Name.C,
X509Name.O,
X509Name.CN
}, new Hashtable
{
[X509Name.C] = "CN",
[X509Name.O] = "ICH",
[X509Name.CN] = "*.fulu.com"
});
var password = "123456"; //證書密碼
var signatureAlgorithm = "SHA256WITHRSA"; //簽名演算法
//生成證書
CertificateUtilities.GenerateCertificate("fuluca.pfx", password, signatureAlgorithm, issuer, subject, DateTime.UtcNow.AddDays(-1), DateTime.UtcNow.AddYears(2), "fulu passport");
//載入證書
X509Certificate2 pfx = new X509Certificate2("fuluca.pfx", password, X509KeyStorageFlags.Exportable);
var keyPair = DotNetUtilities.GetKeyPair(pfx.PrivateKey);
var subjectPublicKeyInfo = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(keyPair.Public);
var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(keyPair.Private);
var privateKey = Base64.ToBase64String(privateKeyInfo.ParsePrivateKey().GetEncoded());
var publicKey = Base64.ToBase64String(subjectPublicKeyInfo.GetEncoded());
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("Pfx證書私鑰:");
Console.WriteLine(privateKey);
Console.WriteLine("Pfx證書公鑰:");
Console.WriteLine(publicKey);
var data = "hello rsa";
Console.WriteLine($"加密原文:{data}");
var pkcs1data = RSA.EncryptToBase64(data, AsymmetricKeyUtilities.GetAsymmetricKeyParameterFormPublicKey(publicKey), Algorithms.RSA_ECB_PKCS1Padding);
Console.WriteLine("加密結果:");
Console.WriteLine(pkcs1data);
Console.WriteLine("解密結果:");
var datares = RSA.DecryptFromBase64(pkcs1data,
AsymmetricKeyUtilities.GetAsymmetricKeyParameterFormPrivateKey(privateKey), Algorithms.RSA_ECB_PKCS1Padding);
Console.WriteLine(datares);
}
```
![image](http://note.youdao.com/yws/public/resource/8c2a81e9627c847bab7fc30a28aa7ce7/xmlnote/B4DA10E7D2274A00B98610B3C6750653/21423)
生成的證書檔案:
![image](http://note.youdao.com/yws/public/resource/8c2a81e9627c847bab7fc30a28aa7ce7/xmlnote/EEE6C8259F6D4DF8AFE4295823BC239B/21426)
### 證書安裝
雙擊證書檔案進行安裝,儲存位置選擇當前使用者。
![Dingtalk_20200920193639](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/Dingtalk_20200920193639.jpg)
證書儲存選擇個人
![Dingtalk_20200920194257](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/Dingtalk_20200920194257.jpg)
檢視安裝的證書
可以在MMC的證書管理單元中對證書儲存區進行管理。Windows沒有給我們準備好直接的管理證書的入口。自己在MMC中新增,步驟如下:
1. 開始→執行→MMC,開啟一個空的MMC控制檯。
2. 在控制檯選單,檔案→新增/刪除管理單元→新增按鈕→選”證書”→新增→選”我的使用者賬戶”→關閉→確定
![Dingtalk_20200920194558](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/Dingtalk_20200920194558.jpg)
展開 證書控制檯根節點→證書-當前使用者→個人→證書,找到證書,可以看到下圖中選中的即為我們建立的證書檔案
![Dingtalk_20200920195213](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/Dingtalk_20200920195213.jpg)
雙擊證書,可以看到證書的相關資訊
![Dingtalk_20200920195238](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/Dingtalk_20200920195238.jpg)
### OpenSSL安裝
工具:openssl
安裝軟體:Win64 OpenSSL v1.1.1g Light
下載地址:http://slproweb.com/products/Win32OpenSSL.html
![Dingtalk_20200920202710](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/Dingtalk_20200920202710.jpg)
### PFX檔案提取公鑰私鑰
```cs
openssl pkcs12 -in fulusso.pfx -nocerts -nodes -out private.key
輸入密碼
openssl rsa -in private.key -out pfx_pri.pem
openssl rsa -in private.key -pubout -out pfx_pub.pem
```
安裝好OpenSSL後,開啟Win64 OpenSSL Command Prompt,讀取到證書檔案所在目錄,按上述命令執行
![lADPDgfLPCS4fKbNBBPNB5g](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/lADPDgfLPCS4fKbNBBPNB5g.jpg)
開啟證書所在目錄,可以看到檔案 private.key、pfx_pri.pem、pfx_pub.pem 已經生成好了。
![Dingtalk_20200920201928](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/Dingtalk_20200920201928.jpg)
用文字工具開啟私鑰檔案pfx_pri.pem,如下圖:
![Dingtalk_20200920202004](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/Dingtalk_20200920202004.jpg)
開啟公約檔案pfx_pub.pem,如下圖:
![Dingtalk_20200920202023](https://fulu-item11-zjk.oss-cn-zhangjiakou.aliyuncs.com/images/Dingtalk_20200920202023.jpg)
比對與上文控制檯打印出的公鑰、私鑰一致。
## 下期預告
下一篇將介紹國密演算法,敬請期待。。。
福祿ICH·架構組
福祿娃