2

I have gone to almost all the post related to this exception. Actually my problem is I have an java application through which I am hitting an URL and getting response from it.

code to hit URL is :

HttpGet getRequest = new HttpGet("https://urlto.esb.com");
HttpResponse httpResponse = null;       
DefaultHttpClient httpClient = new DefaultHttpClient();
httpResponse = httpClient.execute(getRequest); 

Here I am getting javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated

So after some Google search I come to know that I can import certificate in keystore of java where the application is running. so I imported certificate in keystore and this code is working. but i don't want this solution so after some more searching I come to know that I can use TrustManager for the same thing without importing certificate into keystore. So I have written code like:

@Test
    public void withTrustManeger() throws Exception {
        DefaultHttpClient httpclient = buildhttpClient();
        HttpGet httpGet = new HttpGet("https://urlto.esb.com");
        HttpResponse response = httpclient.execute( httpGet );

        HttpEntity httpEntity = response.getEntity();
        InputStream inputStream = httpEntity.getContent();
        BufferedReader reader = new BufferedReader(new InputStreamReader(
                inputStream));
        StringBuilder sb = new StringBuilder();
        String line = null;
        while ((line = reader.readLine()) != null) {
            sb.append(line + "\n");
        }
        inputStream.close();
        String jsonText = sb.toString();
        System.out.println(jsonText);
    }

    private DefaultHttpClient buildhttpClient() throws Exception {
        DefaultHttpClient httpclient = new DefaultHttpClient();

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, getTrustingManager(), new java.security.SecureRandom());

        SSLSocketFactory socketFactory = new SSLSocketFactory(sc);
        Scheme sch = new Scheme("https", 443, socketFactory);
        httpclient.getConnectionManager().getSchemeRegistry().register(sch);
        return httpclient;
    }

    private TrustManager[] getTrustingManager() {
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            @Override
            public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                return null;
            }

            @Override
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
                // Do nothing               
            }

            @Override
            public void checkServerTrusted(X509Certificate[] certs, String authType) {
                // Do nothing
            }

        } };
        return trustAllCerts;
    }

This code is also working but My question is I am not checking anything related to certificates then how connection is trusted. after debugging I come to know that only checkServerTrusted is hitting. So I have write something in checkServerTrusted to validate certificates that come in certs and the one which is in my application like some .cer or .crt file.

Every Help will be appreciated.

Update after @EpicPandaForce (Using Apache HttpClient 4.3)

        try 
        {
            keyStore = KeyStore.getInstance("JKS");
            InputStream inputStream = new FileInputStream("E:\\Desktop\\esbcert\\keystore.jks");
            keyStore.load(inputStream, "key".toCharArray());
            SSLContextBuilder sslContextBuilder = SSLContexts.custom().loadTrustMaterial(keyStore, new TrustSelfSignedStrategy());
            sslcontext = sslContextBuilder.build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext);
            HttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
            HttpGet httpGet = new HttpGet("https://url.esb.com");
            HttpResponse response = httpclient.execute( httpGet );

            HttpEntity httpEntity = response.getEntity();
            InputStream httpStram = httpEntity.getContent();
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    httpStram));
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
            httpStram.close();
            inputStream.close();
            String jsonText = sb.toString();
            System.out.println(jsonText);

        } 
        catch(Exception e) 
        {
            System.out.println("Loading keystore failed.");
            e.printStackTrace();
        }
Amogh
  • 4,453
  • 11
  • 45
  • 106
  • have you seen http://stackoverflow.com/questions/8693991/java-ignore-expired-ssl-sertificate/8694377#8694377 ? – EpicPandaForce Jan 28 '15 at 15:45
  • @EpicPandaForce, Yes but how can I verify certificate. Means the one which is came from server with the one that I have. – Amogh Jan 29 '15 at 05:00

2 Answers2

2

Technically, seeing as you are using Apache HttpClient 4.x, a simpler solution would be the following:

    SSLContext sslcontext = null;
    try {
        SSLContextBuilder sslContextBuilder = SSLContexts.custom()
            .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy());
        sslcontext = sslContextBuilder.build();

Where trustStore is initialized like this

    KeyStore keyStore = null;
    try {
        keyStore = KeyStore.getInstance("BKS", BouncyCastleProvider.PROVIDER_NAME); //you can use JKS if that is what you have
        InputStream inputStream = new File("pathtoyourkeystore");
        try {
            keyStore.load(inputStream, "password".toCharArray());
        } finally {
            inputStream.close();
        }
    } catch(Exception e) {
        System.out.println("Loading keystore failed.");
        e.printStackTrace();
    }
    return keyStore;
}

And then create the HttpClient

SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext);
httpclient = HttpClients
                .custom()
                .setSSLSocketFactory(sslsf).build();

EDIT: Exact code for me was this:

        SSLContextBuilder sslContextBuilder = SSLContexts.custom()
            .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy());
        sslcontext = sslContextBuilder.build();

        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
            sslcontext, new String[] {"TLSv1"}, null,
            SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
        );
        httpclient = HttpClients
            .custom()
            .setHostnameVerifier(SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER)
            .setSSLSocketFactory(sslsf).build();
EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • Oh by this I can load my keystore @ runtime, am i getting correctly? – Amogh Jan 29 '15 at 09:01
  • Yup! But this makes your request trust only the certificate that you provided in the keystore. If you have that certificate in your app, you should be able to load it. – EpicPandaForce Jan 29 '15 at 10:37
  • Perfect that's what i want. So what if i import the certificate in `cacerts` file in my development PC and while building jar i will copy this cacerts into my app and while making request i will load the keystore (cacerts from my app) as you shown in answer – Amogh Jan 29 '15 at 10:46
  • 2
    I think you can do that, but I think the while `cacerts` file from the Java directory is overkill. If you have the `certificate` in your disposal, then you can create a new `JKS` keystore using `keytool` (so don't specify the `BouncyCastleProvider` as the provider, it is not needed), which would contain only the certificate you want. But `cacerts` would also work, the default password to the `cacerts` is `changeme`. You can put the `cacerts` keystore into the app and then access it in the classpath too (I think that's `Thread.currentThread().getContextClassLoader().getResourceAsStream("...")`. – EpicPandaForce Jan 29 '15 at 10:51
  • Okay, First I will try with creating my own `JKS` file using `keytool` and try to load it, If fails I come to you :). but just I have seen that classes [`SSLContextBuilder`](http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/ssl/SSLContextBuilder.html),[`SSLContexts`](http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/ssl/SSLContexts.html) are from HttpClient 4.3 and I am using 4.2.x – Amogh Jan 29 '15 at 10:57
  • What will be provider in case of my own JKS file? or is it optional in 4.3? – Amogh Jan 29 '15 at 10:59
  • Gah. it's not `changeme`, it's `changeit`. Sorry about that. The provider for the `KeyStore.getInstance(inputStream)` call will default to the first provider, which I believe will be the provider by `Sun`. I personally used the latest HttpClient, that's what I got the example from. – EpicPandaForce Jan 29 '15 at 11:07
  • I have updated code in question, but my Junit test case getting failed. After debugging I come to know that it's getting failed at LOC `SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext);` no exception is thrown program is terminated. but in Junit window I got Error `java.lang.NoSuchFieldError: INSTANCE` where I am doing wrong. – Amogh Feb 06 '15 at 07:30
  • what version of HttpClient are you using exactly? As per the following constructor http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/ssl/SSLConnectionSocketFactory.html#SSLConnectionSocketFactory(javax.net.ssl.SSLContext) you are supposed to be able to use it. I used `compile 'org.apache.httpcomponents:httpclient-android:4.3.5'` dependency in my android project where this code worked without a problem. – EpicPandaForce Feb 06 '15 at 08:13
  • I added the exact code from my project, maybe that will fix it. – EpicPandaForce Feb 06 '15 at 08:16
  • I am using HttpClient 4.4 – Amogh Feb 06 '15 at 08:40
0

I fixed it by this setting:

final SSLConnectionSocketFactory sslsf;

    try {
        TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
        sslsf = new SSLConnectionSocketFactory(sslContext,
                NoopHostnameVerifier.INSTANCE);
    } catch (NoSuchAlgorithmException | KeyStoreException e) {
        throw new RuntimeException(e);
    } catch (KeyManagementException e) {
        throw new RuntimeException(e);
    }
    final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
            .register("http", new PlainConnectionSocketFactory())
            .register("https", sslsf)
            .build();
Victor Lv
  • 81
  • 4