android 使用https請求請求資料
android 使用https請求
今年年初就已經吵吵著要把大部分的服務端由http轉成https了,但是由於很多公司還是比較懶,而且有的公司可能不想再多掏一些錢去對自己的網址加入CA認證,所以這件事就一直拖下來了,但是隨著使用者資料越來越多暴露在一些不法分子眼前,所以資訊保安越來越被使用者重視,一些金融、貸款公司已經開始使用這種技術了,今天就來講解一下android上面的通過https對伺服器進行請求。
首先先來解答一下疑惑,我們在做測試的時候經常會對https://www.baidu.com或者https://www.github.com進行一個網路工具類的測試,但是不知道大家有沒有注意到,這些網址連結之前全都是https://的,為什麼我們在訪問的時候完全沒有報錯,也能夠正常的訪問呢。ok,其實這兩個大網站已經被CA認證過了,而CA證書已經被android裝置預設支援了,所以這些CA認證的網站可以直接通過HttpUrlConnection進行連線,當然也可以通過HttpsUrlConnection進行連線。
但是除了被CA認證的網址就不能夠正常的連線了,比如我們訪問神奇的網站12306時,在他的註冊頁面其實已經升級成了Https協議的了,而且這個網站的證書是沒有被CA認證過的。我們來分別對比下百度的 和github 還有我們神奇的網站12306的 簽名檔案~~~
見圖:
第一張是百度網站的 證書檔案資訊
這張是github的證書資訊
這張當然就是12306的嘍
大家可以自己開啟電腦瀏覽器,開啟開發者工具(或者F12),然後點選security就能看到這個資訊了,如果對證書感興趣,還可以點選上邊的“view certificate”去檢視證書的資訊
我們通過上面的三個圖,不難分析出baidu 和github使用的是CA的認證的證書,而12306並沒有,12306用的是自己證書
這就導致了12306需要我們特別去寫一下網路請求,而百度和github則不需要。
那我們怎麼去正常的訪問12306呢,這裡我封裝了一個網路請求類,核心程式碼在這裡:
try {
URL url = new URL(path);
//1.改成s
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
//2.SSLContext 初始化
SSLContext tls = SSLContext.getInstance("TLS");
MyX509TrustManager myX509TrustManager = new MyX509TrustManager(getX509Certificate(context));
TrustManager[] trustManagers={myX509TrustManager};
tls.init(null,trustManagers,new SecureRandom());
//3.ssl工廠
SSLSocketFactory factory = tls.getSocketFactory();
//4.新增一個主機名稱校驗器
conn.setHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
if (hostname.equals("kyfw.12306.cn")) {
return true;
}else{
return false;
}
}
});
conn.setSSLSocketFactory(factory);
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
conn.setConnectTimeout(5000);
conn.connect();
InputStream inputStream = conn.getInputStream();
StringBuilder sb=new StringBuilder();
int flag;
byte[] buf=new byte[1024];
while((flag=inputStream.read(buf))!=-1){
sb.append(new String(buf,0,flag));
}
String s = sb.toString();
//呼叫對方傳入callback完成回撥操作
callBack.onSuccess(s);
} catch (Exception e) {
e.printStackTrace();
callBack.onFail(e);
}
主要的思路是:
1.先將我們常用的httpurlconnection變成HttpsConnection
2.初始化我們的SSLContext 這個類是ssl的一個幫助類,能夠幫助我們去驗證我們的證書檔案
這個環節中其實稍微多一些操作,比如獲取SSLContext的例項
SSLContext tls = SSLContext.getInstance("TLS");
還有建立SSLContext的初始化引數,這個MyX509TrustManager是我自己封裝的類
MyX509TrustManager myX509TrustManager = new MyX509TrustManager(getX509Certificate(context));
封裝的類如下
package com.guaju.httpsrequesttest.http;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.X509TrustManager;
/**
* Created by guaju on 2017/11/7.
*/
public class MyX509TrustManager implements X509TrustManager {
//如果需要對證書進行校驗,需要這裡去實現,如果不實現的話是不安全
X509Certificate mX509Certificate;
public MyX509TrustManager(X509Certificate mX509Certificate) {
this.mX509Certificate = mX509Certificate;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
for (X509Certificate certificate:chain){
//檢查證書是否有效
certificate.checkValidity();
try {
certificate.verify(mX509Certificate.getPublicKey());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (NoSuchProviderException e) {
e.printStackTrace();
} catch (SignatureException e) {
e.printStackTrace();
}
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
而上個類中傳入的構造方法中的證書檔案
X509Certificate mX509Certificate;
這個證書檔案需要通過我們下載的證書去獲得,12306的證書在官網首頁,而我們如果開發自己的公司的網站的話,後臺應該會給我們一份,我們拿著這個證書放到assets目錄下(或者後臺的伺服器上)就可以轉變成我們自己的簽名了,下午是12306下載證書的地方:
下方是提供的將assets中的證書檔案流轉化成證書的方法,很簡單:
//拿到自己的證書
X509Certificate getX509Certificate(Context context) throws IOException, CertificateException {
InputStream in = context.getAssets().open("srca.cer");
CertificateFactory instance = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) instance.generateCertificate(in);
return certificate;
}
建立TrustManager陣列,將上方的MyX509TrustManager放進陣列中去
TrustManager[] trustManagers={myX509TrustManager};
然後呼叫SSLContext的初始化方法
tls.init(null,trustManagers,new SecureRandom());
3.上方的sslcontext初始化好之後,我們就可以做SSLSocketFactory的建立了
SSLSocketFactory factory = tls.getSocketFactory();
然後把這個工廠為我們的連線conn設定上
conn.setSSLSocketFactory(factory);
4.新增主機名稱校驗器,記住這裡的主機名稱地址需要檢視我們的證書的配置,不能瞎寫
證書配置如下圖
下方的名稱及上方的常用名稱
conn.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { if (hostname.equals("kyfw.12306.cn")) { return true; }else{ return false; } } });
這樣的話就可以訪問12306的註冊頁面了。
ok,為了方便大家使用,我已經提交到github了,如果大家覺得不錯,記得star 或者fork哦 ,多謝~~~
尊重作者勞動成果: