1. 程式人生 > >Android Retrofit HTTPS 單向認證 雙向認證

Android Retrofit HTTPS 單向認證 雙向認證

由於最近要做一個安全性比較高的專案,因此需要用到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