Android使用OkHttp請(qǐng)求自簽名的https網(wǎng)站的示例
前言
很多公司考慮到安全問(wèn)題,項(xiàng)目中都采用https加密協(xié)議進(jìn)行數(shù)據(jù)傳輸。但是一些公司又不想花一筆錢去CA申請(qǐng)證書,所以就采用自簽名的證書。
OkHttp默認(rèn)是可以訪問(wèn)通過(guò)CA認(rèn)證的HTTPS鏈接,例如百度首頁(yè)也是https鏈接(https://www.baidu.com/)。但是如果是你們公司自簽名(即自己用keytool生成的證書,而不是采用通過(guò)CA認(rèn)證的證書)的服務(wù)器,OkHttp是無(wú)法訪問(wèn)的,例如訪問(wèn)12306網(wǎng)站(https://kyfw.12306.cn/otn/),會(huì)報(bào)如下錯(cuò)誤:

HTTPS的工作原理
HTTPS在傳輸數(shù)據(jù)之前需要客戶端(瀏覽器)與服務(wù)端(網(wǎng)站)之間進(jìn)行一次握手,在握手過(guò)程中將確立雙方加密傳輸數(shù)據(jù)的密碼信息。握手過(guò)程的簡(jiǎn)單描述如下:
- 瀏覽器將自己支持的一套加密算法、HASH算法發(fā)送給網(wǎng)站。
- 網(wǎng)站從中選出一組加密算法與HASH算法,并將自己的身份信息以證書的形式發(fā)回給瀏覽器。證書里面包含了網(wǎng)站地址,加密公鑰,以及證書的頒發(fā)機(jī)構(gòu)等信息。
- 瀏覽器獲得網(wǎng)站證書之后,開(kāi)始驗(yàn)證證書的合法性,如果證書信任,則生成一串隨機(jī)數(shù)字作為通訊過(guò)程中對(duì)稱加密的秘鑰。然后取出證書中的公鑰,將這串?dāng)?shù)字以及HASH的結(jié)果進(jìn)行加密,然后發(fā)給網(wǎng)站。
- 網(wǎng)站接收瀏覽器發(fā)來(lái)的數(shù)據(jù)之后,通過(guò)私鑰進(jìn)行解密,然后HASH校驗(yàn),如果一致,則使用瀏覽器發(fā)來(lái)的數(shù)字串使加密一段握手消息發(fā)給瀏覽器。
- 瀏覽器解密,并HASH校驗(yàn),沒(méi)有問(wèn)題,則握手結(jié)束。接下來(lái)的傳輸過(guò)程將由之前瀏覽器生成的隨機(jī)密碼并利用對(duì)稱加密算法進(jìn)行加密。
握手過(guò)程中如果有任何錯(cuò)誤,都會(huì)使加密連接斷開(kāi),從而阻止了隱私信息的傳輸。
使用OKHTTP請(qǐng)求自簽名的https服務(wù)器數(shù)據(jù)
以下我們使用12306網(wǎng)站為例
1. 首先去12306網(wǎng)站首頁(yè)下載證書 http://www.12306.cn/

2. 將下載的證書srca.cer放到工程的assets文件夾下。

3. 添加HTTPS工具類
package com.alpha58.okhttp;
import android.content.Context;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
import java.util.Collection;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.OkHttpClient;
/**
* Created by admin on 2017/03/12.
*/
public final class HTTPSUtils {
private OkHttpClient client;
public Context mContext;
/**
* 獲取OkHttpClient實(shí)例
* @return
*/
public OkHttpClient getInstance()
{
return client;
}
/**
* 初始化HTTPS,添加信任證書
* @param context
*/
public HTTPSUtils(Context context) {
mContext = context;
X509TrustManager trustManager;
SSLSocketFactory sslSocketFactory;
final InputStream inputStream;
try {
inputStream = mContext.getAssets().open("srca.cer"); // 得到證書的輸入流
try {
trustManager = trustManagerForCertificates(inputStream);//以流的方式讀入證書
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{trustManager}, null);
sslSocketFactory = sslContext.getSocketFactory();
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
client = new OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustManager)
.build();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 以流的方式添加信任證書
*/
/**
* Returns a trust manager that trusts {@code certificates} and none other. HTTPS services whose
* certificates have not been signed by these certificates will fail with a {@code
* SSLHandshakeException}.
* <p>
* <p>This can be used to replace the host platform's built-in trusted certificates with a custom
* set. This is useful in development where certificate authority-trusted certificates aren't
* available. Or in production, to avoid reliance on third-party certificate authorities.
* <p>
* <p>
* <h3>Warning: Customizing Trusted Certificates is Dangerous!</h3>
* <p>
* <p>Relying on your own trusted certificates limits your server team's ability to update their
* TLS certificates. By installing a specific set of trusted certificates, you take on additional
* operational complexity and limit your ability to migrate between certificate authorities. Do
* not use custom trusted certificates in production without the blessing of your server's TLS
* administrator.
*/
private X509TrustManager trustManagerForCertificates(InputStream in)
throws GeneralSecurityException {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in);
if (certificates.isEmpty()) {
throw new IllegalArgumentException("expected non-empty set of trusted certificates");
}
// Put the certificates a key store.
char[] password = "password".toCharArray(); // Any password will work.
KeyStore keyStore = newEmptyKeyStore(password);
int index = 0;
for (Certificate certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificate);
}
// Use it to build an X509 trust manager.
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(
KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, password);
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
return (X509TrustManager) trustManagers[0];
}
/**
* 添加password
* @param password
* @return
* @throws GeneralSecurityException
*/
private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
try {
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); // 這里添加自定義的密碼,默認(rèn)
InputStream in = null; // By convention, 'null' creates an empty key store.
keyStore.load(in, password);
return keyStore;
} catch (IOException e) {
throw new AssertionError(e);
}
}
}
4.代碼中請(qǐng)求
public void getHttpsHtml(View view) {
Request request = new Request.Builder()
.url("https://kyfw.12306.cn/otn/")
.build();
HTTPSUtils httpsUtils = new HTTPSUtils(this);
httpsUtils.getInstance().newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
System.out.println("--------------onFailure--------------" + e.toString());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
System.out.println("--------------onResponse--------------" + response.body().string());
}
});
}
5. 最后能打印出這些信息就說(shuō)明請(qǐng)求成功啦!

注意:別忘了加權(quán)限和依賴okhttp庫(kù)
Demo地址:https://github.com/Alpha58/okhttps
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android中使用OkHttp包處理HTTP的get和post請(qǐng)求的方法
- Android使用OkHttp發(fā)送post請(qǐng)求
- Android小知識(shí)之OkHttp的2種請(qǐng)求方式詳解
- Android Okhttp請(qǐng)求查詢購(gòu)物車的實(shí)例代碼
- Android基于OkHttpUtils網(wǎng)絡(luò)請(qǐng)求的二次封裝
- 詳解Android中使用OkHttp發(fā)送HTTP的post請(qǐng)求的方法
- Android M(6.x)使用OkHttp包解析和發(fā)送JSON請(qǐng)求的教程
- 詳解Android的OkHttp包編寫異步HTTP請(qǐng)求調(diào)用的方法
- android實(shí)現(xiàn)okHttp的get和post請(qǐng)求的簡(jiǎn)單封裝與使用
相關(guān)文章
Android開(kāi)發(fā)使用Messenger及Handler進(jìn)行通信的方法示例
這篇文章主要介紹了Android開(kāi)發(fā)使用Messenger及Handler進(jìn)行通信的方法,結(jié)合實(shí)例形式分析了Android使用Messenger及Handler定義客戶端與服務(wù)器端實(shí)現(xiàn)通信的相關(guān)操作技巧,需要的朋友可以參考下2017-12-12
利用kotlin實(shí)現(xiàn)一個(gè)餅圖實(shí)例代碼
餅狀圖是以不同顏色的圓的切片表示的值。下面這篇文章主要給大家介紹了關(guān)于利用kotlin實(shí)現(xiàn)一個(gè)餅圖的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-12-12
Flutter?Widget?之package?mason實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了Flutter?Widget?之package:?mason實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
Android編程實(shí)現(xiàn)popupwindow定時(shí)消失的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)popupwindow定時(shí)消失的方法,結(jié)合實(shí)例形式分析了Android使用定時(shí)器實(shí)現(xiàn)popupwindow定時(shí)消失的相關(guān)操作技巧,需要的朋友可以參考下2018-01-01
Android工程:引用另一個(gè)Android工程的方法詳解
本篇文章是對(duì)在Android中引用另一個(gè)Android工程的方法進(jìn)行了詳細(xì)的分析介紹。需要的朋友參考下2013-05-05
Android Recyclerview實(shí)現(xiàn)上拉加載更多功能
在項(xiàng)目中使用列表的下拉刷新和上拉加載更多是很常見(jiàn)的功能。下文給大家?guī)?lái)了Android Recyclerview上拉加載更多功能,需要的朋友參考下吧2017-05-05
Android中封裝RecyclerView實(shí)現(xiàn)添加頭部和底部示例代碼
這篇文章主要給大家介紹了關(guān)于Android中封裝RecyclerView實(shí)現(xiàn)添加頭部和底部的相關(guān)資料,網(wǎng)上這方面的資料很多,但都不是自己需要的,索性自己寫一個(gè)分享出來(lái)供大家參考學(xué)習(xí),需要的朋友們下面隨著小編一起來(lái)學(xué)習(xí)學(xué)習(xí)吧。2017-08-08

