证书与公钥基础设施
证书,就是一个用来加密的东西。
- 数据整体加密,使内容难以被第三方窥探。
- 关键部位提取出来加密,生成一个签名,附加在数据上,用于确保原数据未被篡改,
发送方也无法抵赖。
证书的作用如此之大,如果制作方不中立,很难令人信服。
所以有一套完善的机制来保证证书的有效性,可靠性,这就是我们的公钥基础设施(Public Key Infrastructure, PKI)。
其中最为关键的是证书颁发机构(Certification Authority, CA),负责颁发证书、认证证书、管理已颁发证书。
CA 自己创建一个证书(公钥 + 私钥),信任这个 CA 的人使用它的证书来签名和验证自己的证书,一级一级的传递下来。
如果需要一个大家都信任的证书,就需要向一个大家都信任的 CA 申请,提交身份信息 + 密钥。
CA 确认之后,使用私钥为之签名,然后这个身份信息 + 颁发者信息(序列号) + 公钥 + 算法 + 签名 + 有效期等,合在一起就是一个证书了。
如果其他人需要确认这个证书的真假,就拿 CA 的公钥(公开的)来校验一下,通过之后就认为其有效。
PKI 的这一整套机制,全部遵循国际电信联盟制定与颁发的 X.509 标准。
几乎 IT 届所有通讯技术中使用的加密都离不开这一套(还有一套机制,就是 PGP 体系的分布式信任模型 Web of Trust)。
PEM
编码格式,就是常见的-----BEGIN XXX-----
+ base64 +-----END XXX-----
DER
编码格式,二进制PFX
参考下面说的 PKCS#12, 公钥 / 私钥 / 证书,好像是用于安全转移
通常用它来打包一个私钥及有关的 X.509 证书,或者打包信任链的全部项目。
其文件常用 .p12 后缀或 .pfx 后缀JKS
Java Key Store, 私钥 + 证书,常用 Java Keytool 做合成或提取CER
/CRT
常见后缀,表示证书PKCS
The Public-Key Cryptography Standards,RSA 公司设计的公钥加密标准,目前共有 15 部分,其中:PKCS#1
RSA Cryptography Standard v2.2, RFC8017PKCS#3
Diffie–Hellman Key Agreement Standard v1.4PKCS#5
Password-based Encryption Standard v2.1, RFC8018, PBKDF2PKCS#8
Private-Key Information Syntax Standard v1.2, RFC5958PKCS#10
Certification Request Standard v1.7, RFC2986PKCS#12
Personal Information Exchange Syntax Standard v1.1 RFC7292
似乎是根据微软的 PFX 格式发展过来的,现在可以把 PFX 看做 PKCS#12 的同义词
一般的习惯:
- 公钥和私钥,用 .key 后缀
- 证书一般采用 .cer 或 .crt 做后缀
- 不管是公钥、私钥,还是证书,都有按照保存格式,使用 .pem 或 .der 后缀的
- 还有合并存储的可能,最简单的就是
cat ca.key ca.crt > ca.pem
至少 Python 的 SSL 库加载证书时,支持私钥和证书合并存储在同一个文件
什么是自签名
模拟 PKI 机制,先创建一个证书,将其作为根证书。然后使用这个根证书给其他新创建的证书做签名,形成一个证书链。
这种自己给自己背书的行为,就叫自签名,得到的签名证书就叫自签名证书。
一般,我们在 Socket 编程中,使用 TLS 的场景,似乎都不对证书做校验,只要求两边都有证书就行了。
这种情况下,自签名似乎是个脱裤子放屁的事情,直接生成 CA 证书使用就行了。
但是也有很多场景,就是应用层的互联网协议,一般需要完整的 PKI 支持,比如浏览器(HTTPS),会逐层校验证书合法性。
这个日后我会再写一篇 HTTPS 通信方面的文章,对这套 PKI 机制的全流程应用做一个简单梳理。
Step 1: Create 2048 Bit RSA Key
生成一个 2048 位 RSA 密钥(看完发现,其实这一步可以省略)
# 私钥 server.key
openssl genrsa -out server.key 2048
# 公钥 server_pub.key
openssl rsa -in server.key -pubout -out server_pub.key
# 查看详情
openssl rsa -noout -text -in server.key
openssl rsa -noout -text -in server_pub.key -pubin
# PKCS#1 => PKCS#8
openssl pkcs8 -topk8 -in server.key -out server_pk8.key -passout pass:create_rsa_key # 默认需要加密
openssl pkcs8 -topk8 -in server.key -out server_pk8.key -nocrypt # 不要加密
PS:公钥可以公开,私钥不可泄露
加密(可选)
# man genrsa or man openssl-genrsa
# -aes128, -aes192, -aes256, -aria128, -aria192, -aria256, -camellia128, -camellia192, -camellia256, -des, -des3, -idea
openssl genrsa -aes256 -passout pass:create_rsa_key -out server.key 2048
openssl rsa -in server.key -pubout -out server_pub.key -passin pass:create_rsa_key
# 私钥加密
openssl rsa -in server.key -out server_passwd.key -passin pass:create_rsa_key -aes256
# 加密私钥去掉密码
openssl rsa -in server_passwd.key -out server.key -passin pass:create_rsa_key
Step 2: 创建签名请求 Create Certificate Sign Request
server.csr
(请求)
如果不传入一个密钥文件,则会自动生成一个,默认文件名为 privkey.pem
openssl req -new -keuy server.key -out server.csr
免输入:
openssl req -new -key server.key -out server.csr -subj "/C=CN/ST=Hubei/L=Wuhan/O=markjour.com/OU=R&D/CN=Hu Ang/emailAddress=who@markjour.com" -passout pass:create_csr
# 免加密
openssl req -new -key server.key -out server.csr -subj "/C=CN/ST=Hubei/L=Wuhan/O=markjour.com/OU=R&D/CN=Hu Ang/emailAddress=who@markjour.com" -nodes
# 使用新的密钥
openssl req -new -out server.csr -subj "/C=CN/ST=Hubei/L=Wuhan/O=markjour.com/OU=R&D/CN=Hu Ang/emailAddress=who@markjour.com" --nodes -keyout server_generated.key
查看详情:
openssl req -noout -text -in server.csr
Step 3: 创建自签名证书 Create Self-Signed Certificate
我们自己做 CA,所以需要一个 CA 证书:ca.crt
(证书)和 ca.key
(密钥)
# 证书请求
# openssl req
# 使用新的密钥(RSA2048),默认格式:PKCS8
# -newkey rsa:2048 -keyout ca.key -passout pass:xxx
# 证书相关
# -x509 -days 365 -subj "xxx"
openssl req \
-newkey rsa:2048 -keyout ca.key -passout pass:create_crt \
-out ca.crt -x509 -days 365 -subj "/C=CN/ST=Hubei/L=Wuhan/O=markjour.com/OU=R&D/CN=Hu Ang/emailAddress=signer@markjour.com"
# 如果不想加密,可以使用 -nodes(no des)
openssl req \
-newkey rsa:2048 -keyout ca.key -nodes \
-out ca.crt -x509 -days 365 -subj "/C=CN/ST=Hubei/L=Wuhan/O=markjour.com/OU=R&D/CN=Hu Ang/emailAddress=signer@markjour.com"
# 使用已有私钥
openssl req \
-new -key server.key -nodes \
-out ca.crt -x509 -days 365 -subj "/C=CN/ST=Hubei/L=Wuhan/O=markjour.com/OU=R&D/CN=Hu Ang/emailAddress=signer@markjour.com"
# -new 参数不要似乎也行 (可能是默认行为)
openssl req \
-keyout ca.key -nodes \
-out ca.crt -x509 -days 365 -subj "/C=CN/ST=Hubei/L=Wuhan/O=markjour.com/OU=R&D/CN=Hu Ang/emailAddress=signer@markjour.com"
Step 4: 自签名 Self Sign CSR
自己做 CA,给自己的请求签名:
# -signkey val Self sign cert with arg
# 很奇怪,通过密钥怎么得到的 CA 信息
openssl x509 -req -days 365 -in server.csr \
-signkey ca.key \
-out server.crt -passin pass:create_crt
# 还看到一处这样调用,效果似乎没有差别:
# -CA infile Set the CA certificate, must be PEM format
# -CAkey val The CA key, must be PEM format; if not in CAfile
# -CAcreateserial Create serial number file if it does not exist
openssl x509 -req -days 365 -in server.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out server.crt -passin pass:create_crt
一个证书(X.509 Certificate)这就好了:server.crt
查看详情:
openssl x509 -in server.crt -noout -text
其他
# 格式转换,默认 PEM,所以所有 PEM 可以不写
openssl rsa -inform PEM -outform DER -in server.key -out server.der
openssl x509 -inform PEM -outform DER -in cert.pem -out cert.der
openssl x509 -inform DER -outform PEM -in cert.cer -out cert.pem
# PEM => PFX
openssl pkcs12 -export -inkey privateKey.key -certfile CACert.crt -in cert.pem -out cert.pfx
# set no password: -passout pass:
# PFX to PEM (证书和私钥在一起?)
openssl pkcs12 -in cert.pfx -out cert.cer -nodes
# 证书 + 私钥,合并成 PKCS#12 证书
openssl pkcs12 -inkey ca.key -in ca.crt -export -out ca.pfx --passout pass:pfx3000
openssl pkcs12 -export -in server.crt -inkey server.key -out server.pfx
openssl pkcs12 -export -in server.crt -inkey server.key -passin pass:111111 \
-password pass:111111 -out server.pfx
# 证书链:加上上级证书
# -name 指定 server.crt 别名
# -name 指定 ca.crt 别名
openssl pkcs12 -export -in server.crt -inkey server.key -passin pass:111111 \
-chain -CAfile ca.crt \
-password pass:111111 -out server-all.pfx
# 从 PKCS#12 导出
openssl pkcs12 -in server.pfx -password pass:111111 -passout pass:111111 -out out/server.pem
openssl pkcs12 -in server.pfx -password pass:111111 -passout pass:111111 -nocerts -out out/server.key
openssl pkcs12 -in server.pfx -password pass:111111 -nokeys -out out/server.key
openssl pkcs12 -in server-all.pfx -password pass:111111 -nokeys -cacerts -out out/ca.crt
openssl pkcs12 -in server-all.pfx -password pass:111111 -nokeys -clcerts -out out/server.crt
# 查看 DER 证书
openssl x509 -in server.der -inform der -noout -text0
# 导出 SMTP 服务器的证书
openssl s_client -showcerts -connect smtp.qq.com:25 -starttls smtp </dev/null 2>/dev/null | sed -n "/-----BEGIN/,/-----END/p" > chain.pem
PuTTY 的 ppk 格式
PuTTY-User-Key-File-2: ssh-rsa
Encryption: none
Comment: imported-openssh-key
Public-Lines: 6
<-- base64 lines -->
Private-Lines: 14
<-- base64 lines -->
Private-MAC: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 生成 PPK 文件
# 支持的密钥类型(-t):ed25519, ecdsa, rsa, dsa, rsa1
# RSA1 好像是为 SSHv1 准备的
puttygen -t rsa -b 2048 -C 'just for ppk testing' -o markjour.ppk -q
# 支持的输出类型(-O):
# private output PuTTY private key format
# private-openssh export OpenSSH private key
# private-openssh-new export OpenSSH private key (force new format)
# private-sshcom export ssh.com private key
# public RFC 4716 / ssh.com public key
# public-openssh OpenSSH public key
# fingerprint output the key fingerprint
puttygen markjour.ppk -O private-openssh -o markjour-id_rsa # 提取私钥
puttygen markjour.ppk -O public-openssh -o markjour-id_rsa.pub # 提取公钥
puttygen markjour.ppk -O public-openssh # 输出公钥
puttygen markjour.ppk -O fingerprint # 输出指纹
疑惑:为什么 ppk 的私钥部分只有 PEM 私钥的一半?
利用公钥部分做校验,压缩了体积么?
验证私钥 Key、证书 CRT、证书请求 CSR 是否匹配
openssl pkey -in server.key -pubout -outform pem | sha256sum
openssl x509 -in server.crt -pubkey -outform pem -noout | sha256sum
openssl req -in server.csr -pubkey -outform pem -noout | sha256sum
附件
- openssl-self-signed.sh
创建一个自签名证书 - openssl-generate-x509.sh
使用本文所说的方法,使用自签名证书来生成两个证书:server.crt 和 client.crt
参考资料与拓展阅读
- How To Generate Self Signed X.509 Certificates with OpenSSL?
- CSDN, 使用 openssl 生成证书(含openssl详解)
- /docs/man1.0.2/man1/openssl-req.html
- https://zh.wikipedia.org/wiki/公开密钥认证
- https://en.wikipedia.org/wiki/Public_key_certificate
- https://en.wikipedia.org/wiki/公开密钥基础建设
- https://en.wikipedia.org/wiki/Public_key_infrastructure
- https://zh.wikipedia.org/wiki/X.509
- https://en.wikipedia.org/wiki/X.509
- https://zh.wikipedia.org/wiki/证书颁发机构
- https://en.wikipedia.org/wiki/Certificate_authority
- https://en.wikipedia.org/wiki/PKCS
- https://en.wikipedia.org/wiki/PKCS_12
- https://en.wikipedia.org/wiki/Key_authentication
- https://en.wikipedia.org/wiki/Public-key_cryptography
- https://en.wikipedia.org/wiki/Key-agreement_protocol