對于網絡安全锯岖,我們的重視程度遠遠及不上歐美等國家茅茂,部署Https的網站遠遠少于他們;但隨著互聯網不斷普及,國民對數據安全的需求也越來越高余黎,Https逐漸在國內普及開來。
最近公司項目也開始轉為Https赖临,故簡單記錄一下Android Https的一些設置鲤脏。
生成相關證書
- 從后臺那里拿到P12格式的證書文件,如:server.pfx;
- 生成服務器cer證書
安裝openssl
我采用的是直接下載安裝包厨姚,下載地址衅澈;也可以選擇下載源碼自己編譯openssl。
生成證書server.cer
安裝之后配置環(huá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 證書
證書格式轉換工具XCA
- 下載安裝:地址
- 打開程序,點擊:File>New DataBase新建庫文件份蝴,選擇保存位置犁功,輸入密碼,即成功創(chuàng)建庫婚夫。
- 導入證書server.cer:點擊:Import>Certificate浸卦,選擇之前生成的證書server.cer,導入请敦。
-
導出p7b格式證書:
選擇p7b格式镐躲,點擊ok導出xxxx.p7b的證書
-
打開xxxx.p7b證書文件,找到證書侍筛,右鍵>所有任務>導出
-
導出X.509格式證書萤皂,選擇之前server.cer文件覆蓋保存,確認生成server.cer證書
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認證請求
我們需要兩個證書:
- server.pfx:客戶端證書禽笑,用于請求的時候給服務器來驗證身份
- client.truststore:客戶端證書庫入录,用于驗證服務器端身份,防止釣魚
SSLSocketFactory方式進行SSL認證
將兩個證書添加到android項目的assets目標下面佳镜,建立SSL驗證工具僚稿,代碼如下:
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 {
// 服務器端需要驗證的客戶端證書
KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_P12);
// 客戶端信任的服務器端證書
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()))
主機名驗證
如果出現主機名驗證錯誤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雙向驗證的配置全部完成了蚀同。
本文基于筆者項目實踐,關于SSL認證筆者還沒有了解透徹啊掏,如有錯誤蠢络,請多多指出。