OpenSSL 是什么?
OpenSSL 是开源的程序套件,该套件由三局部组成:
- libcrypto:具备通用性能的加密库,外面蕴含泛滥加密算法
- libssl:实现 SSL/TLS 性能
-
openssl:多功能的命令行工具,能够实现加密、解密、自建 CA、创立证书、撤消证书等性能
本文次要介绍如何应用 OpenSSL 自建 CA,生成 SSL 证书、撤消证书。
根底概念
- 非对称加密:
对称加密算法在加密和解密时,应用同一密钥。非对称加密算法须要两个密钥,即公开密钥和公有密钥,公开密钥和公有密钥是一对。用公开密钥对数据进行加密,只有用对应的公有密钥能力解密;用公有密钥对数据进行加密,只有用对应的公开密钥能力解密。因为加密和解密应用两个不同的密钥,所以这类算法叫非对称加密算法。
在非对称加密中,应用的次要算法包含 RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)等。
非对称加密的毛病是加密和解密破费工夫长、速度慢,只适宜对大量数据进行加密 - Private Key:
证书的私钥,PEM 格局,通常应用 RSA 算法生成。在生成私钥时,能够指定明码对其进行爱护 - CSR(Certificate Signing Request,证书签名申请):
用于申请证书,在制作 CSR 文件时,须要应用私钥文件。CSR 文件必须由 CA 进行签名,才可造成证书(CRT) -
CRT(数字证书):
数字证书是通过证书受权核心(CA)签名的、蕴含公开密钥拥有者信息和公开密钥的文件。数字证书的一个重要特色是 – 只在特定的时间段内无效。以数字证书为外围的加密技术能够对网络上传输的信息进行加密和解密,数字签名和签名认证,确保传输信息的机密性、完整性、不可抵赖性:- 保密性 – 只有收件人能力浏览信息
- 认证性 – 确认信息发送者的身份
- 完整性 – 信息在传递过程中不会被篡改
- 不可抵赖性 – 发送者不能否定已发送的信息
数字证书的颁发过程个别为: - 用户首先产生本人的密钥对
- 将公共密钥和局部个人身份信息传递给证书受权核心
- 证书受权核心在核实用户身份后,将给用户颁发数字证书,其中蕴含用户的身份信息和公共密钥,同时还附有证书受权核心的签名信息
数字证书工作原理: - 应用接管单方约定的 HASH 算法对报文计算出固定位数的摘要。在数学上保障:只有改变报文中的任何一位,从新计算出的报文摘要值就与原先的值不相符。这样即可保障报文的不可更改性
- 应用发送者的私钥对报文摘要值进行加密,而后连同原报文一起发送给接收者,加密后的报文摘要即为数字签名
- 接管方收到数字签名后,用同样的 HASH 算法对原报文计算出报文摘要值,而后与应用发送者的公开密钥对数字签名进行解密失去的报文摘要值进行比拟,如果相等,则阐明报文的确来自所称的发送者
- 之所以对报文摘要进行加密,而不是对原报文进行加密,是因为 RSA 加解密十分耗时,被加密的报文越大,耗时越多
证书的内容包含:CA 信息、公钥用户的信息、公钥、CA 的签名和有效期等。证书的格局和验证办法广泛遵循 X.509 国际标准
- CA(Certificate Authority,证书受权核心):
CA 是负责发放和治理数字证书的权威机构,作为受信赖的第三方,承当公钥体系(PKI)中公钥的合法性检查的责任。
CA 也领有一个证书(内含公钥)和私钥。用户通过验证 CA 的签名从而信赖证书,任何人都能够失去 CA 的证书,用以验证它所签发的证书。
如果用户想要甄别证书的真伪,须要用 CA 的公钥对证书上的签名进行验证,一旦通过,认为证书无效 - 证书链(Certificate Chain)
证书链由两个环节组成:信赖锚环节(CA 证书)和已签名证书环节。自签名的证书仅有一个环节的长度:信赖锚环节就是已签名证书自身。
证书链能够领有任意环节的长度。在三节的证书链中,信赖锚证书能够对两头证书进行签名,两头证书的拥有者能够用本人的私钥对另一个证书进行签名。
证书链是 CA 收回的证书序列,最终以根 CA 证书完结。
证书最后生成时,是一个自签名证书,自签名证书是签名者(Issuer)和主题(Subjet)雷同的证书(即用证书本人的公钥对证书的签名进行认证)。自签名证书是证书链中的最初一个证书。
证书链中的每个证书都须要应用链中的前一个证书的公钥进行验证,直至达到自签名的根证书 -
CRL(Certificate Revocation List,证书撤消列表):
用于指定证书发布者认为有效的证书列表。CRL 肯定是被 CA 签订的,CRL 中蕴含被撤消的证书的序列号。
证书具备指定的寿命,但 CA 能够通过撤消证书缩短这一寿命。CA 通过公布证书撤消列表,列出被认为不能再应用的证书的序列号。CA 能够指定证书被撤消的起始日期,也能够在证书撤消列表中退出撤消证书的理由:- 透露密钥
- 透露 CA
- 从属关系扭转
- 被取代
- 业务终止
CA 撤消证书意味着 CA 在证书失常到期之前撤销其应用该密钥对的无关申明。在撤消的证书到期之后,CRL 中的无关条目会被删除,以缩短 CRL 列表的大小。
在验证签名期间,应用程序能够查看 CRL,以确定给定证书和密钥对是否可信。如果不可信,应用程序能够判断撤消的理由或日期对应用有疑难的证书是否有影响。如果日期早于该证书被撤消的日期,那么该证书仍被认为无效。
应用程序在取得 CRL 之后,能够缓存下来,在它到期之前,始终应用它。如果 CA 公布了新 CRL,那么领有无效 CRL 的应用程序不会应用新 CRL,直到应用程序领有的 CRL 到期为止
-
SNI(Server Name Indication):
依据 HTTPS 的工作原理,浏览器在拜访 HTTPS 站点时,先与服务器建设 SSL/TLS 连贯,建设连贯的第一步是申请服务器的证书,而服务器在发送证书时,不晓得浏览器拜访的是哪个域名,所以不能依据不同的域名发送不同的证书。
SNI 是解决一个服务器应用多个域名和证书的 SSL/TLS 扩大。它的工作原理是:在连贯到服务器,建设 SSL/TLS 连贯之前,客户端先发送要拜访的站点的域名(HostName),这样服务器就能够依据这个域名返回适合的证书。目前大多数操作系统和浏览器都曾经很好地反对 SNI 扩大,OpenSSL 0.9.8 曾经内置这一性能
应用 OpenSSL 自建 CA
-
初始化环境
$ mkdir /var/MyCA $ cd /var/MyCA $ mkdir certs private $ chmod g-rwx,o-rwx private $ echo "01" > serial $ touch index.txt
创立实现后,目录构造如下:
$ tree . . ├── cacert.pem # CA 证书 ├── certs # 该目录下保留 CA 颁发的所有证书的正本 ├── index.txt # 排序数据库,用来追踪曾经颁发的证书 ├── openssl.cnf # openssl 的配置文件 ├── private # 保留 CA 私钥的目录 │ └── cakey.pem # CA 私钥 └── serial # 用来追踪最初一次颁发的证书的序列号
2 directories, 5 files
-
创立 openssl 的配置文件 openssl.cnf
其内容如下:[ca] default_ca = myca [myca] dir = /var/MyCA certificate = $dir/cacert.pem database = $dir/index.txt new_certs_dir = $dir/certs private_key = $dir/private/cakey.pem serial = $dir/serial default_crl_days = 7 default_days = 365 default_md = sha256 policy = myca_policy x509_extensions = certificate_extensions [myca_policy] commonName = supplied stateOrProvinceName = supplied countryName = supplied emailAddress = supplied organizationName= supplied organizationalUnitName = optional [certificate_extensions] basicConstraints = CA:false
-
生成根证书(Root Certificate)
根证书是证书链的最初一个证书,也是自签名证书,即 Issuer 和 Subject 雷同的证书。为生成根证书,首先向 openssl.cnf 配置文件中,减少上面的配置信息:[req] default_bits = 2048 default_keyfile = /var/MyCA/private/cakey.pem default_md = sha256 prompt = no distinguished_name = root_ca_distinguished_name x509_extensions = root_ca_extensions
[root_ca_distinguished_name]
commonName = My Test CA # 名称
stateOrProvinceName = BJ # 州或省代码
countryName = CN # 国家代码
emailAddress = test@cert.com # 邮箱地址
organizationName = Root Certification Authority
[root_ca_extensions]
basicConstraints = CA:true
将所有必要信息写进配置文件,而不是在命令行输出,这是惟一指定 X.509v3 扩大的形式,也能让咱们对如何创立根证书有清晰的把握。
为 openssl 命令指定配置文件有两种形式:
- 通过 OPENSSL_CONF 环境变量设置
-
通过 -config 命令行选项设置
应用上面命令:$ openssl req -x509 -newkey rsa -out cacert.pem -outform PEM -days 365 -config /var/MyCA/openssl.cnf
或
$ OPENSSL_CONF=/var/MyCA/openssl.cnf openssl req -x509 -newkey rsa -out cacert.pem -outform PEM -days 365
生成根证书(其中 -days 365 是证书的有效期)。
上面验证 CA 根证书:
$ openssl x509 -in cacert.pem -text -noout
Certificate:
Data:Version: 3 (0x2) Serial Number: 12778548659037981755 (0xb156864d3f00cc3b)
Signature Algorithm: md5WithRSAEncryption
Issuer: CN=Test 06 29 2016, ST=BJ, C=CN/emailAddress=jordan23nbastar@yeah.net, O=Root Certification Authority Validity Not Before: Jun 29 07:08:24 2016 GMT Not After : Jun 29 07:08:24 2017 GMT Subject: CN=Test 06 29 2016, ST=BJ, C=CN/emailAddress=jordan23nbastar@yeah.net, O=Root Certification Authority Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:ce:2c:54:34:2b:a9:30:3e:51:20:c3:ad:21:b7: 21:9e:5c:f8:23:7a:4a:12:0f:9b:30:a8:1b:5f:10: 7e:51:01:ed:64:61:04:bb:28:82:16:05:5b:5a:e8: 5b:b6:21:73:68:eb:d7:89:db:a0:d7:ce:8a:92:f5: 8f:b3:92:ea:cd:d5:05:dd:49:a1:c4:7e:fd:1e:60: 8d:71:a4:e9:d5:35:ad:e8:c7:1a:e8:6d:52:6d:ff: 30:b1:ff:80:7d:59:4c:91:ca:67:c2:56:d6:ad:5b: 8c:58:ea:70:b3:60:97:0e:98:d5:35:46:f3:fb:ad: 57:ef:a6:55:b2:b5:13:f7:47:a8:c1:31:06:86:fc: 8a:ae:08:2e:fd:9e:ae:fe:f7:d5:35:c7:d4:45:de: 79:70:d8:c6:73:bd:47:75:90:24:d0:22:f4:f8:76: f2:e8:2a:ef:3f:64:16:a7:8d:40:b0:94:76:f3:56: 7f:61:b4:54:c0:76:5f:33:a1:61:97:33:98:21:5c: ec:88:95:3e:56:f1:2f:be:d9:68:cb:90:84:42:00: 24:49:c0:26:19:0f:f3:09:f7:06:1d:3b:b7:29:ac: b3:31:ee:23:f5:58:c9:4e:de:5f:82:02:5a:50:87: fe:72:5d:6c:4c:65:bb:59:1e:42:ab:20:42:c8:6b: 4f:f7 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:TRUE
Signature Algorithm: md5WithRSAEncryption
af:9a:28:1b:67:44:87:d1:cc:cb:1f:e7:88:e5:74:f3:cd:d5: ca:cc:f6:51:f9:e6:31:c2:93:c4:37:18:0a:29:29:68:b2:41: d7:37:be:40:5d:87:04:d8:b2:03:4a:8e:fc:5e:b0:9a:bc:4c: 2d:88:69:90:63:21:34:16:e2:30:7f:76:c0:b8:64:79:e3:0f: 2e:f6:91:af:a3:fd:c2:45:40:68:5f:19:a0:e3:6e:19:d1:2a: 21:32:76:32:15:03:79:c3:da:aa:1d:1e:97:c4:de:d8:40:4a: df:75:4f:a6:61:ed:d5:54:40:be:30:f4:0d:ac:86:2a:30:7d: c3:28:69:e9:7b:b3:c0:19:8e:86:e7:7d:f3:55:04:ee:b1:6f: 98:f8:33:0d:16:4b:bb:d5:3d:e1:0c:31:13:1d:a6:ee:40:1f: 5f:d8:fb:b9:bc:4c:8b:4b:b9:d9:5b:b2:0d:f9:3c:84:16:90: eb:25:33:af:cf:e5:94:94:dc:a5:64:8b:22:d0:ad:55:a7:50: fe:e6:91:9e:98:2e:3c:03:04:2d:5a:54:97:7a:a7:c6:9f:dc: 11:86:2d:43:1d:7c:b1:48:27:c9:77:a7:3f:93:d6:ee:96:f5: ef:27:2c:88:fd:64:30:28:fb:72:4b:cc:be:99:d4:67:00:e4: c7:21:b4:4b
应用 OpenSSL 生成证书
- Create key
$ openssl genrsa -out prvtkey.pem 2048 #(without password protected)
或
$ openssl genrsa -des3 -out prvtkey.pem 2048 #(password protected) - Create certification signing request
$ openssl req -new -key prvtkey.pem -out cert.csr
该命令应用上一步生成的密钥文件 prvtkey.pem,生成证书签名申请文件 cert.csr,接下来将应用这个文件去 CA 申请证书 -
Send certificate request to Certification Authority
应用下面自建的 CA 为 CSR 文件签名:
$ cd /var/MyCA/
$ openssl ca -in cert.csr -config /var/MyCA/openssl.cnf生成的证书将被放到 certs/ 目录,并且 index.txt 和 serial 的内容也将发生变化
验证证书
接下来应用两个 Python 程序验证证书。
client.py:
import socket
import ssl
def main():
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.load_verify_locations(<CA 根证书 >)
with socket.create_connection(("127.0.0.1", 9443)) as sock:
with context.wrap_socket(sock, server_hostname=< 域名 >) as ssock:
msg = "do I connect with server ?".encode("utf-8")
ssock.send(msg)
msg = ssock.recv(1024).decode("utf-8")
print(f"receive msg from server: {msg}")
ssock.close()
if name == “__main__”:
main()
server.py:
import ssl
import socket
def main():
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(< 证书 >, < 私钥 >, < 明码 >)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0) as sock:
sock.bind(('127.0.0.1', 9443))
sock.listen(5)
with context.wrap_socket(sock, server_side=True) as ssock:
while True:
client_socket, addr = ssock.accept()
msg = client_socket.recv(1024).decode("utf-8")
print(f"receive msg from client {addr}:{msg}")
msg = f"yes, you have connected with server.\r\n".encode("utf-8")
client_socket.send(msg)
client_socket.close()
if name == “__main__”:
main()
应用 OpenSSL 撤消证书和生成 CRL
- 撤消证书:
$ openssl ca -revoke certs/01.pem -config /var/MyCA/openssl.cnf
Using configuration from /var/MyCA/openssl.cnf
Enter pass phrase for /var/MyCA/private/cakey.pem:
Revoking Certificate 01.
Data Base Updated -
生成证书撤消列表(CRL):
还能够增加 -crldays 或 -crlhours 参数,以阐明下一个撤消列表将在多少天(或多少小时)后公布
$ openssl ca -gencrl -out testca.crl -config /var/MyCA/openssl.cnf
-
用以下命令查看 testca.crl:
$ openssl crl -in testca.crl -text -noout
Certificate Revocation List (CRL):Version 1 (0x0)
Signature Algorithm: md5WithRSAEncryption
Issuer: /CN=Test 06 29 2016/ST=BJ/C=CN/emailAddress=jordan23nbastar@yeah.net/O=Root Certification Authority Last Update: Jun 29 07:45:30 2016 GMT Next Update: Jul 6 07:45:30 2016 GMT
Revoked Certificates:
Serial Number: 01Revocation Date: Jun 29 07:34:11 2016 GMT
Signature Algorithm: md5WithRSAEncryption
45:ac:4e:f5:be:e2:ee:87:5f:99:d7:20:a3:14:aa:3c:18:46: e9:75:92:0b:86:f2:52:1a:cf:24:70:f5:da:ec:fc:77:6f:2d: ee:be:4f:ab:39:89:89:ff:6c:b8:89:a9:10:4a:4a:45:3c:15: cc:78:5b:de:b0:99:40:72:bd:5d:d2:a3:49:4a:90:94:74:4d: 80:4e:6f:21:29:81:f8:a7:25:c7:b9:6c:e5:68:76:a9:d3:89: 8d:a0:f3:ce:42:6e:f0:34:63:a0:47:37:2d:12:e6:16:8e:c6: 20:4c:e8:77:6b:8a:77:ff:95:83:02:b9:3f:d7:46:3d:64:62: f5:a6:39:db:c8:26:e0:e0:a0:eb:97:6a:7e:2c:2c:6d:78:32: 2d:fc:09:f4:92:96:1d:22:7b:6c:37:71:53:51:26:bb:4d:b7: cd:7b:51:ae:49:7e:54:06:55:c1:db:5d:e5:61:4a:c2:ac:93: 7b:c8:c6:fc:f0:ee:68:bd:67:33:78:1e:9c:a3:dd:1c:48:2d: 93:0c:e5:2b:ae:f1:55:07:ae:03:e3:17:f5:d5:b7:4d:b1:26: ce:c7:6a:78:46:a4:cb:e1:35:07:8f:60:c1:98:44:4a:f4:3d: 1a:2f:9f:4a:2c:c8:a3:8a:2e:2a:5a:dd:d9:54:4e:54:73:c1: 75:b0:77:cd