I’m trying to send HTTPS request from my Java EE program to the host that requires certificate authentication. I have a proper keystore file, truststore with imported CA, the listing of both shows that certificates are inside.
But I receive the following error:
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
Viewing the certificate contents in the part of Extensions I see the following:
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 ]
So my certificate does contain KeyUsage [ DigitalSignature ]
The code snippet of the place throwing the exception looks like the following:
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); } } ... }
and checkKeyUsage function:
private boolean checkKeyUsage(X509Certificate cert, int bit) throws CertificateException { boolean[] keyUsage = cert.getKeyUsage(); if (keyUsage == null) { return true; } return (keyUsage.length > bit) && keyUsage[bit]; }
it fails in return (keyUsage.length > bit) && keyUsage[bit];
The question is why the result of above expression = false? When bit = 0 and cert.getKeyUsage() must return an array of boolean [true, false, false, false, false, false, false, false, false]
Advertisement
Answer
The error actually comes from verifying the server’s certificate. That certificate has a key usage section that doesn’t include a digitalSignature bit.
Some cipher suites require the digital signature bit, specifically Diffie-Hellman key exchange (DHE_RSA and ECDHE_RSA). You may be able to avoid this error by avoiding those cipher types. Otherwise the server certificate needs to support it.