Java – Unable to authenticate to SSL site in java: “pathLenConstraint violated – this cert must be the last cert in the certification path”

httpjavassl

I'm trying to read from a secure (i.e. SSL) web page, in Java code.
I'm trying to use both URLConnection (java.net) and Apache's HTTPClient.
In both cases, when I make the request, I get this exception:

javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException:
PKIX path validation failed:
java.security.cert.CertPathValidatorException:
basic constraints check failed:
pathLenConstraint violated – this cert
must be the last cert in the
certification path at
com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:150)
at
com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1518)
at
com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:174)
at
com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Handshaker.java:168)
at
com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:848)
at
com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:106)
at
com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Handshaker.java:495)
at
com.sun.net.ssl.internal.ssl.Handshaker.process_record(Handshaker.java:433)
at
com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:818)
at
com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1030)
at
com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1057)
at
com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1041)
at
sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:402)
at
sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
at
sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:934)
at
sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:234)
at
com.sap.river.coghead.rest.Main.testJavaHTTPConnection(Main.java:45)
at
com.sap.river.coghead.rest.Main.main(Main.java:32)
Caused by:
sun.security.validator.ValidatorException:
PKIX path validation failed:
java.security.cert.CertPathValidatorException:
basic constraints check failed:
pathLenConstraint violated – this cert
must be the last cert in the
certification path at
sun.security.validator.PKIXValidator.doValidate(PKIXValidator.java:187)
at
sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:139)
at
sun.security.validator.Validator.validate(Validator.java:203)
at
com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:172)
at
com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(SSLContextImpl.java:320)
at
com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:841)
… 13 more Caused by:
java.security.cert.CertPathValidatorException:
basic constraints check failed:
pathLenConstraint violated – this cert
must be the last cert in the
certification path at
sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:139)
at
sun.security.provider.certpath.PKIXCertPathValidator.doValidate(PKIXCertPathValidator.java:316)
at
sun.security.provider.certpath.PKIXCertPathValidator.engineValidate(PKIXCertPathValidator.java:178)
at
java.security.cert.CertPathValidator.validate(CertPathValidator.java:206)
at
sun.security.validator.PKIXValidator.doValidate(PKIXValidator.java:182)
… 18 more

Note that I've succeeded in establishing a non-ssl connection, to a different host though.
I'm also able to view this page using the browser – the certificates are validated correctly there.

Do you I need to somehow change the order of certificates as they are retrieved from the server?
Is there some configuration I'm missing?

Thanks in advance,

Lior

Best Answer

I dug in further and the answer lies in the fact that I needed to import the necessary certificates into the keystore used by the JVM to authenticate SSL. The key store is the 'cacerts' file under the jre/lib/security folder in the jre that is used to run the program.

I manually exported the site's certificates - all of them.
Then I imported it into my default keystore using the 'keytool' utility provided by Sun. Note that you have to import them in the correct order.
I then put the new keystore instead of the JRE's one - and it worked.

I guess it would've been better to import the certificates directly to the JRE's keystore, but the tool asked me for a password which i didn't know.

I believe there's also a way to program around this more easily, just haven't found it yet. I'll be happy to get some pointers (TrustManager class in JSSE?).

Finally, some credit. This post here: http://javaishdiscoveries.blogspot.com/2009/02/battle-with-cacerts-and-https.html helped to point me in the right direction.

Related Topic