Java 的 TLS 握手怎么协商算法、怎么验证证书?出错时的报错分别对应哪一步?这篇用 keytool 生成真实的证书和信任库,在回环地址上跑服务端与客户端,逐项验证(会话复用与 mTLS 在后面的篇目)。
成功:协议 TLSv1.3,套件 TLS_AES_256_GCM_SHA384,服务端证书 CN=localhost JDK 默认开启的协议:[TLSv1.3, TLSv1.2];默认开启的 TLS 1.3 套件:[TLS_AES_256_GCM_SHA384, TLS_AES_128_GCM_SHA256, TLS_CHACHA20_POLY1305_SHA256] 默认开启的套件一共 31 个
默认信任库(不含这个自签名证书):失败:SSLHandshakeException: (certificate_unknown) PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path t ← 根因 SunCertPathBuilderException: unable to find valid certification path to requested target 信任库里导入了服务端证书:成功:协议 TLSv1.3,套件 TLS_AES_256_GCM_SHA384,服务端证书 CN=localhost 信任了证书,但证书是签给 example.test 的(SAN 只有 example.test),却用 localhost 连接:失败:SSLHandshakeException: (certificate_unknown) No subject alternative DNS name matching localhost found. ← 根因 CertificateException: No subject alternative DNS name matching localhost found. 同样的连接,但不做主机名校验:成功:协议 TLSv1.3,套件 TLS_AES_256_GCM_SHA384,服务端证书 CN=example.test CA 签发的服务端证书(有效期内),客户端只信任 CA:成功:协议 TLSv1.3,套件 TLS_AES_256_GCM_SHA384,服务端证书 CN=localhost CA 签发、但已过期 8 天的服务端证书:失败:SSLHandshakeException: (certificate_expired) PKIX path validation failed: java.security.cert.CertPathValidatorException: validity check failed ← 根因 CertificateExpiredException: NotAfter: Thu Sep 24 23:08:13 CST 2026
服务端只开 [TLSv1.3],客户端只开 [TLSv1.3] → 成功:协议 TLSv1.3,套件 TLS_AES_256_GCM_SHA384,服务端证书 CN=localhost 服务端只开 [TLSv1.2],客户端只开 [TLSv1.2] → 成功:协议 TLSv1.2,套件 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,服务端证书 CN=localhost 服务端只开 [TLSv1.2],客户端只开 [TLSv1.3] → 失败:SSLHandshakeException: (protocol_version) Received fatal alert: protocol_version 服务端只开 [TLSv1.3, TLSv1.2],客户端只开 [TLSv1.2, TLSv1.1] → 成功:协议 TLSv1.2,套件 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,服务端证书 CN=localhost
第 1 次连接:session 创建时间 96661,session id 长度 32,协议 TLSv1.3 第 2 次连接:session 创建时间 96661,session id 长度 32,协议 TLSv1.3 同一个 SSLContext 的 6 次连接耗时(µs):[45681, 6815, 4812, 4356, 4386, 3930](第一次包含类加载与完整握手)
客户端线程:产生 ClientHello 服务端线程:处理 ClientHello 服务端线程:产生 ServerHello 客户端线程:处理 ServerHello 客户端线程:处理 EncryptedExtensions 客户端线程:处理 Certificate 服务端线程:产生 CertificateVerify 服务端线程:产生 Finished 客户端线程:处理 CertificateVerify 客户端线程:处理 Finished 客户端线程:产生 Finished 服务端线程:处理 Finished