我正在尝试从我的Java EE程序向需要证书身份验证的主机发送HTTPS请求。我有一个正确的密钥库文件,带有导入CA的信任库,两个列表都显示证书在里面。
但是我收到以下错误:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: KeyUsage does not allow digital signatures
at ...
...
Caused by: sun.security.validator.ValidatorException: KeyUsage does not allow digital signatures
at sun.security.validator.EndEntityChecker.checkTLSServer(EndEntityChecker.java:270)
at sun.security.validator.EndEntityChecker.check(EndEntityChecker.java:141)
at sun.security.validator.Validator.validate(Validator.java:264)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:231)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:126)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1319)
... 29 more
在扩展部分中查看证书内容我看到以下内容:
Extensions:
#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: 33 87 72 1D 09 2F DF FF 1A A7 D1 C0 E1 CF C5 FA 3.r../..........
0010: A4 19 54 2E ..T.
]
]
#2: ObjectId: 2.16.840.1.113730.1.1 Criticality=false
NetscapeCertType [
SSL client
]
#3: ObjectId: 2.5.29.35 Criticality=false
AuthorityKeyIdentifier [
KeyIdentifier [
0000: 74 9F 43 07 CC 75 FA D3 D0 13 0F 65 36 CC 4A 9A t.C..u.....e6.J.
0010: E0 8E 9C 52 ...R
]
]
#4: ObjectId: 2.5.29.31 Criticality=false
CRLDistributionPoints [
[DistributionPoint:
[URIName: http://test.az:7447/Test%20CA.crl]
]]
#5: ObjectId: 2.5.29.15 Criticality=true
KeyUsage [
DigitalSignature
]
所以我的证书确实包含 KeyUsage [DigitalSignature]
抛出异常的地方的代码段如下所示:
private final static int KU_SIGNATURE = 0;
...
private void checkTLSServer(X509Certificate cert, String parameter)
throws CertificateException {
Set<String> exts = getCriticalExtensions(cert);
...
} else if (KU_SERVER_SIGNATURE.contains(parameter)) {
if (checkKeyUsage(cert, KU_SIGNATURE) == false) {
throw new ValidatorException
("KeyUsage does not allow digital signatures",
ValidatorException.T_EE_EXTENSIONS, cert);
}
}
...
}
和 checkKeyUsage 功能:
private boolean checkKeyUsage(X509Certificate cert, int bit)
throws CertificateException {
boolean[] keyUsage = cert.getKeyUsage();
if (keyUsage == null) {
return true;
}
return (keyUsage.length > bit) && keyUsage[bit];
}
它在返回(keyUsage.length&gt;位)&amp;&amp;的keyUsage [比特];
问题是为什么上面的表达式的结果= false?当bit = 0且cert.getKeyUsage()必须返回一个boolean数组[true,false,false,false,false,false,false,false,false]
答案 0 :(得分:8)
错误实际上来自验证服务器的证书。该证书的密钥用法部分不包含digitalSignature位。
某些密码套件需要数字签名位,特别是Diffie-Hellman密钥交换(DHE_RSA和ECDHE_RSA)。您可以通过避免这些密码类型来避免此错误。否则,服务器证书需要支持它。
答案 1 :(得分:0)
这个错误也发生在我身上,虽然@Chris D 的回答是正确的,但我想给你一个解决这个问题的方法。
我们的 Java 应用程序必须通过 HTTPS 与服务器通信。如果我们能够调整此服务器的 TLS,我们就可以解决问题。
基本上我们需要在 HTTPS 服务器上禁用所有基于 Diffie Hellman 的密码。您需要排除所有包含 DH
的密码。
在 Haproxy 的情况下,我使用了这些指令:
global
ssl-default-bind-ciphers AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
如果是 Java HTTPS 服务器,我已经设置了这个环境变量:
SERVER_SSL_CIPHERS=TLS_RSA_WITH_AES_128_GCM_SHA256:TLS_RSA_WITH_AES_256_GCM_SHA384:TLS_RSA_WITH_AES_128_CBC_SHA256:TLS_RSA_WITH_AES_256_CBC_SHA256:TLS_RSA_WITH_AES_128_CBC_SHA:TLS_RSA_WITH_AES_256_CBC_SHA:TLS_RSA_WITH_3DES_EDE_CBC_SHA
然后将转换为 Java 属性 server.ssl.ciphers
如果你想在客户端强制使用一些密码,这个问题可能就是你要找的Java - How can I disable a TLS cipher for only some protocols using JVM Config?