runsisi's

technical notes

SSL/TLS 自签名证书

2019-12-11 runsisi#tcp/ip

自签名证书

生成私钥,生成 csr,然后使用同一个私钥进行签名:

// add -aes256, -des3, etc. to encrypt the private key
$ openssl genrsa -out self.key 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
....................................+++++
........+++++
e is 65537 (0x010001)
$ openssl req -new -key self.key -out self.csr -subj /C=CN/ST=HuNan/L=Changsha/O='Example, Ltd.'/CN=self.example.com/emailAddress=self@example.com
$ openssl x509 -in self.csr -req -days 365 -signkey self.key -out self.crt
Signature ok
subject=C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = self@example.com
Getting Private key

或者在生成 csr 的同时生成私钥,然后使用同一个私钥进行签名:

// remove -nodes to encrypt the private key
$ openssl req -new -newkey rsa:2048 -nodes -out self.csr -keyout self.pem -subj /C=CN/ST=HuNan/L=Changsha/O='Example, Ltd.'/CN=self.example.com/emailAddress=self@example.com
Generating a RSA private key
.................................................................+++++
...............................................................................................+++++
writing new private key to 'self.pem'
-----
$ openssl x509 -in self.csr -req -days 365 -signkey self.pem -out self.crt
Signature ok
subject=C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", CN = self.example.com, emailAddress = self@example.com
Getting Private key

查看私钥:

$ openssl rsa -in self.key -noout -text
RSA Private-Key: (2048 bit, 2 primes)
...
$ openssl rsa -in self.pem -noout -text
RSA Private-Key: (2048 bit, 2 primes)
...

CA 证书

实际上就是生成 CA:TRUE 的自签名证书:

$ openssl genrsa -out ca.key 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
.....................+++++
..............+++++
e is 65537 (0x010001)
$ openssl req -new -x509 -days 365 -key ca.key -out ca.crt -subj /C=CN/ST=HuNan/L=Changsha/O='Example, Ltd.'/emailAddress=runsisi@example.com

也可以合并为一步:

$ openssl req -new -x509 -days 365 -newkey rsa:2048 -nodes -out ca.crt -keyout ca.pem -subj /C=CN/ST=HuNan/L=Changsha/O='Example, Ltd.'/emailAddress=ca@example.com
Generating a RSA private key
.....................+++++
...............................................................+++++
writing new private key to 'ca.pem'
-----

如果不提供 -subj 选项,则会以交互式的方式输入:

$ openssl req -new -x509 -days 365 -newkey rsa:2048 -nodes -out ca.crt -keyout ca.pem
Generating a RSA private key
.......+++++
...............+++++
writing new private key to 'ca.pem'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:HuNan
Locality Name (eg, city) []:ChangSha
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example, Ltd.
Organizational Unit Name (eg, section) []:
Common Name (e.g. server FQDN or YOUR name) []:
Email Address []:ca@example.com

查看 CA 证书:

$ openssl x509 -in ca.crt -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            46:28:b8:5f:71:65:79:53:2c:83:ca:d7:64:51:dd:5c:c0:7c:c4:79
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = ca@example.com
        Validity
            Not Before: Dec 11 08:41:18 2019 GMT
            Not After : Dec 10 08:41:18 2020 GMT
        Subject: C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = ca@example.com
...
        X509v3 extensions:
            X509v3 Subject Key Identifier:
                FF:F2:BC:D1:3F:AC:59:64:8A:15:AB:84:4C:0D:2E:26:1C:B6:03:F0
            X509v3 Authority Key Identifier:
                keyid:FF:F2:BC:D1:3F:AC:59:64:8A:15:AB:84:4C:0D:2E:26:1C:B6:03:F0

            X509v3 Basic Constraints: critical
                CA:TRUE
...

证书签名

使用 CA 证书为自签名证书(CA:FALSE)签名:

$ openssl x509 -in self.crt -CA ca.crt -CAkey ca.pem -CAcreateserial -out self.crt
Getting CA Private Key

生成 csr(subject 的 CN 字段要求提供,或者通过 subjectAltName 提供域名或 IP 地址):

$ openssl req -new -newkey rsa:2048 -nodes -out www.csr -keyout www.pem -subj /C=CN/ST=HuNan/L=Changsha/O='Example, Ltd.'/CN=www.example.com/emailAddress=www@example.com
Generating a RSA private key
....................+++++
.............................................+++++
writing new private key to 'www.pem'
-----
$ openssl req -new -newkey rsa:2048 -nodes -out email.csr -keyout email.pem -subj /C=CN/ST=HuNan/L=Changsha/O='Example, Ltd.'/CN=email.example.com/emailAddress=email@example.com
Generating a RSA private key
.........................................................+++++
...........+++++
writing new private key to 'email.pem'
-----

使用 CA 证书为 csr 生成签名证书:

$ openssl x509 -in www.csr -req -CA ca.crt -CAkey ca.pem -CAcreateserial -out www.crt
Signature ok
subject=C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", CN = www.example.com, emailAddress = www@example.com
Getting CA Private Key
$ openssl x509 -in email.csr -req -CA ca.crt -CAkey ca.pem -CAcreateserial -out email.crt
Signature ok
subject=C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", CN = email.example.com, emailAddress = email@example.com
Getting CA Private Key

也可以使用如下的方式进行签名(命令行中具体的目录名需参考 openssl 配置文件,CentOS 7.x 为 /etc/pki/tls/openssl.cnf,Ubuntu 18.04 为 /usr/lib/ssl/openssl.cnf):

$ mkdir -p demoCA/{crl,newcerts}
$ touch demoCA/{index.txt,index.txt.attr}
$ echo 00 > demoCA/serial
$ echo 00 > demoCA/crlnumber
$ openssl ca -in www.csr -cert ca.crt -keyfile ca.pem -out www.crt
Using configuration from /usr/lib/ssl/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 0 (0x0)
        Validity
            Not Before: Dec 12 01:38:14 2019 GMT
            Not After : Dec 11 01:38:14 2020 GMT
        Subject:
            countryName               = CN
            stateOrProvinceName       = HuNan
            organizationName          = Example, Ltd.
            commonName                = www.example.com
            emailAddress              = www@example.com
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
...
$ openssl ca -in email.csr -cert ca.crt -keyfile ca.pem -out email.crt
Using configuration from /usr/lib/ssl/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: Dec 12 01:38:37 2019 GMT
            Not After : Dec 11 01:38:37 2020 GMT
        Subject:
            countryName               = CN
            stateOrProvinceName       = HuNan
            organizationName          = Example, Ltd.
            commonName                = email.example.com
            emailAddress              = email@example.com
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
...

此时,所有的证书信息都记录在 index.txt 数据库中:

$ ls demoCA/newcerts/
00.pem  01.pem
$ cat demoCA/index.txt
V       201211013814Z           00      unknown /C=CN/ST=HuNan/O=Example, Ltd./CN=www.example.com/emailAddress=www@example.com
V       201211013837Z           01      unknown /C=CN/ST=HuNan/O=Example, Ltd./CN=email.example.com/emailAddress=email@example.com

校验证书:

$ openssl verify -CAfile ca.crt self.crt
self.crt: OK
$ openssl verify -CAfile ca.crt www.crt
www.crt: OK
$ openssl verify -CAfile ca.crt email.crt
email.crt: OK

subjectAltName

CN 只能为一个域名或者带通配符的域名,而 subjectAltName 能解决多域名或 IP 地址的问题,默认配置文件中 req_extensions = v3_req 没有启用,因此生成的 csr 都不带 X509v3 extensions,而支持 subjectAltName 必须要启用 X509v3 支持,因此要么修改 openssl 的默认配置文件,或者显式创建新的配置文件。

$ vi san.cnf
[req]
distinguished_name = 123_dn
req_extensions = abc_req # The extensions to add to a certificate request
x509_extensions = xxx_ca # The extensions to add to the self signed cert

[123_dn]

[abc_req]
basicConstraints = CA:FALSE
# self-signed certificate seems not to be detected if keyUsage extension is present and does not assert the keyCertSig bit
# https://github.com/openssl/openssl/issues/1418
# keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[xxx_ca]
basicConstraints = critical,CA:true

[alt_names]
DNS.1 = server1.example.com
DNS.2 = mail.example.com
DNS.3 = www.sub.example.com
DNS.4 = support.example.com
IP.1 = 192.168.5.2
IP.2 = 192.168.5.3
IP.3 = 192.168.5.4

生成带 subjectAltName 的 csr:

$ openssl req -new -newkey rsa:2048 -nodes -out san.csr -keyout san.pem -subj /C=CN/ST=HuNan/L=Changsha/O='Example, Ltd.'/CN=san.example.com/emailAddress=san@example.com -config san.cnf -extensions abc_req
Generating a RSA private key
...................................................+++++
..................+++++
writing new private key to 'san.pem'
-----
$ openssl req -in san.csr -noout -text
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", CN = san.example.com, emailAddress = san@example.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
...
                    04:6b
                Exponent: 65537 (0x10001)
        Attributes:
        Requested Extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            X509v3 Subject Alternative Name:
                DNS:server1.example.com, DNS:mail.example.com, DNS:www.sub.example.com, DNS:support.example.com, IP Address:192.168.5.2, IP Address:192.168.5.3, IP Address:192.168.5.4
...

为带 subjectAltName 的 csr 生成签名证书:

$ openssl x509 -in san.csr -req -CA ca.crt -CAkey ca.pem -CAcreateserial -out san.crt -extfile san.cnf -extensions abc_req
Signature ok
subject=C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", CN = san.example.com, emailAddress = san@example.com
Getting CA Private Key
$ openssl x509 -in san.crt -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            75:fc:dd:6f:af:19:da:52:e3:d1:44:df:17:a3:6c:86:cb:a5:70:a9
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = ca@example.com
        Validity
            Not Before: Dec 13 02:37:19 2019 GMT
            Not After : Jan 12 02:37:19 2020 GMT
        Subject: C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", CN = san.example.com, emailAddress = san@example.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
...
                    04:6b
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            X509v3 Subject Alternative Name:
                DNS:server1.example.com, DNS:mail.example.com, DNS:www.sub.example.com, DNS:support.example.com, IP Address:192.168.5.2, IP Address:192.168.5.3, IP Address:192.168.5.4
...

另一种方式生成带 subjectAltName 的签名证书(注意不同的发行版配置文件路径不同):

# vi /usr/lib/ssl/openssl.cnf
...
copy_extensions = copy
...
$ openssl ca -in san.csr -cert ca.crt -keyfile ca.pem -out san.crt
Using configuration from /usr/lib/ssl/openssl.cnf
Check that the request matches the signature
Signature ok
Certificate Details:
        Serial Number: 19 (0x13)
        Validity
            Not Before: Dec 13 02:39:37 2019 GMT
            Not After : Dec 12 02:39:37 2020 GMT
        Subject:
            countryName               = CN
            stateOrProvinceName       = HuNan
            organizationName          = Example, Ltd.
            commonName                = san.example.com
            emailAddress              = san@example.com
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                1F:DE:14:C0:CE:EC:49:A0:10:DD:F5:2A:15:EB:F5:D5:CF:B5:17:09
            X509v3 Authority Key Identifier:
                keyid:18:33:EA:DF:52:16:80:D6:58:9D:00:1D:3C:EE:97:2D:BE:61:66:C8

            X509v3 Subject Alternative Name:
                DNS:server1.example.com, DNS:mail.example.com, DNS:www.sub.example.com, DNS:support.example.com, IP Address:192.168.5.2, IP Address:192.168.5.3, IP Address:192.168.5.4
Certificate is to be certified until Dec 12 02:39:37 2020 GMT (365 days)
Sign the certificate? [y/n]:y
...

生成带 subjectAltName 的自签名证书:

$ openssl req -new -newkey rsa:2048 -nodes -out self.csr -keyout self.pem -subj /C=CN/ST=HuNan/L=Changsha/O='Example, Ltd.'/CN=self.example.com/emailAddress=self@example.com -config san.cnf -extensions abc_req
Generating a RSA private key
......................................................................................+++++
....................................+++++
writing new private key to 'self.pem'
-----
$ openssl x509 -in self.csr -req -days 365 -signkey self.pem -out self.crt -extfile san.cnf -extensions abc_req
Signature ok
subject=C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", CN = self.example.com, emailAddress = self@example.com
Getting Private key

使用 CA 证书进行签名:

$ openssl x509 -in self.crt -CA ca.crt -CAkey ca.pem -CAcreateserial -out self.crt
Getting CA Private Key
$ openssl verify -CAfile ca.crt self.crt
self.crt: OK
$ openssl x509 -in self.crt -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            38:5e:e8:d5:8a:53:aa:53:3d:cc:e4:44:a7:ea:8a:7f:b8:77:67:07
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", CN = self.example.com, emailAddress = self@example.com
        Validity
            Not Before: Dec 12 13:20:29 2019 GMT
            Not After : Dec 11 13:20:29 2020 GMT
        Subject: C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", CN = self.example.com, emailAddress = self@example.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    00:b2:b8:44:d7:5f:be:76:f8:22:f2:f9:2b:4b:38:
...
                    98:85
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            X509v3 Key Usage:
                Digital Signature, Non Repudiation, Key Encipherment
            X509v3 Subject Alternative Name:
                DNS:server1.example.com, DNS:mail.example.com, DNS:www.sub.example.com, DNS:support.example.com, IP Address:192.168.5.2, IP Address:192.168.5.3, IP Address:192.168.5.4
...

或者一步生成自签名证书,然后使用 CA 证书进行签名:

$ openssl req -new -x509 -days 365 -newkey rsa:2048 -nodes -out self.crt -keyout self.pem -subj /C=CN/ST=HuNan/L=Changsha/O='Example, Ltd.'/emailAddress=self@example.com -config san.cnf -extensions abc_req
Generating a RSA private key
....................................................................+++++
..........................................................................................................+++++
writing new private key to 'self.pem'
-----
$ openssl x509 -in self.crt -CA ca.crt -CAkey ca.pem -CAcreateserial -out self.crt
Getting CA Private Key
$ openssl verify -CAfile ca.crt self.crt
self.crt: OK
$ openssl x509 -in self.crt -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            58:1d:84:dc:af:e6:10:d4:9b:0f:0f:88:6a:40:2a:6f:9f:38:4e:78
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = ca@example.com
        Validity
            Not Before: Dec 12 13:23:37 2019 GMT
            Not After : Jan 11 13:23:37 2020 GMT
        Subject: C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = self@example.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
...
                    b5:2d
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            X509v3 Key Usage:
                Digital Signature, Non Repudiation, Key Encipherment
            X509v3 Subject Alternative Name:
                DNS:server1.example.com, DNS:mail.example.com, DNS:www.sub.example.com, DNS:support.example.com, IP Address:192.168.5.2, IP Address:192.168.5.3, IP Address:192.168.5.4
...

生成带 x509 v3 扩展的 CA 证书:

$ openssl req -new -x509 -days 365 -newkey rsa:2048 -nodes -out ca.crt -keyout ca.pem -subj /C=CN/ST=HuNan/L=Changsha/O='Example, Ltd.'/emailAddress=ca@example.com -config san.cnf -extensions xxx_ca
Generating a RSA private key
...............+++++
........................................+++++
writing new private key to 'ca.pem'
-----
$ openssl x509 -in ca.crt -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            63:87:64<img class="emoji-icon" alt="emoji-ab" data-icon="emoji-ab" style="display: inline; margin: 0; margin-top: 1px; position: relative; top: 5px; width: 25px" src="data:image/png;base64, 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" title="emoji-ab" />b4:fb:88:68:f1:2e:06:da:c7:ae:ae:cf:1c:f4:81:30
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = ca@example.com
        Validity
            Not Before: Dec 13 02:12:35 2019 GMT
            Not After : Dec 12 02:12:35 2020 GMT
        Subject: C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = ca@example.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
...
                    9b:ab
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: critical
                CA:TRUE
...

证书吊销

吊销证书(吊销通过 openssl x509 命令签名的证书同样需要创建 demoCA 等目录结构):

$ openssl ca -revoke email.crt -cert ca.crt -keyfile ca.pem
Using configuration from /usr/lib/ssl/openssl.cnf
Revoking Certificate 01.
Data Base Updated
$ cat demoCA/index.txt
V       201211013814Z           00      unknown /C=CN/ST=HuNan/O=Example, Ltd./CN=www.example.com/emailAddress=www@example.com
R       201211013837Z   191212014354Z   01      unknown /C=CN/ST=HuNan/O=Example, Ltd./CN=email.example.com/emailAddress=email@example.com

生成 crl:

$ openssl ca -gencrl -out ca.crl -cert ca.crt -keyfile ca.pem
Using configuration from /usr/lib/ssl/openssl.cnf

查看 crl:

$ openssl crl -in ca.crl -noout -text
Certificate Revocation List (CRL):
        Version 2 (0x1)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = ca@example.com
        Last Update: Dec 11 11:22:27 2019 GMT
        Next Update: Jan 10 11:22:27 2020 GMT
        CRL extensions:
            X509v3 CRL Number:
                0
Revoked Certificates:
    Serial Number: 00
        Revocation Date: Dec 11 11:20:51 2019 GMT
...

校验 crl:

$ openssl crl -in ca.crl -CAfile ca.crt -noout
verify OK

crl 验证

服务端使用合法证书(www.crt):

$ openssl s_server -accept 9999 -state -verify 1 -verify_return_error -cert www.crt -key www.pem -CAfile ca.crt -CRL ca.crl -crl_check
verify depth is 1
Using default temp DH parameters
ACCEPT
SSL_accept:before SSL initialization
SSL_accept:before SSL initialization
SSL_accept:SSLv3/TLS read client hello
SSL_accept:SSLv3/TLS write server hello
SSL_accept:SSLv3/TLS write change cipher spec
SSL_accept:TLSv1.3 write encrypted extensions
SSL_accept:SSLv3/TLS write certificate request
SSL_accept:SSLv3/TLS write certificate
SSL_accept:TLSv1.3 write server certificate verify
SSL_accept:SSLv3/TLS write finished
SSL_accept:TLSv1.3 early data
SSL_accept:TLSv1.3 early data
depth=0 C = CN, ST = HuNan, O = "Example, Ltd.", CN = email.example.com, emailAddress = email@example.com
verify error:num=23:certificate revoked
SSL3 alert write:fatal:certificate revoked
SSL_accept:error in error
ERROR
verify error:certificate revoked
140665704964544:error:1417C086:SSL routines:tls_process_client_certificate:certificate verify failed:../ssl/statem/statem_srvr.c:3668:
shutting down SSL
CONNECTION CLOSED

客户端使用已吊销证书(email.crt):

$ openssl s_client -connect 127.0.0.1:9999 -state -verify 1 -verify_return_error -cert email.crt -key email.pem -CAfile ca.crt -CRL ca.crl -crl_check
verify depth is 1
CONNECTED(00000005)
SSL_connect:before SSL initialization
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS read server hello
SSL_connect:TLSv1.3 read encrypted extensions
SSL_connect:SSLv3/TLS read server certificate request
depth=1 C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = ca@example.com
verify return:1
depth=0 C = CN, ST = HuNan, O = "Example, Ltd.", CN = www.example.com, emailAddress = www@example.com
verify return:1
SSL_connect:SSLv3/TLS read server certificate
SSL_connect:TLSv1.3 read server certificate verify
SSL_connect:SSLv3/TLS read finished
SSL_connect:SSLv3/TLS write change cipher spec
SSL_connect:SSLv3/TLS write client certificate
SSL_connect:SSLv3/TLS write certificate verify
SSL_connect:SSLv3/TLS write finished
---
Certificate chain
 0 s:C = CN, ST = HuNan, O = "Example, Ltd.", CN = www.example.com, emailAddress = www@example.com
   i:C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = ca@example.com
 1 s:C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = ca@example.com
   i:C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = ca@example.com
---
Server certificate
-----BEGIN CERTIFICATE-----
...
/eE9AMa+QvawSb4QHEo7jnhHD5lZrqXbUn5K+D7BCvDc/tNFaZmeECdN7ykyjecE
PC6r6RRokaZ8MANcLnLOiw==
-----END CERTIFICATE-----
subject=C = CN, ST = HuNan, O = "Example, Ltd.", CN = www.example.com, emailAddress = www@example.com

issuer=C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = ca@example.com

---
Acceptable client certificate CA names
C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = ca@example.com
Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA224:ECDSA+SHA1:RSA+SHA224:RSA+SHA1
Shared Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 2668 bytes and written 2644 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
SSL3 alert read:fatal:certificate revoked
140167942967744:error:14094414:SSL routines:ssl3_read_bytes:sslv3 alert certificate revoked:../ssl/record/rec_layer_s3.c:1528:SSL alert number 44

服务端使用已吊销证书(email.crt):

$ openssl s_server -accept 9999 -state -verify 1 -verify_return_error -cert email.crt -key email.pem -CAfile ca.crt -CRL ca.crl -crl_check
verify depth is 1
Using default temp DH parameters
ACCEPT
SSL_accept:before SSL initialization
SSL_accept:before SSL initialization
SSL_accept:SSLv3/TLS read client hello
SSL_accept:SSLv3/TLS write server hello
SSL_accept:SSLv3/TLS write change cipher spec
SSL_accept:TLSv1.3 write encrypted extensions
SSL_accept:SSLv3/TLS write certificate request
SSL_accept:SSLv3/TLS write certificate
SSL_accept:TLSv1.3 write server certificate verify
SSL_accept:SSLv3/TLS write finished
SSL_accept:TLSv1.3 early data
SSL3 alert read:fatal:certificate revoked
SSL_accept:error in error
ERROR
140038365897152:error:14094414:SSL routines:ssl3_read_bytes:sslv3 alert certificate revoked:../ssl/record/rec_layer_s3.c:1528:SSL alert number 44
shutting down SSL
CONNECTION CLOSED

客户端使用合法证书(www.crt):

$ openssl s_client -connect 127.0.0.1:9999 -state -verify 1 -verify_return_error -cert www.crt -key www.pem -CAfile ca.crt -CRL ca.crl -crl_check
verify depth is 1
CONNECTED(00000005)
SSL_connect:before SSL initialization
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS write client hello
SSL_connect:SSLv3/TLS read server hello
SSL_connect:TLSv1.3 read encrypted extensions
SSL_connect:SSLv3/TLS read server certificate request
depth=0 C = CN, ST = HuNan, O = "Example, Ltd.", CN = email.example.com, emailAddress = email@example.com
verify error:num=23:certificate revoked
SSL3 alert write:fatal:certificate revoked
SSL_connect:error in error
140099815223744:error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed:../ssl/statem/statem_clnt.c:1924:
---
no peer certificate available
---
Acceptable client certificate CA names
C = CN, ST = HuNan, L = Changsha, O = "Example, Ltd.", emailAddress = ca@example.com
Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512:ECDSA+SHA224:ECDSA+SHA1:RSA+SHA224:RSA+SHA1
Shared Requested Signature Algorithms: ECDSA+SHA256:ECDSA+SHA384:ECDSA+SHA512:Ed25519:Ed448:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA-PSS+SHA256:RSA-PSS+SHA384:RSA-PSS+SHA512:RSA+SHA256:RSA+SHA384:RSA+SHA512
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 2312 bytes and written 318 bytes
Verification error: certificate revoked
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 23 (certificate revoked)
---

测试代码

服务端:

package main

import (
        "fmt"
        "log"
        "net/http"
)

func HelloServer(w http.ResponseWriter, req *http.Request) {
        w.Header().Set("Content-Type", "text/plain")
        w.Write([]byte("Hello world.\n"))
}

func main() {
        http.HandleFunc("/", HelloServer)
        fmt.Println("serving..")
        err := http.ListenAndServeTLS(":6666", "email.crt", "email.pem", nil)
        if err != nil {
                log.Fatal("ListenAndServe: ", err)
        }
}

客户端:

#!/usr/bin/env python2

import requests

r = requests.get('https://email.example.com:6666/', verify='ca.crt')
print(r.text)
#!/usr/bin/env python2

import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.ssl_ import create_urllib3_context
import ssl

class CrlAdapter(HTTPAdapter):
    def __init__(self, *args, **kwargs):
        self._cacert = kwargs.pop('cacert', None)
        self._cacrl = kwargs.pop('cacrl', None)

        super(CrlAdapter, self).__init__(*args, **kwargs)

    def init_poolmanager(self, *args, **kwargs):
        ctx = create_urllib3_context()
        ctx.verify_mode = ssl.CERT_REQUIRED
        ctx.load_verify_locations(self._cacert)
        ctx.verify_flags = ssl.VERIFY_CRL_CHECK_LEAF
        ctx.load_verify_locations(self._cacrl)
        kwargs['ssl_context'] = ctx
        return super(CrlAdapter, self).init_poolmanager(*args, **kwargs)

s = requests.Session()
s.mount('https://email.example.com:6666/', CrlAdapter(cacert='ca.crt', cacrl='ca.crl'))

r = s.get('https://email.example.com:6666/')
print(r.text)
$ curl https://email.example.com:6666/ --cacert ca.crt --crlfile ca.crl
Hello world.

cfssl

$ vi xxx.json
{
    "CN": "runsisi.com",
    "names": [
        {
            "C":  "CN",
            "L":  "ChangSha",
            "O":  "Runsisi, Inc.",
            "OU": "WWW",
            "ST": "HuNan"
        }
    ]
}
$ out=$(cfssl genkey -initca -loglevel 5 xxx.json)
$ echo $out
{"cert":"-----BEGIN CERTIFICATE-----\nMIICHDCCAcKgAwIBAgIUermxpDiRqq0U2emeD+g+L9yaqW0wCgYIKoZIzj0EAwIw\nbDELMAkGA1UEBhMCQ04xDjAMBgNVBAgTBUh1TmFuMREwDwYDVQQHEwhDaGFuZ1No\nYTEWMBQGA1UEChMNUnVuc2lzaSwgSW5jLjEMMAoGA1UECxMDV1dXMRQwEgYDVQQD\nEwtydW5zaXNpLmNvbTAeFw0xOTEyMTEwNzIxMDBaFw0yNDEyMDkwNzIxMDBaMGwx\nCzAJBgNVBAYTAkNOMQ4wDAYDVQQIEwVIdU5hbjERMA8GA1UEBxMIQ2hhbmdTaGEx\nFjAUBgNVBAoTDVJ1bnNpc2ksIEluYy4xDDAKBgNVBAsTA1dXVzEUMBIGA1UEAxML\ncnVuc2lzaS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAR+vCa1BnrmtUSV\nrtpF085rRoQz8L/bx4fZWWlafjFIxqG60/J0p4Y0PxgMmB+Rpc3cQpLxQRvKjdOK\ndJ56uXWyo0IwQDAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNV\nHQ4EFgQU58t3jLfpHb6iGI+3BBSXqUY9jfswCgYIKoZIzj0EAwIDSAAwRQIgXqOM\nSBieo4nNwABjLjPiAi2SvrrdRSlLZI1JnMYOdu4CIQCdYwDjaux297xDIMPdUy3C\nUuAvx8uRw7cuHxa+gho+iQ==\n-----END CERTIFICATE-----\n","csr":"-----BEGIN CERTIFICATE REQUEST-----\nMIIBJzCBzgIBADBsMQswCQYDVQQGEwJDTjEOMAwGA1UECBMFSHVOYW4xETAPBgNV\nBAcTCENoYW5nU2hhMRYwFAYDVQQKEw1SdW5zaXNpLCBJbmMuMQwwCgYDVQQLEwNX\nV1cxFDASBgNVBAMTC3J1bnNpc2kuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\nQgAEfrwmtQZ65rVEla7aRdPOa0aEM/C/28eH2VlpWn4xSMahutPydKeGND8YDJgf\nkaXN3EKS8UEbyo3TinSeerl1sqAAMAoGCCqGSM49BAMCA0gAMEUCIGbqSA1hBKQD\nErf6CqO6q732YRpT5Z9bR/VZZ23WEIpWAiEApO7d0byE/XaaR8MIrn6NijpLTPm6\nH0/kgiB3FpaNNoU=\n-----END CERTIFICATE REQUEST-----\n","key":"-----BEGIN EC PRIVATE KEY-----\nMHcCAQEEIIFo0G4zMgO6JjH2k17oPsnTlKRQJlzdxcMIOUfWhXc7oAoGCCqGSM49\nAwEHoUQDQgAEfrwmtQZ65rVEla7aRdPOa0aEM/C/28eH2VlpWn4xSMahutPydKeG\nND8YDJgfkaXN3EKS8UEbyo3TinSeerl1sg==\n-----END EC PRIVATE KEY-----\n"}
$ echo $out | jq .cert | xargs printf -- > xxx.crt
$ echo $out | jq .csr | xargs printf -- > xxx.csr
$ echo $out | jq .key | xargs printf -- > xxx.key
$ ls
xxx.crt  xxx.csr  xxx.json  xxx.key
$ openssl req -in xxx.csr -noout -text
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: C = CN, ST = HuNan, L = ChangSha, O = "Runsisi, Inc.", OU = WWW, CN = runsisi.com
...
$ openssl x509 -in xxx.crt -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            7a:b9:b1:a4:38:91:aa:ad:14:d9:e9:9e:0f:e8:3e:2f:dc:9a:a9:6d
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: C = CN, ST = HuNan, L = ChangSha, O = "Runsisi, Inc.", OU = WWW, CN = runsisi.com
...
$ openssl pkey -in xxx.key -noout -text
Private-Key: (256 bit)
priv:
    81:68:d0:6e:33:32:03:ba:26:31:f6:93:5e:e8:3e:
    c9:d3:94:a4:50:26:5c:dd:c5:c3:08:39:47:d6:85:
    77:3b
pub:
    04:7e:bc:26:b5:06:7a:e6:b5:44:95:ae:da:45:d3:
    ce:6b:46:84:33:f0:bf:db:c7:87:d9:59:69:5a:7e:
...
$ openssl x509 -in xxx.crt -noout -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            09:bb:aa:d9:22:44:98:c6:64:c7:69:b6:0a:4c:4c:4b:98:05:0d:09
        Signature Algorithm: ecdsa-with-SHA256
        Issuer: C = CN, ST = HuNan, L = ChangSha, O = "Runsisi, Inc.", OU = WWW, CN = runsisi.com
        Validity
            Not Before: Dec 12 02:11:00 2019 GMT
            Not After : Dec 10 02:11:00 2024 GMT
        Subject: C = CN, ST = HuNan, L = ChangSha, O = "Runsisi, Inc.", OU = WWW, CN = runsisi.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:a2:57:84:eb:08:8b:cf:7c:ae:d6:b6:b7:10:97:
                    f6:c9:97:3a:9e:26:bb:c9:64:45:6b:b9:0c:3b:c9:
                    52:fc:c1:a9:4f:1b:e9:26:37:6c:5f:5c:6e:3e:5d:
                    a3<img class="emoji-icon" alt="emoji-ab" data-icon="emoji-ab" style="display: inline; margin: 0; margin-top: 1px; position: relative; top: 5px; width: 25px" src="data:image/png;base64, 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" title="emoji-ab" />d4:e0:42:9c:e0:ea:ea:46:ca:34:93:03:bd:
                    2d:4a:a0:8c:55
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        X509v3 extensions:
            X509v3 Key Usage: critical
                Certificate Sign, CRL Sign
            X509v3 Basic Constraints: critical
                CA:TRUE
...

参考资料

man openssl

SEE ALSO
       asn1parse(1), ca(1), ciphers(1), cms(1), config(5), crl(1), crl2pkcs7(1), dgst(1), dhparam(1), dsa(1), dsaparam(1), ec(1), ecparam(1), enc(1), engine(1), errstr(1), gendsa(1),
       genpkey(1), genrsa(1), nseq(1), ocsp(1), passwd(1), pkcs12(1), pkcs7(1), pkcs8(1), pkey(1), pkeyparam(1), pkeyutl(1), prime(1), rand(1), rehash(1), req(1), rsa(1), rsautl(1),
       s_client(1), s_server(1), s_time(1), sess_id(1), smime(1), speed(1), spkac(1), srp(1), storeutl(1), ts(1), verify(1), version(1), x509(1), crypto(7), ssl(7), x509v3_config(5)

What is a CSR (Certificate Signing Request)?

https://www.sslshopper.com/what-is-a-csr-certificate-signing-request.html

What is the SSL Certificate Common Name?

https://support.dnsimple.com/articles/what-is-common-name/

What is the SSL Certificate Subject Alternative Name?

https://support.dnsimple.com/articles/what-is-ssl-san/

Why does the x509 command not copy extension in certificate request?

https://github.com/openssl/openssl/issues/10458

self-signed certificate seems not to be detected if keyUsage extension is present and does not assert the keyCertSig bit

https://github.com/openssl/openssl/issues/1418

Self-signed SSL certificates and how to trust them

https://tarunlalwani.com/post/self-signed-certificates-trusting-them/

x509v3_config

https://www.openssl.org/docs/manmaster/man5/x509v3_config.html

OpenSSL CA keyUsage extension

https://superuser.com/questions/738612/openssl-ca-keyusage-extension

Python ssl.OP_NO_SSLv3() Examples

https://www.programcreek.com/python/example/79576/ssl.OP_NO_SSLv3

Introducing CFSSL - CloudFlare’s PKI toolkit

https://blog.cloudflare.com/introducing-cfssl/

CFSSL: Cloudflare’s PKI and TLS toolkit

https://github.com/cloudflare/cfssl

Using own CA’s w/Python

https://forum.opnsense.org/index.php?PHPSESSID=fsg465sv9sh3p9gnjfvdio6eb6&topic=6679.0