1. 程式人生 > >Android Volley的使用(三)Volley中如何使用https

Android Volley的使用(三)Volley中如何使用https

概念

https(Hyper Text TransferProtocol over Secure Socket Layer)簡單講就是在http中加入了SSL(Secure Sockets Layer,安全套接層)層的安全版本。https請求是安全的,所以請求過程中不可以被抓包。

在典型的 SSL 使用場景中,會使用一個包含公鑰及與其匹配的私鑰的證書配置伺服器。讓客戶端也擁有其信任的一個或多個證書集。如果請求的URL地址時發現證書不在此集合中,則不會信任伺服器而請求失敗。但是這樣做也存在缺點。伺服器在後續時間裡可能存在升級更強的金鑰,使用新的公鑰替換證書中的公鑰。這時由於客戶端不能及時更新的話就會出現問題。

為彌補這些缺點,通常使用來自知名頒發者(稱為證書頒發機構 (CA))發放的證書配置伺服器。主機平臺一般包含其信任的知名 CA 的列表。從 Android 4.2 (Jelly Bean) 開始,Android 目前包含在每個版本中更新的100 多個 CA。CA 具有一個證書和一個私鑰,這點與伺服器相似。為伺服器發放證書時,CA 使用其私鑰簽署伺服器證書。然後,客戶端可以驗證該伺服器是否具有平臺已知的 CA 發放的證書。詳細可以參考官方說明https://developer.android.com/training/articles/security-ssl.html

實現1

根據上面概念介紹,如果伺服器是使用來自知名證書頒發機構發放的證書配置,那麼在Android裡會自動幫忙我們去驗證該證書是否有效,我們在使用Volley請求網路時,根本就是不需要做任何事情,只需要將http URL改成https URL即可。

實現2

如果伺服器沒有使用第三方知名證書頒發機構發放的證書配置,是通過典型使用場景,即使用一個包含公鑰及與其匹配的私鑰的證書配置伺服器。那麼也需要客戶端配置與其匹配的證書。我們在《Android Volley的使用(一)基本網路請求》中介紹到可以通過程式碼

RequestQueue requestQueue =Volley.newRequestQueue(getApplicationContext()); 

來建立請求佇列RequestQueue物件。其實我們從原始碼中可以發現,newRequestQueue方法還有一個過載的版本:newRequestQueue(Context context, HttpStack stack)。那麼客戶端配置證書使用如下:

SSLSocketFactory sslSocketFactory =initSSLSocketFactory();
HurlStack stack = new HurlStack(null,sslSocketFactory);
RequestQueue requestQueue =Volley.newRequestQueue(mContext, stack);
private final String RFC ="XXX...";
 
/**
 * 生成SSLSocketFactory
 * 這裡的程式碼與之前相比,沒有什麼不同
 *
 *@return
 */
private SSLSocketFactoryinitSSLSocketFactory() {
   //生成證書:Certificate
   CertificateFactory cf = null;
   SSLSocketFactory factory = null;
   try {
       cf = CertificateFactory.getInstance("X.509");
       InputStream caInput = new ByteArrayInputStream(RFC.getBytes());                     // 通過證書生成的RFC格式資料字串
       //InputStream caInput = new BufferedInputStream(newFileInputStream("xxx.crt"));       // 證書檔案
 
       Certificate ca = null;
       try {
           ca = cf.generateCertificate(caInput);
       } finally {
           try {
                caInput.close();
           } catch (IOException e) {
                e.printStackTrace();
           }
       }
 
       //初始化公鑰:keyStore
       String keyType = KeyStore.getDefaultType();
       KeyStore keyStore = KeyStore.getInstance(keyType);
       keyStore.load(null, null);
       keyStore.setCertificateEntry("ca", ca);
 
       //初始化TrustManagerFactory
       String algorithm = TrustManagerFactory.getDefaultAlgorithm();
       TrustManagerFactory managerFactory =TrustManagerFactory.getInstance(algorithm);
       managerFactory.init(keyStore);
 
       //初始化sslContext
       SSLContext sslContext = SSLContext.getInstance("TLS");
       sslContext.init(null, managerFactory.getTrustManagers(), null);
       factory = sslContext.getSocketFactory();
 
    }catch (CertificateException e) {
       e.printStackTrace();
    }catch (NoSuchAlgorithmException e) {
       e.printStackTrace();
    }catch (KeyStoreException e) {
       e.printStackTrace();
    }catch (IOException e) {
       e.printStackTrace();
    }catch (KeyManagementException e) {
       e.printStackTrace();
    } 
   return factory;
}

實現3

這種方法屬性比較極端的做法,就是跳轉證書驗證。其實使用https請求驗證是否合法,在Volley中主要是HostnameVerifier介面中的verify方法進行驗證,所以如果我們在此方法中返回true,則可以跳過證書的驗證,實現如下:

新建SsX509TrustManager類,並繼承X509TrustManager

public class SsX509TrustManager implementsX509TrustManager {
   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 | FileTemplates.
    }
 
   @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 | FileTemplates.
    }
 
   public boolean isClientTrusted(X509Certificate[] chain) {
       return true;
    }
 
   public boolean isServerTrusted(X509Certificate[] chain) {
       return true;
    }
 
   @Override
   public X509Certificate[] getAcceptedIssuers() {
       return _AcceptedIssuers;
    }
 
   /**
    * 允許所有的SSL請求,新增在new StringRequest()之前
    */
   public static void allowAllSSL() {
       HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
 
           @Override
           public boolean verify(String arg0, SSLSession arg1) {
                return true;
           }
 
       });
 
       SSLContext context = null;
       if (trustManagers == null) {
           trustManagers = new TrustManager[]{new SsX509TrustManager()};
       }
 
       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());
    }
}

然後在建立請求Request物件前加上一行程式碼:SsX509TrustManager.allowAllSSL();即可,如:

RequestQueue requestQueue =Volley.newRequestQueue(getApplicationContext()); 
SsX509TrustManager.allowAllSSL();
StringRequest stringRequest = newStringRequest(Request.Method.GET, "https://kyfw.12306.cn/otn/regist/init", 
       new Response.Listener<String>() { 
           @Override 
           public void onResponse(String s) { 
                // 請求成功 
           } 
       }, 
       new Response.ErrorListener() { 
            @Override 
           public void onErrorResponse(VolleyError volleyError) { 
                // 請求失敗 
           } 
       }); 
requestQueue.add(stringRequest);