.NET Core加解密实战系列之——使用BouncyCastle制作p12(.pfx)数字证书
簡(jiǎn)介
加解密現(xiàn)狀,編寫(xiě)此系列文章的背景:
需要考慮系統(tǒng)環(huán)境兼容性問(wèn)題(Linux、Windows)
語(yǔ)言互通問(wèn)題(如C#、Java等)(加解密本質(zhì)上沒(méi)有語(yǔ)言之分,所以原則上不存在互通性問(wèn)題)
網(wǎng)上資料版本不一、或不全面
.NET官方庫(kù)密碼算法提供不全面,很難針對(duì)其他語(yǔ)言(Java)進(jìn)行適配
本系列文章主要介紹如何在 .NET Core 中使用非對(duì)稱(chēng)加密算法、編碼算法、消息摘要算法、簽名算法、對(duì)稱(chēng)加密算法、國(guó)密算法等一系列算法,如有錯(cuò)誤之處,還請(qǐng)大家批評(píng)指正。
本系列文章旨在引導(dǎo)大家能快速、輕松的了解接入加解密,乃至自主組合搭配使用BouncyCastle密碼術(shù)包中提供的算法。
本系列代碼項(xiàng)目地址:https://github.com/fuluteam/ICH.BouncyCastle.git
上一篇文章《.NET Core加解密實(shí)戰(zhàn)系列之——對(duì)稱(chēng)加密算法》:https://www.cnblogs.com/fulu/p/13650079.html
功能依賴(lài)
BouncyCastle(https://www.bouncycastle.org/csharp) 是一個(gè)開(kāi)放源碼的輕量級(jí)密碼術(shù)包;它支持大量的密碼術(shù)算法,它提供了很多 .NET Core標(biāo)準(zhǔn)庫(kù)沒(méi)有的算法。
支持 .NET 4,.NET Standard 1.0-2.0,WP,Silverlight,MonoAndroid,Xamarin.iOS,.NET Core
| 功能 | 依賴(lài) |
| Portable.BouncyCastle | Portable.BouncyCastle ? 1.8.6 |
前言
在工作中我們難免會(huì)接觸對(duì)接外部系統(tǒng)(如銀行、支付寶、微信等),對(duì)接過(guò)程中又無(wú)可避免會(huì)對(duì)數(shù)據(jù)的加解密和加簽驗(yàn)簽。一般第三方會(huì)提供一個(gè)授權(quán)證書(shū),讓我們自行解密提取秘鑰。為了讓你拿到證書(shū)后不會(huì)像我當(dāng)初一樣一臉懵逼,咱們來(lái)看看如何使用C#代碼制作使用p12證書(shū)。
當(dāng)然,比較常見(jiàn)的,還是推薦大家使用OpenSSL。
OpenSSL是目前最流行的 SSL密碼庫(kù)工具,其提供了一個(gè)通用、健壯、功能完備的工具套件,用以支持SSL/TLS 協(xié)議的實(shí)現(xiàn)。
官網(wǎng):https://www.openssl.org/source/
什么是p12證書(shū)
公鑰加密技術(shù)12號(hào)標(biāo)準(zhǔn)(Public Key Cryptography Standards #12,PKCS#12)為存儲(chǔ)和傳輸用戶(hù)或服務(wù)器私鑰、公鑰和證書(shū)指定了一個(gè)可移植的格式。它是一種二進(jìn)制格式,這些文件也稱(chēng)為PFX文件。
P12證書(shū)包含了私鑰、公鑰并且有口令保護(hù),在證書(shū)泄露后還有最后一道保障。沒(méi)有證書(shū)口令無(wú)法提取秘鑰。
對(duì)PKCS標(biāo)準(zhǔn)感興趣的小伙伴可以參考百度百科PKCS介紹
什么是X.509格式
在密碼學(xué)中,X.509是定義公鑰證書(shū)格式的標(biāo)準(zhǔn)。X.509證書(shū)用于許多Internet協(xié)議,包括TLS/SSL,它是HTTPS(用于瀏覽web的安全協(xié)議)的基礎(chǔ)。它們也用于離線(xiàn)應(yīng)用程序,比如電子簽名。一個(gè)X.509證書(shū)包含一個(gè)公鑰和一個(gè)標(biāo)識(shí)(主機(jī)名、組織或個(gè)人),由證書(shū)頒發(fā)機(jī)構(gòu)簽名或自簽名。當(dāng)證書(shū)由受信任的證書(shū)頒發(fā)機(jī)構(gòu)簽名時(shí),或者通過(guò)其他方法進(jìn)行驗(yàn)證時(shí),持有該證書(shū)的人可以依賴(lài)于它包含的公鑰來(lái)與另一方建立安全通信,或者驗(yàn)證由相應(yīng)私鑰數(shù)字簽名的文檔。
X.509還定義了證書(shū)撤銷(xiāo)列表,這是一種分發(fā)被簽名機(jī)構(gòu)認(rèn)為無(wú)效的證書(shū)信息的方法,以及認(rèn)證路徑驗(yàn)證算法,該算法允許證書(shū)由中間CA證書(shū)簽名,而中間CA證書(shū)又由其他證書(shū)簽名,最終到達(dá)信任錨。
X.509由國(guó)際電信聯(lián)盟標(biāo)準(zhǔn)化部門(mén)(ITU-T)定義,并基于ITU-T的另一個(gè)標(biāo)準(zhǔn)ASN.1。
SSL Certificate (編碼)格式
SSL Certificate實(shí)際上就是X.509 Certificate。X.509是一個(gè)定義了certificate結(jié)構(gòu)的標(biāo)準(zhǔn)。它在SSL certificate中定義了一個(gè)數(shù)據(jù)域。X.509使用名為 Abstract Syntax Notation One (ASN.1)的通用語(yǔ)言來(lái)描述certificate的數(shù)據(jù)結(jié)構(gòu)。
X.509 certificate 有幾種不同的格式,例如 PEM,DER,PKCS#7 和 PKCS#12。PEM和PKCS#7格式使用Base64 ASCII編碼,而DER和PKCS#12使用二進(jìn)制編碼。certificate文件基于不同的編碼格式有不同的文件擴(kuò)展名。
如下圖就展示了X.509證書(shū)的編碼方式和文件擴(kuò)展名。
X.509 證書(shū)結(jié)構(gòu)
X.509證書(shū)的結(jié)構(gòu)是用ASN.1(Abstract Syntax Notation One:抽象語(yǔ)法標(biāo)記)來(lái)描述其數(shù)據(jù)結(jié)構(gòu),并使用ASN1語(yǔ)法進(jìn)行編碼。
X.509 v3數(shù)字證書(shū)的結(jié)構(gòu)如下:
certificate 證書(shū)
Version Number版本號(hào)
Serial Number序列號(hào)
ID Signature Algorithm ID簽名算法
Issuer Name頒發(fā)者名稱(chēng)
Validity period 有效期
Not before起始日期
Not after截至日期
Subject Name主題名稱(chēng)
Subject pbulic Key Info 主題公鑰信息
Public Key Algorithm公鑰算法
Subject Public Key主題公鑰
Issuer Unique Identifier (optional)頒發(fā)者唯一標(biāo)識(shí)符(可選)
Subject Unique Identifier (optional)主題唯一標(biāo)識(shí)符(可選)
Extensions (optional) 證書(shū)的擴(kuò)展項(xiàng)(可選)
Certificate Sigature Algorithm證書(shū)簽名算法
Certificate Signature證書(shū)的簽名
證書(shū)操作
證書(shū)生成
/// <summary>/// 生成證書(shū)/// </summary>/// <param name="notAfter">證書(shū)失效時(shí)間</param>/// <param name="keyStrength">密鑰長(zhǎng)度</param>/// <param name="password">證書(shū)密碼</param>/// <param name="signatureAlgorithm">設(shè)置將用于簽署此證書(shū)的簽名算法</param>/// <param name="issuer">設(shè)置此證書(shū)頒發(fā)者的DN</param>/// <param name="subject">設(shè)置此證書(shū)使用者的DN</param>/// <param name="friendlyName">設(shè)置證書(shū)友好名稱(chēng)(可選)</param>/// <param name="notBefore">證書(shū)生效時(shí)間</param>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密鑰對(duì)生成器keyPairGenerator.Init(keyGenerationParameters);var subjectKeyPair = keyPairGenerator.GenerateKeyPair();ISignatureFactory signatureFactory = new Asn1SignatureFactory(signatureAlgorithm, subjectKeyPair.Private, random);//the certificate generatorX509V3CertificateGenerator certificateGenerator = new X509V3CertificateGenerator();var spki = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(subjectKeyPair.Public);//設(shè)置一些擴(kuò)展字段//允許作為一個(gè)CA證書(shū)(可以頒發(fā)下級(jí)證書(shū)或進(jìn)行簽名)certificateGenerator.AddExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(true));//使用者密鑰標(biāo)識(shí)符certificateGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier, false, new SubjectKeyIdentifier(spki));//授權(quán)密鑰標(biāo)識(shí)符certificateGenerator.AddExtension(X509Extensions.AuthorityKeyIdentifier, false, new AuthorityKeyIdentifier(spki));certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, true, new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth));//證書(shū)序列號(hào)BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), random);certificateGenerator.SetSerialNumber(serialNumber);certificateGenerator.SetIssuerDN(issuer); //頒發(fā)者信息certificateGenerator.SetSubjectDN(subject); //使用者信息certificateGenerator.SetNotBefore(notBefore); //證書(shū)生效時(shí)間certificateGenerator.SetNotAfter(notAfter); //證書(shū)失效時(shí)間certificateGenerator.SetPublicKey(subjectKeyPair.Public);Org.BouncyCastle.X509.X509Certificate certificate = certificateGenerator.Generate(signatureFactory);//生成cer證書(shū),公鑰證書(shū)//var certificate2 = new X509Certificate2(DotNetUtilities.ToX509Certificate(certificate))//{// FriendlyName = friendlyName, //設(shè)置友好名稱(chēng)//};cer公鑰文件//var bytes = certificate2.Export(X509ContentType.Cert);//using (var fs = new FileStream(certPath, FileMode.Create))//{// fs.Write(bytes, 0, bytes.Length);//}//另一種代碼生成p12證書(shū)的方式(要求使用.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); //設(shè)置證書(shū)var chain = new X509CertificateEntry[1];chain[0] = certEntry;store.SetKeyEntry(friendlyName, new AsymmetricKeyEntry(subjectKeyPair.Private), chain); //設(shè)置私鑰using (var fs = File.Create(filename)){store.Save(fs, password.ToCharArray(), random); //保存};}private static void Certificate_Sample(){//頒發(fā)者DNvar 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",});//使用者DNvar 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"; //證書(shū)密碼var signatureAlgorithm = "SHA256WITHRSA"; //簽名算法//生成證書(shū)CertificateUtilities.GenerateCertificate("fuluca.pfx", password, signatureAlgorithm, issuer, subject, DateTime.UtcNow.AddDays(-1), DateTime.UtcNow.AddYears(2), "fulu passport");//加載證書(shū)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證書(shū)私鑰:");Console.WriteLine(privateKey);Console.WriteLine("Pfx證書(shū)公鑰:");Console.WriteLine(publicKey);var data = "hello rsa";Console.WriteLine($"加密原文:{data}");var pkcs1data = RSA.EncryptToBase64(data, AsymmetricKeyUtilities.GetAsymmetricKeyParameterFormPublicKey(publicKey), Algorithms.RSA_ECB_PKCS1Padding);Console.WriteLine("加密結(jié)果:");Console.WriteLine(pkcs1data);Console.WriteLine("解密結(jié)果:");var datares = RSA.DecryptFromBase64(pkcs1data,AsymmetricKeyUtilities.GetAsymmetricKeyParameterFormPrivateKey(privateKey), Algorithms.RSA_ECB_PKCS1Padding);Console.WriteLine(datares);}生成的證書(shū)文件:
證書(shū)安裝
雙擊證書(shū)文件進(jìn)行安裝,存儲(chǔ)位置選擇當(dāng)前用戶(hù)。
證書(shū)存儲(chǔ)選擇個(gè)人
查看安裝的證書(shū)
可以在MMC的證書(shū)管理單元中對(duì)證書(shū)存儲(chǔ)區(qū)進(jìn)行管理。Windows沒(méi)有給我們準(zhǔn)備好直接的管理證書(shū)的入口。自己在MMC中添加,步驟如下:
開(kāi)始→運(yùn)行→MMC,打開(kāi)一個(gè)空的MMC控制臺(tái)。
在控制臺(tái)菜單,文件→添加/刪除管理單元→添加按鈕→選”證書(shū)”→添加→選”我的用戶(hù)賬戶(hù)”→關(guān)閉→確定
展開(kāi) 證書(shū)控制臺(tái)根節(jié)點(diǎn)→證書(shū)-當(dāng)前用戶(hù)→個(gè)人→證書(shū),找到證書(shū),可以看到下圖中選中的即為我們創(chuàng)建的證書(shū)文件
雙擊證書(shū),可以看到證書(shū)的相關(guān)信息
OpenSSL安裝
工具:openssl
安裝軟件:Win64 OpenSSL v1.1.1g Light
下載地址:http://slproweb.com/products/Win32OpenSSL.html
PFX文件提取公鑰私鑰
openssl pkcs12 -in fulusso.pfx -nocerts -nodes -out private.key輸入密碼openssl rsa -in private.key -out pfx_pri.pemopenssl rsa -in private.key -pubout -out pfx_pub.pem安裝好OpenSSL后,打開(kāi)Win64 OpenSSL Command Prompt,讀取到證書(shū)文件所在目錄,按上述命令執(zhí)行
打開(kāi)證書(shū)所在目錄,可以看到文件 private.key、pfx_pri.pem、pfx_pub.pem 已經(jīng)生成好了。
用文本工具打開(kāi)私鑰文件pfx_pri.pem,如下圖:
打開(kāi)公約文件pfx_pub.pem,如下圖:
比對(duì)與上文控制臺(tái)打印出的公鑰、私鑰一致。
下期預(yù)告
下一篇將介紹國(guó)密算法,敬請(qǐng)期待。。。
?
總結(jié)
以上是生活随笔為你收集整理的.NET Core加解密实战系列之——使用BouncyCastle制作p12(.pfx)数字证书的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 基于REACT和.NET CORE集成W
- 下一篇: ASP.NET Core 3.x启动时运