14

I'm trying to use loopj for making async HTTP requests. Works great, except when I try to access https site with self-signed cert. I get

javax.net.ssl.SSLPeerUnverifiedException: No peer certificate.

I guess the default ssl options can be overriding using setSSLSocketFactory(SSLSocketFactory sslSocketFactory) method, but I'm not sure how to do it or it might not be the right way at all.

Please suggest how can I solve this issue ?

Marijn
  • 10,367
  • 5
  • 59
  • 80
bneupaane
  • 537
  • 1
  • 7
  • 16

6 Answers6

41

You do it almost exactly the same as explained here for HttpClient except a little bit simpler - Trusting all certificates using HttpClient over HTTPS

Create a custom class:

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.conn.ssl.SSLSocketFactory;
public class MySSLSocketFactory extends SSLSocketFactory {
    SSLContext sslContext = SSLContext.getInstance("TLS");

    public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
        super(truststore);

        TrustManager tm = new X509TrustManager() {
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }

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

        sslContext.init(null, new TrustManager[] { tm }, null);
    }

    @Override
    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException, UnknownHostException {
        return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
    }

    @Override
    public Socket createSocket() throws IOException {
        return sslContext.getSocketFactory().createSocket();
    }
}

Then when you create your client instance:

try {
      KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
      trustStore.load(null, null);
      sf = new MySSLSocketFactory(trustStore);
      sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
      client.setSSLSocketFactory(sf);   
    }
    catch (Exception e) {   
    }
Community
  • 1
  • 1
7wonders
  • 1,639
  • 1
  • 17
  • 34
  • 2
    This worked a charm! I like that you referenced the other example but modified it here specifically for use with loopj's async http client. Thanks! – Twice Circled Jan 16 '13 at 17:21
  • I must add that I ended up getting frustrated with the loopj library and switched to basic-http-client library instead - http://code.google.com/p/basic-http-client/ – 7wonders Apr 26 '13 at 19:54
  • 1
    What this does is remove all security features. That error message was there for a reason - you should have a proper certificate instead of disabling certificate checks! – MarioVilas Feb 11 '14 at 21:18
  • I started implementing a workaround this way, and then I read http://developer.android.com/training/articles/security-ssl.html which specifically warns against providing a TrustManager that does nothing. Since my application requires a working client certificate anyway, I am using the "default" TM from that article. In my SSLSocketFactory constructor: `TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); sslContext.init(null, tmf.getTrustManagers(), null);` – Ari Lacenski Mar 20 '14 at 19:52
  • 1
    This example trusts every certificate, but what if i want to trust only my own server certificate.How to do that?? – cafebabe1991 Jun 09 '14 at 14:12
  • Sorry, I think it is insecure way. And I found the answer in the source code: > if (fixNoHttpResponseException) { log.d(LOG_TAG, "Beware! Using the fix is insecure, as it doesn't verify SSL certificates."); } – insomnia Oct 12 '16 at 03:36
  • loopj's AsynHttpClient expects `cz.msebera.android.httpclient.conn.ssl.SSLSocketFactory` but `org.apache.http.conn.ssl.SSLSocketFactory` is given here, how do you overcome this? – Mehmed May 02 '17 at 08:11
9

You can use constructor AsyncHttpClient(boolean fixNoHttpResponseException, int httpPort, int httpsPort). From version loopj library 1.4.4 and bigger. For example

mClient = new AsyncHttpClient(true, 80, 443);

and you get warning message to logcat at the Verbose log.

Beware! Using the fix is insecure, as it doesn't verify SSL certificates.
Youngjae
  • 24,352
  • 18
  • 113
  • 198
horkavlna
  • 3,042
  • 1
  • 16
  • 14
  • How do you know what `httpPort` and `httpsPort` to use ? – Seeven May 27 '15 at 14:54
  • When you look to documentation http://loopj.com/android-async-http/doc/com/loopj/android/http/AsyncHttpClient.html .These ports are using your server when you connecting on the REST API and you know how ports use your server ;). Of course you can make more generally and you can implement config.xml and switched it depend build gradle flavors or use constants in gradle and reference your BuildConfig object. Or the most easiest you can use constructor without parameter new AsyncHttpClient() because http port 80 is generally and https 443 is this same or write directly to the url – horkavlna May 27 '15 at 16:04
  • I still get SSL handshake error in 4.3, do you have any idea? – Mehmed May 02 '17 at 08:02
6

Simpler way is to use built-in MySSLSocketFactory in loopj, so you don't have to create another class

try {
        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        trustStore.load(null, null);
        MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);
        sf.setHostnameVerifier(MySSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        client.setSSLSocketFactory(sf);
}
catch (Exception e) {}
Nicholas Ng
  • 1,428
  • 18
  • 23
  • 1
    Thanks, it worked! I placed this block of code after my `AsyncHttpClient client = new AsyncHttpClient();` instanciation and before my `client.post(...)` call. – Seeven May 27 '15 at 15:08
6

As explained in many places simply bypassing verification of the certificates is wrong on so many levels. Do not do that!

What you should do instead is to create .bks file from your cert(for that purpose you gonna need Bouncy Castle):

keytool -importcert -v -trustcacerts -file "path/to/certfile/certfile.crt" -alias IntermediateCA -keystore "outputname.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "path/to/bouncycastle/bcprov-jdk15on-154.jar" -storetype BKS -storepass atleastsix

Next place your newly created outputname.bks inside res/raw folder.

Create helper function(it could be inside own class or whatever you like):

private static SSLSocketFactory getSocketFactory(Context ctx) {
        try {
            // Get an instance of the Bouncy Castle KeyStore format
            KeyStore trusted = KeyStore.getInstance("BKS");
            // Get the raw resource, which contains the keystore with
            // your trusted certificates (root and any intermediate certs)
            InputStream in = ctx.getResources().openRawResource(R.raw.outputname); //name of your keystore file here
            try {
                // Initialize the keystore with the provided trusted certificates
                // Provide the password of the keystore
                trusted.load(in, "atleastsix".toCharArray());
            } finally {
                in.close();
            }
            // Pass the keystore to the SSLSocketFactory. The factory is responsible
            // for the verification of the server certificate.
            SSLSocketFactory sf = new SSLSocketFactory(trusted);
            // Hostname verification from certificate
            // http://hc.apache.org/httpcomponents-client-ga/tutorial/html/connmgmt.html#d4e506
            sf.setHostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER); // This can be changed to less stricter verifiers, according to need
            return sf;
        } catch (Exception e) {
            throw new AssertionError(e);
        }
    }

And last but not least, set your AsyncHttpClient to use the new socket factory:

AsyncHttpClient client = new AsyncHttpClient();
client.setSSLSocketFactory(getSocketFactory(context));
hris.to
  • 6,235
  • 3
  • 46
  • 55
  • This worked great! Thank you so much! The issue I had was with this line: `MySSLSocketFactory sf = new MySSLSocketFactory(trustStore);` – SuperStubbs Jun 20 '16 at 18:59
  • 2
    Thank you! This is the first answer which actually is secure. All other answer seem to accept all certificates. This is like not using https at all. – Sn0wfreeze Aug 14 '16 at 16:59
  • Thanks you! And I think we must learn ssl/openssl/keytool/KeyStore.getINstance("{KeyStore type}") as "BKS","AndroidCAStore","AndroidKeyStore","BCPKCS12","BKS","BouncyCastle","PKCS12","PKCS12-DEF" first. And now I change using Asynchttp to Retrofit2. – insomnia Oct 12 '16 at 03:34
  • Thanks, it works. I do not agree with insomnia, BKS is common format in Android, so I do not see any problem. I only have a single complaint - SSLSocketFactory is now deprecated. It is probably bug in library. SSLSocketFactory point to SSLConnectionSocketFactory but all code in AsyncHttpClient is linked to deprecated class SSLSocketFactory. So we cannot use SSLConnectionSocketFactory with AsyncHttpClient . – rbrisuda Jan 23 '17 at 12:01
0

With Https and certificate I have done it successfully with the help of two docs HttpsUrlConnection and Portecle.

Vineet Shukla
  • 23,865
  • 10
  • 55
  • 63
0

Don't NUKE all SSL certificates.. Trusting all certificates is a BAD PRACTICE!!!

  • Accept only your SSL certificate.

Take a look at my solution. Some contents from this Gist can help your to figure how to do this.

OBS.: I'm using Android Volley.

https://gist.github.com/ivanlmj/f11fb50d35fa1f2b9698bfb06aedcbcd

ivanleoncz
  • 9,070
  • 7
  • 57
  • 49