使用HttpsURLConnection的3種方法小結

最近遇到網絡安全方面的問題,要將http轉移到https,由於在工程中使用了HttpURLConnection,所以要相應的轉而使用HttpsURLConnection,當然大部分是參考的網絡上一些前輩們的成果,過程中也遇到了一些坑,在這裏進行一下總結。

由於https涉及到證書的認證方式,這裏簡單介紹一下: 
關於證書,可以簡單把它理解爲網站的身份證。而給網站頒發身份證的就是CA(證書頒發機構)。 
可以頒發證書的CA有很多(國內外都有),只有少數CA被認爲是權威、公正的,這些CA頒發的證書,瀏覽器、操作系統才認爲是信得過的。 
Android系統中,就有一個根證書信任列表,若我們的證書是由這個列表中的某個根證書的子證書,就不需要在https使用過程中特別指定了。 
我們自己也可以自己製作證書,例如使用OpenSSL就可以生成一個CA根證書,然後用這個根證書頒發兩個子證書server和client,server證書放在服務器端,而這個client證書就可以用於瀏覽器或者安卓app中。這種自己製作的證書,就必須在app中指定了,否則https握手是不能成功的。 
我就按使用證書的不同方式來進行分別說明:

1,信任系統提供的證書(權威CA頒發); 
2,全部信任證書; 
3,信任指定證書;

1,信任系統提供的證書

這是最簡單的方式,相比較於http訪問,轉到https協議,只是將HttpURLConnection 替換爲 HttpsURLConnection 就夠了。 
下面是一個使用 HttpsURLConnection 實現的POST請求:

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);// 設置允許輸出
                    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();

                    /* 服務器返回的響應碼 */
                    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();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49

調用示例:

httpPostData(MainActivity.this, url, content);
  • 1
  • 1

2,全部信任證書

添加HTTPSTrustManager類,如下:

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());
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

再在所有https開始進行請求之前,執行一次即可:

HTTPSTrustManager.allowAllSSL();//信任所有證書
  • 1
  • 1

後面就是正常的進行https訪問就可以了:

httpPostData(MainActivity.this, url, content);
  • 1
  • 1

3,信任指定證書

先要獲取到證書,我們可以放到assert目錄下,例如這裏使用的證書的文件名爲“root.crt”。 
通過如下函數來讀取,並返回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;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

然後,在使用 HttpsURLConnection 的過程中,也就是httpsPostData()函數中,使用指定證書的 SSLContext 即可:

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

當然,如果仔細看了前面的 httpsPostData()函數內容的話,就知道前面的代碼中已經有這句話了,只是被註釋掉了。打開就可以了,就是使用指定證書的了。

至於調用POST請求的地方,是一樣的:

httpPostData(MainActivity.this, url, content);
  • 1
  • 1

最後總結一下:

1,全部信任證書:不太安全,Google也不推薦。但是畢竟是https,比http安全多了,只是還存在被中間人攻擊的風險;

2,信任指定證書:這種方式保證了網絡傳輸鏈路的安全,是可以防住中間人攻擊的。 
但是問題可能會出在App上:這個證書直接放在app中,若是由於某些原因導致證書需要更新,就需要更新所有的app,在用戶量較大的情況下,這幾乎是不可能完成的任務。

3,信任系統提供的證書(CA頒發);這種方式最好,既安全又好維護,更換一個CA頒發的證書,對代碼不需要做任何改動,但是可能需要花錢。也可以找些免費的證書,但是使用期限可能有較大的限制。

這三種方式各有特色,具體採用哪種方式,還是需要根據自己項目的實際情況來確定。

參考:

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

原創地址:http://blog.csdn.net/lintax/article/details/70195138

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章