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