1. 程式人生 > >.NET Core加解密實戰系列之——使用BouncyCastle製作p12(.pfx)數字證書

.NET Core加解密實戰系列之——使用BouncyCastle製作p12(.pfx)數字證書

## 簡介 加解密現狀,編寫此係列文章的背景: - 需要考慮系統環境相容性問題(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·架構組 福祿娃