Using HttpClient with SSL and certificates - java

While I've been familiar with HTTPS and the concept of SSL, I have recently begun some development and found I am a little confused.
The requirement was that I write a small Java application that runs on a machine attached to a scanner. When a document is scanned this is picked up and the file (usually PDF) sent over the internet to our application server that will then process it. I've written the application using Apache Commons libraries and HTTPClient.
The second requirement was to connect over SSL, requiring a certificate. Following guidance on the HTTPclient page I am using AuthSSLProtocolSocketFactory from the contributions page.
The constructor can take a keystore, keystore password, truststore and truststore password. As an initial test our DBA enabled SSL on one of our development webservers and provided me with a .p12 file which when I imported into IE allows me to connect successfully.
I am a bit confused between keystores and truststores and what steps I need to take using the keytool. I tried importing the p12 into a keystore file but get the error:
keytool error: java.lang.Exception: Input not an X.509 certificate
I followed a suggestion of importing the p12 into Internet Explorer and exporting as a .cer which I can then successfully import into a keystore. When I supply this as a keystore argument of the AuthSSLProtocolSocketFactory I get a meaningless errror, but if I try it as a truststore it seems like it reads it fine but ultimately I get
Caused by: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
I am unsure if I have missed some steps, I am misunderstanding SSL and mutual authentication altogether or this is mis-configuration on the server side.
Can anyone provide suggestions or point me towards resources that might help me figure this out please?

The keystore holds your private keys and associated certificates. The truststore hold the certificates that you trust and that can therefore be used for certificate path building and verification.
Here are some links that may be useful:
java.lang.Exception: Input not an X.509 certificate
Import private key and certificate into Java Key Store
Configuring Keystores and Truststores

make sure your certificate file does not have anything before and after these.
-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----

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

SSLHandshakeException: PKIX path building failed with Unable to find certificate chain

I am trying to Connect SSRS ReportServer with HTTPS Protocol from Java Client it's giving javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: java.security.cert.CertPathBuilderException: Unable to find certificate chain
But I have added the certificate in Java Keystore, It the keystore the certificate is showing with the defined alias name.
But Same SSRS Request call is working with HTTP Protocol.
Note: We are using a wildcard certificate from Go-Daddy for secure communication
Can you verify that the certificate is added to 'Java Truststore' and not Keystore (although both have .jks extension).
Have a look at this https://docs.oracle.com/cd/E19830-01/819-4712/ablqw/index.html
Keystore file, keystore.jks, contains the Application Server’s certificate, including its private key
Truststore file, cacerts.jks, contains the Application Server’s trusted certificates
When you got your certificate, there should have been a p7b (possibly) included with it.
Essentially, when GoDaddy issues a certificate, they are not issued from the root directly.
Most likely you have one issued from an intermediate authority.
You can get the intermediate certificates at https://ssl-ccp.godaddy.com/repository?origin=CALLISTO
The certificate chain needs to be appended to the actual certificate and imported on the server.
The PEM file containing the cert being imported should look like:
<actual certificate>
<intermediate certificate>
[<intermediate certificate> ...]
After that you shouldn't have a need to do anything else on the client.
EDIT
After re-reading my answer, I realized there may be a little bit of vagueness with regard to the process. So in efforts to bring this more full cycle here we go.
Generate keypair / self-signed certificate as say PKCS#12 container.
Create PKCS#10 certificate signing request to send to GoDaddy of above certificate.
GoDaddy returns signed X.509 Certificate (typically with instructions on how to install it).
Taking the certificate (assuming PEM format) and concatenate the issuance chain from https://ssl-ccp.godaddy.com/repository?origin=CALLISTO and import that back into the PKCS#12 from step 1.
Assign the PKCS#12 as the TLS certificate store on the SSRS server.

Keystore and truststore issue with elasticsearch and searchguard ssl

Let me give a little bit of background before I ask the question, so that we have clarity on the problem itself. We need to support one way SSL with Elasticsearch (v5.2.x), using searchguard ssl. We have a list of procedures for developers (not for production), which takes care of generating a self signed SSL cert. Here we've one root (locally created) and the actual cert. If we import the keystore (containing the private key and signed cert) and the truststore (containing the root cert) everything works fine.
But couple of days back, we got one request from a client. There, in production we need to support SSL. So, we followed the steps below:
with our script, we generated the private key, imported it into the keystore, and also generated the csr.
We gave the client the csr. He got it signed by a proper CA, and gave us back the cert.
Now the chain of trust is of length 3, for the cert given. So, there's a root CA, which signed the cert (issuer1), which signed the cert (issuer2), which in turn signed the actual csr.
For importing the actual cert to the keystore, we imported all the three parents, and then imported the actual cert.
Then we removed all the parent certs from keystore. So, now the keystore has only the private key and the actual cert.
We imported all the three parent certs into the truststore.
Now, if we start Elasticsearch, the following error is thrown: [ERROR][c.f.s.s.t.SearchGuardSSLNettyTransport] [uyyIg3i] SSL Problem Received fatal alert: certificate_unknown
javax.net.ssl.SSLException: Received fatal alert: certificate_unknown
Funny thing is, the exact procedure works, if we have only the root ca, signing the actual csr. Any help is appreciated for figuring out the root cause of this issue, as I'm sort of out of ideas now.
After couple of tiresome debugging sessions, we found out the CN-name and the actual host-name to be different. After making both of them same, we got it working.

certificate mechanism between webservice provider and consumer

What are the exact steps done by server and client to place a ssl certificate mechanism in a webservice call? Who(client/server/both) will generate .keystore,.p7b/.cer files? I have googled a lot but couldn't find the answer to it.
In my case, i am the client running a java application which consumes a soap webservice call. I have a .p7b file given by WebService provider. I know where to place the files(.keystore, .cer) and how to use it in the application.
But, my questions are
Do i need to generate keystore file or should i get it from
webservice provider? If i need to generate, how? Do i need private
key/passphrase?
I need a .cer file, so how can i use keytool to convert .p7b to .cer
file?
Thank you for your help in advance.
It looks like you're calling a web service where the HTTP connection is protected by TLS/SSL using X509 certificates. That means the server has set up a keystore with those certificates as well as the corresponding private keys. When you call the web service, the server will retrieve from its keystore the certificate used for the trust establishment (that is, to protect the TLS connection to the web service) and sends it to the client. When the client receives the response from the server it will check the trust of that certificate. Now we have two scenarios:
If the server uses a self-signed certificate (can be used for developments and testing, but not in production), then the client won't recognize it as trusted because it's not stored in the client's truststore. By default, in a Java environment, the truststore is searched (by order) in the following two locations: $JAVA_HOME/lib/security/jssecacerts and $JAVA_HOME/lib/security/cacerts. A custom truststore can also be used by running the client with -Djavax.net.ssl.trustStore and -Djavax.net.ssl.trustStorePassword or by using a custom TrustManager. As such, if the server self-signed certificate is not stored in one of these locations, the secure connection will fail. So the client will have to import the certificate into its truststore. To circumvent the import of self-signed certificates into the client's truststore, you can create a custom X509TrustManager as stated here.
If the server uses a certificate signed by one of the recognized root CA authorities, then it'll be validated automagically because those CA's certificates are already installed in Java's default truststore. As such, the trusted TLS connection will be successful.
In the case where the server does not require client authentication the process is over (this is what happens when you connect to most HTTPS websites via browser).
If the server requires client authentication, then the client will need to provide its own certificate from its keystore to the server, and the server will need to have it installed in its truststore. The web service provider must provide to the client the specification for the certificate profile that the client should use.
Here you can find a good clarification to the keystore vs truststore terminology.
By default in Java environments, keystores and truststores are JKS files.
So you're saying you have a .p7b file provided by the web service provider. Quoting from this page:
PKCS#7/P7B Format
The PKCS#7 or P7B format is usually stored in Base64 ASCII format and has a file extention of .p7b or .p7c. P7B certificates contain "-----BEGIN PKCS7-----" and "-----END PKCS7-----" statements. A P7B file only contains certificates and chain certificates, not the private key. Several platforms support P7B files including Microsoft Windows and Java Tomcat.
So that P7B file contains the server certificate or certificate chain (more on this here).
I believe you're in a no-client-auth scenario. Therefore, you won't need your own keystore. You'll only need to import the server's certificate (P7B file) into the truststore you're using. You can directly import a P7B file without converting it to CER format:
keytool -import -trustcacerts -alias web_service -keystore my_truststore.jks -file web_service.p7b
In the case you still want a CER formatted certificate, you can convert from P7B to CER like this (to answer to your 2nd question):
openssl pkcs7 -print_certs -in certificate.p7b -out certificate.cer
If in fact client authentication is needed, then you'll need to create your keystore with your private key and public certificate and provide it to the connection by either the -Djavax.net.ssl.keyStore and -Djavax.net.ssl.keyStorePassword parameters or through a KeyManager. The same workflow previously explained applies now in the opposite direction.

Why do I get a handshake failure (Java SSL)

I'm connecting to a web service over HTTPS. I've done all that I think is required to make it work, but in the end I get a handshake failure.
I found out that as a new user I can't post more than 2 links due to "spam protection" - thanx a lot stackoverflow...anyway here's a link to a pastebin post with all the links spelled out...so when I write "link#1" here it's a reference to these links: http://pastebin.com/y4zGNRC7
I verified the same behavior using HttpClient (GET on the service URL) and actually calling the web service via a CXF proxy
I'm setting both the keystore and truststore - I tried both the "in code" way ( link#1 ) and setting the system properties - i.e. System.setProperty("javax.net.ssl.keyStore", "mykeystore.jks");
SSL debug is on ( javax.net.debug=all )
SSL debug blurts out the contents of both keystore and truststore (i.e. looks like java "knows about them") - link#2
seems like there's some client-server communication going on, but then it crashes for some reason link#3
I successfully connected to the server using the client and CA certificates both in a browser (Chrome) and using openssl s_client
wireshark shows less client-server talk from java ( link#4 ) then for example from Chrome ( link#5 )
Another strange thing is, that I seem to be getting the same behavior when I set the keystore and when I don't (the only difference is that when I do the keystore contents get printed in the console, but that's it).
I tried googling the problem and I saw numerous similar posts here on stackoverflow, but nothing helped.
I tried changing the protocol version ("TLSv1", "SSLv3", even the weird v2 Hello).
Any help would be appreciated - maybe there's some fundamental thing I might have overlooked...I'm getting desperate here...
Thanx
PS I'm running java 1.6 update 30 on Fedora Core 15 (64bit)
The problem was that even though the keystore and truststore was set, java decided not to send the client certificate to the server. The reason for this was the fact, that the server requested a certificate signed by the RootCA authority, but the client certificate is signed by a SubCA authority (which is issued by the RootCA).
Originally the keystore only contained the client cert and the truststore the SubCA cert.
I then tried to add the SubCA cert to the keystore too, but java just ignored it.
So this solves the hanshake failure mystery, but not my problem.
I created a separate question for that...sigh :-(
why doesn't java send the client certificate during SSL handshake?
I think the trust store not containing the CA is the most likely issue. You can use the Java keytool to import the certificate for the site into the cacerts file doing something like:
keytool -keystore pathtocacerts -import -trustcacerts -v -alias aliasName -file root.crt
The default cacerts keystore password is changeit. The cacerts file is usually under jre/lib/security directory.
You don't provide enough information, but I'm guessing your client truststore is not properly configured. The truststore contains the trusted certificates that are used to sign other certs, and must include the root certificate(s) for the server and client cert chains. The client keystore contains the client SSL certificate and private key.

Categories