简介

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

  • 需要考虑系统环境兼容性问题(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/

什么是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,如下图:

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

下期预告

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

福禄ICH·架构组
福禄娃

.NET Core加解密实战系列之——使用BouncyCastle制作p12(.pfx)数字证书的更多相关文章

  1. .NET Core加解密实战系列之——消息摘要与数字签名算法

    目录 简介 功能依赖 消息摘要算法 MD算法 家族发展史 应用场景 代码实现 MD5 示例代码 SHA算法 应用场景 代码实现 SHA1 SHA256 示例代码 MAC算法 HMAC算法的典型应用 H ...

  2. .NET Core加解密实战系列之——对称加密算法

    简介 加解密现状,编写此系列文章的背景: 需要考虑系统环境兼容性问题(Linux.Windows) 语言互通问题(如C#.Java等)(加解密本质上没有语言之分,所以原则上不存在互通性问题) 网上资料 ...

  3. .NET Core加解密实战系列之——RSA非对称加密算法

    目录 简介 功能依赖 生成RSA秘钥 PKCS1格式 PKCS8格式 私钥操作 PKCS1与PKCS8格式互转 PKCS1与PKCS8私钥中提取公钥 PEM操作 PEM格式密钥读取 PEM格式密钥写入 ...

  4. Java 加解密技术系列文章

    Java 加解密技术系列之 总结 Java 加解密技术系列之 DH Java 加解密技术系列之 RSA Java 加解密技术系列之 PBE Java 加解密技术系列之 AES Java 加解密技术系列 ...

  5. 11.Java 加解密技术系列之 总结

    Java 加解密技术系列之 总结 序 背景 分类 常用算法 原理 关于代码 结束语 序 上一篇文章中简单的介绍了第二种非对称加密算法 — — DH,这种算法也经常被叫做密钥交换协议,它主要是针对密钥的 ...

  6. 10.Java 加解密技术系列之 DH

    Java 加解密技术系列之 DH 序 概念 原理 代码实现 结果 结束语 序 上一篇文章中简单的介绍了一种非对称加密算法 — — RSA,今天这篇文章,继续介绍另一种非对称加密算法 — — DH.当然 ...

  7. 9.Java 加解密技术系列之 RSA

    Java 加解密技术系列之 RSA 序 概念 工作流程 RSA 代码实现 加解密结果 结束语 序 距 离上一次写博客感觉已经很长时间了,先吐槽一下,这个月以来,公司一直在加班,又是发版.上线,又是新项 ...

  8. 8.Java 加解密技术系列之 PBE

    Java 加解密技术系列之 PBE 序 概念 原理 代码实现 结束语 序 前 边的几篇文章,已经讲了几个对称加密的算法了,今天这篇文章再介绍最后一种对称加密算法 — — PBE,这种加密算法,对我的认 ...

  9. 7.java 加解密技术系列之 AES

    java 加解密技术系列之 AES 序 概念 原理 应用 代码实现 结束语 序 这篇文章继续介绍对称加密算法,至于今天的主角,不用说,也是个厉害的角色 — — AES.AES 的出现,就是为了来替代原 ...

随机推荐

  1. CODING 仪表盘功能正式推出,实现工作数据可视化!

    CODING 仪表盘功能现已正式推出!该功能旨在用一张张统计卡片的形式,统计并展示使用 CODING 中所产生的数据.这意味着无需额外的设置,就可以收集归纳宝贵的工作数据并予之量化分析.这些海量的数据 ...

  2. 基于python tkinter的点名小程序

    import datetimeimport jsonimport osimport randomimport tkinter as tkimport openpyxl # 花名册文件名excel_fi ...

  3. Spring Boot Jpa 多条件查询+排序+分页

    事情有点多,于是快一个月没写东西了,今天补上上次说的. JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将 ...

  4. 多元线性回归检验t检验(P值),F检验,R方等参数的含义

    做线性回归的时候,检验回归方程和各变量对因变量的解释参数很容易搞混乱,下面对这些参数进行一下说明: 1.t检验:t检验是对单个变量系数的显著性检验   一般看p值:    如果p值小于0.05表示该自 ...

  5. Git-commit-中添加表情

    git commit 中使用表情 我们经常可以在github上看到国外大佬的commit信息中有很多可爱的表情,这是怎么做到的呢? ok,可以这样使用哦:git commit -m '提交信息 :em ...

  6. 分享一个操作pdf文件的js文件-pdfObject.js(文件预览、下载、打印等操作都具备)

    获取相关资料或者源码的朋友可以关注下公众号,回复关键字pdf20200518即可

  7. 《MySQL数据库》MySQL备份恢复

    前言 MySQL数据库最重要的部分就是数据,所以保证数据不被损坏尤为重要,大家都知道911事件,当时非常多的数据丢失,导致经济混乱.接下来我们就来讲讲MySQL是如何保障数据完整,应对特殊情况,如何恢 ...

  8. 力扣Leetcode 面试题51. 数组中的逆序对 - 归并排序

    在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对.输入一个数组,求出这个数组中的逆序对的总数. 示例 1: 输入: [7,5,6,4] 输出: 5 限制: 0 <= ...

  9. Urule开源版系列4——Core包核心接口之规则解析过程

    Urule运行规则文件,是如何进行的,通过一个请求doTest来探一下 com.bstek.urule.console.servlet.respackage.PackageServletHandler ...

  10. Excel-时间函数将时间换成小时

    问题场景 用考勤打卡时间算员工饱和度. 场景 计算员工实际工作时长,需要算出打卡时长再减去午休时长1.5小时. 目标 算出实际工作时长. 解决方案 利用单元格格式设置进行简单计算. 第一步:在F2单元 ...