Android 偶遇 HTTPS

yaozhuan 8年前發布 | 13K 次閱讀 安卓開發 Android開發 移動開發 HTTPS

 

HTTPS ,該來的總要來的。

最近領導對移動端開發提出了很多優化的要求啊!其中一點就是數據安全性,之前安卓后端接口一直是用的HTTP,那么我想了想,HTTPS應該是入門級的了,趕緊找資料整理了下!

對于向權威機構申請過證書的網絡地址,用 OkHttp 或者 HttpsURLConnection 都可以直接訪問,不需要做額外的事情。但是申請證書要$$的,所以開發的時候我們接口經常是使用自簽名證書,或者即使上線了也還是用自簽名的,因為安卓用到的基本都是數據接口,又不會用瀏覽器訪問,不想付錢不行咩!

訪問自簽名網址

使用keytool生成證書

keytool是JDK提供的管理加密密鑰、X.509證書鏈和可信證書密鑰庫的簡便工具。安卓開發必定安裝了JDK并且一般都會配置好環境變量,所以你可以直接在終端或DOC窗口輸入 keytool 命令來查看幫助。

1.生成密鑰對

keytool -genkey -alias server -keyalg RSA -keystore server.jks

-alias 后面跟的是唯一別名, -keystore 后面填保存秘鑰對的文件路徑

還可以添加一個 -validity 天數 聲明有效期

需要注意的地方:執行命令之后第一個問題讓你輸入名字的地方最好設置成 域名 ,比如這樣 baidu.com 或者這樣 localhost ,反正匹配你要調式的域名就對了,當然,如果你在安卓上調試,那么本地地址可能用不了。

2.導出證書

上面生成了服務端使用的密鑰對,現在可以通過它生成證書給客戶端使用

keytool -export -alias server -storepass 123456 -keystore server.jks -file server.cer

-storepass 后面跟的是你剛才設置的密碼,不加這個也沒關系,它會主動問你!; -file 設置了保存證書的路徑

服務端配置

這里我使用tomcat8進行測試,它的配置很簡單,修改tomcat目錄下的conf/server.xml文件,添加如下內容,這里設置了端口號為8443

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
            maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
            clientAuth="false" sslProtocol="TLS" 
            keystoreFile="密鑰庫文件路徑,也就是.jks文件"
            keystorePass="密碼" />

安卓端配置

  • 加載證書

把之前生成的證書(.cer)放到安卓項目的 assets 或者 raw 目錄下,讀取文件流用以下方法獲取SSLSocketFactory 。

public static SSLSocketFactory getSslSocketFactory(InputStream certificates)
    {
        SSLContext sslContext = null;
        try
        {
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");

            Certificate ca;
            try {
                ca = certificateFactory.generateCertificate(certificates);

            } finally {
                certificates.close();
            }

            // Create a KeyStore containing our trusted CAs
            String keyStoreType = KeyStore.getDefaultType();
            KeyStore keyStore = KeyStore.getInstance(keyStoreType);
            keyStore.load(null, null);
            keyStore.setCertificateEntry("ca", ca);

            // Create a TrustManager that trusts the CAs in our KeyStore
            String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
            tmf.init(keyStore);

            // Create an SSLContext that uses our TrustManager
            sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, tmf.getTrustManagers(), null);

        } catch (Exception e)
        {
            e.printStackTrace();
        }

        return sslContext != null? sslContext.getSocketFactory():null;
    }

OkHttp

在OkHttp中使用很簡單,獲取SSLSocketFactory之后通過OkHttp的構建方法傳入就行了。

使用的OkHttp版本是 3.2.0 。

OkHttpClient client = new OkHttpClient.Builder()
                .sslSocketFactory(sslSocketFactory)
                .build();

設置完之后你就可以訪問該證書對應的域名地址了,不需要別的附加操作了。

HttpsURLConnection

OkHttp的API與安卓中默認提供的URLConnection是很接近的,所以配置也是如出一轍。

URL url = new URL("https://....");

HttpsURLConnection httpsURLConnection = (HttpsURLConnection) url.openConnection();

httpsURLConnection.setSSLSocketFactory(sslSocketFactory);

雙向驗證

雙向認證需要兩個密鑰實體,一個放服務端一個放客戶端。前面我們已經實現單向的認證,現在只需要給客戶端生成一個密鑰庫,并且讓服務端信任客戶端就可以了。

生成客戶端密鑰

keytool -genkey -alias android -keyalg RSA -keystore android.jks

導出客戶端證書(字符串形式)

keytool -keystore android.jks -alias android -exportcert -rfc > android.pem

將導出的證書添加信任到服務端的密鑰庫

keytool -importcert -trustcacerts -alias android -keystore server.jks -file android.pem

服務端配置

修改tomcat目錄下的conf/server.xml文件

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
            maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
            sslProtocol="TLS" 
            keystoreFile="密鑰庫文件路徑,也就是.jks文件"
            keystorePass="密碼"
            //修改兩條內容,其它和之前單向認證一樣就行
            clientAuth="true"
            truststoreFile="和keystoreFile填一樣" />

安卓端配置

剛才生成了客戶端的密鑰庫 android.jks 。但是安卓默認是不支持jks格式的!比較常規的解決方式是用 Portecle 工具將它轉換成bks文件。

點這里下載Portecle工具

下載完之后解壓并在目錄下運行命令: java -jar portecle.jar 或者也可以直接雙擊它打開

運行之后就會出來UI界面,用它打開 android.jks 然后選菜單 Tools –> Change Keystore Type –> BKS 在彈出框輸入密碼進行轉換,最后別忘記選菜單 File –> Save Keystore As 將它另存為 android.kbs (名字隨意)

可能會出現這樣的異常:java.security.KeyStoreException: java.io.IOException: Error initialising store of key store

解決辦法是下載 JCE 然后替換掉 JDK\jre\lib\security 和 JRE\lib\security 這兩個目錄下的同名文件,并重啟Portecle

生成kbs文件之后,把它放到安卓的目錄下 assets 或者 raw 。

然后把獲取SSLSocketFactory的方法改成下面這樣

public static SSLSocketFactory getSslSocketFactory
        (InputStream certificates,InputStream key,String keyPassword)
{
    SSLContext sslContext = null;
    try
    {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");

        Certificate ca;
        try {
            ca = certificateFactory.generateCertificate(certificates);

        } finally {
            certificates.close();
        }

        String keyStoreType = KeyStore.getDefaultType();
        KeyStore keyStore = KeyStore.getInstance(keyStoreType);
        keyStore.load(null, null);
        keyStore.setCertificateEntry("ca", ca);

        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keyStore);

        String keyStoreType2 = "BKS";
        KeyStore keyStore2 = KeyStore.getInstance(keyStoreType2);
        keyStore2.load(key, keyPassword.toCharArray());

        String kmfAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
        KeyManagerFactory kmf = KeyManagerFactory.getInstance(kmfAlgorithm);
        kmf.init(keyStore2,keyPassword.toCharArray());

        sslContext = SSLContext.getInstance("TLS");
        sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

    } catch (Exception e)
    {
        e.printStackTrace();
    }

    return sslContext != null? sslContext.getSocketFactory():null;
}

上面的都改完之后,雙向驗證的配置就完成了。你可以打開瀏覽器訪問下你配置好的地址,應該不能訪問,提示你: 不接受您的登錄證書,或者您的登錄證書可能已過期 。因為你的系統沒有加入剛才生成的客戶端密鑰庫,安卓端像上面一樣設置完SSLSocketFactory就可以正常訪問了。

額外的,我把獲取SSLSocketFactory的方法封裝了下 HttpsUtil ,用 getSslSocketFactory 方法就行了

來自: http://blog.majiajie.me/2016/05/11/Android-偶遇HTTPS/

 本文由用戶 yaozhuan 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!