8

I try to connect to a server with a self-signed certificate. I use this code to accept all certificates.

public class CertificateAcceptor {

    public void initializeTrustManager() {
        try {
            SSLContext context = SSLContext.getInstance("SSL");
            context.init(null, createTrustManager(), new SecureRandom());
            HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
    }

    private TrustManager[] createTrustManager() {

    TrustManager[] trustAllCerts = new TrustManager[] {
            new X509TrustManager() {

                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }

                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    // leave blank to trust all clients
                }

                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                    // leave blank to trust all servers
                    for (X509Certificate c : chain) {
                        System.out.println(c.toString());
                    }
                }

            }
        };
        return trustAllCerts;
    }

}

But nevertheless i get the following error:

javax.net.ssl.SSLException: hostname in certificate didn't match: <xyz.ch> != <localhost>
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:220)
    at org.apache.http.conn.ssl.BrowserCompatHostnameVerifier.verify(BrowserCompatHostnameVerifier.java:54)
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:149)
    at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:130)
    at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:339)
    at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:123)
    at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:147)
    at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:108)
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:415)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:641)
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:576)

I'm sure that my Certificate code is executed, so what could be the problem?

  • 1
    The problem is not with the trustmanager. Hostname verification is a separate security step which checks the domain of the URL you are requesting against a name (should be the domain aka hostname of the server) defined in the certificate of the server you're trying to hit. In your case, the name in the URL you are using and the name in the server certificate do not match. – Hawkeye Parker Nov 09 '12 at 06:44

5 Answers5

20

ALLOW_ALL is not the correct answer. You should set up your certificate with the correct name by using keytool with the ext extension:

keytool -genkeypair \
   -keystore keystore.jks \
  -dname "CN=OLEKSIYS-W3T, OU=Sun Java System Application Server, O=Sun Microsystems, L=Santa Clara, ST=California, C=US" \
  -keypass changeit \
  -storepass changeit \
  -keyalg RSA \
  -keysize 2048 \
  -alias default \
  -ext SAN=DNS:localhost,IP:127.0.0.1 \
  -validity 9999

See http://tersesystems.com/2014/03/23/fixing-hostname-verification/ for more details.

Joshua Taylor
  • 84,998
  • 9
  • 154
  • 353
Will Sargent
  • 4,346
  • 1
  • 31
  • 53
  • After generating the keystore, do you initialize it separately or as part of a `SSLContext`? If you have time to look at my [complete question](http://stackoverflow.com/questions/30105561/find-or-intialize-the-keystore-needed-to-solve-hostname-in-certificate-didnt-m) I would appreciate it. – Atl LED May 07 '15 at 15:38
  • You need to add the certificate to your trust store. The usual way to do this is to either add it to cacerts using keytool, or to set the system property javax.net.ssl.trustStore to point to another trust store. – Will Sargent Jun 06 '16 at 17:50
7

You may use SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER

SSLSocketFactory sf = new SSLSocketFactory(
    SSLContext.getInstance("TLS"),
    SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Scheme sch = new Scheme("https", 443, sf);
httpclient.getConnectionManager().getSchemeRegistry().register(sch);

HttpGet httpget = new HttpGet("https://host/");
...
...
dierre
  • 7,140
  • 12
  • 75
  • 120
Prashant Bhate
  • 10,907
  • 7
  • 47
  • 82
  • 7
    This is an incorrect answer. You should set up your certificate with the correct name by using keytool with the `ext` extension: ``` keytool -genkeypair \ -keystore keystore.jks \ -dname "CN=OLEKSIYS-W3T, OU=Sun Java System Application Server, O=Sun Microsystems, L=Santa Clara, ST=California, C=US" \ -keypass changeit \ -storepass changeit \ -keyalg RSA \ -keysize 2048 \ -alias s1as \ -ext SAN=DNS:localhost,IP:127.0.0.1 \ -validity 9999 ``` and see http://tersesystems.com/2014/03/23/fixing-hostname-verification/ – Will Sargent Apr 06 '14 at 23:25
  • 3
    This definitely workse, but @WillSargent is right. If you use an "allow all" approach your app becomes vulnerable. You'll probably be fine with an app that isn't overly popular but be careful when it comes to work / freelance projects. – Jacksonkr Dec 18 '14 at 18:17
  • 3
    @Jackson it's way worse than that. The FTC has sued people for disabling security in this way. http://www.ftc.gov/news-events/press-releases/2014/03/fandango-credit-karma-settle-ftc-charges-they-deceived-consumers – Will Sargent Dec 19 '14 at 02:44
  • Oh wow!! I had no idea that was a real thing. It surprises me that the ftc goes through the work to find out about these things. Thanks for the info. – Jacksonkr Dec 19 '14 at 15:48
  • This answer doesn't work anymore in a later version. Will get "SSLContextImpl is not initialized" exception. Use @Abhishek Tyagi answert instead – Jianwu Chen Apr 16 '21 at 15:16
2

The Answer by Prashant may not work, as you need to initialize the SSLContext as well.

I would do it something like,

SSLSocketFactory sf=null ;
        SSLContext sslContext = null;
        StringWriter writer;
        try {
            sslContext = SSLContext.getInstance("TLS")  ;
            sslContext.init(null,null,null);
        } catch (NoSuchAlgorithmException e) {
            //<YourErrorHandling>
        }  catch (KeyManagementException e){
            //<YourErrorHandling>
        }

        try{
            sf = new SSLSocketFactory(sslContext, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);

        } catch(Exception e) {
            //<YourErrorHandling>

    }
        Scheme scheme = new Scheme("https",443,sf);
        httpClient.getConnectionManager().getSchemeRegistry().register(scheme);
Abhishek Tyagi
  • 2,239
  • 1
  • 15
  • 15
2

Have you tried calling setDefaultHostnameVerifier of HttpsURLConnection.

See this link for an example: Accepting a certificate for HTTPs on Android

Community
  • 1
  • 1
jglouie
  • 12,523
  • 6
  • 48
  • 65
0

My issue was, that Java SE 6 or smaller does not support SNI. That means, that one IP can only have one ssl cert. But on my server, there were 3 diffferent apis with different ssl certs. Java SE 7 or greater support SNI and everything worked just fine. (or only running one api on the server)

Jodo
  • 4,515
  • 6
  • 38
  • 50