自签发证书配置 HTTPS 单向双向验证

好久以前阅读《HTTP/2 in Action》一书起了个头,又重新放回了书架。近来再次对 HTTPS/TLS 来了劲,自己的博客用的是 Let's Encrypt 签发的证书,这次实践一下自签发证书的过程与配置,并实现单向和双向的认证方式。

如果是配置单向认证的过程需要有以下三个证书
  1. 根(CA) 证书: root.crt
  2. 服务端私钥文件: server.key
  3. 服务端公钥证书: server.crt

证书是含有组织与域名或(CA) 信息以及公钥的文件, root.key 和 root.crt 将被用于签发其他的证书。这里的 crt 证书是 x509 格式的。

浏览器只会信任某些 CA 机构签发的证书,如 DigiCert, GlobalSign, GoDaddy, Amazon Root CA,Let's Encrypt 等。如果是不被信任 CA 签发的证书,我们在浏览器中打开相应的 HTTPS url 就会看到 'Not Secure - Your connection is not private' 的提示,要继续访问需自行承担可能的安全责任。

如果是公司内部,或相互信任的公司之间的通信,用自签发的证书也不成问题。不过直接用浏览器或其他 HTTPS 客户端信任又免费的 Let's Encrypt 来签发证书可免去浏览器的警告或某些特殊配置。

根证书,服务端证书或后面将要讲到的客户端证书的完整生成过程都是三步
  1. openssl genrsa 生成私钥 xxx.key
  2. openssl req -new -out xxx.csr -key xxx.key 产生证书请求文件
  3. openssl x509 -req in xxx.csr -out xxx.crt -signkey xxx.key -CAcreateserial -days 365 生成相应证书文件。生成根证书时无需指定 -CA 参数,因为自己就是 CA,其他证书则需用 -CA root.crt 让根证书予以签发

根据不同的情景,以上某些步骤可以合并操作。

生成根(CA)证书

最终要得到的 root.key, root.crt 用于签发服务端或客户端证书。如果对中间过程没有兴趣的,可用该节最后的 openssl req -x509... 一条命令完成所有。

1. 生成根私钥 root.key
1openssl genrsa -out root.key 2048

虽然 openssl genrsa 支持最小的长度是 512,但 Nginx 要求最短长度为 2048。

2. 生成根证书请求文件 root.csr
1openssl req -new -out root.csr -key root.key \
2  -subj "/C=US/ST=Illinois/L=Chicago/O=CA Company/CN=RootCA"

-subj 参数指定所有组织相关的信息,免得按提示要多次输入,C, ST, O 分别是国家,省(州), 组织名,这里省略了 OU 部门名称,根据实际进行输入即可。最后 CN,此处是根证书,可随便指定,无需真正的域名。

生成证书请求文件是一个中间步骤,目的是准备组织和域名信息,再加上私钥用于签发最后的证书文件(包含公钥)

3. 生成自签发的根证书 root.crt

CA 就是自己,所以不用指定 -CA 参数
1openssl x509 -req -in root.csr -out root.crt -signkey root.key -CAcreateserial -days 365
其实最终只需要 root.key 和 root.csr 文件,所以 #2, #3 两步可合而为一,并且不生成中间过程的 root.csr 请求文件,只生成  root.crt 文件
1openssl req -x509 -new -nodes -key root.key -days 365 -out root.crt \
2  -subj "/C=US/ST=Illinois/L=Chicago/O=CA Company/CN=RootCA"

** 由于是自签发根证书,只需一条命令便能生成需要的 root.key 和 root.crt 文件
1openssl req -x509 -newkey rsa:2048 -nodes -keyout root.key -out root.crt \
2  -subj "/C=US/ST=Illinois/L=Chicago/O=CA Company/CN=RootCA"

如果是在 macOS 中可用命令 open root.crt 查看,默认会用 Keychain Access/Add Certificates 打开,然后可看到信息

或者用 openssl 查看,下面显示了部分信息
 1openssl x509 -noout -text -in root.crt
 2Certificate:
 3    Data:
 4        Version: 3 (0x2)
 5        Serial Number:
 6            34:10:b8:8a:00:86:76:c3:e0:b5:23:4c:0a:79:30:1f:f9:aa:79:17
 7        Signature Algorithm: sha256WithRSAEncryption
 8        Issuer: C=US, ST=Illinois, L=Chicago, O=CA Company, CN=RootCA
 9        Validity
10            Not Before: Nov 27 04:48:07 2024 GMT
11            Not After : Dec 27 04:48:07 2024 GMT
12        Subject: C=US, ST=Illinois, L=Chicago, O=CA Company, CN=RootCA
13        Subject Public Key Info:
14            Public Key Algorithm: rsaEncryption
15                Public-Key: (2048 bit)
16                Modulus:
17                    00:b0:84:85:c5:95:aa:f6:ae:e4:e4:97:43:7c:cb:

生成服务端私钥与证书

正常的过程是要先有私钥 server.key,证书请求文件 server.csr, 然后用前面的根(CA)证书 root.key 和 root.crt 签发服务端证书,可参考上节中生成 root.key 和 root.csr 的步骤。

1. 生成服务端私钥与证书请求文件

这里用一条命令同时生成 server.key 和 server.csr 两文件
1openssl req -newkey rsa:2048 -nodes -keyout server.key -out server.csr \
2  -subj "/C=US/ST=Illinois/L=Chicago/O=X Company/CN=server.local"

-subj 参数要留意 CN 必须与服务器的访问方式对应,如果将用 IP 地址(如 192.168.86.101) 访问, 则 CN=192.168.86.101。这里假设将用 server.local 域名来访问,所以设置 CN=server.local,后面在客户端将通过 /etc/hosts 中配置 127.0.0.1 server.local 来访问本地的 https://server.local 服务。 

2. 用根证书签发服务端证书
1openssl x509 -req -in server.csr -out server.crt -CA root.crt -CAkey root.key -CAcreateserial -days 365

到现在我们就有了 server.key 和 server.crt 文件,同样的方式可以查看 server.crt 的信息。

私钥,证书请求文件和证书的内容格式

不管是私钥,公钥还是证书,或更多我们常见到  key, csr, crt, cer, der, pem, pfx, p12, jks 等扩展名,其实它们内部的格式都差不多,基本像下面那样
-----BEGIN XXX-----
<ASCII 字符串, 通常是 BASE64 格式>
-----END XXX-----
以上扩展名的缩写为
  1. PEM(Privacy-Enhanced Mail)
  2. DER(Distinguished Encoding Rules)
  3. CRT/CER(Certificate), KEY(key)
  4. P12(PKCS#12)
  5. PFX(Predecessor of PKCS#12)
  6. JKS(Java Key Storage)

比如我们查看前面生成的 key, csr, crt 文件内部如下:
cat server.key
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCnGmYO71gf4bQF
......
3TFxXQvsM5LjL/JCckYRscUiVQiaZzkNi044XeTRLKLrrsEKxOKJ+Dn7R5c4lBIQ
+kP41vHSScLrhltkjKZfQiHQ
-----END PRIVATE KEY----- cat server.csr
-----BEGIN CERTIFICATE REQUEST-----
MIICpDCCAYwCAQAwXzELMAkGA1UEBhMCVVMxETAPBgNVBAgMCElsbGlub2lzMRAw
......
jr3ySo1hczjdhn4TiYR7AtLMTQj4X8aTnSfDBpn95+/jR1x9KjeCwLBgCJWXmtwo
Fu5hcsQXfjc=
-----END CERTIFICATE REQUEST----- cat server.crt
-----BEGIN CERTIFICATE-----
MIIDhzCCAm+gAwIBAgIUb2sb3c3BaZk+z1fOprzKO6Zcu18wDQYJKoZIhvcNAQEL
......
4YXaB54t+I8x/eiYu1W77hJjAG52SsVb/2QiJdbFT0VDwcVrMdMRutMjlg==
-----END CERTIFICATE-----
在 csr, crt 文件中内容进行 Base64 解码可看到组织及域名信息。有时候我们看到的  *.pem 文件,它可能就是一个 *.crt 文件,比如愿意的外把上面的 server.crt 改名成 server.pem 也行,关键看其中的内容是 BEGIN xxx。

配置 Nginx 支持 HTTPS 单向验证 (TLS)

有了前面生成的服务端私钥 server.key, 证书 server.crt 后,首先应用到 Nginx Web 服务中。以 Docker 启用的 Ubuntu:24.04 容器为例, 假定 server.key, server.crt 在当前目录中

1docker run -it -v ${PWD}:/certs -p 443:443 ubuntu:24.04
2
3root@3010925dc0b1:/# apt update && apt install -y nginx
4root@3010925dc0b1:/# echo Ok > /usr/share/nginx/html/index.html

覆盖 /usr/shar/nginx/html/index.html 的内容为 "Ok", 以免后的 curl 命令输出过长。

编辑 /etc/nginx/nginx.cfg, 在 http 块中加上
1    server {
2        listen 443 ssl;
3        server_name server.local;
4        ssl_certificate server.crt;
5        ssl_certificate_key server.key;
6    }

然后启动 nginx 或重新启动 nginx -s reload

用 curl 测试

已在 /etc/hosts 中配置了 127.0.0.1 server.local 映射。或可在另一个 Docker 容器中测试(docker run -it --net host ubuntu:24.04)
root@docker-desktop:~# curl https://server.local
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
因为服务器端的证书是自签发的,默认不被信任,需用 -k 信任证书,重试,顺便加上 -v 参数
 1root@docker-desktop:~# curl  -kv https://server.local
 2* Host server.local:443 was resolved.
 3* IPv6: (none)
 4* IPv4: 127.0.0.1
 5*   Trying 127.0.0.1:443...
 6* Connected to server.local (127.0.0.1) port 443
 7* ALPN: curl offers h2,http/1.1
 8* TLSv1.3 (OUT), TLS handshake, Client hello (1):
 9* TLSv1.3 (IN), TLS handshake, Server hello (2):
10* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
11* TLSv1.3 (IN), TLS handshake, Certificate (11):
12* TLSv1.3 (IN), TLS handshake, CERT verify (15):
13* TLSv1.3 (IN), TLS handshake, Finished (20):
14* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
15* TLSv1.3 (OUT), TLS handshake, Finished (20):
16* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS
17* ALPN: server accepted http/1.1
18* Server certificate:
19*  subject: C=US; ST=Illinois; L=Chicago; O=Demo Server; CN=server.local
20*  start date: Nov 27 05:34:22 2024 GMT
21*  expire date: Nov 27 05:34:22 2025 GMT
22*  issuer: C=US; ST=Illinois; L=Chicago; O=CA Company; CN=RootCA
23*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
24*   Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
25* using HTTP/1.x
26> GET / HTTP/1.1
27> Host: server.local
28> User-Agent: curl/8.5.0
29> Accept: */*
30>
31* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
32* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
33* old SSL session ID is stale, removing
34< HTTP/1.1 200 OK
35< Server: nginx/1.24.0 (Ubuntu)
36< Date: Wed, 27 Nov 2024 06:41:10 GMT
37< Content-Type: text/html
38< Content-Length: 3
39< Last-Modified: Wed, 27 Nov 2024 06:38:57 GMT
40< Connection: keep-alive
41< ETag: "6746be81-3"
42< Accept-Ranges: bytes
43<
44Ok

控制台输出明确显示了完整的 TLS 握手过程
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20): 
用浏览器访问,同样因为我们自己的 rootCA 是不被浏览器信任的,所以看到的是这样

点击地址栏 "Not Secure" 可查看服务端证书以及 CA 的信息。点 "Advanced" -> "Processed to server.local (unsafe)" 自担安全的信任该网站 server.local 而访问它的内容

还能用 openssl s_client 命令访问
  1$ openssl s_client -connect server.local:443
  2Connecting to 127.0.0.1
  3CONNECTED(00000003)
  4depth=0 C=US, ST=Illinois, L=Chicago, O=Demo Server, CN=server.local
  5verify error:num=20:unable to get local issuer certificate
  6verify return:1
  7depth=0 C=US, ST=Illinois, L=Chicago, O=Demo Server, CN=server.local
  8verify error:num=21:unable to verify the first certificate
  9verify return:1
 10depth=0 C=US, ST=Illinois, L=Chicago, O=Demo Server, CN=server.local
 11verify return:1
 12---
 13Certificate chain
 14 0 s:C=US, ST=Illinois, L=Chicago, O=Demo Server, CN=server.local
 15   i:C=US, ST=Illinois, L=Chicago, O=CA Company, CN=RootCA
 16   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
 17   v:NotBefore: Nov 27 05:34:22 2024 GMT; NotAfter: Nov 27 05:34:22 2025 GMT
 18---
 19Server certificate
 20-----BEGIN CERTIFICATE-----
 21MIIDhzCCAm+gAwIBAgIUaVkSmjyS/M/82njsEW5Dgy2Pg8EwDQYJKoZIhvcNAQEL
 22BQAwWDELMAkGA1UEBhMCVVMxETAPBgNVBAgMCElsbGlub2lzMRAwDgYDVQQHDAdD
 23aGljYWdvMRMwEQYDVQQKDApDQSBDb21wYW55MQ8wDQYDVQQDDAZSb290Q0EwHhcN
 24MjQxMTI3MDUzNDIyWhcNMjUxMTI3MDUzNDIyWjBfMQswCQYDVQQGEwJVUzERMA8G
 25A1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xFDASBgNVBAoMC0RlbW8g
 26U2VydmVyMRUwEwYDVQQDDAxzZXJ2ZXIubG9jYWwwggEiMA0GCSqGSIb3DQEBAQUA
 27A4IBDwAwggEKAoIBAQDJasqYBxyiMjL6DW6MpiKDloStV4tkFonYaVUMm2fr7ZiY
 28Lj5A+rNXcLBvZlaf93hC11EdZljom9l2aIybRRR0ml0NEycE+sGHbOs0PFqtN2nB
 29gV/15J5gqTeCO7HpBWkhg3R0+PPj46WK03idz4f0M2Dz9qgnJC+/j3enMX32/t4X
 30KG8/lwiah1bAcBq4OU6N6syfpAA10ISljLjg8ztnb0u0WMNOLYgWd6/aOlZk2Wp6
 31k7AJrKRiTfjz7Pi3aDMjaTTziQ0Erz+ngD8RYbUhuZIIFDZNGPiJMDSoaj0ffzNz
 32fuqVN4TqvMBwGWtKmQ/ofC93cVGXcjZNIUvjSqLrAgMBAAGjQjBAMB0GA1UdDgQW
 33BBQzMRYTcWvhMTsRDN4Av2J3Vga6nTAfBgNVHSMEGDAWgBQ5N633VoXMBr+O0TH3
 34P8m/B/hHnzANBgkqhkiG9w0BAQsFAAOCAQEAo4cjfmljrr/m9Vsuzw+yD5sp1TPb
 35sIp+OF7DWKhRn7CxPv/wnmpexx3RVgBFpJ3erpgFSH+Zu8tP7vRf0t6mqUaWmkMG
 36w/iBAJ46tnVlaeiPWEnjkvOUHNb9V1OXFOXQMad0NfyAMulJBHMJiyl73JGqziVP
 37fI9U5NgHVWLUtWlvGkdAiukg+RDGSPQYrxaDuwYnsl+vdbmFRw9Yn7I3BxRTqtHo
 38FkbWIgTt0URil7dc5t775ZIFVKusH+XC+Kt+wk2Mjp1KCkZ0Kk7auQHfdbjm9mNp
 39OJjjwmVs1QeBnuMfdMqLsvEn2FG4pJZ4blq/Z/coLDUHoXo0tk3FDc9vEA==
 40-----END CERTIFICATE-----
 41subject=C=US, ST=Illinois, L=Chicago, O=Demo Server, CN=server.local
 42issuer=C=US, ST=Illinois, L=Chicago, O=CA Company, CN=RootCA
 43---
 44No client certificate CA names sent
 45Peer signing digest: SHA256
 46Peer signature type: RSA-PSS
 47Server Temp Key: X25519, 253 bits
 48---
 49SSL handshake has read 1467 bytes and written 403 bytes
 50Verification error: unable to verify the first certificate
 51---
 52New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
 53Protocol: TLSv1.3
 54Server public key is 2048 bit
 55This TLS version forbids renegotiation.
 56Compression: NONE
 57Expansion: NONE
 58No ALPN negotiated
 59Early data was not sent
 60Verify return code: 21 (unable to verify the first certificate)
 61---
 62---
 63Post-Handshake New Session Ticket arrived:
 64SSL-Session:
 65    Protocol  : TLSv1.3
 66    Cipher    : TLS_AES_256_GCM_SHA384
 67    Session-ID: 0B11B6BFDDB73E9DD08C3FC42AECF250348B22D3E0EB16A3129AD8D527C0A8F4
 68    Session-ID-ctx:
 69    Resumption PSK: 002B9CD5B0A52362F873BA3D665106A9FD736FB0A50AA330948984FB1E73EDCEC62CEEAAE44710F409008D73911DAFBE
 70    PSK identity: None
 71    PSK identity hint: None
 72    SRP username: None
 73    TLS session ticket lifetime hint: 300 (seconds)
 74    TLS session ticket:
 75    0000 - 9c 74 9e 82 50 ea 6a 63-98 fc b0 d9 d7 53 01 6b   .t..P.jc.....S.k
 76    0010 - 8d 91 b5 e9 0e 48 97 5c-51 d0 a1 3a 0a 61 b0 79   .....H.\Q..:.a.y
 77    0020 - 71 9a 59 1d 7f f4 74 9b-aa 3c f3 52 0b 1f 17 c8   q.Y...t..<.R....
 78    0030 - 25 cf 37 bf fc f8 93 55-e4 65 fd 5a 5e dc 66 4c   %.7....U.e.Z^.fL
 79    0040 - aa 8b 42 a9 46 4f 92 a2-32 25 35 94 ca ad fc 54   ..B.FO..2%5....T
 80    0050 - 73 cd 1f 9b ef 31 d3 c8-5d 30 7d c7 28 9e 66 cb   s....1..]0}.(.f.
 81    0060 - e9 99 c7 f9 f3 a6 05 04-dc 7e 77 5c 4b f3 d0 e2   .........~w\K...
 82    0070 - 1f 7a cd 9c f8 d5 ab c4-dd 20 3e 86 4d ff ce 1a   .z....... >.M...
 83    0080 - 4a 88 8e 50 53 e7 74 1a-96 87 c6 02 af 35 7f 4e   J..PS.t......5.N
 84    0090 - f5 f3 a8 0f 0d 97 3b 73-d5 ca 22 bf 56 de 60 db   ......;s..".V.`.
 85    00a0 - 38 13 e1 bd ac c4 42 1d-58 df f2 eb 84 91 4d 21   8.....B.X.....M!
 86    00b0 - 13 2f a3 f6 4f 9f 61 18-44 1f f1 d2 82 88 03 95   ./..O.a.D.......
 87    00c0 - f0 5d 88 f7 ee a5 b6 68-7f 73 4f db c8 52 34 84   .].....h.sO..R4.
 88    00d0 - a5 ae 39 c3 0f 05 b0 e6-d7 3e 76 32 43 29 54 8f   ..9......>v2C)T.
 89    00e0 - 0c 57 af dc a4 ac 08 45-3c 33 85 9a df 59 47 6b   .W.....E<3...YGk
 90
 91    Start Time: 1733008466
 92    Timeout   : 7200 (sec)
 93    Verify return code: 21 (unable to verify the first certificate)
 94    Extended master secret: no
 95    Max Early Data: 0
 96---
 97read R BLOCK
 98---
 99Post-Handshake New Session Ticket arrived:
100SSL-Session:
101    Protocol  : TLSv1.3
102    Cipher    : TLS_AES_256_GCM_SHA384
103    Session-ID: 224F519D21BCA495AE6008B2EA2322BAFD83FC8D05B93849CA4AF31F74662DD9
104    Session-ID-ctx:
105    Resumption PSK: 31A997D5DE3B4AD430063FC9C2E5EAC9251ED7C6E8B546BCEAC4E6E6364E9897D7119F837639206D344EBBEE6666217A
106    PSK identity: None
107    PSK identity hint: None
108    SRP username: None
109    TLS session ticket lifetime hint: 300 (seconds)
110    TLS session ticket:
111    0000 - 9c 74 9e 82 50 ea 6a 63-98 fc b0 d9 d7 53 01 6b   .t..P.jc.....S.k
112    0010 - f0 db 7b cc e0 30 87 b4-ad 38 35 d8 94 6f dd 90   ..{..0...85..o..
113    0020 - f3 95 f3 99 94 60 c4 db-57 fd 6f 15 d5 d4 97 7a   .....`..W.o....z
114    0030 - b6 2b db f5 aa f1 31 0b-fd 48 87 e1 66 c9 47 cb   .+....1..H..f.G.
115    0040 - f0 1a da 02 b3 db e5 c1-8a 63 74 e6 23 6b 99 09   .........ct.#k..
116    0050 - 9a b4 f2 38 58 65 ab 13-6c c5 3e 56 7b 10 cd cb   ...8Xe..l.>V{...
117    0060 - 05 b2 70 e0 17 4d 06 fc-b3 65 d9 65 a6 1b 74 8b   ..p..M...e.e..t.
118    0070 - a1 fa e8 92 78 4d b8 5a-ef 02 24 6e 9a 1c 05 61   ....xM.Z..$n...a
119    0080 - 35 a6 4d 6a a0 08 bb 7e-48 f5 c9 71 4c 11 34 b9   5.Mj...~H..qL.4.
120    0090 - 39 36 77 69 3b bf 25 33-fd ca 19 5a 07 0a cd d3   96wi;.%3...Z....
121    00a0 - ae 5c 45 66 7d 16 6c 7d-3c 60 fd 96 1a c3 56 52   .\Ef}.l}<`....VR
122    00b0 - 43 75 cf 70 d2 b0 4f f3-99 08 15 35 41 6f 34 ad   Cu.p..O....5Ao4.
123    00c0 - c9 a0 69 6c ed 60 70 c3-19 62 0c 43 4d 1d 1a 36   ..il.`p..b.CM..6
124    00d0 - be e8 5a e9 a5 1a 93 ef-11 db 10 9a 03 a2 71 55   ..Z...........qU
125    00e0 - fa 3e 59 d5 89 c2 88 8b-94 a3 47 00 66 6f 96 d6   .>Y.......G.fo..
126
127    Start Time: 1733008466
128    Timeout   : 7200 (sec)
129    Verify return code: 21 (unable to verify the first certificate)
130    Extended master secret: no
131    Max Early Data: 0
132---
133read R BLOCK

openssl s_client 更有助于我们理解 TLS 握手的详细过程。

Apache2 中的配置 HTTPS

和 Nginx 差不多,先要启用相应的模块和站点
1a2enmod ssl
2a2ensite default-ssl

再编辑 default-ssl.conf,配置一个 443 虚拟机
1<VirtualHost *:443>
2    ServerName server.local
3    DocumentRoot /var/www/html
4
5    SSLEngine on
6    SSLCertificateFile /etc/ssl/certs/server.crt
7    SSLCertificateKeyFile /etc/ssl/private/server.key
8    ....
9</VirtualHost>

apachectl -k restart 重启即可

SpringBoot Tomcat 中的配置

如果开发的一个 SpringBoot Web 应用直接面对客户,它的前面没有 Nginx/Apache 或其他的 Load Balancer, 这一节或许对我们会有所帮助。所有的关于 server.ssl 的配置可参考 SpringBoot 官方文档 Common Application Properties / Server Properties

相关的配置项有
 1server.ssl.bundle: The name of a configured SSL bundle.
 2server.ssl.certificate: Path to a PEM-encoded SSL certificate file.
 3server.ssl.certificate-private-key: Path to a PEM-encoded private key file for the SSL certificate.
 4server.ssl.ciphers: Supported SSL ciphers.
 5server.ssl.client-auth: Client authentication mode. Requires a trust store.
 6server.ssl.enabled: Whether to enable SSL support. default: true
 7server.ssl.enabled-protocols: Enabled SSL protocols. 
 8server.ssl.key-alias: Alias that identifies the key in the key store.
 9server.ssl.key-password: Password used to access the key in the key store.
10server.ssl.key-store: Path to the key store that holds the SSL certificate (typically a jks file).
11server.ssl.key-store-password: Password used to access the key store.
12server.ssl.key-store-provider: Provider for the key store.
13server.ssl.key-store-type: Type of the key store.
14server.ssl.protocol: SSL protocol to use. default: TLS
15server.ssl.server-name-bundles: Mapping of host names to SSL bundles for SNI configuration.
16server.ssl.trust-certificate: Path to a PEM-encoded SSL certificate authority file.
17server.ssl.trust-certificate-private-key: Path to a PEM-encoded private key file for the SSL certificate authority.
18server.ssl.trust-store: Trust store that holds SSL certificates.
19server.ssl.trust-store-password: Password used to access the trust store.
20server.ssl.trust-store-provider: Provider for the trust store.
21server.ssl.trust-store-type: Type of the trust store.

从中我们发现 server.ssl.certificateserver.ssl.certificate-private-key 可实现与 Nginx/Apache 类似的配置

试着在 application.properties 中加上
1server.ssl.certificate=/certs/server.crt
2server.ssl.certificate-private-key=/certs/server.key

启动 SpringBoot Web 项目,看到控制台输出
Tomcat started on port 8080 (https) with context path '/'
测试
curl -kv https://server.local:8080/
没问题,一样的效果,需要注意的是 SpringBoot Tomcat 不同时启动 HTTP 和 HTTPS 服务,配置了相应的 server.ssl 则只启动 HTTPS 服务,并且端口号复用 server.port 的配置。

如果采用 key-store 的配置方式则要用 Java 的 keytool 命令把 openssl 生成的  server.key, server.crt 生成 keystore.jks 文件,或者直接用 keytool 替代 openssl 来生成它所需的 keystore.jks 文件。

配置 Nginx 支持 HTTPS 双向验证 (mTLS)

为配置 mTLS(mutual TLS),除了前面的 root.key, root.crt, server.key, server.crt 外,还需要为客户端生成相应的 key 与证书。何谓双向验证是除了传统的客户端信任服务端,服务端还要检查客户端的证书,决定是否信任,是否提供服务给该客户端。我们可以为不同的客户生独立的客户端私钥与证书,或多个客户可共享。服务端单向验证在客户端可选择信任或不信任,即使不信任也可以使用服务; 而双向认证让服务端更有主动权,服务认为是一个非法的客户(未提供客户端证书或提供了非法的客户端证书)能够拒绝服务。双向认证让 HTTPS 通信更安全,不易被中间人攻击,以至于让无比缺德的 zscaler 都会无能为力。

生成客户端 client1.key, client1.crt
1openssl req -newkey rsa:2048 -nodes -keyout client1.key -out client1.csr -subj "/C=US/ST=Illinois/L=Chicago/O=Company 1/CN=client1"
2openssl x509 -req -in client1.csr -out client1.crt -CA root.crt -CAkey root.key -CAcreateserial -days 365

编辑 /etc/nginx/nginx.cfg, server {} 块
1    server {
2        listen 443 ssl;
3        server_name server.local;
4        ssl_certificate server.crt;
5        ssl_certificate_key server.key;
6
7        ssl_client_certificate /certs/root.crt;
8        ssl_verify_client on;
9    }

nginx -s reload

现在用  curl -k 完全信息服务端也没用,服务端可不买这个账
 1root@docker-desktop:~# curl  -kv https://server.local
 2* Host server.local:443 was resolved.
 3* IPv6: (none)
 4* IPv4: 127.0.0.1
 5*   Trying 127.0.0.1:443...
 6* Connected to server.local (127.0.0.1) port 443
 7* ALPN: curl offers h2,http/1.1
 8* TLSv1.3 (OUT), TLS handshake, Client hello (1):
 9* TLSv1.3 (IN), TLS handshake, Server hello (2):
10* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
11* TLSv1.3 (IN), TLS handshake, Request CERT (13):
12* TLSv1.3 (IN), TLS handshake, Certificate (11):
13* TLSv1.3 (IN), TLS handshake, CERT verify (15):
14* TLSv1.3 (IN), TLS handshake, Finished (20):
15* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
16* TLSv1.3 (OUT), TLS handshake, Certificate (11):
17* TLSv1.3 (OUT), TLS handshake, Finished (20):
18* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS
19* ALPN: server accepted http/1.1
20* Server certificate:
21*  subject: C=US; ST=Illinois; L=Chicago; O=Demo Server; CN=server.local
22*  start date: Nov 27 05:34:22 2024 GMT
23*  expire date: Nov 27 05:34:22 2025 GMT
24*  issuer: C=US; ST=Illinois; L=Chicago; O=CA Company; CN=RootCA
25*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
26*   Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
27* using HTTP/1.x
28> GET / HTTP/1.1
29> Host: server.local
30> User-Agent: curl/8.5.0
31> Accept: */*
32>
33* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
34* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
35* old SSL session ID is stale, removing
36< HTTP/1.1 400 Bad Request
37< Server: nginx/1.24.0 (Ubuntu)
38< Date: Wed, 27 Nov 2024 06:41:48 GMT
39< Content-Type: text/html
40< Content-Length: 246
41< Connection: close
42<
43<html>
44<head><title>400 No required SSL certificate was sent</title></head>
45<body>
46<center><h1>400 Bad Request</h1></center>
47<center>No required SSL certificate was sent</center>
48<hr><center>nginx/1.24.0 (Ubuntu)</center>
49</body>
50</html>
51* Closing connection
52* TLSv1.3 (IN), TLS alert, close notify (256):
53* TLSv1.3 (OUT), TLS alert, close notify (256):

400 Bad Request, 因为 No required SSL certificate was sent

主动带上 client1.key 和  client1.crt 就行,只要服务端的证书是不被广泛接受的,就要用 -k 来手动接受证书,即使是服务端与客户端相互承认也不能省略 -k
 1root@docker-desktop:~# curl --cert client1.crt --key client1.key -kv https://server.local
 2* Host server.local:443 was resolved.
 3* IPv6: (none)
 4* IPv4: 127.0.0.1
 5*   Trying 127.0.0.1:443...
 6* Connected to server.local (127.0.0.1) port 443
 7* ALPN: curl offers h2,http/1.1
 8* TLSv1.3 (OUT), TLS handshake, Client hello (1):
 9* TLSv1.3 (IN), TLS handshake, Server hello (2):
10* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
11* TLSv1.3 (IN), TLS handshake, Request CERT (13):
12* TLSv1.3 (IN), TLS handshake, Certificate (11):
13* TLSv1.3 (IN), TLS handshake, CERT verify (15):
14* TLSv1.3 (IN), TLS handshake, Finished (20):
15* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
16* TLSv1.3 (OUT), TLS handshake, Certificate (11):
17* TLSv1.3 (OUT), TLS handshake, CERT verify (15):
18* TLSv1.3 (OUT), TLS handshake, Finished (20):
19* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 / X25519 / RSASSA-PSS
20* ALPN: server accepted http/1.1
21* Server certificate:
22*  subject: C=US; ST=Illinois; L=Chicago; O=Demo Server; CN=server.local
23*  start date: Nov 27 05:34:22 2024 GMT
24*  expire date: Nov 27 05:34:22 2025 GMT
25*  issuer: C=US; ST=Illinois; L=Chicago; O=CA Company; CN=RootCA
26*  SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
27*   Certificate level 0: Public key type RSA (2048/112 Bits/secBits), signed using sha256WithRSAEncryption
28* using HTTP/1.x
29> GET / HTTP/1.1
30> Host: server.local
31> User-Agent: curl/8.5.0
32> Accept: */*
33>
34* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
35* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
36* old SSL session ID is stale, removing
37< HTTP/1.1 200 OK
38< Server: nginx/1.24.0 (Ubuntu)
39< Date: Wed, 27 Nov 2024 06:42:04 GMT
40< Content-Type: text/html
41< Content-Length: 3
42< Last-Modified: Wed, 27 Nov 2024 06:38:57 GMT
43< Connection: keep-alive
44< ETag: "6746be81-3"
45< Accept-Ranges: bytes
46<
47Ok

除了用命令 curl,同样可用 openssl s_clinet 携带 client1.key 和 client1.crt 来访问
openssl s_client -connect server.local:443 -key client1.key -cert client1.crt
  1openssl s_client -connect server.local:443 -key client1.key -cert client1.crt
  2Connecting to 127.0.0.1
  3CONNECTED(00000003)
  4depth=0 C=US, ST=Illinois, L=Chicago, O=Demo Server, CN=server.local
  5verify error:num=20:unable to get local issuer certificate
  6verify return:1
  7depth=0 C=US, ST=Illinois, L=Chicago, O=Demo Server, CN=server.local
  8verify error:num=21:unable to verify the first certificate
  9verify return:1
 10depth=0 C=US, ST=Illinois, L=Chicago, O=Demo Server, CN=server.local
 11verify return:1
 12---
 13Certificate chain
 14 0 s:C=US, ST=Illinois, L=Chicago, O=Demo Server, CN=server.local
 15   i:C=US, ST=Illinois, L=Chicago, O=CA Company, CN=RootCA
 16   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
 17   v:NotBefore: Nov 27 05:34:22 2024 GMT; NotAfter: Nov 27 05:34:22 2025 GMT
 18---
 19Server certificate
 20-----BEGIN CERTIFICATE-----
 21MIIDhzCCAm+gAwIBAgIUaVkSmjyS/M/82njsEW5Dgy2Pg8EwDQYJKoZIhvcNAQEL
 22BQAwWDELMAkGA1UEBhMCVVMxETAPBgNVBAgMCElsbGlub2lzMRAwDgYDVQQHDAdD
 23aGljYWdvMRMwEQYDVQQKDApDQSBDb21wYW55MQ8wDQYDVQQDDAZSb290Q0EwHhcN
 24MjQxMTI3MDUzNDIyWhcNMjUxMTI3MDUzNDIyWjBfMQswCQYDVQQGEwJVUzERMA8G
 25A1UECAwISWxsaW5vaXMxEDAOBgNVBAcMB0NoaWNhZ28xFDASBgNVBAoMC0RlbW8g
 26U2VydmVyMRUwEwYDVQQDDAxzZXJ2ZXIubG9jYWwwggEiMA0GCSqGSIb3DQEBAQUA
 27A4IBDwAwggEKAoIBAQDJasqYBxyiMjL6DW6MpiKDloStV4tkFonYaVUMm2fr7ZiY
 28Lj5A+rNXcLBvZlaf93hC11EdZljom9l2aIybRRR0ml0NEycE+sGHbOs0PFqtN2nB
 29gV/15J5gqTeCO7HpBWkhg3R0+PPj46WK03idz4f0M2Dz9qgnJC+/j3enMX32/t4X
 30KG8/lwiah1bAcBq4OU6N6syfpAA10ISljLjg8ztnb0u0WMNOLYgWd6/aOlZk2Wp6
 31k7AJrKRiTfjz7Pi3aDMjaTTziQ0Erz+ngD8RYbUhuZIIFDZNGPiJMDSoaj0ffzNz
 32fuqVN4TqvMBwGWtKmQ/ofC93cVGXcjZNIUvjSqLrAgMBAAGjQjBAMB0GA1UdDgQW
 33BBQzMRYTcWvhMTsRDN4Av2J3Vga6nTAfBgNVHSMEGDAWgBQ5N633VoXMBr+O0TH3
 34P8m/B/hHnzANBgkqhkiG9w0BAQsFAAOCAQEAo4cjfmljrr/m9Vsuzw+yD5sp1TPb
 35sIp+OF7DWKhRn7CxPv/wnmpexx3RVgBFpJ3erpgFSH+Zu8tP7vRf0t6mqUaWmkMG
 36w/iBAJ46tnVlaeiPWEnjkvOUHNb9V1OXFOXQMad0NfyAMulJBHMJiyl73JGqziVP
 37fI9U5NgHVWLUtWlvGkdAiukg+RDGSPQYrxaDuwYnsl+vdbmFRw9Yn7I3BxRTqtHo
 38FkbWIgTt0URil7dc5t775ZIFVKusH+XC+Kt+wk2Mjp1KCkZ0Kk7auQHfdbjm9mNp
 39OJjjwmVs1QeBnuMfdMqLsvEn2FG4pJZ4blq/Z/coLDUHoXo0tk3FDc9vEA==
 40-----END CERTIFICATE-----
 41subject=C=US, ST=Illinois, L=Chicago, O=Demo Server, CN=server.local
 42issuer=C=US, ST=Illinois, L=Chicago, O=CA Company, CN=RootCA
 43---
 44Acceptable client certificate CA names
 45C=US, ST=Illinois, L=Chicago, O=CA Company, CN=RootCA
 46Requested 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:RSA+SHA224
 47Shared 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
 48Peer signing digest: SHA256
 49Peer signature type: RSA-PSS
 50Server Temp Key: X25519, 253 bits
 51---
 52SSL handshake has read 1632 bytes and written 1624 bytes
 53Verification error: unable to verify the first certificate
 54---
 55New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
 56Protocol: TLSv1.3
 57Server public key is 2048 bit
 58This TLS version forbids renegotiation.
 59Compression: NONE
 60Expansion: NONE
 61No ALPN negotiated
 62Early data was not sent
 63Verify return code: 21 (unable to verify the first certificate)
 64---
 65---
 66Post-Handshake New Session Ticket arrived:
 67SSL-Session:
 68    Protocol  : TLSv1.3
 69    Cipher    : TLS_AES_256_GCM_SHA384
 70    Session-ID: C60065DBCD8AFF88C350CB29F9076F57A95E7290534C518EBB9BF617EBFF0863
 71    Session-ID-ctx:
 72    Resumption PSK: 6D54823943FE7170A07CE3A7770B014EB6182BCD336B43D8A8AFEFB41E424CAD149143C0D6D5E04A793F0E40A47411FB
 73    PSK identity: None
 74    PSK identity hint: None
 75    SRP username: None
 76    TLS session ticket lifetime hint: 300 (seconds)
 77    TLS session ticket:
 78    0000 - 38 07 5a 8e ad d0 49 f4-67 f0 aa a2 e2 82 ae 57   8.Z...I.g......W
 79    0010 - 0d e8 fc 3a 97 ad eb 17-f0 1c 1b 30 d7 13 47 c5   ...:.......0..G.
 80    0020 - 6c 08 7b 91 f6 51 b0 2d-82 05 c6 04 9e f4 a4 1f   l.{..Q.-........
 81    0030 - 31 ef 95 6f 9e b3 ed 15-25 31 59 00 bc 11 d8 b9   1..o....%1Y.....
 82    0040 - 58 00 e3 d7 54 92 77 8e-59 5e 02 f1 11 e7 32 d9   X...T.w.Y^....2.
 83    0050 - 97 03 1e b5 ec ed 4a 6f-1b 2b 44 6b 0d b2 a0 6b   ......Jo.+Dk...k
 84    0060 - 27 10 ae 11 e4 66 6e 55-1d 69 95 05 72 0d cc 63   '....fnU.i..r..c
 85    0070 - cc 36 df 05 06 c6 d0 73-48 af 83 eb 12 de a9 73   .6.....sH......s
 86    0080 - 51 1c ff a1 2a ed e6 4f-25 11 03 9d 6c a0 28 e4   Q...*..O%...l.(.
 87    0090 - d7 b3 c9 c9 8f 45 da d1-53 c5 63 2e 90 25 67 e0   .....E..S.c..%g.
 88    00a0 - b4 77 dc 42 99 25 9d 9a-10 ab c5 79 30 bd 42 e2   .w.B.%.....y0.B.
 89    00b0 - 1c 71 90 40 d9 09 70 8d-61 7b a3 e8 c5 41 95 43   .q.@..p.a{...A.C
 90    00c0 - 10 b6 d1 45 c1 61 02 29-b7 28 80 65 53 3b 8f 79   ...E.a.).(.eS;.y
 91    00d0 - cc fa 1a 61 bb 32 e1 8b-e3 c3 7e 38 38 f0 d7 8c   ...a.2....~88...
 92    00e0 - 3b d3 79 23 1c 3b 99 f0-8f 86 2f 09 50 cc 6f 97   ;.y#.;..../.P.o.
 93    00f0 - a8 a4 63 00 c8 9e a5 25-d0 0b 18 b8 92 5f 2c 65   ..c....%....._,e
 94    0100 - 3f b8 7c 91 1d a6 7b 72-c9 a9 8b 00 e0 e3 ce cb   ?.|...{r........
 95    0110 - c1 74 34 78 e6 42 08 57-20 77 2f 70 45 79 bd 63   .t4x.B.W w/pEy.c
 96    0120 - 0a f3 60 08 da 37 c3 6b-b3 07 8f c8 7e 8e a5 a1   ..`..7.k....~...
 97    0130 - a2 f4 6d f4 f2 3f 97 e5-75 1a 30 19 d2 14 7a 76   ..m..?..u.0...zv
 98    0140 - 53 3e e6 75 a0 23 50 c5-39 05 60 c7 8f 44 b0 de   S>.u.#P.9.`..D..
 99    0150 - 25 3f 06 86 23 19 a4 54-d5 da b2 45 29 c4 3e f0   %?..#..T...E).>.
100    0160 - 98 36 6c 62 6c 74 20 b8-7a 8c f6 08 a0 15 81 1e   .6lblt .z.......
101    0170 - 40 07 62 7d f9 cf 9e 93-55 fe 1d 4f 22 b9 e1 1d   @.b}....U..O"...
102    0180 - 98 78 fe 64 50 31 fb 0c-a8 5d 1a 7a 89 22 da 06   .x.dP1...].z."..
103    0190 - bf 98 28 5d 60 ff a9 2d-76 70 8a 6b 24 11 27 77   ..(]`..-vp.k$.'w
104    01a0 - d1 02 8d 32 ca fd f3 53-0a 1f cd 63 63 b3 92 c7   ...2...S...cc...
105    01b0 - 7c ba 2d cf f8 bf 7e d2-39 f3 19 cf c1 3b 1f 0b   |.-...~.9....;..
106    01c0 - 8c a5 be 4a 64 d5 b7 f6-43 f2 fe fe d8 90 42 cf   ...Jd...C.....B.
107    01d0 - e5 86 1c c6 41 b3 2f 8f-3f cb 7e 27 1e 47 3c 88   ....A./.?.~'.G<.
108    01e0 - ca 00 20 e7 f6 ac ef 25-63 7e f1 59 15 d3 b9 6c   .. ....%c~.Y...l
109    01f0 - b4 99 f5 f3 4e 0b 32 f2-3d 71 36 3e 30 fb e4 5a   ....N.2.=q6>0..Z
110    0200 - 90 74 ae 32 8a 6d eb 05-cf 44 69 dd ba bc 9c 89   .t.2.m...Di.....
111    0210 - 6e 66 61 85 81 42 d6 ad-89 2d be b2 59 d7 d2 15   nfa..B...-..Y...
112    0220 - 6e 2d fe f4 b0 24 7c 15-ff d9 a7 70 f0 af fb f0   n-...$|....p....
113    0230 - 50 53 93 34 d6 58 26 7d-2f d7 2b 03 33 78 37 20   PS.4.X&}/.+.3x7
114    0240 - 21 50 1a 49 d7 aa 28 5f-4d ef d5 ea 39 c7 0b a2   !P.I..(_M...9...
115    0250 - fc e7 4f b1 6f 26 cc cc-e9 59 69 ab 22 dd 5f 5b   ..O.o&...Yi."._[
116    0260 - 38 aa 06 f5 d4 7e be 8d-42 83 7c 30 e0 5d 61 4a   8....~..B.|0.]aJ
117    0270 - c9 78 32 c9 e0 54 1e 64-3e 85 41 96 e0 be ed 4c   .x2..T.d>.A....L
118    0280 - ff 3e 82 fe 87 88 3d 90-b0 33 5a 0a 9c 43 c4 75   .>....=..3Z..C.u
119    0290 - 82 31 4e c7 a1 c9 86 73-8e 38 84 38 73 0e aa 48   .1N....s.8.8s..H
120    02a0 - 50 bd db 58 e9 8d 84 2b-f6 b1 4e 2d b4 08 04 ab   P..X...+..N-....
121    02b0 - 05 26 22 89 7a 91 a1 fc-11 a6 c2 98 19 3c 8e 56   .&".z........<.V
122    02c0 - 29 6d 44 99 1a a3 a5 94-8a f4 8b ea 60 c0 27 01   )mD.........`.'.
123    02d0 - ee 37 c4 e4 30 2d f2 68-fd a2 44 72 d8 af ad 96   .7..0-.h..Dr....
124    02e0 - 65 28 7c 47 b7 15 69 39-7a 02 64 d6 44 33 37 70   e(|G..i9z.d.D37p
125    02f0 - e7 33 61 ae 0a 62 97 89-01 b2 3b 33 57 71 48 fc   .3a..b....;3WqH.
126    0300 - 05 97 b2 2a c7 ec ca d1-87 ec de 5e b9 2d 3e 3a   ...*.......^.->:
127    0310 - 24 9b e7 b4 b5 17 9c 33-9d 9b 52 21 9e 9b 2f 66   $......3..R!../f
128    0320 - 85 9d 85 1c 1e ce 83 ab-c6 92 1d da 7b 1c fc 25   ............{..%
129    0330 - 84 29 a8 f0 fd 4c c4 26-d8 d5 db 85 e5 03 a4 86   .)...L.&........
130    0340 - ef 0f 8b ed ce 40 57 06-34 3d 23 81 bc 56 67 06   .....@W.4=#..Vg.
131    0350 - 4c 18 ba eb eb 3c 5d b9-ca 9d 7e 4d 61 99 70 98   L....<]...~Ma.p.
132    0360 - e9 9e e0 6c 7e 92 bd 81-ff f3 b1 0a 50 9d 85 3c   ...l~.......P..<
133    0370 - af 25 35 f0 ba 9f 06 b5-21 46 0e 14 8a d0 fe c7   .%5.....!F......
134    0380 - 56 53 97 55 ad d7 9a e6-ea e1 a9 95 17 56 0a 6e   VS.U.........V.n
135    0390 - 46 61 42 6d 29 14 3a 59-78 b7 6f 30 aa 4d a5 e0   FaBm).:Yx.o0.M..
136    03a0 - f4 f7 ec 21 11 ee fa 80-08 61 9c 53 86 ea 7b 02   ...!.....a.S..{.
137    03b0 - d8 69 fb 39 50 13 dc ad-07 86 86 e4 f6 17 18 af   .i.9P...........
138    03c0 - f2 d9 60 f2 66 eb 58 50-b7 2f 41 73 b1 2b c0 b1   ..`.f.XP./As.+..
139    03d0 - 54 48 b9 e4 35 95 10 fc-4d b2 7d 2b 2d d0 ca 68   TH..5...M.}+-..h
140    03e0 - be 04 02 11 14 65 08 c7-c4 fe 98 cd 2d 3e 13 48   .....e......->.H
141    03f0 - f1 2c 69 42 a5 d3 ac ec-03 4a f9 2e ad 27 7b 96   .,iB.....J...'{.
142    0400 - 81 b4 aa 74 d0 23 6b f8-1c c0 fd 70 00 cc 7f 5b   ...t.#k....p...[
143    0410 - 1e cb bd df b3 c1 83 61-af 2f 81 1d fe f4 de 95   .......a./......
144    0420 - 37 6e 8c 67 2a 8a 27 cc-6c 9b 70 db 7f a4 96 d4   7n.g*.'.l.p.....
145    0430 - 65 ef 07 68 17 df 86 8e-9b ac 27 e8 d0 3c 21 78   e..h......'..&lt;!x
146    0440 - 7b 61 2e 71 3b 99 4b 4c-81 4d 54 5c 67 a7 45 38   {a.q;.KL.MT\g.E8
147    0450 - a0 04 d9 3b c8 0a f2 8b-50 4c 2c d7 a2 5d c0 48   ...;....PL,..].H
148    0460 - fd 38 ca 75 c1 50 53 80-48 64 eb 22 62 7e 62 14   .8.u.PS.Hd."b~b.
149
150    Start Time: 1733008819
151    Timeout   : 7200 (sec)
152    Verify return code: 21 (unable to verify the first certificate)
153    Extended master secret: no
154    Max Early Data: 0
155---
156read R BLOCK
157---
158Post-Handshake New Session Ticket arrived:
159SSL-Session:
160    Protocol  : TLSv1.3
161    Cipher    : TLS_AES_256_GCM_SHA384
162    Session-ID: EEFDEAF0CA2B63A2C27D92CCBFF99004E2FBC173147833C923060155B7F066CD
163    Session-ID-ctx:
164    Resumption PSK: 0DA3469A6889259B9E4D679C94DE33ED35B749E75CBA7D9BC30BD4718A84FFFECB8CE825CE012FCD56A1FE4AD1F7256E
165    PSK identity: None
166    PSK identity hint: None
167    SRP username: None
168    TLS session ticket lifetime hint: 300 (seconds)
169    TLS session ticket:
170    0000 - 38 07 5a 8e ad d0 49 f4-67 f0 aa a2 e2 82 ae 57   8.Z...I.g......W
171    0010 - 2b db e4 2a 84 d0 dc 89-75 5d 9e 3a d2 b2 95 bf   +..*....u].:....
172    0020 - e1 40 be 1b 1f fb bf 10-ab 8b 6c 02 62 cc 91 64   .@........l.b..d
173    0030 - de 97 13 a4 52 2d 55 35-f6 96 af 15 37 c0 c3 ff   ....R-U5....7...
174    0040 - b6 3b f3 85 1c 26 b2 84-cf f6 14 85 f8 5d 47 fd   .;...&.......]G.
175    0050 - 29 aa 71 3e 36 af 75 3f-5c 5c 3a d4 41 f3 fc fd   ).q>6.u?\\:.A...
176    0060 - b4 57 73 6d 4f c4 91 f3-7b 3f 8f 14 ac de f3 12   .WsmO...{?......
177    0070 - 8b 53 27 b8 bc eb 85 c3-c7 5e 62 63 7f c6 c6 44   .S'......^bc...D
178    0080 - 3e 06 79 9e 75 b9 84 7a-d9 73 7f e9 e5 f2 ce a9   >.y.u..z.s......
179    0090 - 7e c4 ed 98 d1 ec a0 11-22 d5 2e c3 43 ed 17 ca   ~......."...C...
180    00a0 - ed 2d 6e c6 92 55 ff 38-69 b1 75 54 8c e1 8a b9   .-n..U.8i.uT....
181    00b0 - c8 c1 7f 54 90 46 c0 cb-d2 1e f0 b4 41 de d3 27   ...T.F......A..'
182    00c0 - b3 2e 2c 6d ff 7c c0 5e-f7 75 31 c3 e1 4b 8d e4   ..,m.|.^.u1..K..
183    00d0 - 85 05 a9 d1 f7 14 e7 45-60 f8 86 d8 e7 1e f2 83   .......E`.......
184    00e0 - 66 b6 52 4c fb 81 48 be-f6 24 5f 94 31 91 e3 d8   f.RL..H..$_.1...
185    00f0 - 5f 56 f1 7d 2d f0 b4 6f-d1 04 fa 5a 5c cd d8 03   _V.}-..o...Z\...
186    0100 - f0 2f a4 7e af ee ae 0c-69 71 b6 22 b1 af 0f be   ./.~....iq."....
187    0110 - 15 7a 0b 78 55 6c 8b 09-6b e2 9f 39 e3 2f a1 0d   .z.xUl..k..9./..
188    0120 - 5f b1 ac 81 ff 50 43 f0-32 5d 14 1b 82 3c c6 5e   _....PC.2]...<.^
189    0130 - 7b 1b 27 81 a0 62 77 6c-ae 12 56 b8 85 d5 a8 f2   {.'..bwl..V.....
190    0140 - c7 db ef bc 3e 63 8d 58-82 50 16 a5 9d a4 e7 c4   ....>c.X.P......
191    0150 - 3c c1 21 87 41 eb 17 b9-7e 55 10 e5 d4 8e e1 72   <.!.A...~U.....r
192    0160 - b3 2c 28 3d 98 a8 51 96-63 7c 15 6d 06 6b e8 74   .,(=..Q.c|.m.k.t
193    0170 - a4 2e 6c 38 ab cb 93 94-d4 f8 c0 84 20 5a db ce   ..l8........ Z..
194    0180 - f8 79 88 6f fc 67 58 8b-09 23 6b 2f 6b 99 08 85   .y.o.gX..#k/k...
195    0190 - cb 1b ac f8 64 31 eb 91-e1 74 d1 da 64 2f 0f 9f   ....d1...t..d/..
196    01a0 - 2b df 97 ca 3e 12 f6 54-41 84 0e 26 0b af 5f 6f   +...>..TA..&.._o
197    01b0 - 7f fd 16 a2 f4 7f 1a af-b4 9d b5 58 46 09 d5 7c   ...........XF..|
198    01c0 - 79 6b 16 9a 4a f3 d4 9d-10 ac 18 c2 ae eb a2 24   yk..J..........$
199    01d0 - 8e 4c 45 26 57 82 fb d5-9a 24 c3 08 9b cd e1 b7   .LE&W....$......
200    01e0 - 30 85 73 71 09 5d d1 78-0d 16 33 11 0d 10 63 8e   0.sq.].x..3...c.
201    01f0 - ed d3 3b d8 2a 04 72 ac-76 1d 6c 83 b3 8e e8 25   ..;.*.r.v.l....%
202    0200 - aa da ad c5 24 17 04 c1-0a ef 10 41 a5 25 0d d7   ....$......A.%..
203    0210 - 12 8c 68 e8 10 e8 41 8c-92 07 8f 08 97 1f 8e 1a   ..h...A.........
204    0220 - a5 fa 47 b7 96 f9 29 d6-00 71 6e e4 bb 90 6e 4a   ..G...)..qn...nJ
205    0230 - 31 f8 af de a7 b0 34 c3-68 d2 a4 5c c6 42 6a 14   1.....4.h..\.Bj.
206    0240 - 9e d4 85 ee 11 b7 8e b2-58 d1 55 ac 5c cd 9b 36   ........X.U.\..6
207    0250 - 82 f1 dd 0e bb fe 5b 9c-14 b9 51 2b 9a 8d e3 28   ......[...Q+...(
208    0260 - 9f f6 e1 67 a3 9a 9c 93-27 54 7c ce 0f 54 18 c5   ...g....'T|..T..
209    0270 - 4a 00 95 0c 75 ee df ef-29 1b 2c 3d c5 f3 d9 50   J...u...).,=...P
210    0280 - 4d 91 3d 07 81 63 6e e0-5c 79 b8 bc 56 c9 b5 eb   M.=..cn.\y..V...
211    0290 - 10 95 70 48 46 f5 4c 20-48 ef 43 a8 fc 58 ae 08   ..pHF.L H.C..X..
212    02a0 - 80 0e 3d ee 37 eb be 55-61 80 50 22 f2 34 5a bf   ..=.7..Ua.P".4Z.
213    02b0 - 12 08 9a d9 44 c0 a3 2c-08 32 96 c5 62 7b 97 15   ....D..,.2..b{..
214    02c0 - 7f c8 07 48 ad 4f 01 83-60 bf d6 90 54 3f e3 c4   ...H.O..`...T?..
215    02d0 - 3d 2e b6 18 33 ad b0 09-a8 db e5 1e 94 98 7c 71   =...3.........|q
216    02e0 - 5d 91 84 a9 93 2f d1 87-cf dc cd fc 3e 80 4b ff   ]..../......>.K.
217    02f0 - 97 4b e7 d1 fd 29 59 69-7c 46 b3 86 48 ec bd 40   .K...)Yi|F..H..@
218    0300 - 89 4b 74 c7 2d eb 19 16-ca b6 8c 0c 88 39 fa c9   .Kt.-........9..
219    0310 - b2 ca f9 b6 d6 c8 58 c1-b5 f5 20 c1 33 6e 1f ee   ......X... .3n..
220    0320 - 8a 32 e3 51 9d 1f a7 f7-14 58 a8 c8 6f c6 0d 6f   .2.Q.....X..o..o
221    0330 - dd b5 5f 44 29 bd 17 fc-cd f9 d1 78 ad 92 7b 8b   .._D)......x..{.
222    0340 - 36 5d ce f0 8a 9d b8 94-9e f7 2d 7e 7e 95 59 c8   6]........-~~.Y.
223    0350 - 43 7b 8c fd ed 3e cf dd-37 8b 24 32 62 24 ac 64   C{...>..7.$2b$.d
224    0360 - 4d 16 cf c2 13 19 7d 67-0e 25 57 31 47 e4 ed 2c   M.....}g.%W1G..,
225    0370 - f7 93 64 61 b9 98 1f 11-53 69 75 1d 85 3c 54 48   ..da....Siu..<TH
226    0380 - c8 69 a6 be d7 d0 1e 9c-e4 41 30 53 f4 96 a6 34   .i.......A0S...4
227    0390 - 38 f0 98 00 17 d0 fa cf-26 b9 15 3f 20 d9 d9 80   8.......&..? ...
228    03a0 - 3a f4 e0 f6 19 da 69 99-e9 8f 4d 1e ba d5 27 8b   :.....i...M...'.
229    03b0 - 4c 13 51 1f 99 bc 82 ff-cf 0f 73 97 7f 35 61 d1   L.Q.......s..5a.
230    03c0 - 1b 5d 80 6b 0b fd 4c 95-33 74 9a bb 33 a5 6a fe   .].k..L.3t..3.j.
231    03d0 - 3c 68 77 58 bd 00 9e 93-db 6c 62 95 3b c8 0b 3a   <hwX.....lb.;..:
232    03e0 - 25 9d c2 7f 7f ba 19 7d-a5 f5 19 c8 cd db a6 f6   %......}........
233    03f0 - d0 cb 23 c7 c8 cb 5e e2-25 7f 25 88 82 2e b6 19   ..#...^.%.%.....
234    0400 - 11 a3 70 63 e3 8e 5b 79-08 4e ab e8 c8 47 5c 0f   ..pc..[y.N...G\.
235    0410 - 03 4e 18 47 8b c9 9c e4-e5 16 f7 77 90 fe 70 f8   .N.G.......w..p.
236    0420 - ad 4b b4 a2 2a 92 55 5c-0e 52 e4 67 dd d1 84 0c   .K..*.U\.R.g....
237    0430 - 90 cc 82 3c 47 90 c7 46-02 46 7f 34 07 2d cf 4a   ...<G..F.F.4.-.J
238    0440 - 72 f0 d3 3a 4c 5c dd ee-13 a9 7b ca ad e2 28 71   r..:L\....{...(q
239    0450 - 90 2f 98 1c ea 61 05 16-58 f0 f6 ad 72 9d 5b 94   ./...a..X...r.[.
240    0460 - ae 27 ec 83 05 b0 92 e9-5a 0a a5 b6 84 91 f3 2d   .'......Z......-
241
242    Start Time: 1733008819
243    Timeout   : 7200 (sec)
244    Verify return code: 21 (unable to verify the first certificate)
245    Extended master secret: no
246    Max Early Data: 0
247---
248read R BLOCK
249closed

那浏览器该怎么办呢?

我们可用 openssl 由 client1.crt 和 client1.key 生成 PKCS#12 格式的客户端证书上,然后导入到浏览器当中
1openssl pkcs12 -export  -in client1.crt -inkey client1.key -out client1.p12
2Enter Export Password:
3Verifying - Enter Export Password:

如果不想设置的密码的话在两次提示输入密码时直接回车跳过,生成 client1.p12

比如在 Chrome 浏览器,可找到 Settings/Privacy and security/Security/Advanced/Manage certificates/Personal/Imports..., 然后导入前面生成的 client1.p12 文件,有密码的话就输入相同的密码。再浏览 https://server.local 就没问题了。 

用 Postman 直接浏览 https://server.local 也同样得到 400 Bad Request 的错误,也需要配置客户端证书才能正常访问。Postman 同时支持 client1.crt,client1.key 组合,和 client1.p12(有密码则输入), 请看下方的截图

完后 Postman 就能看到 https://server.local 的正确输出了。

curl 也可以用 p12 格式的证书
1curl --cert-type P12 --cert client1.p12 -kv https://server.local
2
3# 如有密码
4curl --cert-type P12 --cert client1.p12:<password> -kv https://server.local

Apache2 中的 mTLS 双向认证配置

类似的在 Apache2 中的配置就是在启用 HTTPS 单向验证的基础上于 default-ssl.conf 中再加上
1# 指定 CA 证书
2SSLCACertificateFile /certs/root.crt
3
4# 启用客户端认证
5SSLVerifyClient require            
6# SSLVerifyDepth 2

SpringBoot Tomcat 的 mTLS 双向认证

在启用了 HTTPS 单身验证的情况下加上 server.ssl.trust-certificate 和  server.ssl.client-auth 配置,完整的相关配置是
1server.ssl.certificate=/certs/server.crt
2server.ssl.certificate-private-key=/certs/server.key
3server.ssl.trust-certificate=/certs/root.crt
4server.ssl.client-auth=need

可分别用下面的 curl  进行测试
$ curl -k https://server.local:8080
curl: (56) LibreSSL SSL_read: LibreSSL/3.3.6: error:1404C412:SSL routines:ST_OK:sslv3 alert bad certificate, errno 0
$ curl --cert client1.crt --key client1.key -k https://server.local:8080
Ok

其他相关内容

以下相关内容在本文中不作详细展开,具体做法可参考 基于证书的双向认证(mTLS)技术方案。比

  1. keytool 替代 openssl 生成证书,在 crt, jdk, p12 之间的证书转换
  2. Tomcat 中如何使用 key-store 方式配置 HTTPS
  3. Tomcat 中启用了 HTTPS 后,@EnableWebSecurity 之后如何信任 x509 证书
  4. Java 作为客户端调用相应服务,如何处理 TLS 和 mTLS
  5. Nginx 作反向代理时作为 HTTPS 客户端时的配置

链接:

  1. 基于证书的双向认证(mTLS)技术方案
  2. nginx 自签证书实现配置 https 双向认证
  3. 巧用 Nginx 快速实现 HTTPS 双向认证
  4. springboot2.0 配置ssl证书详解
永久链接 https://yanbin.blog/self-certs-https-tls-mtls-config/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。