1. 基础
1.1. SSL
证书通常使用在SSL(Secure Sockets Layer安全套接字协议)传输协议中,用于实现端侧与服务侧的密钥交换。现在应该叫它"TLS",但是由于习惯的原因,还是常叫它"SSL"。SSL使用场景最多的是在网站上,用它实现安全的HTTP传输。
HTTP协议传输是不加密内容的,这样,内容在传播的时候,就可能被人监听。在安全性要求较高的场景中,必须加密传输,比如,登录时传输密码的场景。
HTTPS就是带加密的HTTP协议,而HTTPS的加密传输是基于SSL实现的,使得,即使在网络传输中的某个节点别人监听,也不会泄露数据。SSL是在HTTP的下层实现加解密,加解密对用户和开发者来说,都是透明的。也就是说,在加密前,你的服务器是怎样实现的,加密后,不用做任何改变。
还有一个名词就是OpenSSL。简单地说,SSL是一种规范,而OpenSSL是SSL的一个实现,提供了一堆强大的工具,强大到90%我们都用不到。理论上来说,目前的技术水平很难破解SSL,但是SSL的实现却可能有漏洞,如著名的”心脏出血“。
1.2. 什么是证书
SSL中用到证书,那么证书是什么?
证书可以理解为一个包含了签发方信息、拥有者信息、公钥、由签发方私钥产生的签名等信息的文档,当然还包括其他一些信息。校验用户证书是否可信,实际上就是检验该证书是否由合法的机构签发的。校验时,通过该证书中的结构信息找到对应机构的证书,利用机构证书中的公钥去校验用户证书中的签名是否正确。
从上述校验方法可以看出,证书是否可信,是由其签发方证书来校验的,而机构的证书是否可信,是由上一层机构的证书来校验的,如此就形成一条证书链,最顶层机构的证书的就是常说的根证书。
在网站的场景中,网站产生密钥库,记录网站的公钥、私钥及其他一些信息;然后从机构申请证书。生成的证书中包括了公钥等信息,同时包括了证书链,用来发布给网站的用户。用户访问网站时,先下载网站证书,校验证书链,然后使用其中的公钥与网站交换密钥。
1.3. 证书的种类
一种是自签证书,另一种是由机构签发的证书。
自签证书,也就是颁发者是自己,使用自己的私钥来对证书的信息进行签名。根证书就是自签证书。客户端一般预置了知名机构的根证书,并且信任由这些根证书签发的证书。
颁发证书,就是通过这些机构签发证书,可能是根级机构,也可能是二级机构,使用机构的私钥来对证书的信息进行签名。
1.4. 证书的生成
可以通过openssl或者jdk提供的keytool来生成证书、以及证书对应的密钥库。本文使用keytool举例。理解了证书生成的过程后,可以很容易对应到openssl的生成方法。
1.5. 基本概念
随便搜一下TLS证书,会出现一大堆关键字:X.509、p12、pfx、pkcs、pem、csr、cer、crt、jks、crl等,如此繁杂,一下就让人想从入门到放弃。所以,先把这些关键词理一理。
1.5.1. X.509
X.509就是一个广泛应用的数字证书标准,简单来说就是定义了数字证书里面包含哪些字段、如何存储,可以参照RFC5280。协议中定义的参数都是采用DER编码(Distinguished Encoding Rules,X.690)。DER编码可以理解为一种TLV(Tag Length Value)格式编码。以一个实际的证书内容为例:
Certificate:
Data:
Version: 3 (0x2) //表示为X509 v3版本证书
Serial Number: 1 (0x1) //序列号,签发时需保证同一签发方的每个证书都唯一的Serial Number
Signature Algorithm: sha256WithRSAEncryption //签名算法,先计算SHA256摘要,再使用签发方私钥进行RSA加密
Issuer: C=CN,ST=JS, L=NJ, O=Dreamer, OU=Dreamer, CN=Dreamer //签发方DN(Distingushed Name),见后续说明
Validity //证书有效期
Not Before: Jul 29 14:02:13 2018 GMT
Not After : Jul 26 14:02:13 2028 GMT
Subject: C=CN, ST=JS, O=Dreamer, OU=JM, CN=*.dreamer.com //证书拥有方DN(Distingushed Name)
Subject Public Key Info: //证书公钥信息
Public Key Algorithm: rsaEncryption
Public-Key: (1024 bit)
Modulus:
00:c5:b8:68:a2:9c:bd:11:0c:83:34:a2:97:a5:8e:
72:75:2a:bc:f4:75:fc:d0:a3:47:7d:e4:6b:4f:ed:
dd:79:7c:0f:ce:6e:e7:d2:7d:10:cd:e8:07:56:34:
58:e3:2b:2e:c9:e3:7f:ae:27:2d:f7:a3:17:6f:dd:
65:d7:f8:4f:d0:be:9c:3b:9b:ea:ed:86:d2:19:67:
81:60:53:64:c9:d1:be:17:7d:5d:7f:cc:58:1d:b6:
e1:51:0d:ba:32:ac:4d:73:a4:fc:8f:6a:79:f9:44:
25:03:b6:1c:3e:0f:e9:b8:36:b1:07:07:59:54:40:
d7:2c:52:ab:68:fe:ed:e2:6f
Exponent: 65537 (0x10001)
X509v3 extensions: //v3版本扩展信息
X509v3 Basic Constraints:
CA:FALSE //表示证书拥有方是否是CA机构,可签发证书,如为True,则还可添加一个pathLengthConstraint来显示签发链的长度
Netscape Comment:
OpenSSL Generated Certificate
X509v3 Subject Key Identifier: //证书拥有方密钥ID,CA: TRUE时需要加入此扩展
67:30:EE:FB:39:A4:92:56:9C:1A:E8:94:10:A4:3B:EA:EC:2E:04:9E
X509v3 Authority Key Identifier: //证书签发方对应的密钥ID,用于查找签发方公钥(特别是存在多个密钥时)
DirName:/C=CN/ST=JS/L=NJ/O=Dreamer/OU=Dreamer/CN=Dreamer
serial:A0:09:E3:A9:D2:C1:86:7C
X509v3 Subject Alternative Name: //证书拥有方的别名,用于多域名证书的签发
DNS:*.example.com, DNS:*.jm.com
Signature Algorithm: sha256WithRSAEncryption //签发机构对证书的签名信息
91:db:9b:0c:9b:6e:68:24:d3:2f:3a:67:b5:c0:6c:f0:c8:4c:
f8:87:86:93:eb:fc:dc:ef:dc:7b:2e:2c:0e:7b:52:23:4d:de:
d9:69:a8:ee:ae:aa:14:04:ca:1a:03:87:fe:11:60:fe:16:8f:
87:9d:9e:d0:3a:be:33:03:f6:25:8a:10:37:f8:90:9d:67:5c:
36:a6:1e:3c:59:d9:8f:eb:22:0e:f7:3c:7d:47:10:9b:0b:03:
f0:8c:70:b0:3c:40:c6:5d:cc:6b:ba:40:ce:89:04:c7:3c:be:
af:bd:1d:94:6b:83:39:29:74:de:12:fc:63:0d:0f:39:31:3b:
48:fd
除了注释中的说明外,还需要补充以下几点:
- Subject和Issuer均为DN格式,常见格式为:C=国家,ST=省市, L=区县市, O=组织机构, OU=组织单位, CN=通用名称;
- DN是证书链查找的关键,验证时会根据Issuer的DN去匹配机构的证书,具体的匹配方法是比较DN的数量以及各DN参数值;
- DN中的CN通用名称一般为域名,如需支持子域名,例如server.com、bcd.server.com,可使用泛域名形式*.server.com;
- Subject Alternative Name扩展字段可用于多域名证书。
1.5.2. 证书编码格式(DER&PEM)
X.509中说明证书各个字段编码均适用DER格式的编码,证书是一个二进制格式文件。
那PEM(Privacy Enhanced Mail)又是什么格式呢?按照网上普遍说法,X.509有DER和PEM两种编码格式,其实这种说法个人认为有些不妥,实际上这两种编码格式并不在一个层面上。前面说过DER是一种TLV格式的二进制编码协议,那PEM是否也类似呢?其实PEM并没有去理解X.509的内部参数,而是在X.509进行DER编码之后,对二进制数据做了一次BASE64编码,然后加上文件头尾,如下所示:
-----BEGIN CERTIFICATE-----
MIICMTCCAZoCCQCgCeOp0sGGfDANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJD
TjELMAkGA1UECAwCSlMxCzAJBgNVBAcMAk5KMRAwDgYDVQQKDAdEcmVhbWVyMRAw
DgYDVQQLDAdEcmVhbWVyMRAwDgYDVQQDDAdEcmVhbWVyMB4XDTE4MDcyODE1NTEx
NFoXDTI4MDcyNTE1NTExNFowXTELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAkpTMQsw
CQYDVQQHDAJOSjEQMA4GA1UECgwHRHJlYW1lcjEQMA4GA1UECwwHRHJlYW1lcjEQ
MA4GA1UEAwwHRHJlYW1lcjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwjDr
aM8SubYIN/dqmJLCHYWBet7yQ80H3VPcbeYPja2Fq1VPb0vKMXzfd8BdaJ3roown
ZCJlfxCFPqN/z8/a0BS+ukmknOcYeYoN+vpVg9Oq3fH0iy+TRg+ydOVwmyAXJk0D
GS7WFjHv6DYRlH/xgKXuHGXwytNpQHZdDzq6bV0CAwEAATANBgkqhkiG9w0BAQsF
AAOBgQCaoAdYiWpGKcvc89ZPwL/Zd0KgSLnAln/38a69N5LqtDgWD9a6PDjHHmTF
/cN/p8hJ3LdEXfPGtFj06+KaG6OVOAo5RSqOHc5DcMs1nAImqIAuLt2rOCmsY+li
T9tweI2raih6OMTKAeIW5m46T28oPlNgeEMy2Uj2CevS6tCaLQ==
-----END CERTIFICATE-----
区分DER、PEM的方法很简单,打开有乱码的是DER格式,打开类似上面格式的就是PEM格式。通常而言,存放DER的文件扩展名为“.cer”,是二进制数据,而存放PEM的文件扩展名为“.crt”,是ASCII码的文本文件。
DER/PEM文件可以使用以下命令查看:
openssl x509 -in certificate.der -inform der -text -noout
openssl x509 -in certificate.pem -text -noout
1.5.3. 证书相关的扩展名
扩展名是比较容易误导人的地方,除了.der或.pem后缀外,还有以下常见后缀:
crt | 应该是certificate的缩写,常见于*nix系统,大多是PEM编码,也可能是DER编码 |
cer | Certificate Signing Request,常见于Windows系统,大多是DER编码,也可能是PEM编码 |
key | 用户存放密钥信息,可能是DER编码,也可能是PEM编码。可参照PKCS#1(RFC8017)查看其具体字段和定义;但密钥明文存储方式有危险,一般使用PKCS#8格式(RFC5958)加密存储,即设置一个提取密钥 |
csr | 证书签发请求,其实与证书内容相似,但不包含签发方信息,签发方根据csr并添加自身的签发信息,从而生成证书文件,详情可参照(PKCS#10 RFC2314) |
pfx/p12 |
将证书和私钥一并打包成一个文件,并且设置“提取密码”; 【注意】PKCS12不支持设置密钥库条目密码,默认与密钥库密码一致 |
jks keystore truststore |
jks(Java Key Storage)常见于JAVA相关应用,实际上和PKCS12类似,将证书和私钥一并打包并设置“提取密码”。至于keystore和truststore只是概念上的区别,keystore一般表示用户或服务器证书,而truststore一般表示CA证书 |
想要深入研究,可以看一下PKCS协议族,上述的key、csr、pfx/p12等都是该协议族中定义的扩展。
1.6. 查看证书
1.6.1. Windows
在开始菜单的运行中,输入certlm.msc管理本地计算机的证书,或certmgr.msc管理当前用户的证书。
1.6.2. Android
以荣耀为例,其他类型手机操作都类似。
在设置中搜索“证书”,可以找到“安装证书”与“受信任的凭据”。在受信任的凭据中可以看到“系统”与“当前用户”两类证书,两个列表中可以打开或关闭相应的证书。
在安装证书中可以安装自己的证书。这个操作难度较大,不推荐让用户自己操作。
2. 生成根证书
2.1. 生成根证书密钥库
根证书是一张自签名的证书,使用者和颁发者都是自己。使用下面的命令生成根证书的密钥库(如果不指定keystore文件路径,则默认存在用户目录下的.keystore中),输入密钥库的密码,填写根证书的信息。
keytool -genkeypair -validity 36500 -keyalg RSA -keypass 123456 -storepass 123456 -alias rootca -keystore root.keystore
此命令使用RSA算法,,并保存到root.keystore中。
使用以下命令查看密钥库的信息,会发现发布者与所有者都是testroot:
keytool -list -keystore root.keystore -keypass 123456 -storepass 123456 -v
2.2. 从根证书密钥库导出根证书
导出证书:
keytool -exportcert -validity 36500 -alias rootca -file root.cer -keystore root.keystore -keypass 123456 -storepass 123456
查看证书:
3. 生成二级证书
3.1. 生成二级证书密钥库
二级证书需要由根证书签发。
首先使用keytool生成二级证书的密钥库,但是此时还是自签的,我们需要从中生成一个二级证书请求(其中包含了二级证书的公钥);
然后将证书请求发送到rootca签发二级证书;
最后,我们将rootca签发的二级证书导入到证书密钥库中,完成二级证书的生成。
下面命令是生成二级证书密钥库,密钥库的名称为subca,此时仍是自签的,证书颁发者和使用者都是自已:
keytool -genkeypair -validity 36500 -keyalg RSA -keypass 123456 -storepass 123456 -alias subca -keystore sub.keysto
3.2. 从二级证书密钥库中生成证书请求
从二级CA密钥库中导出证书请求,下面是导出证书请求的命令,subca是上面生成的二级CA密钥库的名字,最终导出证书请求文件为subca.csr:
keytool -certreq -alias subca -file sub.csr -keystore sub.keystore -keypass 123456 -storepass 123456
3.3. 使用根证书签发证书
通过keytool工具的签发证书功能,使用rootca对二级证书请求(subca.csr)签发一张二级证书。命令如下,alias指定证书的颁发者,infile指定证书请求文件,outfile是二级证书的文件名:
keytool -gencert -validity 36500 -alias rootca -infile sub.csr -outfile sub.cer -keystore root.keystore -keypass 123456 -storepass 123456
3.4. 导入二级证书到密钥库中
因为本地生成的密钥库仍然是自签名的,此时需要将根证书签发的二级证书导入密钥库中,导入前需要先见自签名的根证书导入密钥库,这点非常重要,否则会报”无法从回复中建立链“,命令如下:
keytool -importcert -alias rootca -file root.cer -keystore sub.keystore -storepass 123456 -keypass 123456
然后,才能导入sub.cer到密钥库,alias subca就是之前用来生成密钥库或证书请求时用到的秘钥对别名:
keytool -importcert -alias subca -file sub.cer -keystore sub.keystore -storepass 123456 -keypass 123456
此时查看一下密钥库中的subca的秘钥对的证书的颁发者。
keytool -list -alias subca -v -keystore sub.keystore -storepass 123456