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: [email protected], CN=http://www.valicert.com/, OU=ValiCert Class 1 Policy Validation Authority, O="ValiCert, Inc.", L=ValiCert Validation Network
Issuer: [email protected], 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.
This question is related to
java
security
ssl
tomcat
apache-commons-httpclient
According to the documentation you need to specify the key store:
Protocol authhttps = new Protocol("https",
new AuthSSLProtocolSocketFactory(
new URL("file:my.keystore"), "mypassword",
new URL("file:my.truststore"), "mypassword"), 443);
HttpClient client = new HttpClient();
client.getHostConfiguration().setHost("localhost", 443, authhttps);
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.
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
BasicHttpClientConnectionManager cm = new BasicHttpClientConnectionManager();
builder.setConnectionManager( cm );
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.
This is what worked for me:
KeyStore keyStore = KeyStore.getInstance("PKCS12");
FileInputStream instream = new FileInputStream(new File("client-p12-keystore.p12"));
try {
keyStore.load(instream, "password".toCharArray());
} finally {
instream.close();
}
// Trust own CA and all self-signed certs
SSLContext sslcontext = SSLContexts.custom()
.loadKeyMaterial(keyStore, "password".toCharArray())
//.loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
.build();
// Allow TLSv1 protocol only
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslcontext,
new String[] { "TLSv1" },
null,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); //TODO
CloseableHttpClient httpclient = HttpClients.custom()
.setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER) //TODO
.setSSLSocketFactory(sslsf)
.build();
try {
HttpGet httpget = new HttpGet("https://localhost:8443/secure/index");
System.out.println("executing request" + httpget.getRequestLine());
CloseableHttpResponse response = httpclient.execute(httpget);
try {
HttpEntity entity = response.getEntity();
System.out.println("----------------------------------------");
System.out.println(response.getStatusLine());
if (entity != null) {
System.out.println("Response content length: " + entity.getContentLength());
}
EntityUtils.consume(entity);
} finally {
response.close();
}
} finally {
httpclient.close();
}
}
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
Source: Stackoverflow.com