1. CA 的基本原理简介(了解相关背景知识的读者请跳过这一部分)
我们回想一下在生活中,两个以前从未谋面的人如何核实对方的身份。由于每一个公民都有派出所颁发的身份证,两个人只要通过查看对方的身份证,就可以大体上知道对方是谁。这个验证身份的过程如下图:
在互联网上,识别陌生实体的思路与上述过程类似。首先建立一个有公信力的认证机构 CA(即Certificate Authority),用户向 CA 提交申请,请求 CA 为自己颁发数字证书。用户可以是个人、企业、组织、网站等,数字证书可以被视为一个特殊的数据结构,最常用的数字证书格式规范是 X.509,它由 RFC 5280《Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile》定义。CA 检查用户的实际身份、用户提交申请材料中所宣称的身份这二者是否一致,如果一致,就使用数字签名技术,为用户签署数字证书。有了 CA 这样一个可信第三方作为信任基础,两个用户无需见面,就能通过验证对方的数字证书,在互联网上识别对方的身份。大体过程如下图所示:
如果整个互联网上只有一个 CA,那么一旦这个 CA 被攻击者攻破,则整个依赖这个 CA 颁发证书建立起来的信任体系就会崩溃。为了提高安全性,可以使用多个不同层级的 CA,如下图所示:
在这张图中使用了两级 CA,根 CA 为它自己和下级 CA 颁发数字证书,底层 CA 为用户颁发数字证书。根 CA 只为少量的下级 CA 颁发证书,不需要一直提供在线服务,这就减轻了根 CA 长期暴露在网络上遭受攻击的可能性,也不用考虑服务性能问题。不受服务性能羁绊,根 CA 可以使用长度更长的私钥,这样安全性更强。在多层 CA 服务体系中,上一级的 CA 为下一级 CA 颁发证书,只通过底层 CA 为普通用户颁发证书。如果某一个底层 CA 出故障无法工作或被攻击者攻破,则最多只需要吊销该 CA 颁发的数字证书,其他 CA 及其下属用户不受影响。可以很容易地再创建一个底层 CA、或者由其他底层 CA 来替代它。通过采用层次 CA 体系结构,能够有效提高整个 CA 信任体系的生存能力和抗攻击能力。
2. 使用 OpenSSL 命令行创建包含 subAltName 扩展项的证书
开源软件 OpenSSL 在编译以后,将会生成一个名为 openssl 的可执行文件。可以使用它来生成非对称密钥对、创建数字证书。为了辅助用户设置证书中某些项的值,OpenSSL 中包含了一个名为 openssl.cnf 的配置文件。使用这个默认的配置文件,可以使用 OpenSSL 命令行创建数字证书,但是在生成的数字证书中不包含 subAltName 扩展项。接下来本文将介绍如何修改配置文件,再使用 OpenSSL 命令行程序创建包含 subAltName 扩展项的数字证书。
本文使用的 OpenSSL 版本是 1.0.2d,操作系统是 64 位Windows 7,编译 OpenSSL 的过程可以参考:https://blog.csdn.net/henter/article/details/8364532 。将 OpenSSL 编译为 32 位库或 64 位库均可,在创建证书文件时并没有差别。
编译完 OpenSSL 之后,为了后续操作的方便,建议新建一个目录,这里假定目录名为 ca_test,将 OpenSSL 目录下 apps 子目录下的 openssl.cnf 文件拷贝到 ca_test 目录下。接下来以下操作二选一:
A) 拷贝 out32 子目录下的 openssl.exe,libeay32.lib,ssleay32.lib 到 ca_test 目录;
B) 拷贝 out32dll 子目录下的 openssl.exe,libeay32.dll,ssleay32.dll 到 ca_test 目录。
进入命令行界面,执行命令 openssl.exe,将会出现 OpenSSL 提示符,接下来就可以输入 OpenSSL 特有的命令,进行创建证书的操作了。如下图:
可以使用 OpenSSL 命令行直接创建一张供终端用户使用的 X.509 证书,但是一般在做测试时,经常会用到多张不同的用户证书。为了方便,在本文中先创建一张 CA 证书,接下来用这张 CA 证书对应的非对称密钥对,为一个终端用户签发证书。这里仅使用一个 CA,未使用多级 CA。
第一阶段:创建 CA 的证书(注意 这个 CA 的证书是自签名证书);
这里使用 RSA 公钥密码算法,首先创建一个名为 root-key.pem 的文件,里面包含供 CA 使用的 RSA 私钥,私钥长度设为 2048 比特,命令如下:
genrsa -out .\root-key.pem 2048
执行过程如下图:
可以用文本编辑器查看 root-key.pem 文件,内容如下:
创建证书请求文件 root-req.csr,命令如下:
req -new -out .\root-req.csr -key .\root-key.pem -config .\openssl.cnf
命令在执行时,会提示用户依次输入国家、省、市、公司名、部门名等信息。如果对于某一项不想输入具体的值,可以通过输入 . 跳过该项。在本例中,依次输入的值如下:
CN
Shanghai
Shanghai
TestCorp
CA
RootCA
.
.
.
以上执行过程如下图:
对于具体命令的用法,可以到 OpenSSL 官网上查看文档,本文中不再做解释。比如对于 req 命令的介绍链接在:https://www.openssl.org/docs/man1.0.2/man1/req.html
使用 CA 的密钥文件 root-cert.pem 和证书请求文件 root-req.csr,创建 CA 的数字证书 root-cert.pem。命令如下:
x509 -req -in .\root-req.csr -out .\root-cert.pem -signkey .\root-key.pem -CAcreateserial -days 3650 -extfile .\openssl.cnf -extensions v3_ca
执行过程如下:
可以使用文本编辑器查看证书文件 root-cert.pem,内容如下:
在 Windows 7 下不能直接通过双击文件图标打开以 pem 为后缀的证书文件,但是可以打开以 cer 为后缀的证书文件。为了方便在 Windows 下查看证书内容,可以用以下命令对证书的格式做转换,生成一个以 cer 为后缀的证书文件:
x509 -inform PEM -outform DER -in .\root-cert.pem -out .\root-cert.cer
在 Windows 中双击 root-cert.cer 文件图标,将弹出操作系统自带的证书查看程序,显示证书内容如下:
第二阶段:使用 CA 证书对应的非对称密钥对,为用户签发证书。
首先为用户生成包含 RSA 私钥的 pem 文件,这里假定文件名为 client-key.pem,私钥长度设为 2048 比特。命令如下:
genrsa -out .\client-key.pem 2048
先备份一下 openssl.cnf 文件,然后使用文本编辑器打开它,在其中找到 [ v3_req ] ,在该段内容的末尾另起一行加入以下内容:
subjectAltName = @alternate_names
[ alternate_names ]
IP.1 = 192.168.15.1
DNS.1 = 127.0.0.1
DNS.2 = www.mytestweb.com
注意这里设置的值仅作为示例,用户应根据实际设置自己需要的值。在 = 前后都有一个空格,在 alternate_names 的前后也各有一个空格。 IP.1 的值是是一个 IP 地址,DNS.1 的值可以是 IP 地址也可以是一个域名。具体的取值根据实际需要自行设置,可以添加形式如下的任意多个项:
IP.2 = ...
IP.3 = ...
...
DNS.3 = ...
DNS.4 = ...
...
编辑完成后保存文件。修改部分的内容如下图:
为用户创建证书请求文件,假定文件名为 client-req.csr。命令如下:
req -new -out .\client-req.csr -key .\client-key.pem -config .\openssl.cnf
命令在执行时,会提示用户依次输入国家、省(或州)、市、公司名、部门名等信息。如果对于某一项不想输入具体的值,可以通过输入 . 跳过该项。在本例中,依次输入的值如下:
CN
Shanghai
Shanghai
TestCorp
DEV
127.0.0.1
.
.
.
命令执行过程如下:
注意生成用户证书时,用户证书中的国家名、省名要与 CA 证书中的对应项相同。即使用 OpenSSL 命令行时, CA 只能为一定地理范围之内(即在同一国家、同一个省或州的范围之内)的用户签发证书。并且对于输入的 Common Name 这一项,其取值必须与 openssl.cnf 中 DNS.1 项的值相同。
接下来使用 CA 证书对应的非对称密钥对,根据用户的证书申请文件为用户创建证书。这里假定生成的证书文件名为 client-cert.pem,命令如下:
x509 -req -in .\client-req.csr -out .\client-cert.pem -CA root-cert.pem -CAkey root-key.pem -CAcreateserial -days 3650 -extfile .\openssl.cnf -extensions v3_req
命令执行过程如下图:
为了方便在 Windows 下查看证书,将以 pem 为后缀的证书文件 client-cert.pem 转换为以 cer 为后缀的证书文件,命令如下:
x509 -inform PEM -outform DER -in .\client-cert.pem -out .\client-cert.cer
使用 CA 的证书校验一下客户的证书是否有效,命令如下:
verify -CAfile .\root-cert.pem .\client-cert.pem
命令执行过程如下:
双击 client-cert.cer 文件图标查看证书文件,显示如下:
可以看到在生成的证书中,包含“使用者备用名称”(即subjectAltName)这一项,其值就是在 openssl.cnf 文件中设置的值。
OpenSSL 只提供了一个配置文件 openssl.cnf,这个文件在为 CA 生成证书及为普通用户生成证书时都会被用到,这种处理方式是不妥的。在不同应用场景下,该文件的内容应该有所不同。为了防止混淆,在修改 openssl.cnf 之前最好先备份它。更为合理的处理方式是编写两个配置文件,一个用于生成 CA 证书,另一个用于生成一般的用户证书,将两套不同的配置区分开,可惜目前 OpenSSL 并没有采用这种方式。
如果想让浏览器能够识别证书,需要向网上的专业数字证书服务提供商购买证书。如果只是在很小范围内使用数字证书,可以自己创建证书,然后将证书手动添加到浏览器能识别的证书列表中。这里以 IE 浏览器为例,过程如下:
在 IE 菜单中点击“工具”,在下列列表中点击“Internet选项”,如下图:
在弹出小窗体中点击“证书”按钮,如下图:
根据具体的证书,在选项卡中选择“个人”、“其他人”,点击“导入”按钮,就可以导入这个用户对应的以 cer 为后缀的证书文件,比如前面生成的用户证书 client-cert.cer。如下图:
如果在选项卡中选择“受信任的根证书颁发机构”,点击“导入”按钮,就可以导入 CA 的证书,比如前面生成的 CA 证书 root-cert.cer。如下图:
在导入根证书颁发机构的证书之后,IE 浏览器会自动信任所有由该 CA 颁发的用户证书。在做测试时这样做可以带来很大的便利,只需要做一次导入 CA 证书的操作,后续就不再需要手动添加多张用于测试、临时生成的用户证书了。