I'm having this big problem and I can't find any answer on google, so I try to write here.
I'm developing a web service client. To connect to the web service, I have to create an ssl channel with client authentication and use a proxy (the call must be done from a whitelisted IP). To do this, I'm using the HttpClient 4.2.3 library with Java 1.5 (compatibility mode with 1.4), under WebSphere 6.1 environment.
Being work-related, I'm deleting all references to the actual web service I'm calling so you will find some weird variable/host/class names here and there :)
I've set up the connection like this
// Costruzione del proxy
Resources res = new Resources();
String proxyHost = res.get(PROXY_HOST);
int proxyPort = Integer.parseInt(res.get(PROXY_PORT));
HttpHost proxy = new HttpHost(proxyHost, proxyPort);
// Costruzione del post method
String serviceUrl = "https://" + someHost + meth.getServiceURL();
HttpPost post = new HttpPost(serviceUrl);
post.addHeader("Content-Type", "text/xml");
String soapAction = "https://" + someHost + meth.getAction();
post.addHeader("SOAPAction", soapAction);
AbstractHttpEntity postBody = new StringEntity(soapEnvelope);
post.setEntity(postBody);
// Costruzione dell'ambiente di chiamata HTTPS
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(keyStore, res.get(KEY_STORE_PASS_CONF), trustStore);
Scheme httpsScheme = new Scheme("https", HTTPS_PORT, sslSocketFactory);
Scheme httpScheme = new Scheme("http", HTTP_PORT, PlainSocketFactory.getSocketFactory());
final SchemeRegistry schemeRegistry = new SchemeRegistry();
schemeRegistry.register(httpScheme);
schemeRegistry.register(httpsScheme);
PoolingClientConnectionManager connManager = new PoolingClientConnectionManager(schemeRegistry);
HttpParams params = new BasicHttpParams();
HttpClient client = new DefaultHttpClient(connManager, params);
client.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);
// CHIAMATA A MEF
HttpResponse resp = client.execute(post);
Keystore and Truststore have been loaded from a file
File ksFile = new File(res.get(KEY_STORE_PATH_CONF));
File tsFile = new File(res.get(TRUST_STORE_PATH_CONF));
InputStream ksInput, tsInput;
try {
ksInput = new FileInputStream(ksFile);
tsInput = new FileInputStream(tsFile);
} catch ... {}
KeyStore keyStore, trustStore;
try {
String ksPassword = res.get(KEY_STORE_PASS_CONF);
String tsPassword = res.get(TRUST_STORE_PASS_CONF);
keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(ksInput, ksPassword.toCharArray());
trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(tsInput, tsPassword.toCharArray());
} catch ... {}
When I try to call the client.execute method, it gives me this exception
javax.net.ssl.SSLException: hostname in certificate didn't match: <right.host.it> != <unknown.host.it>
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java)
at org.apache.http.conn.ssl.BrowserCompatHostnameVerifier.verify(BrowserCompatHostnameVerifier.java)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java)
at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java)
at org.apache.http.conn.ssl.SSLSocketFactory.createLayeredSocket(SSLSocketFactory.java:628)
at org.apache.http.impl.conn.DefaultClientConnectionOperator.updateSecureConnection(DefaultClientConnectionOperator.java:232)
at org.apache.http.impl.conn.ManagedClientConnectionImpl.layerProtocol(ManagedClientConnectionImpl.java:401)
at org.apache.http.impl.client.DefaultRequestDirector.establishRoute(DefaultRequestDirector.java:842)
at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:649)
at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:480)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:906)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:805)
at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java)
at my.class.package.MyClass.callWs(MyWebService.java)
There are some weird things that I noticed:
- the unknown host in the exception message is from the same domain of the right one, so it is taken necessarily from a certificate of that company
- I've tried to debug the call, and it looks like it takes other certificates. The unknown.host.it address is taken from the CN of an x509 certificate (seen in debug), and both certificates in the keystore file contains other CNs. Moreover, I have two certificates in my keystore and when the first AbstractVerifier.verify method is called there are three certificates in the call parameters, one of them containing the unknown.host.it string in the CN
- actually, I can't find the unknown.host.it string in any certificate in my possess
The problem is probably that I don't know how actually SSL works, so if you see any completely wrong thing in what I said it's probably the key to resolve my problem.
Can you help me in some way? Thanks a lot!
#UPDATE 1: the day after :) Since I want to receive an answer first (the company has to test the software), I've decided to force the software to accept every hostname. I want to change this later, but for now I just want it to work. So I've added this (deprecated :) ) line to my code
SSLSocketFactory sslSocketFactory = new SSLSocketFactory(keyStore, res.get(KEY_STORE_PASS_CONF), trustStore);
sslSocketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Now it passes the hostname verification (surprise surprise :) ) but still gives me a 403-Forbidden error. The service owner says I'm calling from a whitelisted IP, so the problem lies, I suppose, in the fact that the server doesn't recognize me. They say I'm not presenting any certificate.
My way to add the certificate was in the SSLSocketFactory constructor, where I pass the keystore with my certificate inside. Do I have to specificate to HttpClient that I want to self-identify to the server while handshaking?
Thanks :)