Summary of Three Methods of Using HttpsURLConnection

Recently encountered network security problems, to transfer http to https, because the use of HttpURLConnection in the project, so the corresponding use of HttpsURLConnection, of course, most of the results of some predecessors on the reference network, the process also encountered some pits, here to summarize.

Since https involves certificate authentication, here is a brief introduction:
With regard to certificates, it can be simply understood as the identity card of the website. And it is CA (Certificate Authority) that issues identity cards to websites.
There are many CAs that can issue certificates (both at home and abroad). Only a few CAs are considered authoritative and fair. The certificates issued by these CAs are considered credible by browsers and operating systems.
In Android, there is a root certificate trust list. If our certificate is a sub-certificate of a root certificate in this list, we don't need to specify it in the use of https.
We can also make our own certificates, such as using OpenSSL to generate a CA root certificate, and then issue two sub-certificates server and client with this root certificate. The server certificate is placed on the server side, and this client certificate can be used in browsers or Android apps. This self-made certificate must be specified in the app, otherwise the https handshake will not succeed.
I will explain them separately according to the different ways of using certificates:

1. Certificates provided by trust system (issued by authoritative CA);
2. All trust certificates;
3. Trust the designated certificate;

1. Certificates provided by the trust system

This is the simplest way to go to the https protocol than to http access, just replace HttpURLConnection with HttpsURLConnection.
The following is a POST request implemented using HttpsURLConnection:

public static void httpsPostData(final Context context, final String urlPath, final String content){
        new Thread()
        {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                Looper.prepare();
                URL url;
                try {
                    url = new URL(TimeValidity.addTimeValidityUrl(urlPath));
                    HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
                    //conn.setSSLSocketFactory(getSSLContext(context).getSocketFactory());
                    conn.setConnectTimeout(TIMEOUT_LONG);//5
                    conn.setReadTimeout(TIMEOUT_LONG);
                    conn.setDoOutput(true);// Setting Allowable Output
                    conn.setRequestMethod("POST");
                    conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)");
                    conn.setRequestProperty("Charset", "UTF-8");
                    conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                    OutputStream os = conn.getOutputStream();
                    os.write(content.getBytes());
                    os.close();

                    /* The response code returned by the server */
                    int code = conn.getResponseCode();
                    Log.i("https","code="+code);
                    if (code == 200) {
                        BufferedReader in = new BufferedReader(
                                new InputStreamReader(conn.getInputStream(), "UTF-8"));
                        String retData = null;
                        String responseData = "";
                        while ((retData = in.readLine()) != null) {
                            responseData += retData;
                        }
                        in.close();                     
                    } else {
                        Log.i("https","return error");
                    }
                } catch (MalformedURLException e) {
                    e.printStackTrace();                    
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();                
                }
                Looper.loop();
            }
        }.start();
    }

Call examples:

httpPostData(MainActivity.this, url, content);

2. All Trust Certificates

Add the HTTPSTrustManager class as follows:

public class HTTPSTrustManager implements X509TrustManager {

    private static TrustManager[] trustManagers;
    private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[] {};

    @Override
    public void checkClientTrusted(
            java.security.cert.X509Certificate[] x509Certificates, String s)
            throws java.security.cert.CertificateException {
        // To change body of implemented methods use File | Settings | File
        // Templates.
    }

    @Override
    public void checkServerTrusted(
            java.security.cert.X509Certificate[] x509Certificates, String s)
            throws java.security.cert.CertificateException {
        // To change body of implemented methods use File | Settings | File
        // Templates.
    }

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

    public static void allowAllSSL() {
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {

            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                // TODO Auto-generated method stub
                return true;
            }

        });

        SSLContext context = null;
        if (trustManagers == null) {
            trustManagers = new TrustManager[] { new HTTPSTrustManager() };
        }

        try {
            context = SSLContext.getInstance("TLS");
            context.init(null, trustManagers, new SecureRandom());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
    }
}

Before all https start requesting, execute once again:

HTTPSTrustManager.allowAllSSL();//Trust all certificates

The following is normal https access.

httpPostData(MainActivity.this, url, content);

3. Trust the designated certificate

To get the certificate first, we can put it in the assert directory. For example, the file name of the certificate used here is "root.crt".
Read through the following functions and return to the SSLContext:

    public static SSLContext getSSLContext(Context inputContext){
        SSLContext context = null;
        try {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            InputStream in = inputContext.getAssets().open("root.crt");
            Certificate ca = cf.generateCertificate(in);
            KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
            keystore.load(null, null);
            keystore.setCertificateEntry("ca", ca);
            String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            tmf.init(keystore);
            // Create an SSLContext that uses our TrustManager
            context = SSLContext.getInstance("TLS");
            context.init(null, tmf.getTrustManagers(), null);
        } catch (Exception e){
            e.printStackTrace();
        }
        return context;
    }

Then, in the process of using HttpsURLConnection, that is, in the httpsPostData() function, use the SSLContext of the specified certificate:

conn.setSSLSocketFactory(getSSLContext(context).getSocketFactory());

Of course, if you look carefully at the content of the httpsPostData() function, you will know that the previous code already has this sentence, but it has been commented out. Open it and use the specified certificate.

As for the place where the POST request is invoked, it is the same:

httpPostData(MainActivity.this, url, content);

In conclusion:

1. All Trust Certificates: Not very secure, nor recommended by Google. But after all, https is much safer than http, but there is still the risk of being attacked by intermediaries.

2. Trust Designated Certificate: This method guarantees the security of network transmission link and can prevent man-in-the-middle attack.
But the problem may lie in App: this certificate is directly placed in app. If the certificate needs to be updated for some reason, it needs to update all apps. In the case of large number of users, this is almost impossible to accomplish.

3. Trust the certificate provided by the system (CA issued); this is the best way to replace a certificate issued by CA, which is safe and easy to maintain, without any changes to the code, but may cost money. You can also find some free certificates, but there may be greater limitations on the duration of use.

These three ways have their own characteristics, which way to adopt, or need to be determined according to the actual situation of their own projects.

Reference resources:

http://blog.csdn.net/whu_zhangmin/article/details/45868057
http://www.cnblogs.com/cxjchen/p/3152832.html

Keywords: Java network Android OpenSSL

Added by binarylime on Tue, 09 Jul 2019 01:24:04 +0300