.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.BouncyCastle Portable.BouncyCastle • 1.8.6

前言

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

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

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

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

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

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

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

证书操作

证书生成

/// 
/// 生成证书
/// 
/// 证书失效时间
/// 密钥长度
/// 证书密码
/// 设置将用于签署此证书的签名算法
/// 设置此证书颁发者的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);
}

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

生成的证书文件:

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

证书安装

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

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

证书存储选择个人

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

查看安装的证书

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

  1. 开始→运行→MMC,打开一个空的MMC控制台。
  2. 在控制台菜单,文件→添加/删除管理单元→添加按钮→选”证书”→添加→选”我的用户账户”→关闭→确定

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

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

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

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

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

OpenSSL安装

工具:openssl

安装软件:Win64 OpenSSL v1.1.1g Light

下载地址:http://slproweb.com/products/Win32OpenSSL.html

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

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,读取到证书文件所在目录,按上述命令执行

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

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

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

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

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

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

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

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

下期预告

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

福禄ICH·架构组 福禄娃

你可能感兴趣的:(.NET Core加解密实战系列之——使用BouncyCastle制作p12(.pfx)数字证书)