先說明一下酬蹋,我的是讀取本地資源目錄的證書及老,不是繞過驗(yàn)證抽莱,也不是校驗(yàn)服務(wù)端證書。
這是一個(gè)很簡單的功能骄恶,確實(shí)食铐。 但是沒搞定之前還是難受得丫批。 百度上僧鲁,簡書上的方法都搜過了虐呻,用過了,沒有用寞秃。最后發(fā)現(xiàn)是自己不會(huì)用(我就知道是這樣子的斟叼!mmp) 寫一篇文章記錄一下。希望能夠幫到(估計(jì)也沒有人和我一樣弱雞了)春寿。
正題開始
1.因?yàn)槲覀兊腍5朗涩,經(jīng)常被插入小廣告,于是就提議要搞HTTPS绑改,然后申請去了幾萬谢床,證書下來了(運(yùn)維老哥給到我們手里)
然后我看了網(wǎng)上的帖子后綴不一樣,我又屁顛屁顛的跑去問了一下厘线。 老哥給我解釋了一下crt的全稱识腿,然后知道了有人叫crt,有人叫cer造壮。然后我把證書復(fù)制到assets目錄
2.然后我開始了雙屏CV操作渡讼,發(fā)現(xiàn)復(fù)制過來不能用。然后我朋友建議我去看看OkGo的項(xiàng)目耳璧。
下載下來看了一下硝全,廖大神有寫各種HTTPS的請求方式。真的很全了楞抡! 我用的是方法三伟众。
3.然后我直接就是把他的這個(gè)HttpsUtils方法復(fù)制出來了,然后在我自己的請求封裝那里加上召廷。
代碼如下:
Tips:之前有些帖子的證書是放在RAW文件夾里凳厢。然后這個(gè)地方腦殘了一下,沒注意getAssets這個(gè)方法竞慢。
然后到這里就結(jié)束了先紫。。筹煮。遮精。吧? 我都打開網(wǎng)易云放歌了! 然后這個(gè)時(shí)候APP跑上來本冲,請求出錯(cuò)准脂。。等等檬洞。狸膏。
看看Logcat這個(gè)東西吧! 請求的地址還是http添怔。
這時(shí)候想到了點(diǎn)什么湾戳,加證書并不會(huì)自動(dòng)給你加S,然后去請求的广料。
乖乖的把請求地址加上S砾脑。~
放歌吧!
放歌請點(diǎn)擊:Time
最后:廖大神的那個(gè)“優(yōu)踢而絲”我放上來吧~
**/****
* ================================================
* 作 者:jeasonlzy(廖子堯)Github地址:https://github.com/jeasonlzy
* 版 本:1.0
* 創(chuàng)建日期:16/9/11
* 描 述:Https相關(guān)的工具類
* 修訂歷史:
* ================================================
*/
public class HttpsUtils {
public static class SSLParams {
public SSLSocketFactorysSLSocketFactory;
public X509TrustManagertrustManager;
}
public static SSLParamsgetSslSocketFactory() {
return getSslSocketFactoryBase(null, null, null);
}
/**
* https單向認(rèn)證
* 可以額外配置信任服務(wù)端的證書策略艾杏,否則默認(rèn)是按CA證書去驗(yàn)證的拦止,若不是CA可信任的證書,則無法通過驗(yàn)證
*/
public static SSLParamsgetSslSocketFactory(X509TrustManager trustManager) {
return getSslSocketFactoryBase(trustManager, null, null);
}
/**
* https單向認(rèn)證
* 用含有服務(wù)端公鑰的證書校驗(yàn)服務(wù)端證書
*/
public static SSLParamsgetSslSocketFactory(InputStream... certificates) {
return getSslSocketFactoryBase(null, null, null, certificates);
}
/**
* https雙向認(rèn)證
* bksFile 和 password -> 客戶端使用bks證書校驗(yàn)服務(wù)端證書
* certificates -> 用含有服務(wù)端公鑰的證書校驗(yàn)服務(wù)端證書
*/
public static SSLParamsgetSslSocketFactory(InputStream bksFile, String password, InputStream... certificates) {
return getSslSocketFactoryBase(null, bksFile, password, certificates);
}
/**
* https雙向認(rèn)證
* bksFile 和 password -> 客戶端使用bks證書校驗(yàn)服務(wù)端證書
* X509TrustManager -> 如果需要自己校驗(yàn)糜颠,那么可以自己實(shí)現(xiàn)相關(guān)校驗(yàn)汹族,如果不需要自己校驗(yàn),那么傳null即可
*/
public static SSLParamsgetSslSocketFactory(InputStream bksFile, String password, X509TrustManager trustManager) {
return getSslSocketFactoryBase(trustManager, bksFile, password);
}
private static SSLParamsgetSslSocketFactoryBase(X509TrustManager trustManager, InputStream bksFile, String password, InputStream... certificates) {
SSLParams sslParams =new SSLParams();
try {
KeyManager[] keyManagers =prepareKeyManager(bksFile, password);
TrustManager[] trustManagers =prepareTrustManager(certificates);
X509TrustManager manager;
if (trustManager !=null) {
//優(yōu)先使用用戶自定義的TrustManager
manager = trustManager;
}else if (trustManagers !=null) {
//然后使用默認(rèn)的TrustManager
manager =chooseTrustManager(trustManagers);
}else {
//否則使用不安全的TrustManager
manager =UnSafeTrustManager;
}
// 創(chuàng)建TLS類型的SSLContext對象其兴, that uses our TrustManager
SSLContext sslContext = SSLContext.getInstance("TLS");
// 用上面得到的trustManagers初始化SSLContext顶瞒,這樣sslContext就會(huì)信任keyStore中的證書
// 第一個(gè)參數(shù)是授權(quán)的密鑰管理器,用來授權(quán)驗(yàn)證元旬,比如授權(quán)自簽名的證書驗(yàn)證榴徐。第二個(gè)是被授權(quán)的證書管理器,用來驗(yàn)證服務(wù)器端的證書
sslContext.init(keyManagers, new TrustManager[]{manager}, null);
// 通過sslContext獲取SSLSocketFactory對象
sslParams.sSLSocketFactory = sslContext.getSocketFactory();
sslParams.trustManager = manager;
return sslParams;
}catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}catch (KeyManagementException e) {
throw new AssertionError(e);
}
}
private static KeyManager[]prepareKeyManager(InputStream bksFile, String password) {
try {
if (bksFile ==null || password ==null)return null;
KeyStore clientKeyStore = KeyStore.getInstance("BKS");
clientKeyStore.load(bksFile, password.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(clientKeyStore, password.toCharArray());
return kmf.getKeyManagers();
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static TrustManager[]prepareTrustManager(InputStream... certificates) {
if (certificates ==null || certificates.length <=0)return null;
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
// 創(chuàng)建一個(gè)默認(rèn)類型的KeyStore匀归,存儲(chǔ)我們信任的證書
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index =0;
for (InputStream certStream : certificates) {
String certificateAlias = Integer.toString(index++);
// 證書工廠根據(jù)證書文件的流生成證書 cert
Certificate cert = certificateFactory.generateCertificate(certStream);
// 將 cert 作為可信證書放入到keyStore中
keyStore.setCertificateEntry(certificateAlias, cert);
try {
if (certStream !=null) certStream.close();
}catch (IOException e) {
e.printStackTrace();
}
}
//我們創(chuàng)建一個(gè)默認(rèn)類型的TrustManagerFactory
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
//用我們之前的keyStore實(shí)例初始化TrustManagerFactory坑资,這樣tmf就會(huì)信任keyStore中的證書
tmf.init(keyStore);
//通過tmf獲取TrustManager數(shù)組,TrustManager也會(huì)信任keyStore中的證書
return tmf.getTrustManagers();
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static X509TrustManagerchooseTrustManager(TrustManager[] trustManagers) {
for (TrustManager trustManager : trustManagers) {
if (trustManagerinstanceof X509TrustManager) {
return (X509TrustManager) trustManager;
}
}
return null;
}
/**
* 為了解決客戶端不信任服務(wù)器數(shù)字證書的問題穆端,網(wǎng)絡(luò)上大部分的解決方案都是讓客戶端不對證書做任何檢查袱贮,
* 這是一種有很大安全漏洞的辦法
*/
public static X509TrustManagerUnSafeTrustManager =new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)throws CertificateException {
}
@Override
public X509Certificate[]getAcceptedIssuers() {
return new X509Certificate[]{};
}
};
/**
* 此類是用于主機(jī)名驗(yàn)證的基接口。 在握手期間体啰,如果 URL 的主機(jī)名和服務(wù)器的標(biāo)識(shí)主機(jī)名不匹配攒巍,
* 則驗(yàn)證機(jī)制可以回調(diào)此接口的實(shí)現(xiàn)程序來確定是否應(yīng)該允許此連接。策略可以是基于證書的或依賴于其他驗(yàn)證方案荒勇。
* 當(dāng)驗(yàn)證 URL 主機(jī)名使用的默認(rèn)規(guī)則失敗時(shí)使用這些回調(diào)柒莉。如果主機(jī)名是可接受的,則返回 true
*/
public static HostnameVerifierUnSafeHostnameVerifier =new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
}