Certificate path discovery in Java

2020-02-15 04:05发布

问题:

I try to make an https connection via java build-in functionality for that (HttpURLConnection). But I get this exception:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
...
...

My Certificate chain is:

Root certificate -> Intermediate certificate -> Web server certificate

The used certificate is correct by the meaning of "Path discovery". The trust anchor is the Root certificate, which is imported in java's keystore on my system. The intermediate certificate is not... BUT

  1. Intermediate certificate is signed by the root, who I trust - so I trust on the intermediate too.
  2. Web server certificate is signed with Intermediate certificate, which I trust (point 1)

So the validation has to pass successfully? Do I getting something wrong?

Somewhere i read this:

Browsers can do auto-discovery, server to server doesn't.

but absence of this functionality is very basic. Are there clear way of doing this auto-discovery?

** Update

Yes, this was the issue, GPI. I was confused, because browsers could validate the server certificate but java application could not. The cause of that behaviour was:

  • the server sends only final certificate, not the whole certificate chain;
  • the certificate was recently bought and it was signed with relatively new intermediate certificate;
  • the browsers have relatively up-to-date list of certificates including the intermediate certificate;
  • java have relative not up-to-date list of certificates, and intermediate certificate was not inside.
  • browsers validate the final certificate over the intermediate certificate java could not check the certificate chain because: 1. the chain was not sent; 2. the signer of final certificate (the intermediate one) was not a trust anchor.

The solutions could be:

  • server to return the whole certificate chain
  • the intermediate certificate to be added in java trust store

回答1:

I trust you've already checked your chain, so we can take for granted that RootCert signed IntermediateCert and IntermediateCert signed ServerCert, with valid X500 name chaining and all...

That said, your logic is valid that trusting the RootCert is enough, but do not forget that in order to build a path, your client must have in its posesssion all the certificates in the path.

In your case, if you trust the root cert only, then it is up to the server to advertise the rest of the certificate chain (intermediate and final). If nobody ever "gives" the HTTP client the intermediate cert, then the client will fail as going from Root to Server without knowing Intermediate is not possible.

You can actually see what your server certificate chain is by launching your client with the -Djavax.net.debug=all option. If the chain is of length 1, then your server only advertises the final certificate and there is no way the client can guess that the intermediate cert exists.

(One can also check using a browser and asking to see the server certificate, but you should note that the browser will show you the whole path up to the trust anchor, so if you want to infer what the server chain is, you have to remove your browser's anchors out of this path).

On a production service, you should refer to your certificate provider's website to know what is to be considered as the root certificate (it might not be the topest level one). This effective root should be your client's trust anchor, and any server should advertise at least all other certs in the chain up from the final one in the path (the one whose common name is the DNS name of the server).



回答2:

In order to connect via Https you need to use an HttpsURLConnection object. You cannot create a connection with an HttpURLConnection object.