I have enabled https in tomcat and have a self-signed certificate for server auth. I have created an http client using Apache httpClient. I have set a trust manager loading the server certificate. The http client can connect with server no problem. To see what is going on I enabled debugging:
System.setProperty("javax.net.debug", "ssl");
I saw the following which I can not understand at all:
***
adding as trusted cert:
Subject: CN=Me, OU=MyHouse, O=Home, L=X, ST=X, C=BB
Issuer: CN=Me, OU=MyHouse, O=Home, L=X, ST=X, C=BB
Algorithm: RSA; Serial number: 0x4d72356b
Valid from Sat Mar 05 15:06:51 EET 2011 until Fri Jun 03 16:06:51 EEST 2011
My certificate is displayed and is added to truststore (as I see). Then:
trigger seeding of SecureRandom
done seeding SecureRandom
Here is the part from debugging traces I do not get:
trustStore is: C:\Program Files\Java\jre6\lib\security\cacerts
trustStore type is : jks
trustStore provider is :
init truststore
adding as trusted cert:
Subject: CN=SwissSign Platinum CA - G2, O=SwissSign AG, C=CH
Issuer: CN=SwissSign Platinum CA - G2, O=SwissSign AG, C=CH
Algorithm: RSA; Serial number: 0x4eb200670c035d4f
Valid from Wed Oct 25 11:36:00 EEST 2006 until Sat Oct 25 11:36:00 EEST 2036
adding as trusted cert:
Subject: EMAILADDRESS=info@valicert.com, CN=http://www.valicert.com/, OU=ValiCert Class 1 Policy Validation Authority, O="ValiCert, Inc.", L=ValiCert Validation Network
Issuer: EMAILADDRESS=info@valicert.com, CN=http://www.valicert.com/, OU=ValiCert Class 1 Policy Validation Authority, O="ValiCert, Inc.", L=ValiCert Validation Network
Algorithm: RSA; Serial number: 0x1
Valid from Sat Jun 26 01:23:48 EEST 1999 until Wed Jun 26 01:23:48 EEST 2019
It seems that it also uses the default java trust store! My question is why does this happen?
In my code I specify explicitly a specific trust-store to use (via truststoremanagers). I was expecting only this to be used. It seems that both my truststore and java's default is being used. Is this how it is supposed to work?
UPDATE:
I tried the following:
System.out.println("TMF No:"+tmf.getTrustManagers().length);
System.out.println("Class is "+tmf.getTrustManagers()[0].getClass().getName());
I thought that I should see 2 trust managers, since 2 keystores (mine and java's default appear to be used).
But the result was only 1 trust manager!
TMF No:1
Class is com.sun.net.ssl.internal.ssl.X509TrustManagerImpl
UPDATE2: As you see in the code bellow I specify my keystore.My expectation is that only this should be used (not this and cacert as well)
HttpClient client = new DefaultHttpClient();
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("JKS");
File trustFile = new File("clientTrustStore.jks");
ks.load(new FileInputStream(trustFile), null);
tmf.init(ks);
sslContext.init(null, tmf.getTrustManagers(),null);
SSLSocketFactory sf = new SSLSocketFactory(sslContext);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Scheme scheme = new Scheme("https", sf, 443);
client.getConnectionManager().getSchemeRegistry().register(scheme);
httpGet = new HttpGet("https://localhost:8443/myApp");
HttpResponse httpResponse = client.execute(httpGet);
Does not make sense to me.
I put together this test app to reproduce the issue using the HTTP testing framework from the Apache HttpClient package:
Even though, Sun's JSSE implementation appears to always read the trust material from the default trust store for some reason, it does not seem to get added to the SSL context and to impact the process of trust verification during the SSL handshake.
Here's the output of the test app. As you can see, the first request succeeds whereas the second fails as the connection to www.verisign.com is rejected as untrusted.
This is what worked for me:
This code is a modified version of http://hc.apache.org/httpcomponents-client-4.3.x/httpclient/examples/org/apache/http/examples/client/ClientCustomSSL.java
According to the documentation you need to specify the key store:
When I used Apache HTTP Client 4.3, I was using the Pooled or Basic Connection Managers to the HTTP Client. I noticed, from using java SSL debugging, that these classes loaded the cacerts trust store and not the one I had specified programmatically.
I wanted to use them but ended up removing them and creating an HTTP Client without them. Note that builder is an HttpClientBuilder.
I confirmed when running my program with the Java SSL debug flags, and stopped in the debugger. I used -Djavax.net.debug=ssl as a VM argument. I stopped my code in the debugger and when either of the above *ClientConnectionManager were constructed, the cacerts file would be loaded.