错误信息
java.security.cert.CertPathValidatorException: Algorithm constraints check failed on keysize limits: RSA 768 bit key used with certificate: CN=api.example.com
也可能输出为
- Algorithm constraints check failed on keysize limits
- javax.net.ssl.SSLHandshakeException: (unsupported_certificate) PKIX path validation failed: java.security.cert.CertPathValidatorException: Algorithm constraints check failed on keysize limits
密钥类型、长度和主体按具体证书填入。TLS 层会把它报告为“(unsupported_certificate) PKIX path validation failed: …”。在我们的测试中,如果服务器证书使用 MD5 签名,连接会更早失败:服务器返回 handshake_failure,因为 JVM 根本不提供 MD5 签名。
Java (PKIX validator) — jdk.certpath.disabledAlgorithms rejects a weak key or signature algorithm in the chain
含义
Java 构建出了可信路径,但其中某张证书使用的密钥(或签名算法)被 jdk.certpath.disabledAlgorithms 禁止,因此路径验证失败。消息中会写明算法、密钥长度和该证书的主体。当前的 JDK 会拒绝证书链中小于 1024 位的 RSA 和 DSA 密钥、小于 224 位的 EC 密钥,以及 MD2/MD5 签名。
原因
密钥过短的旧证书
常见旧设备、嵌入式 Web 服务器和测试环境中用 512 位或 768 位 RSA 密钥生成的证书。
链中的证书使用 MD5 签名(或在 JDK 自带根证书下使用 SHA-1)
偶见jdk.certpath.disabledAlgorithms 还列出了 MD2、MD5,以及 SHA-1(针对以 JDK 自带 CA 为终点的 TLS 服务器证书链)。
JDK 更新收紧了限制
偶见这些限制会不时在安全更新中提高;在旧的更新版本上能通过验证的证书,升级后可能无法通过。
修复方法
- 1.
找出弱证书
打印服务器所发送证书链中每张证书的公钥长度和签名算法。
bash # Key size and signature algorithm of every certificate the server sends openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null 2>/dev/null \ | awk '/BEGIN CERT/{n++; f="c" n ".pem"} /BEGIN CERT/,/END CERT/{print > f}' for f in c*.pem; do openssl x509 -in "$f" -noout -subject -text | grep -E 'subject=|Public-Key|Signature Algorithm' | sort -u; done - 2.
换用现代密钥重新签发
ECDSA P-256 或 RSA 2048+ 搭配 SHA-256 签名,在任何地方都能被接受。
bash # Replace the weak key: ECDSA P-256 (or RSA 2048+) with a SHA-256 signature openssl req -new -newkey ec -pkeyopt ec_paramgen_curve:P-256 -nodes -keyout server.key \ -subj "/CN=api.example.com" -addext "subjectAltName=DNS:api.example.com" -out server.csr - 3.
查看 JDK 的限制
java.security 中的 jdk.certpath.disabledAlgorithms 列出了会被拒绝的内容;可以通过覆盖安全属性来放宽它,但这会重新放行可被破解的密钥。
bash # What this JDK rejects in certificate chains sed -n '/^jdk.certpath.disabledAlgorithms=/,/[^\\]$/p' "$JAVA_HOME/conf/security/java.security"