我创建了一个包含两个域的 LetsEncrypt 证书,one.example.com 和 two.example.com(注意,我知道通配符证书,但这个问题是一个证书中的多个名称)。
该证书以 one.example.com 作为 CN,并以 one.example.com 和 two.example.com 作为 X509v3 主体备用名称属性。
我将证书与 nginx 一起使用。
按照惯例,客户端只需检查 CN 是否与 DNS 名称匹配,我预计只要 one.example.com 和 two.example.com 现在指向同一个 nginx 服务器,我就可以通过这两个 DNS 名称访问它们。但只有 one.example.com 有效。
我可以使用 openssl 重现此问题:
openssl s_client -showcerts -servername one.example.com -connect 192.168.151.97:443
Connecting to 192.168.151.97
CONNECTED(00000003)
depth=2 C=US, O=Internet Security Research Group, CN=ISRG Root X1
verify return:1
depth=1 C=US, O=Let's Encrypt, CN=E5
verify return:1
depth=0 CN=one.example.com
verify return:1
---
Certificate chain
0 s:CN=one.example.com
i:C=US, O=Let's Encrypt, CN=E5
a:PKEY: id-ecPublicKey, 256 (bit); sigalg: ecdsa-with-SHA384
v:NotBefore: Jul 17 05:27:55 2024 GMT; NotAfter: Oct 15 05:27:54 2024 GMT
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
1 s:C=US, O=Let's Encrypt, CN=E5
i:C=US, O=Internet Security Research Group, CN=ISRG Root X1
a:PKEY: id-ecPublicKey, 384 (bit); sigalg: RSA-SHA256
v:NotBefore: Mar 13 00:00:00 2024 GMT; NotAfter: Mar 12 23:59:59 2027 GMT
-----BEGIN CERTIFICATE-----
[...]
-----END CERTIFICATE-----
---
Server certificate
subject=CN=one.example.com
issuer=C=US, O=Let's Encrypt, CN=E5
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: ECDSA
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 2434 bytes and written 410 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 256 bit
This TLS version forbids renegotiation.
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
openssl s_client -showcerts -servername two.example.com -connect 192.168.151.97:443
Connecting to 192.168.151.97
CONNECTED(00000003)
402EB7DC01000000:error:0A000458:SSL routines:ssl3_read_bytes:tlsv1 unrecognized name:ssl/record/rec_layer_s3.c:907:SSL alert number 112
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 7 bytes and written 333 bytes
Verification: OK
---
New, (NONE), Cipher is (NONE)
This TLS version forbids renegotiation.
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
因此您可以看到,one.example.com 可以工作,但是 two.example.com 不工作,尽管它被列为主题备用名称。
即使我明确指定-servername two.example.com
,情况也不会改变。
为什么 browsers/openssl 无法成功验证 two.example.com?
根据这些行,openssl 无法验证证书,因为没有什么可验证的——服务器没有发送证书,而是发送了“无法识别的名称”警报。
这通常意味着客户端请求了
-servername
你的 Nginx 配置中没有匹配的server{}
块。通常,TLS 服务器名称指示请求与服务器配置的匹配不是针对证书本身进行的;而是针对手动配置的名称进行的 - 换句话说,TLS SNI 用于选择虚拟主机,就像它用于选择证书一样(并且一些服务器强制执行这一点),因此当客户端请求 TLS SNI“two.example.com”时,服务器不仅发送恰好匹配的任何证书,而且还专门发送在相应的虚拟主机块中配置的证书。
两者都是 TLS SNI 的合理实现。如果 HTTP 客户端请求服务器未配置的虚拟主机,则一个 HTTP 服务器可能会返回默认虚拟主机,而另一个 HTTP 服务器则会返回“403/404 没有这样的虚拟主机”错误,这通常是出于安全原因(例如,为了防止不需要的域指向它)——同样,当客户端明确请求未配置为虚拟主机的名称的证书时,服务器可能会假定它不适合通过该域进行访问。
(Apache 具有与 SSLStrictSNIVHostCheck 相同的选项。)