1. 程式人生 > >Android 加密傳輸(SSL),雙向認證 筆記

Android 加密傳輸(SSL),雙向認證 筆記

工作需要使用到 Android  加密傳輸(SSL),雙向認證 ,因為未使用過,所以搜尋了總結了一下相關知識,並整理作為筆記

參考部落格 :Retrofit 2.0 詳解(二)載入https請求(轉)

                   Android實現ssl雙向驗證

搜尋的時候關於SSL+http 的解釋有很多,不做細說,本文只記錄如何實現,包括證書製作、myeclipse服務部署以及Android端程式碼。

證書製作

開啟cmd命令視窗

1、生成客戶端keystore

keytool -genkeypair -alias E:\sslhttpTest\keystool\client -keyalg RSA -validity 3650 -keypass 123456 -storepass 123456 -keystore E:\sslhttpTest\keystool\client.jks

注:此處 E:\sslhttpTest\keystool\client  是本人手誤 只需輸入 client 即可,alias 無需路徑

2、生成服務端keystore

keytool -genkeypair -alias server -keyalg RSA -validity 3650 -keypass 123456 -storepass 123456 -keystore E:\sslhttpTest\keystool\server.keystore

3、匯出客戶端證書

keytool -export -alias E:\sslhttpTest\keystool\client -file E:\sslhttpTest\keystool\client.cer -keystore E:\sslhttpTest\keystool\client.jks -storepass 123456

4、匯出服務端證書

keytool -export -alias server -file E:\sslhttpTest\keystool\server.cer -keystore E:\sslhttpTest\keystool\server.keystore -storepass 123456

5、重點:證書交換

將客戶端證書匯入服務端keystore中,再將服務端證書匯入客戶端keystore中, 一個keystore可以匯入多個證書,生成證書列表。

生成客戶端信任證書庫(由服務端證書生成的證書庫):

keytool -import -v -alias server -file E:\sslhttpTest\keystool\server.cer -keystore E:\sslhttpTest\keystool\truststore_s_for_c.jks -storepass 123456

將客戶端證書匯入到伺服器證書庫(使得伺服器信任客戶端證書):

keytool -import -v -alias E:\sslhttpTest\keystool\client -file E:\sslhttpTest\keystool\client.cer -keystore E:\sslhttpTest\keystool\client_for_server.keystore -storepass 123456

6、生成Android識別的BKS庫檔案

下載地址: protecle.jar 百度網盤下載地址

執行protecle.jar將client.jkstruststore_s_for_c.jks分別轉換成client.bkstruststore_s_for_c.bks ,然後放到android客戶端的assert目錄下

File -> open Keystore File -> 選擇證書庫檔案 -> 輸入密碼 -> Tools -> change keystore type -> BKS -> save keystore as -> 儲存即可

7、配置Tomcat伺服器(可選)

找到Tomcat安裝目錄 conf\server.xml檔案,配置8443埠

<Connector
        SSLEnabled="true"
        acceptCount="100"
        clientAuth="true"
        disableUploadTimeout="true"
        enableLookups="true"
        keystoreFile="E:/sslhttpTest/keystool/server.keystore"
        keystorePass="123456"
        maxSpareThreads="75"
        maxThreads="200"
        minSpareThreads="5"
        port="8443"
        protocol="org.apache.coyote.http11.Http11NioProtocol"
        scheme="https"
        secure="true"
        sslProtocol="TLS"
        truststoreFile="E:/sslhttpTest/keystool/client_for_server.keystore"
        truststorePass="123456"/>

注:

可以不配置tomcat ,只需配置 eclipse 的工程目錄中的對應的server.xml檔案 E:\eclipsework\.metadata\.me_tcat7\conf\server.xml(我的工程目錄在E:\eclipsework 根據實際情況找路徑) 。如果 eclipsework\.metadata\.me_tcat7 的目錄下沒有 conf 目錄,需要新建conf資料夾然後將 tomcat 安裝目錄 conf\server.xml檔案複製過來,然後啟動myeclipse的服務,會報錯 根據報錯刪除

<Listener className="org.apache.catalina.startup.VersionLoggerListener" />

即可,配置 server.xml 的埠

<Connector
        SSLEnabled="true"
        acceptCount="100"
        clientAuth="true"
        disableUploadTimeout="true"
        enableLookups="true"
        keystoreFile="E:/sslhttpTest/keystool/server.keystore"
        keystorePass="123456"
        maxSpareThreads="75"
        maxThreads="200"
        minSpareThreads="5"
        port="8443"
        protocol="org.apache.coyote.http11.Http11NioProtocol"
        scheme="https"
        secure="true"
        sslProtocol="TLS"
        truststoreFile="E:/sslhttpTest/keystool/client_for_server.keystore"
        truststorePass="123456"/>

Android 端配置

        SSLHelper

import android.content.Context;

import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;

public class SSLHelper {
    private static final String TAG = "SSLHelper";
    private final static String CLIENT_PRI_KEY = "client";
    private final static String TRUSTSTORE_PUB_KEY = "truststore_s_for_c";
    private final static String CLIENT_BKS_PASSWORD = "123456";
    private final static String TRUSTSTORE_BKS_PASSWORD = "123456";
    private final static String KEYSTORE_TYPE = "BKS";
    private final static String PROTOCOL_TYPE = "TLS";
    private final static String CERTIFICATE_FORMAT = "X509";


    public static SSLSocketFactory getSSLCertifcation(Context context) {
        SSLSocketFactory sslSocketFactory = null;
        try { // 伺服器端需要驗證的客戶端證書,其實就是客戶端的keystore
            KeyStore keyStore = KeyStore.getInstance(KEYSTORE_TYPE);// 客戶端信任的伺服器端證書
            KeyStore trustStore = KeyStore.getInstance(KEYSTORE_TYPE);//讀取證書
            InputStream ksIn = context.getAssets().open(CLIENT_PRI_KEY);
            InputStream tsIn = context.getAssets().open(TRUSTSTORE_PUB_KEY);//載入證書
            keyStore.load(ksIn, CLIENT_BKS_PASSWORD.toCharArray());
            trustStore.load(tsIn, TRUSTSTORE_BKS_PASSWORD.toCharArray());
            ksIn.close();
            tsIn.close(); //初始化
            SSLContext sslContext = SSLContext.getInstance(PROTOCOL_TYPE);
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(CERTIFICATE_FORMAT);
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(CERTIFICATE_FORMAT);
            trustManagerFactory.init(trustStore);
            keyManagerFactory.init(keyStore, CLIENT_BKS_PASSWORD.toCharArray());
            sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
            sslSocketFactory = sslContext.getSocketFactory();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        return sslSocketFactory;
    }
}
UnSafeTrustManager
public class UnSafeTrustManager implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[]{};
        }
    }
Retrofit 初始化
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        //建立okhttp
        OkHttpClient httpClient = new OkHttpClient().newBuilder()
                .addInterceptor(interceptor)
                .retryOnConnectionFailure(true)
                .connectTimeout(30, TimeUnit.SECONDS)
                .sslSocketFactory(SSLHelper.getSSLCertifcation(context), new UnSafeTrustManager())
                .hostnameVerifier(new HttpsUtil.UnSafeHostnameVerifier())//由於還沒有域名,此處設定忽略掉域名校驗
                .build();

        retrofit=new Retrofit.Builder()
                .baseUrl(url)
                .client(httpClient)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();

 

所有程式碼可以在百度網盤下載

百度網盤連結:測試程式下載

密碼:on6o

目錄說明:keystool.zip 測試證書壓縮包

                  LoginSSL.zip 測試後臺壓縮包

                  SSLTest.zip 測試Android 壓縮包 ,使用時請替換 MainActivity中的 url 地址的 ip 修改為測試的IP地址