Self-signed certificate and loopj for Android

2019-03-08 06:25发布

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 ?

标签: android loopj
6条回答
三岁会撩人
2楼-- · 2019-03-08 06:43

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) {   
    }
查看更多
【Aperson】
3楼-- · 2019-03-08 06:43

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));
查看更多
男人必须洒脱
4楼-- · 2019-03-08 06:49

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.
查看更多
唯我独甜
5楼-- · 2019-03-08 06:55

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

查看更多
乱世女痞
6楼-- · 2019-03-08 06:56

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

查看更多
神经病院院长
7楼-- · 2019-03-08 07:05

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) {}
查看更多
登录 后发表回答