chaplean
chaplean

Reputation: 197

KeyUsage does not allow digital signatures

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]

Upvotes: 8

Views: 19070

Answers (3)

martinnemec3
martinnemec3

Reputation: 393

Confirming that this behavior is still the same for the latest JRE version (19).

There seems to be another way how to avoid the problem (beside the one mentioned by Chris D). It looks like when a trust anchor is present inside the truststore java does not trigger the checks responsible for throwing the described exception and blindly trust to the server's certificate.

The related code can be saw here: https://github.com/openjdk/jdk/blob/4cec141a90bc5d3b8ec17c024291d9c74a112cd4/src/java.base/share/classes/sun/security/validator/Validator.java#L259

This means importing the server's certificate directly into the java's truststore resolves the issue.

Upvotes: 0

Naramsim
Naramsim

Reputation: 8762

this error happened also to me and while the answer of @Chris D is correct I thought about giving you a way to overcome this issue.

We are in a situation where our Java application has to talk with a server through HTTPS. If we are able to tune this server's TLS we can solve the issue.

Basically we need to disable all the ciphers based on Diffie Hellman on the HTTPS server. You need to exclude all ciphers which contain DH.

In case of Haproxy I used these directives:

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

In case of a Java HTTPS server I've set this env var:

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

which will then be translated to the Java property server.ssl.ciphers


If you want to force some ciphers on the client-side, this SO question might be what you're looking for Java - How can I disable a TLS cipher for only some protocols using JVM Config?

Upvotes: 1

Chris D
Chris D

Reputation: 495

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.

Upvotes: 11

Related Questions