.NET Core加解密实战系列之——使用BouncyCastle制作p12(.pfx)数字证书

简介

加解密现状,编写此系列文章的背景:

  • 需要考虑系统环境兼容性问题(Linux、Windows)

  • 语言互通问题(如C#、Java等)(加解密本质上没有语言之分,所以原则上不存在互通性问题)

  • 网上资料版本不一、或不全面

  • .NET官方库密码算法提供不全面,很难针对其他语言(Java)进行适配

本系列文章主要介绍如何在 .NET Core 中使用非对称加密算法、编码算法、消息摘要算法、签名算法、对称加密算法、国密算法等一系列算法,如有错误之处,还请大家批评指正。

本系列文章旨在引导大家能快速、轻松的了解接入加解密,乃至自主组合搭配使用BouncyCastle密码术包中提供的算法。

本系列代码项目地址:https://github.com/fuluteam/ICH.BouncyCastle.git

上一篇文章《.NET Core加解密实战系列之——对称加密算法》: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.BouncyCastlePortable.BouncyCastle • 1.8.6

前言

在工作中我们难免会接触对接外部系统(如银行、支付宝、微信等),对接过程中又无可避免会对数据的加解密和加签验签。一般第三方会提供一个授权证书,让我们自行解密提取秘钥。为了让你拿到证书后不会像我当初一样一脸懵逼,咱们来看看如何使用C#代码制作使用p12证书。

当然,比较常见的,还是推荐大家使用OpenSSL。

OpenSSL是目前最流行的 SSL密码库工具,其提供了一个通用、健壮、功能完备的工具套件,用以支持SSL/TLS 协议的实现。
官网:https://www.openssl.org/source/

什么是p12证书

公钥加密技术12号标准(Public Key Cryptography Standards #12,PKCS#12)为存储和传输用户或服务器私钥、公钥和证书指定了一个可移植的格式。它是一种二进制格式,这些文件也称为PFX文件。

P12证书包含了私钥、公钥并且有口令保护,在证书泄露后还有最后一道保障。没有证书口令无法提取秘钥。
对PKCS标准感兴趣的小伙伴可以参考百度百科PKCS介绍

什么是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证书的编码方式和文件扩展名。

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证书的签名

证书操作

证书生成

/// <summary>

/// 生成证书

/// </summary>

/// <param name="notAfter">证书失效时间</param>

/// <param name="keyStrength">密钥长度</param>

/// <param name="password">证书密码</param>

/// <param name="signatureAlgorithm">设置将用于签署此证书的签名算法</param>

/// <param name="issuer">设置此证书颁发者的DN</param>

/// <param name="subject">设置此证书使用者的DN</param>

/// <param name="friendlyName">设置证书友好名称(可选)</param>

/// <param name="notBefore">证书生效时间</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密钥对生成器

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);

}

生成的证书文件:

证书安装

双击证书文件进行安装,存储位置选择当前用户。

证书存储选择个人

查看安装的证书

可以在MMC的证书管理单元中对证书存储区进行管理。Windows没有给我们准备好直接的管理证书的入口。自己在MMC中添加,步骤如下:

  1. 开始→运行→MMC,打开一个空的MMC控制台。

  2. 在控制台菜单,文件→添加/删除管理单元→添加按钮→选”证书”→添加→选”我的用户账户”→关闭→确定

展开 证书控制台根节点→证书-当前用户→个人→证书,找到证书,可以看到下图中选中的即为我们创建的证书文件

双击证书,可以看到证书的相关信息

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.pem

openssl rsa -in private.key -pubout -out pfx_pub.pem

安装好OpenSSL后,打开Win64 OpenSSL Command Prompt,读取到证书文件所在目录,按上述命令执行

打开证书所在目录,可以看到文件 private.key、pfx_pri.pem、pfx_pub.pem 已经生成好了。

用文本工具打开私钥文件pfx_pri.pem,如下图:

打开公约文件pfx_pub.pem,如下图:

比对与上文控制台打印出的公钥、私钥一致。

下期预告

下一篇将介绍国密算法,敬请期待。。。


BouncyCastle 是一个流行的开源加密库,提供了多种加密算法和安全协议的实现。.NET Core 也可以使用 BouncyCastle 库来实现加密和解密的功能。 在 .NET Core使用 BouncyCastle 库,需要先安装 BouncyCastle NuGet 包。可以通过 Visual Studio 中的 NuGet 包管理器搜索并安装,也可以通过命令行使用 dotnet 命令进行安装: ``` dotnet add package BouncyCastle ``` 安装完成后,就可以在代码中使用 BouncyCastle 库提供的加密和解密方法了。以下是一个使用 BouncyCastle 库进行 AES 加密和解密的示例代码: ```csharp using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; public class AesCipher { private readonly byte[] _key; private readonly byte[] _iv; public AesCipher(byte[] key, byte[] iv) { _key = key; _iv = iv; } public byte[] Encrypt(byte[] data) { IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CBC/PKCS7Padding"); cipher.Init(true, new ParametersWithIV(new KeyParameter(_key), _iv)); return cipher.DoFinal(data); } public byte[] Decrypt(byte[] data) { IBufferedCipher cipher = CipherUtilities.GetCipher("AES/CBC/PKCS7Padding"); cipher.Init(false, new ParametersWithIV(new KeyParameter(_key), _iv)); return cipher.DoFinal(data); } } ``` 在上面的示例代码中,我们使用BouncyCastle 库提供的 `IBufferedCipher` 接口来进行加密和解密。`CipherUtilities.GetCipher` 方法可以根据指定的算法名称获取对应的 `IBufferedCipher` 实例。在初始化 `IBufferedCipher` 实例时,我们需要传入加密/解密的模式、密钥和初始化向量等参数。在加密/解密时,我们调用 `DoFinal` 方法来执行加密/解密操作,并返回加密/解密后的结果。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值