Android Retrofit HTTPS 單向認證 雙向認證
阿新 • • 發佈:2018-10-31
由於最近要做一個安全性比較高的專案,因此需要用到HTTPS進行雙向認證。客戶端基於Retrofit + Rxjava+EventBus來實現
基於Retrofit實現HTTPS思路
由於Retrofit是基於OkHttp實現的,因此想通過Retrofit實現HTTPS需要給Retrofit設定一個OkHttp代理物件用於處理HTTPS的握手過程。代理程式碼如下:mOkHttpClient = new OkHttpClient.Builder() .hostnameVerifier(new UnSafeHostnameVerifier())//新增hostName驗證器 .sslSocketFactory(sslSocketFactory) .build(); Retrofit retrofit = new Retrofit.Builder() .addConverterFactory(ScalarsConverterFactory.create())//增加返回值為字串的支援 .baseUrl(BASE_URL)//主機地址 .client(mOkHttpClient)//注意這裡要給retrofit 設定okhttpclient .build(); private class UnSafeHostnameVerifier implements HostnameVerifier { @Override public boolean verify(String hostname, SSLSession session) { return true;//自行新增判斷邏輯,true->Safe,false->unsafe } }
上面程式碼可以看到使用Retrofit請求HTTPS主要使用SSLSocketFactory,所以我們只需構建sslSocketFactory。
在我的例子中,我有一個pem檔案,它包含一個證書和一個加密的私鑰,用於相互SSL身份驗證。 因此我的pem檔案如下所示:
-----BEGIN RSA PRIVATE KEY-----
......
-----END RSA PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
......
-----END CERTIFICATE-----
如果你得到的私鑰如上所示,那你還需要使用openssl執行PKCS8編碼:
openssl pkcs8 -topk8 -in rsa_private_key.pem -out pkcs8_rsa_private_key.pem -nocrypt
得到如下私鑰
-----BEGIN PRIVATE KEY-----
......
-----END PRIVATE KEY-----
進入程式碼正文
建立一個新的java金鑰儲存並匯入私鑰和證書:KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); ks.load(null, keypass.toCharArray()); ks.store(new FileOutputStream(getExternalFilesDir(null) + "/xxx.keystore"), keypass.toCharArray()); ks.load(new FileInputStream(getExternalFilesDir(null) + "/xxx.keystore"), keypass.toCharArray()); //讀取證書檔案 FileInputStream fileInputStream = new FileInputStream(file); byte[] bytes = new byte[fileInputStream.available()]; fileInputStream.read(bytes); //轉化為字串 String str = new String(bytes); //提取出私鑰和證書 String[] s = str.replace("-----BEGIN PRIVATE KEY-----\n", "").split("\n-----END PRIVATE KEY-----\n"); // 獲取指定目錄下的證書 Certificate crt1 = CertificateFactory.getInstance("X509").generateCertificate(new FileInputStream(file)); fileInputStream.close(); byte[] bytes1 = Base64.decode(s[0], Base64.DEFAULT); //主要程式碼 KeyFactory kf = KeyFactory.getInstance("RSA"); PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec(bytes1); PrivateKey ff = kf.generatePrivate(keysp); //證書鏈 Certificate[] chain = new Certificate[]{crt1}; String mainInformation = ((X509Certificate) crt1).getSubjectX500Principal().getName(); //獲取別名 String alias = mainInformation.substring((mainInformation.indexOf("=") + 1), mainInformation.indexOf(",")); //組合,並新增證書到KeyStore ks.setKeyEntry(alias , ff, keypass.toCharArray(), chain);
//祕鑰管理器
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(ks, keypass.toCharArray());
KeyManager[] keyManagers = kmf.getKeyManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, null, null);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
由於我公司伺服器證書是ca認證的所以不需要驗證
TrustManagerFactory如果使用的是p12格式證書則只能把證書當作檔案的形式讀取:
try {
//建立KeyStore
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore .load(getResources().openRawResource(R.raw.certificate_with_key_6), "U3T2C24RW8".toCharArray());
//祕鑰管理器
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(ks, keypass.toCharArray());
KeyManager[] keyManagers = kmf.getKeyManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, null, null);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
} catch (Exception e) {
e.printStackTrace();
Log.e("MainActivity", "-------錯誤111------" + e.toString());
}
到此基本程式碼已完結,歡迎提出意見:
轉載請註明出處: http://blog.csdn.net/q714093365/article/details/73123585