I have an Issue with the verification of a ssl certificate.
What I am trying to do, is sending some data from a java program to a server, which then stores that data.
The issue is, that the ssl certificate validation fails with the following exception:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed [...] unable to find valid certification path to...
I am able to resolve this issue, by adding the "end-user" certificate of the server to the truststore I am using.
The chain looks something like this:
Root Certificate 1
Intermediate Cert 11
Intermediate Certificate x
End-User Certificate
Something to note about this whole chain / process:
The communication is using the companies proxy, which replaces all the certificates, and creates its own chain.
So back to the issue:
By adding the end-user certificate everything runs fine. But only for a little while, before that certificate gets refreshed and the one I added is no longer valid. I have tried adding just the root certificate, just each intermediate certificate, adding all 3 certificates and also adding the certificate which would be used if the proxy does not replace the chain. But somehow the certificate cannot be validated.
Is there something I might be overlooking? Do I have to add something else to be able to validate the certificate?
Edit:
Maybe something to note:
I checked the chain, by using the browser and navigating to said server, and then checking the ssl certificates.
Related
We have an internal web service hosted on https with an authorized root CA. When I tried to call that service from Java Apache HttpClient, it gave me the cert error "PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target".
Using SSL tools, I found out that the web service is sending the cert chain without the intermediate cert and it contains only server cert. So the Java cert error made sense because, only the root CA is present in Java truststore and it is unable to verify the cert chain, hence the error.
But on the other hand, I can call the same web service using C# HttpClient (.Net 4.6.1) without any issues.
Is C# HttpClient ignoring the cert issues with the web service or is it downloading the missing cert? Can anyone please shed some light on this?
Figured this out with very helpful comments from Stephen and Mark. I set up the trace in app.config where I found that C# HttpClient could find the intermediate certificate.
"System.Net Information: 0 : [23580] SecureChannel#41622463 - Remote certificate was verified as valid by the user."
But I had already verified that the intermediate cert wasn't present in Windows cert store which had led to this confusion. I checked there again and turns out the cert was under My User Account and not Computer Account. It must have been installed there when our company desktops were set up.
Now I am not really sure about the difference between the usage of these two cert stores in Windows, but at least I understand how C# client found the cert.
UPDATE: After I have found a solution, I edited the question to be more clear for future reference.
I've got a corporate (ie not well known) CA certificate from a company which provides us a web services to be called from Java. I added this CA certificate to default cacerts trust store (keytool -import -file cert.cer -alias myca -keystore jre/lib/security/cacerts), but connection to the service still fails with the dreaded "PKIX path building failed" message. I have checked that issuer field of the end server certificate is the same as in the CA certificate and also validity dates is ok.
I don't know how to explain this. I can think of the following reasons but I don't know which one is true:
I have noticed that when I add also the end server certificate to trust store, the connection is OK. Maybe cacerts by design don't work as I expect (ie all the certificates signed by an authority added there are considered valid), but instead I have to add all the end server certificates to a trust store including CA certificate of their issuer.
I have to add CA certificate in some other way - by different command, to different file etc.
Maybe the CA certificate is not correct and keytool refuses to consider it a certificate authority.
Maybe PKIX path building fails for other reason.
How can I debug this problem more to find an answer?
Details:
The end server certificate is wildcard certificate
There is no intermediate certificate, just root and the end certificate
I was facing the same problem with "PKIX path building failed" with Let's Encrypt signed certificates at a time Java didn't incorporate the Let's encrypt CA certificate in its default trust store.
My story is written in detail here: http://blog.novoj.net/2016/02/29/how-to-make-apache-httpclient-trust-lets-encrypt-certificate-authority/
At the end I was able to make Java trust "the end of the chain" server certificate by creating internal trust store embedded in my application that contains only root CA certificate (and the backup one).
I much more prefer creating internal application truststore than importing certificate in main Java trust store for two reasons:
you don't need another extra step in install procedure for initializing the global trust store
you limit "the trust" to you application and don't affect another applications running on the same JVM (or better you can even limit the trust to the certain instances of client objects in your application if required)
Maybe I had a different scenario than you're facing, so downvote me if I didn't get the point.
The trust store needs to contain the root certificate (the CA's cert).
I'm not sure if that's what you mean by "the last one I'm the chain", but the CA certificate should be the last one in the certificate chain presented by the server.
If your certificate is signed by a well-known CA, then the CA cert should be in the trust store, and if the server's certificate chain is set up properly, everything should just work.
If yours is a self-signed certificate, then the root certificate will not be in the trust store, and you will have to add it.
Thanks to #pedrofb comment I found out that the reason PKIX path fails is simply that the CA certificate I got is not the CA that signed the end certificate. What made it so complicated is the monstrous incompetence of company that gave me the CA certificate which obviously has two CAs with almost the same description (cn, o, st, c) which differs only in SN and which both issued the same wildcard certificate. Only after I became super paranoid and compared the SNs, I understood the problem.
I've got an app written using the Spark Java framework, with TLS enabled.
See:
Service https = ignite()
.port(8443)
.secure(keystorePath, keystorePass, truststorePath, truststorePass);
This is being served on port 443 via an iptables rule that redirects incoming 443 to 8443.
The problem I am having is that when using the Qualys ssl labs test (https://www.ssllabs.com/ssltest/) the server is not providing the intermediate certificates that have been configured in my truststore.
Similar results occur when I use openssl s_client:
Verify return code: 21 (unable to verify the first certificate)
Along with
depth=0 /OU=Domain Control Validated/OU=PositiveSSL/CN=my.app.com
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 /OU=Domain Control Validated/OU=PositiveSSL/CN=my.app.com
verify error:num=27:certificate not trusted
verify return:1
depth=0 /OU=Domain Control Validated/OU=PositiveSSL/CN=my.app.com
verify error:num=21:unable to verify the first certificate
verify return:1
From what I can tell, it appears that the server (spark java app, or embedded Jetty I suppose) is not serving up the intermediate certificates for chain validation.
Browsers see the site as secure, but I believe that's because the browsers are downloading the necessary intermediate certificates on their own.
The reason that this is a problem is that I am trying to use Stripe payment webhooks, and they have strict regulations in terms of the TLS cert chain being valid.
I am not sure where to begin on figuring out why these intermediate certs are not being served by my app. Could anyone offer some advice?
I have more or less the same exact problem. I followed the instructions at the Java Spark website (sparse that they are) and only get a Server Error for my efforts.
I imported a third-party certificate in my keystore file. I generated the CSR externally to the java keytool.
I moved the keystone file on the server where the "mydomain.com" exists.
I have the same basic code to do a test "secureHello" (per their documentation), passing the path to the keystore.jks file as the first parameter, and the password as the second.
RESULT: SERVER ERROR.
Not sure what I can try next. Clearly, I'm missing something.
4 hours banging my head on the desk so far (today)...
I need to consume some HTTPS web services. To use the certificate, I'm setting (javax.net.ssl) trustStore, trustStorePassword, keyStore and keyStorePassword properties through the System.setProperty.
If I do new URL([HTTPS...WSDL]).openStream(), it works. So, I'm sure that trust and keystore properties are ok (without setting those properties, my routine failed).
The problem is when I try to consume the service, I'm getting the famous
sun.security.provider.certpath.SunCertPathBuilderException: unable to
find valid certification path to requested target
exception.
Anyone have an idea of what I need to do?
Thanks a lot.
There are three possible failures:
You are using a self-signed certificate.
Your certificate has been signed by a common CA (Certificate Authority) using an intermediate certificate, but their intermediate certificate is not served by your web server.
Both the server certificate and the CA intermediate certificate are served by your web server, but the Java truststore does not contain the root certificate.
To solve the last step use:
Import CA root cert
I'm having an issue sending our certificate (client) to another server during a web service call.
We're expected to return the certificate with the CN:
b2b-test
however whenever we receive the servers certificate and attempt to send our own, we're getting:
Unable to find valid certificate path to requested target
From my understanding, if I put our certificate b2b-test inside of our keystore jre/lib/security/cacerts, then when receiving the server request, it should send the appropriate certificate.
How does the client know which key from the keystore it should send to the server? Should I be specifying the exact certificate to send during my request, or is it enough for it to be in the keystore?
Currently the server is sending back the certificate
b2b-a
The certificate chain shows:
*** Certificate chain
chain [0] = [
[
Version: V3
Subject: CN=b2b-a, OU=Web Technologies
The URL for the service is:
https://b2b-a/service
If I configure it like one way ssl, and put the servers certificate into our cacerts, then it picks up their cert and fails the handshake (I'm assuming because it's expecting our cert and not theirs back due to it being a two way setup).
I'm a little stumped here. Any clarification on what I've said is greatly appreciated.
The "Unable to find valid certificate path to requested target" error usually occurs when one side passes a certificate for which the other side doesn't trust any of the certs in the chain. You'll generally (always?) see the error message on the side that's receiving the cert (the side that's missing the truststore).
If you're the client and they're the server, one-way SSL would send their cert from them to you and you would validate it (making sure that the hostname matches the content in the cert), but it would not send any cert from you to them nor would they do any validation.
You'll want to make sure you're configuring a truststore that contains either the b2b-a certificate or a certificate that was used to sign it (but that cert doesn't appear to have any root CAs, so you're stuck with directly importing the cert itself). So yes, you want to do what you wrote in your post and put their certificate into your cacerts. And you'll also need to make sure that the b2b-a service trusts the cert you're sending (or a root CA that was used to sign your cert, if any), so you'll need to put your cert into their cacerts as well, or do something equivalent.
Note that putting a cert in cacerts (or in a truststore JKS) DOES NOT send any certs to anyone; any truststore is used only to allow you to validate that you trust the certificate someone else provides you, either because you trust the cert or because you trust a CA cert that was used to sign the cert. No certs are picked out of the cacerts directory to send to another machine.
If you're using a JKS rather than the JRE directory (which is generally a better idea, since you can specify a different set of trusted certs for your process without changing the default set for anyone running a Java project within your JRE), it's possible to use the same JKS as both a keystore and a truststore (you'll just provide the same filename and password for both the keystore and the truststore properties), or you can have two separate files; either approach will work.
From my understanding, if I put our certificate b2b-test inside of our keystore jre/lib/security/cacerts, then when receiving the server request, it should send the appropriate certificate.
Certainly not. That's a truststore. A source of trusted certificates. Don't mess with it. It isn't used for the purpose you require and in any case it is updated every Java dot release, which clobbers your changes.
Your own certificate and private/public key pair should go in a file of your own, a keystore, which is defined to JSSE by the javax.net.ssl.keyStore and javax.net.ssl.keyStorePassword system properties, or by more complex means described in the JSSE Reference Guide.
The keystore is a precious and confidential file containing your private key. Guard and protect it. Private key leakage would compromise your security and identity.