How to add fingerprint to keystore - java

I had the following exception while writing to a SSL socket
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: Path does not chain with any of the trust anchors
It seems like the certificate of the target server is not trusted. I tested my implementation on a windows 7 machine, jdk7 and tomcat7 with no problem. The Exception is thrown on ubuntu 10 LTS with openJDK 6 and tomcat7. I got the sha1 and md5 fingerprint of the sockets target server. Am I right that the certificat of the server I am trying to stream to is not trustet on my ubuntu server? can I can the fingerprint to the tomcats keystore? If so, how do I do this?

It's not the fingerprint that you need to add to your trust store, but the actual certificate.
You can add the server certificate itself or add one of the CA certificates in the chain (if you wish to trust the all the certificates from that CA, not just that particular server).
To find out what the certificate is, you can use OpenSSL:
openssl s_client -showcerts -connect your.host.name:443
(Replace the host name and 443 by the actual ports you're using.)
The blocks between --BEGIN/END CERT...-- are the certificates in PEM format. You can check their content using openssl x509 -text -noout (and pasting each block there).
Save the certificate you want to import into a plain text file (e.g. certificate.pem). You should only import certificates that you trust. There's a certain leap of faith here. (You might want to connect with your browser and check whether the key material matches, for example.)
To import into your truststore use:
keytool -importcert -keystore truststore.jks -file certificate.pem
(You may need to specify an alias wit -alias some_name_you_choose.)
If you want this to affect your default truststore, replace truststore.jks with the path to lib/security/cacerts in your Java home directory (the default password should be changeit).
Since the target server seems to come from a well-known CA anyway (and it works with some versions of the JRE), the easiest fix is certainly to update your cacerts file manually, taking a copy from a JRE with which it works. After all, as the JSSE Reference Guide says:
IMPORTANT NOTE: The JDK ships with a limited number of trusted root
certificates in the /lib/security/cacerts file. As
documented in keytool, it is your responsibility to maintain (that is,
add/remove) the certificates contained in this file if you use this
file as a truststore.
Depending on the certificate configuration of the servers you contact,
you may need to add additional root certificate(s). Obtain the needed
specific root certificate(s) from the appropriate vendor.
It turns out it's certainly a problem with the certificate chain order (which is incorrect on this site), as shown by Qualys SSL Labs tester.

Related

“PKIX path building failed” error when making an HTTP request from Java

So, you have encountered the fabled PKIX Path Building issue. Does it look something like this?
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed
I bet it does. I sincerely hope you've found this SO question before spending hours frustratedly and futilely foraging the internet for ways to save yourself.
Let's go over at a high level what the problem is, and then dig into concrete solutions. (Credit Atlassian).
Disclaimer for those eager folks looking to mark this as a duplicate: there are many SO questions related to PKIX path building, but all of them beat around the core issue. From personal experience running into this issue (and spending many hours trying to resolve it) I know how hard it is to filter out the noise. I'm authoring this question in an effort to simply and elegantly provide a description of what causes the error, and how to most quickly resolve it. Feel free to edit to add meaningful detail where helpful.
Java Certs
Whenever Java attempts to connect to another application over TLS
(SSL) (e.g.: HTTPS, IMAPS, LDAPS), it will only be able to connect to
that application if it can trust it. The way trust is handled in the
Java world is that you have a keystore (typically
$JAVA_HOME/lib/security/cacerts), also known as the truststore. This
contains a list of all known Certificate Authority (CA) certificates.
Java will only trust certificates that are signed by one of those CAs
or certificates that already exist in that keystore.
This problem is therefore caused if the server presents to you (the
client) a certificate that is self-signed (a CA did not sign it) or a certificate that does not exist within your Java truststore. Java does not trust the certificate and fails to connect to the application. This leaves you feeling sad, and staring at a PKIX path building failed error.
Solution
In general, the solution to this problem is to get the public cert of the host you're trying to make a request to, and add that cert to your tru
ststore. Sounds easy, right? It should be.
1. Getting the cert
You can fetch the public cert of a host using the following: openssl s_client -connect <host>:<port>. This should output to the console the list of certificates and other nonsense for the given host and port. What you want to do is find and copy the lines from BEGIN CERTIFICATE to END CERTIFICATE. Alternatively, you can write this content directly to a file by extending your command a bit: openssl s_client -connect <host>:<port> < /dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > public.crt. The contents of public.crt should look like:
-----BEGIN CERTIFICATE-----
< Certificate content as fetched by the command line. Don't change this
content, only remove what is before and after the BEGIN CERTIFICATE and
END CERTIFICATE. That's what your Sed command is doing for you :-) >
-----END CERTIFICATE-----
2. Importing the Cert into your truststore
You'll want to import your newfound public.crt into the truststore that is being used when the HTTP request is being made from wherever your java application is running. This can be done with something like <JAVA_HOME>/bin/keytool -import -alias <server_name> -keystore <path/to/truststore.jks> -file public.crt.
You can add the cert to the cacerts, usually somewhere like here <JAVA_HOME>/jre/lib/security/cacerts, or directly to the truststore.jks itself (this is what I've been doing recently).
Once this is done, you (the client) now trusts the certificate the server will present, and you'll be able to make your http request. If you're still getting the error, the most likely cause is that you're not adding the cert to the correct truststore. See this SO post for where the truststore usually lives.```
Loosely citing:
Atlassian

Debian SSL certificates ERROR: cannot verify xxx certificate / javax.net.ssl.SSLHandshakeException

I have a strange problem (it's 100% server configuration problem,) for example I want to download something from Dropbox:
Resolving dl.dropboxusercontent.com... 23.23.160.146, 50.17.227.107,
54.221.248.69, ... Connecting to dl.dropboxusercontent.com|23.23.160.146|:443... connected. ERROR:
cannot verify dl.dropboxusercontent.com’s certificate, issued by
“/C=US/ST=CA/O=SonicWALL Inc./CN=SonicWALL Firewall DPI-SSL”:
Self-signed certificate encountered. To connect to
dl.dropboxusercontent.com insecurely, use ‘--no-check-certificate’.
Yes, I know that I can use --non-check-certificate but when I want to use SSL connection in Java app I have something like this:
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
This app works great in other servers or in local machines, any ideas what is wrong here?
/C=US/ST=CA/O=SonicWALL Inc./CN=SonicWALL Firewall DPI-SSL
Your traffic is visibly intercepted by a deep packet inspection firewall that acts as a MITM proxy to monitor your traffic.
This can generally be thought of as "legitimate" MITM attacker. (However legitimate this may be depends on a number of legal and ethical aspects.) Your local network administrator should be able to tell you a little bit about this. If this is part of a company network, this company is monitoring your traffic, including the contents of your HTTPS connection (so it's no longer secure from end-to-end). It the firewall does its job properly, it should still secure the connection from the firewall to the server (It's probably hard to know whether it check certificates properly.)
In general, such a firewall or proxy acts as its own Certification Authority, effectively forging each certificate as requested.
Most clients on the corporate network would trust certificates it issues (like the one you're facing) because system administrators would also install the CA certificate as a trusted certificate into each machine within that network. You probably have it the OS trusted root certificates.
However, it is likely that network administrators did not install this CA certificate into your JRE installation (which uses its own set of trust anchors by default).
Try to export that CA certificate (see the name above) from a reference machine and import it into the truststore you're using (either your JRE's default truststore: cacerts or a new truststore you build and pass to your application via the javax.net.trustStore properties).
Typically, you can use something like this (assuming you've exported the Firewall's CA as "firewallca.pem"):
keytool -import -file firewallca.pem -alias firewallca -keystore truststore.jks
If the truststore.jks file doesn't exist, it will be created. Otherwise, you can take a copy of the cacerts file in the lib/security directory of your JRE. You can also do this directly on the cacerts file (using -keystore /path/to/truststore.jks, provided you have write access to it).
If you choose not to do it on the default truststore (that cacerts file) but use a local truststore like truststore.jks, you'll can use it by using this system property when running your application: -Djavax.net.trustStore=/path/to/truststore.jks
For other ways of configuring your truststore, check this answer.
Another way to fix this is to tell Java to use your OS's truststore. I'll assume you're using Windows here. Using these system properties should work:
-Djavax.net.trustStore=NONE -Djavax.net.trustStoreType=WINDOWS-ROOT
(Try with WINDOWS-MY instead of WINDOWS-ROOT if this doesn't work.)
The WINDOWS-MY/WINDOWS-ROOT is a bit buggy in that it will miss some of the certificates in the Windows store: it uses the certificate "friendly name" (non unique) as the keystore alias (unique), so a certificate with a given friendly name will hide the others with the same name. (This effectively reduces the number of CA certificates that are trusted unfortunately.)
Since you're in an environment where presumably all the traffic goes through your DPI firewall, you'll probably only ever have to use one CA certificate at most. As long is it doesn't share its "friendly name" in the Windows list with any other cert, you should be fine.
You need to add the server's SSL certificate in your client's Java keystore. Take a look at this SO post:
Resolving javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed Error?
TIP: This is because your client's JVM does not 'trust' the server's SSL cert. So you need to add the cert in your keystore.

Converted PFX SSL cert throwing SSLHandshakeException?

I was given a PFX "wildcard" SSL certificate (I believe its a VeriSign cert) for *.ourdomain.example.org. I then used this answer to help me convert the PFX into a JKS keystore entry and add it to a JKS keystore. When I run keytool -list -keystore mykeys.jks I see:
Keystore type: JKS
Keystore provider: SUN
Your keystore contains 1 entry
<my key's long GUUID>, May 1, 2014, PrivateKeyEntry,
Certificate fingerprint (SHA1): <cert's fingerprint>
So I know the converted PFX cert is in there. But at runtime, when JNDI is using SSL to establish a connection to my AD server (over LDAPS), I am seeing the following 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
I'm wondering if something got botched in the conversion from PFX to JKS. I'm pretty sure nothing else is wrong with the SSL cert, because I was using a self-signed cert before using this PFX and my browser was giving me the typical "I do not trust this certificate" warning. After I added the converted PFX and restarted my app, those warnings went away. Any ideas as to what could be going on here?
Firstly, in many cases, you don't need to convert from PKCS#12 (PFX) to JKS, many applications will let you set the keystore type to PKCS12 (instead of the default JKS), which will allow you to use your PFX file directly.
It looks like you're mixing up the usage of trust store and key store here (see this question).
Having a keystore with a certificate and its PrivateKeyEntry (here, coming from a PKCS#12 file) is normally used as a "keystore keystore" (as opposed to a keystore used as a truststore). This would normally be used for the certificate you present to your clients as a server, or the client certificate you use when connecting to a server that requests one.
Here, the "PKIX path building failed" exceptions means that your Java application doesn't trust the remote certificate it is connecting to. Presumably, your AD server has nothing to do with this JKS keystore. Rather, your server that uses your keystore is also a client to that AD server, and it cannot trust the AD server certificate.
You don't say how you've configured your application. Perhaps you've used your keystore for both the keystore and truststore settings (effectively preventing the use of the CA certificates bundled with the default JRE truststore), or perhaps your AD certificate is self-signed or not issued by a CA that is part of the default truststore. If the latter is the case, simply import the AD certificate (just the certificate, not the certificate and its private key) into the trust store (you can either do this into the JRE's cacert file or make a copy of it and use that using the javax.net.ssl.trustStore and related system properties).

Getting ssl exception inspite of both certificates are issued by verified CA's?

I have two webapplication at prrduction say app1 and app2. Both are on different webservers. app1 is verified secure trust corporation and
app2 is verified by TrustWave Holdings,Inc . So both are verified CA's.App2 tries to make HttpsURLConnection to app1 and gets some SSLException. My question
is if both certificates are verfied ones, still do we need to make any certificate entry in jre\lib\security folder? As per my understanding
we do not need this if certificates are verified one. Any pointers will be great help.
EDIT:-
i am on jdk1.6. I am not sure whether these certificates i.e(secure trust corporation and TrustWave Holdings,Inc) are already there
in cacerts file under jre\lib\security. i could find on the web that these are included in mozzila and other browser but not
sure about jdk 1.6?
It takes some effort from a provider to have its root CA cert included in all browsers and iphones and tablets and whatnots. More often than not they don't bother getting their cert shipped with oracle or openjdk. That's the exact reason why we moved away from Godaddy to Comodo... If the server cert isn't bundled you can create a keystore with the issuer certs and yours and tell java to use it via commandline:
-Djavax.net.ssl.trustStore=yourkeystorefile.jks -Djavax.net.ssl.keyStore=yourkeystorefile.jks
technically you would put the servers cert+key in the keystore and cert chain from the issuer to the truststore, but it's fine for java if the two are mixed in one keystore file
You will likely need to add these to /security/cacerts. By default Java does trust some certificate authorities. I'm not sure TrustWave is one of them.
View the keystore using the following command and see if TrustWave is in there.
keytool -list -keystore /usr/java/jdk1.6.0_14/jre/lib/security/cacerts

Do we need to add the verified CA in VM TrustStore when making connection from java?

I am making a call from one application sayApp1 to another application(say App2) authorized by verfied CA like Verisign or Thawte like
String urlStr="https://myApp2/welcome"
HttpsURLConnection conn1 = (HttpsURLConnection) url.openConnection();
conn1.getInputStream()
Do i need to import that certificate issued by App2 somewhere in
VM truststore of app1? This is the case when they are on different tomcat server(so different vm).
What about the same scenario if they are on same tomcat server(i mean same vm)?
i know in case of browser it is not required to import the certificate signed by CA's like verisign,thawte etc but what about when making the connection from java?
Edit:-
As you said This will be the same regardless of the server they are installed
It means ever webserver checks the cacerts file that shipped with Java 1.6.0_30 . so if client jvm has those certicates, we dont have to do anything.
This cacerts file check is done only when we are making the url connection from java code. In case of browser it will just check browser truststore.RIGHT?
To move my site to https i followed below step
C:\Program Files\Java\jdk1.6.0_23>keytool -genkey -alias tomcat -keyalg RSA which generated .keystore file
Finally i made changes in server.xml and it worked keystoreFile="c:/.keystore" keystorePass="changeit"
After going thru too many material on net i am bit confused about which approach i just followed(did i create my own CA or i just created self signed certificate which needs
to be present at client side) ?
It depends. The cacerts file that shipped with Java 1.6.0_30 has 76 entries. If the certificate used by the other application was verified by one of the those vendors using one of those certificates, no importing will be necessary. This will be the same regardless of the server they are installed on if the protocol used is HTTPS. Sometimes vendors have new certificates that will require updates to cacerts. This is typically fixed by JVM upgrades. If the public key of the certificate used to verify the other application is not in cacerts then it will need to be imported to establish trust.
Your question is really confusing (this is the least I can say).
You start saying that you need communication with an application that deploys a certificate signed by a trusted CA like Verisign and end up asking what kind of certificate does keytool generated that you used in your Tomcat!
Anyway:
Do i need to import that certificate issued by App2 somewhere in VM
truststore of app1? This is the case when they are on different tomcat
server(so different vm). What about the same scenario if they are on
same tomcat server(i mean same vm)?
This does not matter.If you don't define for your client app a specific truststore then java's default will be used.
If the app2 sends a certificate signed by a trusted issuer like Verisign then you don't have to do anything (as #laz also points out). Since Verisign's certificate should already be present in your java installation cacerts
After going thru too many material on net i am bit confused about
which approach i just followed(did i create my own CA or i just
created self signed certificate which needs to be present at client
side) ?
You created a self-signed certificate (and a private key of course). You will need to import this in your client's truststore and of course use that keystore as your server's keystore.

Categories