1. 程式人生 > >Android實現ssl雙向驗證

Android實現ssl雙向驗證

Android實現雙向驗證

前言

  • Android端實現雙向認證的難點主要在於Android接受的證書格式是BKS,而一般提供的證書不包含此格式,需要手動轉換
  • 轉換之後如果請求不了,表現為握手失敗(Handshake failed),則一般是證書轉換錯誤
  • 網上有一大堆轉換方法,有的雖能正常生成BKS證書,但還是導致握手失敗,推測應該還是轉換方式有誤
  • 在這裡提供一種自己親自試驗過能正常轉換的方法供大家參考

證書轉換

使用轉換工具

  • 在這裡提供一個比較好用的轉換工具Portecle
  • 下載完後解壓雙擊start.bat即可使用,至於詳細使用請自行百度
  • 使用此工具可以很方便地將jks轉為bks

通過openssl和keytool轉換

  • openssl可以到官網下載,openssl暫時沒有windows版,大夥可以在此處下載Win32OpenSSLwindows版本
  • 下載完記得配置環境變數,同時也記得配置jdk的環境變數,因為後面需要用到keytool命令
  • 現在假設你拿到了客戶端證書client.crt和client.key(或者client.pem和client.key)
  • 通過以下命令生成pfx檔案,期間會讓輸入密碼,輸入你想要的密碼即可,假如這裡輸入的是123456

    openssl pkcs12 -export -out client.pfx -inkey client.key -in client.pem
    或者
    openssl pkcs12 -export -out client.pfx -inkey client.key -in client.crt

  • 執行成功則可以看到client.pfx檔案,接著再執行如下命令

    keytool -importkeystore -srckeystore client.pfx -destkeystore client.jks -srcstoretype PKCS12 -deststoretype JKS

  • 執行成功即可看到生成了client.jks
  • 拿到生成的jks就可以通過Portecle工具轉換成bks了

程式碼實現

SSLHelper.java

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.bks"; private final static String TRUSTSTORE_PUB_KEY = "ca.bks"; 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_STANDARD = "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(); TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(CERTIFICATE_STANDARD); KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(CERTIFICATE_STANDARD); trustManagerFactory.init(trustStore); keyManagerFactory.init(keyStore, CLIENT_BKS_PASSWORD.toCharArray()); //初始化SSLContext SSLContext sslContext = SSLContext.getInstance(PROTOCOL_TYPE); sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new java.security.SecureRandom()); 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; } }
  • HttpsUtil.java
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 java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
public class HttpsUtil {  

    public static class SSLParams  
    {  
        public SSLSocketFactory sSLSocketFactory;  
        public X509TrustManager trustManager;  
    }  

    public static SSLParams getSslSocketFactory(InputStream[] certificates, InputStream bksFile, String password)  
    {  
        SSLParams sslParams = new SSLParams();  
        try  
        {  
            TrustManager[] trustManagers = prepareTrustManager(certificates);  
            KeyManager[] keyManagers = prepareKeyManager(bksFile, password);  
            SSLContext sslContext = SSLContext.getInstance("TLS");  
            X509TrustManager trustManager = null;  
            if (trustManagers != null)  
            {  
                trustManager = new MyTrustManager(chooseTrustManager(trustManagers));  
            } else  
            {  
                trustManager = new UnSafeTrustManager();  
            }  
            sslContext.init(keyManagers, new TrustManager[]{trustManager},null);  
            sslParams.sSLSocketFactory = sslContext.getSocketFactory();  
            sslParams.trustManager = trustManager;  
            return sslParams;  
        } catch (NoSuchAlgorithmException e)  
        {  
            throw new AssertionError(e);  
        } catch (KeyManagementException e)  
        {  
            throw new AssertionError(e);  
        } catch (KeyStoreException e)  
        {  
            throw new AssertionError(e);  
        }  
    }

    public static class  UnSafeHostnameVerifier implements HostnameVerifier
    {  
        @Override  
        public boolean verify(String hostname, SSLSession session)  
        {  
            return true;  
        }  
    }  

    public static 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[]{};
        }  
    }  

    private static TrustManager[] prepareTrustManager(InputStream... certificates)  
    {  
        if (certificates == null || certificates.length <= 0) return null;  
        try  
        {  

            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");  
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());  
            keyStore.load(null);  
            int index = 0;  
            for (InputStream certificate : certificates)  
            {  
                String certificateAlias = Integer.toString(index++);  
                keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));  
                try  
                {  
                    if (certificate != null)  
                        certificate.close();  
                } catch (IOException e)  

                {  
                }  
            }  
            TrustManagerFactory trustManagerFactory = null;  

            trustManagerFactory = TrustManagerFactory.  
                    getInstance(TrustManagerFactory.getDefaultAlgorithm());  
            trustManagerFactory.init(keyStore);  

            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();  

            return trustManagers;  
        } catch (NoSuchAlgorithmException e)  
        {  
            e.printStackTrace();  
        } catch (CertificateException e)  
        {  
            e.printStackTrace();  
        } catch (KeyStoreException e)  
        {  
            e.printStackTrace();  
        } catch (Exception e)  
        {  
            e.printStackTrace();  
        }  
        return null;  

    }  

    private static KeyManager[] prepareKeyManager(InputStream bksFile, String password)  
    {  
        try  
        {  
            if (bksFile == null || password == null) return null;  

            KeyStore clientKeyStore = KeyStore.getInstance("BKS");  
            clientKeyStore.load(bksFile, password.toCharArray());  
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());  
            keyManagerFactory.init(clientKeyStore, password.toCharArray());  
            return keyManagerFactory.getKeyManagers();  

        } catch (KeyStoreException e)  
        {  
            e.printStackTrace();  
        } catch (NoSuchAlgorithmException e)  
        {  
            e.printStackTrace();  
        } catch (UnrecoverableKeyException e)  
        {  
            e.printStackTrace();  
        } catch (CertificateException e)  
        {  
            e.printStackTrace();  
        } catch (IOException e)  
        {  
            e.printStackTrace();  
        } catch (Exception e)  
        {  
            e.printStackTrace();  
        }  
        return null;  
    }  

    private static X509TrustManager chooseTrustManager(TrustManager[] trustManagers)  
    {  
        for (TrustManager trustManager : trustManagers)  
        {  
            if (trustManager instanceof X509TrustManager)  
            {  
                return (X509TrustManager) trustManager;  
            }  
        }  
        return null;  
    }  


    private static class MyTrustManager implements X509TrustManager  
    {  
        private X509TrustManager defaultTrustManager;  
        private X509TrustManager localTrustManager;  

        public MyTrustManager(X509TrustManager localTrustManager) throws NoSuchAlgorithmException, KeyStoreException  
        {  
            TrustManagerFactory var4 = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());  
            var4.init((KeyStore) null);  
            defaultTrustManager = chooseTrustManager(var4.getTrustManagers());  
            this.localTrustManager = localTrustManager;  
        }  


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

        }  

        @Override  
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException  
        {  
            try  
            {  
                defaultTrustManager.checkServerTrusted(chain, authType);  
            } catch (CertificateException ce)  
            {  
                localTrustManager.checkServerTrusted(chain, authType);  
            }  
        }  


        @Override  
        public X509Certificate[] getAcceptedIssuers()  
        {  
            return new X509Certificate[0];  
        }  
    }  

}  
  • retrofit呼叫
        //建立日誌攔截器,用於日誌列印
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        //建立okhttp
        OkHttpClient httpClient = new OkHttpClient().newBuilder()
                .addInterceptor(interceptor)
                .sslSocketFactory(SSLHelper.getSSLCertifcation(this), new HttpsUtil.UnSafeTrustManager())
                .hostnameVerifier(new HttpsUtil.UnSafeHostnameVerifier())//由於還沒有域名,此處設定忽略掉域名校驗
                .build();
         //建立retrofit       
        String baseUrl = "https://180.101.147.89:8743/";
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .client(httpClient)
                //.addConverterFactory(GsonConverterFactory.create())
                .addConverterFactory(ScalarsConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .build();
  • 順便貼下依賴build.gradle
apply plugin: 'com.android.application'

android {
...
}
dependencies {
    ...

    //retrofit
    implementation "com.squareup.retrofit2:retrofit:2.3.0"
    implementation "com.squareup.retrofit2:converter-scalars:2.3.0"
    implementation "com.squareup.retrofit2:converter-gson:2.3.0"
    implementation "com.squareup.okhttp3:logging-interceptor:3.8.1"    
}

參考