Reputation: 143
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 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 :)
Upvotes: 3
Views: 6299
Reputation: 122649
It's likely that your server uses Server Name Indication (SNI) to serve two host names on the same IP address (and port).
You can check this using openssl
, by specifying the server name explicitly. For example, these two commands will connect to the same IP address but request different host names and serve different certificates:
openssl s_client -connect www.google.co.uk:443 -servername www.google.co.uk
openssl s_client -connect www.google.co.uk:443 -servername www.google.com
Java only supports SNI on the client side at the moment, and only since Java 7.
Upvotes: 3