CodingMonkey
CodingMonkey

Reputation: 143

Apache HttpClient 4.2.3 - SSLException: hostname in certificate didn't match

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

Answers (1)

Bruno
Bruno

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

Related Questions