相信很多客戶端開發人員特別是Android開發人員對https如何在程序中使用存有疑惑,項目中有的說“什麼都不用校驗校驗都在服務端”、“ios都不用校驗,系統自帶處理機制”……之類的,不管是使用Volley、OkHttp還是其他網絡框架,不做校驗或使用不安全校驗確實能是客戶端正常訪問https的接口服務地址,但是如此以來給app特別是Android版本的應用程序帶來了極大安全隱患。
網上查找解決方案,千篇一律,思路都說的很好,具體怎麼實現呢?
下面是個人對Android實現https配置的經驗及代碼,分享學習,僅做參考,有問題請留言。
客戶端https認證方式
- 信任所有——不做處理,沒有起到校驗作用,有風險
- 單向認證——自定義信任規則,校驗服務端證書
- 單向認證——使用預埋證書,校驗服務端證書(自簽名證書)
雙向認證——使用bks證書和密碼管理客戶端證書(雙向認證),使用預埋證書,校驗服務端證書(自簽名證書)
網上查到大多數代碼類似如下,此種方式即爲不做校驗處理,風險很高:
/**
* 爲了解決客戶端不信任服務器數字證書的問題,網絡上大部分的解決方案都是讓客戶端不對證書做任何檢查,
* 這是一種有很大安全漏洞的辦法
*/
public static X509TrustManager UnSafeTrustManager = new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
//TODO 此處應該加入自定義的校驗規則
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
};
// 創建TLS類型的SSLContext對象, that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{manager}, null);
//下面以OkHttp設置https爲例,其他網絡框架一樣設置setSslSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslContext.getSocketFactory());
OkHttpClient okHttpClient = builder.build();
下面試我的實現方式:單向認證——自定義信任規則,校驗服務端證書+使用預埋證書校驗服務端證書公鑰有效性
/**
* 自定義校驗規則:1、校驗服務端證書有效性;2、使用本地預埋證書校驗服務端證書公鑰(需要提前把服務端cer證書放入Android功能的assets目錄下,此種方式要考慮都證書有效期,如果過期需要app端及時替換證書——更具自身情況可以去掉預埋證書key校驗代碼)
*/
private class SafeTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
LogUtils.d("checkServerTrusted 校驗服務端證書");
if (chain == null) {
throw new IllegalArgumentException("Check server X509Certificate is null");
}
if (chain.length < 0) {
throw new IllegalArgumentException("Check server X509Certificate is empty");
}
//讀取本地預埋服務端證書獲取其publicKey與服務中攜帶的key比對
InputStream is_local = null;
try {
is_local = new BufferedInputStream(getAssets().open("替換爲自己的證書名稱.cer"));//提前把證書放入assets中
} catch (IOException e) {
e.printStackTrace();
LogUtils.e(e.getMessage());
}
X509Certificate serverCert = (X509Certificate) CertificateFactory.getInstance("X.509").generateCertificate(is_local);
for (X509Certificate certificate : chain) {
// 檢查證書是否過期,簽名是否通過等
certificate.checkValidity();
// 校驗服務端證書公鑰,與app預埋證書公鑰作比對
try {
LogUtils.i("publicKey==" + serverCert.getPublicKey());
certificate.verify(serverCert.getPublicKey());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
LogUtils.e(e.getMessage());
} catch (InvalidKeyException e) {
e.printStackTrace();
LogUtils.e(e.getMessage());
} catch (NoSuchProviderException e) {
e.printStackTrace();
LogUtils.e(e.getMessage());
} catch (SignatureException e) {
e.printStackTrace();
LogUtils.e(e.getMessage());
}
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}//自定義trustManager完成
// 創建TLS類型的SSLContext對象, that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new SafeTrustManager()}, null);
//下面以OkHttp設置https爲例,其他網絡框架一樣設置setSslSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslContext.getSocketFactory());
OkHttpClient okHttpClient = builder.build();
如此就實現了自定義的trustManager校驗規則,還是需要注意預埋證書是有有效期限制的,請慎用,一旦證書失效會導致服務請求不通——建議app添加版本更新接口,而且該接口不要使用強校驗的https請求,使用普通請求,如果證書失效至少能保證版本更新接口正常訪問便於用戶更新新的apk版本。
另外,配置完校驗規則,如果使用阿里聚安全等加固檢測網站檢測經常會報https hostname校驗的高危漏洞,所以最好再添加對服務域名的強校驗,而不是採用信任所有地址的方式(builder.hostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);//此寫法等於沒有做校驗)
下面是我的校驗方式:
/**
* 驗證服務端證書域名是否匹配——有可能與服務請求地址不一致
*/
private class SafeHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
//注意此處有可能與服務請求地址域名不一致,請於申請服務端證書的人員確認申請域名
LogUtils.d("校驗服務域名");
return hostname.equals("*ver.zp.com");
// return true;
}
}
//以OkHttp爲例設置方式——使用服務域名強校驗
builder.hostnameVerifier(SSLSocketFactory.STRICT_HOSTNAME_VERIFIER);
builder.hostnameVerifier(new SafeHostnameVerifier());
總結爲下:
客戶端實現https配置:添加自定義校驗規則、校驗服務域名
更多關於Android https的問題及HttpUrlConnection使用https方式 請移步:
http://blog.csdn.net/u011084603/article/details/73873677
關於webView中設置Https的方法可移步:http://www.cnblogs.com/liyiran/p/7011317.html
獲取證書兩種方法
- 服務器組直接給。如測試 12306 網站的時候,進入網頁,12306 會提供根證書的下載
- 通過網頁獲取。 Chrome 瀏覽器,按 F12 選擇 Security 選擇 Certificate Error 的 View
certificate 在彈出的證書,選擇詳細信息
在詳細信息頁面,點擊複製到文件,一路下一步,然後選擇保存證書的地方,就把證書成功導出來了。
證書的讀取
從 assert 中讀取文件
InputStream is = getAssets().open(“root.cer”);