PKIX Path does not chain with any of the trust anchors error in Windows Environment
Asked Answered
L

6

21

I am a bit of an idiot to how SSL and Webservices work at the fine-grained level. I am developing a system that calls several web services, some with secured URLs and others that are not with little problem. Currently, however, I am doing an integration with Endicia's LabelServer Web API. The webservice is used to calculate and print postage.

The test URL and WSDL is at: https://www.envmgr.com/LabelService/EwsLabelService.asmx

I used wsimport to create and setup a Java client for connecting to this webservice but when I try to all it I get the error

PKIX path validation failed: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors

This error is documented here: Java7 Refusing to trust certificate in trust store

in which it's discussed how Java 7 forces an error with self-signed certificates with "bad" keyusage. Bad in this situation is defined as not containing keyCertSign. The webservice does work with Java 6. I can believe this situation might apply to this certificate since it's only being used as a test server, but I don't know how to verify that.

There's a bug report on it that is solved (http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7018897), but I'm not sure how any of this translates to fixing the problem for a Windows Tomcat environment. I exported the certificate onto my machine, but am uncertain of how to proceed from there.

EDIT: I tried using OpenSSL to modify the certificate and add it to my keystore as described in the "Refusing to trust certificate in trust store" link and it didn't work. It seems like this is a process that is done by the owner of the certificate, right? I wonder if there's some way I can configure my Java 7 environment to let this certificate through.

Lengthways answered 21/5, 2014 at 6:2 Comment(7)
What API(s) are you using to make the connection? Are you programming at a level that you can initialize an SSLContext with a custom X509TrustManager?Load
I've tried a few different APIs. First, wsimport generates its own Service layer. Secondly, I used the Spring WebServiceGatewaySupport to make the call. Both create the same error. I'll see if I can paste the certificate data into a Java file to see if I can make a self-contained class to reproduce the error. As for your second question: I'm not familiar with X509TrustManager. I'll look for some examples and see if I can use it to fix my problem. ThxLengthways
Are you sure that key usage is your problem - e.g. have you tested with earlier versions of Java or attempted to connect to a different site? I ask because I tried your URL and (1) the leaf certificate is not self-signed and (2) the other two certificates in the chain appear to have certificate signing enabled. Are you sure it isn't something like an incomplete default trust store?Load
rhashimoto: I did mention that this works in Java 6 in my OP. I'm not familiar with Certificate analyzation, but from what I've read Java 7 interprets certificates more strictly and I assumed that the SSL Handshake Exception was by design. I don't know about Certificate chains, but if I download these other two certificates and add them to my truststore would that possibly work? Can all certificates be downloaded via a web browser at the one URL?Lengthways
Sorry, missed the fact that it worked on Java 6. But maybe the trust store is different between your Java 6 and 7 installations - I still suspect that's where your problem is. If you have OpenSSL somewhere you can collect certificates with openssl s_client -host www.envmgr.com -port 443 -showcerts. You should only need the root certificate (ValiCert) in your trust store. But perhaps you could try connecting to google.com and see if that fails SSL handshaking the same way - if it does then that would point the finger away from the key usage thing, right?Load
Using openssl worked! I exported the certificate originally with Firefox and I don't know if that didn't export the entire chain or what, but the problem was not solved. I also want to try out X509TrustManager when I have some time.Lengthways
If you want the check and bounty, please write up the steps. You can copy and paste from my answer below.Lengthways
L
16

The default Java certificate checks are pretty strict, and have apparently gotten stricter. One workaround is to initialize an SSLContext with a custom X509TrustManager. A trust manager that does nothing, i.e. is completely insecure, that I once wrote for testing looks like this:

TrustManager[] trustAllCerts = new TrustManager[]{
   new X509TrustManager() {
      public java.security.cert.X509Certificate[] getAcceptedIssuers()
         {
            return null;
         }
      public void checkClientTrusted(
         java.security.cert.X509Certificate[] certs,
         String authType )
         {
         }
      public void checkServerTrusted(
         java.security.cert.X509Certificate[] certs,
         String authType )
         {
         }
   }
};

Obviously you would want to actually check the certificate chain in a real program. You could then try to initialize an SSLContext with it and call SSLContext.setDefault() if your API has no other way of configuring SSL. If the API uses the default SSL context then this should work.

Key usage does not appear to be the issue in this case as the certificate chain is not self-signed. Testing the URL appears to show that the leaf certificate is not self-signed and (2) the other two certificates in the chain appear to have certificate signing enabled. An alternative possibility is that Java 6 and Java 7 have separate trust stores and the root certificate is not in the Java 7 store. You may want to double-check that. If you have access to OpenSSL, you can get the certificate chain from the server with:

openssl s_client -host www.example.com -port 443 -showcerts

Apparently updating the trust store was the key (pun intended). The OP reports:

I downloaded OpenSSL for Windows 64 and then used this command to download the certificate chain:

openssl s_client -host www.webserviceurl.com -port 443 -showcerts > c:\temp\certchain_output.crt

Then I want to import it into my browser's keystore like so (from the JDK's home directory/jre/lib/security):

keytool -import -alias ca -file certchain_output.crt -keystore cacerts -storepass changeit

I believe using X509TrustManager could provide an effective solution as well.

Load answered 30/5, 2014 at 22:29 Comment(5)
Rhashimoto: We call a variety of webservices, so I am trying to call this TrustManager for this one webservice that is generating the error. Upon implementing the TrustManager I see that the "checkServerTrusted()" method is called. From there, I get an array of certificates to test against. My question is do you know what parameters I use, if any, to ensure I am looking at the right certificate? Do I need to deregister this TrustManager after every call so it doesn't conflict with the other webservices? I'm researching answers to these now, but if you have insights they would be appreciated.Lengthways
@Lengthways I think what you want to do in that case is find the original X509TrustManager and in your code first call that one and if that fails then do your own checking. That way you're just extending the current policies so other SSL-using components should still be happy. I found this link which might be helpful.Load
I would not attempt to write your own TrustManager because, as stated, you're making your application less secure. The correct way is to do the XPath validation for PKI and let Java do this for you. If you add the root and the intermediate CA into your java trust store (i.e. cacerts) file it should work without writing any code.Hardcastle
@Load could you please take a look at my problem here: #48286907 my case is a bit different from this one because I have less control over the SSL connection. Thank you!Bahner
I dread the amount of systems that now use your example code in production.Shear
O
7

The certificate chain validation logic has indeed changed between Java 6 and Java 7. It seems that the certificate chain is considered to be invalid because the validity end date of the intermediate certificate is after the validity end date of the root certificate. This is a Java bug which was fixed with JDK 1.7.0_72 and 1.8.0_25.

If you can't upgrade your JDK, here is is a workaround since you say are in a debug environment and you have control over your keystore. You can't change any of the certificates of course (since you don't have the private keys), but you can import the intermediate or the server certificate into a local keystore, which means it will be trusted by default, and the rest of the chain validates without any problem.

  1. Download the certificate for www.envmgr.com or www.starfieldtech.com as trusted_certificate.pem
  2. Use keytool to import the certificate into a local keystore named my_keystore with keytool -keystore my_keystore -importcert -file trusted_certificate.pem
  3. Launch wsimport -J-Djavax.net.ssl.trustStore=my_keystore https://www.envmgr.com/LabelService/EwsLabelService.asmx
Orthopedic answered 28/5, 2014 at 17:22 Comment(2)
I agree, import the intermediate certs and it should work. You really don't want a root CA issuing intermediate certs past the validity period anyway. However, the bug says it only relates to x509, v3 certs. As v1 and v2 certs should still use validity period since AKID and SKID (Authority Key ID and Subject Key ID) were only introduced in v3 and would not be available for path validation in v1 and v2Hardcastle
It's even a bug in JDK 1.8_0181. Seems there was a regression. You can use this to get the fixed version - 1.8.0_25Fourinhand
C
2

Try import the certificate you need into java truststore located into jdk_xxx/jre/lib/security/cacerts using keytool

set the jvm parameter Djavax.net.debug=ssl to see more debug information

Carollcarolle answered 29/5, 2014 at 18:53 Comment(0)
L
2

Rhashimoto helped me find the solution that worked for me:

I downloaded OpenSSL for Windows 64 and then used this command to download the certificate chain:

openssl s_client -host www.webserviceurl.com -port 443 -showcerts > c:\temp\certchain_output.crt

Then I want to import it into my browser's keystore like so (from the JDK's home directory/jre/lib/security):

keytool -import -alias ca -file certchain_output.crt -keystore cacerts -storepass changeit

I believe using X509TrustManager could provide an effective solution as well.

Lengthways answered 30/5, 2014 at 21:48 Comment(1)
Note that that does NOT add any CA cert into your truststore. Even though openssl s_client -showcerts puts the chain (all 3 certs) in the file, keytool -importcert for a new=trustedCert entry uses only the first cert, which is the server cert. (keytool -importcert when used to add a cert/chain to a privateKey does handle a chain.) That means you are trusting this specific server cert directly, which Java CertPath can do, but if the server renews or otherwise updates its cert this stops working.Rely
J
0

Try this OUT, it accepts all

TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null; }
            public void checkClientTrusted(X509Certificate[] certs, String authType) { }
            public void checkServerTrusted(X509Certificate[] certs, String authType) { }

        } };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new java.security.SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

        // Create all-trusting host name verifier
        HostnameVerifier allHostsValid = new HostnameVerifier() {
            public boolean verify(String hostname, SSLSession session) { return true; }
        };
        // Install the all-trusting host verifier
        HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
Jorrie answered 20/6, 2018 at 10:6 Comment(0)
M
0

If it is coming after kubernetes cluster update with newer version and you are accessing pods in your java program, update maven dependency to newer kubernetes client-java from https://mvnrepository.com/artifact/io.kubernetes/client-java.

It worked for me.

Miquelon answered 18/9, 2023 at 12:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.