运营一个小型电子证书认证机构

2023.11.13

公开密钥基础设施( Public Key Infrastructure, PKI ),是确保现今互联网传输安全的基础技术。我们日常上网使用的工具里,很多是基于自由/开源的软件工具,比如 OpenSSLGnuTLSNetwork Security Services ,以及嵌入式平台上的 wolfSSL 等等实现。因为有这些工具,不需要很大的花费,我们就可以运营一个小型的电子证书认证机构,作为给亲朋好友或一个小型公司内部用户验证的手段。本文以一个很常见的工具 OpenSSL 为例,展示运营一个小型证书认证机构的常见操作。

一开始我之所以会想自己运营一个电子证书认证机构,是为了在自己配置的虚拟专用网络( VPN )服务中用电子证书验证和加密传输的数据。

普通用户需要知道的概念

电子证书认证机构工作流程中的专用名词:

为了方便普通的网友理解,这里拿中华人民共和国居民身份证做类比:

实际操作

本文中使用的 OpenSSL 版本为 3.1.4 ,读者如果以本文作为参照的话,只要使用不太旧的 OpenSSL 版本,命令行接口选项应该差不多。在本文的示例中,我们有两个电子证书持有者:颁发者和一个持有者。

在电子证书颁发机构端生成根证书

创建证书颁发机构的私钥,并把它利用 128 位高级加密标准算法加密,然后存储在 encrypted.ca.key.pem 文件中, OpenSSL 会询问两次用于加密私钥数据的密码。

$ openssl ecparam -genkey -name prime256v1 | openssl ec -aes128 -out encrypted.ca.key.pem
read EC key
writing EC key
Enter pass phrase for PEM:
Verifying - Enter pass phrase for PEM:

运行 openssl ecparam -list_curves 可以列出 OpenSSL 支持的椭圆曲线密钥算法

接着用 OpenSSL 的 req 子命令从证书颁发者的私钥生成机构的根证书, OpenSSL 需要从私钥数据生成根证书,所以需要提供上面命令过程中输入的密码以解密私钥文件。

$ openssl req -x509 -new -sha256 -days 1200 -key encrypted.ca.key.pem \
  -subj '/C=CN/O=Swan CA/CN=Swan Root Cert' -out ca.crt.pem
Enter pass phrase for encrypted.ca.key.pem:

这个示例命令中,根证书有效期 1200 天,组织名字由 O=Swan CA 指定,用于识别根证书的 common name 字段由 CN=Swan Root Cert 指定。

可以使用 OpenSSL 的 x509 子命令解码证书数据,查看里面的内容:

$ openssl x509 -in ca.crt.pem -noout -text
.
Signature Algorithm: ecdsa-with-SHA256
Issuer: C = CN, O = Swan CA, CN = Swan Root Cert
Validity
    Not Before: Nov 13 09:26:38 2023 GMT
    Not After : Feb 25 09:26:38 2027 GMT
Subject: C = CN, O = Swan CA, CN = Swan Root Cert
.

电子证书中一些重要字段:

生成某一证书持有者的证书

证书持有者生成自己的私钥文件 user1.key.pem ,此处的文件不会被加密。

$ openssl ecparam -genkey -name prime256v1 | openssl ec -out user1.key.pem

生成证书签名请求( Certificate Signing Request )文件 user1.csr.pem ,然后把文件发送给证书颁发机构。

$ openssl req -new -sha256 -key user1.key.pem -out user1.csr.pem \
  -subj "/C=CN/O=Swan CA/[email protected]"

此处的 O=Swan CA 不一定是必需的。

颁发机构端生成持有者的电子证书

这里需要用到颁发者的私钥文件 encrypted.ca.key.pem 、颁发者的证书文件 ca.crt.pem 以及一个普通电子证书持有者的证书签名请求文件 user1.csr.pem ,然后生成持有者的电子证书文件 user1.crt.pem ,命令中需要提供解密颁发者私钥数据文件 encrypted.ca.key.pem 的密码,。

$ openssl x509 -addtrust clientAuth -addtrust serverAuth \
  -req -days 360 -sha256 -CA ca.crt.pem -CAkey encrypted.ca.key.pem \
  -in user1.csr.pem -out user1.crt.pem

这个命令中,需要配置一些证书的属性,比如 -addtrust clientAuth ,表示持有此电子证书的实体可以作为网络服务客户端参与通信, -addtrust serverAuth 表示此实体可以作为网络服务的服务器端参与通信。实际应用中,各个证书软件工具对标准的证书属性的支持情况,需要查阅各个工具的文档确认。

颁发机构生成这一持有者(让我们把他称为「张三」)的证书文件 user1.crt.pem 后,需要把证书文件发回给张三,然后张三在进行网络通信的时候,就需要给通信的对端(这里我们称其为「李四」)提供自己的证书信息。为了验证证书确实是张三的,李四需要事先获取颁发者的根证书,在通信开始前配置好软件工具指向根证书的文件,用根证书就可以验证通信过程中收到的张三的证书确实是张三的。

重复此段的步骤以生成其他实体或用户的电子证书。

更新某一持有者的电子证书

电子证书颁发机构运营一段时间后,我们需要根据情况更新某一持有者的电子证书,本段展示更新证书的操作步骤。

从持有者私钥重新生成证书签名请求文件 user1.csr.pem ,再重新发给颁发机构。

$ openssl req -new -key user1.key.pem -out user1.csr.pem -subj '/C=CN/O=Swan CA/CN=张三' -utf8

如果持有者的名字字段中含有万国码字符,需要在命令中加上 -utf8 选项。

查看持有者名字字段含有万国码字符的文件时,需要加上 -nameopt utf8 选项,才可以在终端正确显示名字中的字符。

$ openssl req -in user1.csr.pem -nameopt utf8 -noout -text
.
Subject: C=CN, O=Swan CA, CN=张三
.

如果持有者希望更新自己的私钥,可以按生成某一证书持有者的证书段落步骤操作。

后记

根据电子认证机构的用户数量和密钥更新频率,使用本文的命令行手动更新方式可能会不太现实。不过,运营这样一个小型认证机构,主要的挑战可能是在用户对这些技术、对工具不够熟悉。

之前我写了几个快速生成自签名电子证书的脚本,读者可以去 https://gist.github.com/HtwoO/9930212d0c70c4688db57cf3b6187a66 下载脚本来用,或者参考脚本里面的命令。

现今热门的「数字化转型」里很重要的一项基础技术就是数字签章或者叫数字签名技术。如果读者购物时向卖家索取过电子发票,一般下载到的电子发票是 pdf 格式的文件,这些文件通常就是带有数字签章的。在 Linux 或者 macOS 平台上,使用 LibreOffice 办公套件打开 pdf 格式的电子发票,可以查看嵌入在文件中的数字签章。 Adobe 公司的软件对 pdf 数字签章的支持应该不错,可以从他们网站的文档看到他们产品也在做数字签章方面的集成。 Windows 平台的其他 pdf 工具对电子发票数字签章的支持怎样,我还不清楚,现在也没有测试环境。

希望更多人熟悉和使用电子发票,因为它比传统的发票更容易保存和备份。不过因为 pdf 文件一般是二进制文件,里面的信息不是很方便转移到其他工具中,所以有一些新的在开发和推广中的文件格式,比如 ofd 格式。如果用更合适的文件格式,对促进更高效的网购等电子商务活动会更有帮助。

自由/开源软件社区已经使用数字签名技术几十年了,我写本篇文章的一个目的,是希望更多的行业和公司使用数字签名技术,推进无纸化工作流程。

参考资料

Everything you should know about certificates and PKI but are too afraid to ask