對于網(wǎng)絡(luò)安全,我們的重視程度遠(yuǎn)遠(yuǎn)及不上歐美等國家,部署Https的網(wǎng)站遠(yuǎn)遠(yuǎn)少于他們;但隨著互聯(lián)網(wǎng)不斷普及,國民對數(shù)據(jù)安全的需求也越來越高,Https逐漸在國內(nèi)普及開來。
最近公司項(xiàng)目也開始轉(zhuǎn)為Https,故簡單記錄一下Android Https的一些設(shè)置。
生成相關(guān)證書
- 從后臺那里拿到P12格式的證書文件,如:server.pfx;
- 生成服務(wù)器cer證書
安裝openssl
我采用的是直接下載安裝包,下載地址;也可以選擇下載源碼自己編譯openssl。
生成證書server.cer
安裝之后配置環(huán)境變量,用管理員權(quán)限打開或者使用win10的PowerShell,輸入
openssl pkcs12 -in server.pfx -out server.cer
然后輸入證書的密碼和PEM密碼,成功之后生成證書server.cer
生成客戶端信任證書庫client.truststore
由于安卓端的證書類型必須是BKS類型,需要這樣做:
- 下載這個jar:bcprov-ext-jdk15on-159.jar
- 將jar文件放在 Java 主目錄下的 jre/lib/ext目錄下
- 修改jre/lib/security/java.security這個文件:在List of providers 注釋的地方添加這一行
security.provider.11=org.bouncycastle.jce.provider.BouncyCastleProvider - 然后重啟終端,輸入下面代碼生成client.truststore,密碼自己改:
keytool -import -v -alias server -file server.cer -keystore client.truststore -storepass 123456 -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider
如果不添加jar文件,會報Class not found錯誤。
但是,某些情況下(大概是證書的格式或來源不符合要求)還是會報錯:所輸入的不是 X.509 證書
證書格式轉(zhuǎn)換工具XCA
- 下載安裝:地址
- 打開程序,點(diǎn)擊:File>New DataBase新建庫文件,選擇保存位置,輸入密碼,即成功創(chuàng)建庫。
- 導(dǎo)入證書server.cer:點(diǎn)擊:Import>Certificate,選擇之前生成的證書server.cer,導(dǎo)入。
-
導(dǎo)出p7b格式證書:
導(dǎo)出p7b格式證書1
選擇p7b格式,點(diǎn)擊ok導(dǎo)出xxxx.p7b的證書

-
打開xxxx.p7b證書文件,找到證書,右鍵>所有任務(wù)>導(dǎo)出
導(dǎo)出X.509格式證書 -
導(dǎo)出X.509格式證書,選擇之前server.cer文件覆蓋保存,確認(rèn)生成server.cer證書
導(dǎo)出X.509格式證書
7.最后,重新執(zhí)行命令,會提示是否信任此證書? [否]:,輸入是生成client.truststore:
keytool -import -v -alias server -file server.cer -keystore client.truststore -storepass 123456 -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider
Android端SSL認(rèn)證請求
我們需要兩個證書:
- server.pfx:客戶端證書,用于請求的時候給服務(wù)器來驗(yàn)證身份
- client.truststore:客戶端證書庫,用于驗(yàn)證服務(wù)器端身份,防止釣魚
SSLSocketFactory方式進(jìn)行SSL認(rèn)證
將兩個證書添加到android項(xiàng)目的assets目標(biāo)下面,建立SSL驗(yàn)證工具,代碼如下:
public class SSLHelper {
private static final String KEY_STORE_TYPE_BKS = "bks";
private static final String KEY_STORE_TYPE_P12 = "PKCS12";
public static final String KEY_STORE_CLIENT_PATH = "server.pfx";//P12文件
private static final String KEY_STORE_TRUST_PATH = "client.truststore";//truststore文件
public static final String KEY_STORE_PASSWORD = "123456";//P12文件密碼
private static final String KEY_STORE_TRUST_PASSWORD = "123456";//truststore文件密碼
public static SSLSocketFactory getSSLSocketFactory(Context context) {
SSLSocketFactory factory = null;
try {
// 服務(wù)器端需要驗(yàn)證的客戶端證書
KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_P12);
// 客戶端信任的服務(wù)器端證書
KeyStore trustStore = KeyStore.getInstance(KEY_STORE_TYPE_BKS);
InputStream ksIn = context.getResources().getAssets()
.open(KEY_STORE_CLIENT_PATH);
InputStream tsIn = context.getResources().getAssets()
.open(KEY_STORE_TRUST_PATH);
try {
keyStore.load(ksIn, KEY_STORE_PASSWORD.toCharArray());
trustStore.load(tsIn, KEY_STORE_TRUST_PASSWORD.toCharArray());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
ksIn.close();
} catch (Exception e) {
e.printStackTrace();
}
try {
tsIn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
//信任管理器
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStore);
//密鑰管理器
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("X509");
keyManagerFactory.init(keyStore, KEY_STORE_PASSWORD.toCharArray());
//初始化SSLContext
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(),
trustManagerFactory.getTrustManagers(), null);
factory = sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
}
return factory;
}
}
OkHttpClient配置
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(SSLHelper.getSSLSocketFactory(getApplication()))
主機(jī)名驗(yàn)證
如果出現(xiàn)主機(jī)名驗(yàn)證錯誤hostname error,需要添加hostnameVerifier:
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(SSLHelper.getSSLSocketFactory(getApplication()))
.hostnameVerifier(new UnSafeHostnameVerifier());
public class UnSafeHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
至此,Https雙向驗(yàn)證的配置全部完成了。
本文基于筆者項(xiàng)目實(shí)踐,關(guān)于SSL認(rèn)證筆者還沒有了解透徹,如有錯誤,請多多指出。


