Self-signed certificate and loopj for Android

2019-03-08 06:11发布

问题:

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 ?

回答1:

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) {   
    }


回答2:

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.


回答3:

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) {}


回答4:

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));


回答5:

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



回答6:

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



标签: android loopj