Android Https雙向驗證

對于網絡安全锯岖,我們的重視程度遠遠及不上歐美等國家茅茂,部署Https的網站遠遠少于他們;但隨著互聯網不斷普及,國民對數據安全的需求也越來越高余黎,Https逐漸在國內普及開來。
最近公司項目也開始轉為Https赖临,故簡單記錄一下Android Https的一些設置鲤脏。

生成相關證書

  1. 從后臺那里拿到P12格式的證書文件,如:server.pfx;
  2. 生成服務器cer證書

安裝openssl

我采用的是直接下載安裝包厨姚,下載地址衅澈;也可以選擇下載源碼自己編譯openssl。

生成證書server.cer

安裝之后配置環(huán)境變量谬墙,用管理員權限打開或者使用win10的PowerShell今布,輸入

openssl pkcs12 -in server.pfx -out server.cer

然后輸入證書的密碼和PEM密碼经备,成功之后生成證書server.cer

生成客戶端信任證書庫client.truststore

由于安卓端的證書類型必須是BKS類型,需要這樣做:

  1. 下載這個jar:bcprov-ext-jdk15on-159.jar
  2. 將jar文件放在 Java 主目錄下的 jre/lib/ext目錄下
  3. 修改jre/lib/security/java.security這個文件:在List of providers 注釋的地方添加這一行security.provider.11=org.bouncycastle.jce.provider.BouncyCastleProvider
  4. 然后重啟終端部默,輸入下面代碼生成client.truststore侵蒙,密碼自己改:
keytool -import -v -alias server -file server.cer -keystore client.truststore -storepass 123456 -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider

如果不添加jar文件,會報Class not found錯誤傅蹂。
但是纷闺,某些情況下(大概是證書的格式或來源不符合要求)還是會報錯:所輸入的不是 X.509 證書

證書格式轉換工具XCA

  1. 下載安裝:地址
  2. 打開程序,點擊:File>New DataBase新建庫文件份蝴,選擇保存位置犁功,輸入密碼,即成功創(chuàng)建庫婚夫。
  3. 導入證書server.cer:點擊:Import>Certificate浸卦,選擇之前生成的證書server.cer,導入请敦。
  4. 導出p7b格式證書:


    導出p7b格式證書1

選擇p7b格式镐躲,點擊ok導出xxxx.p7b的證書


導出p7b格式證書1
  1. 打開xxxx.p7b證書文件,找到證書侍筛,右鍵>所有任務>導出


    導出X.509格式證書
  2. 導出X.509格式證書萤皂,選擇之前server.cer文件覆蓋保存,確認生成server.cer證書


    導出X.509格式證書

7.最后匣椰,重新執(zhí)行命令裆熙,會提示是否信任此證書? [否]:,輸入生成client.truststore:

keytool -import -v -alias server -file server.cer -keystore client.truststore -storepass 123456 -storetype BKS -provider org.bouncycastle.jce.provider.BouncyCastleProvider

Android端SSL認證請求

我們需要兩個證書:

  1. server.pfx:客戶端證書禽笑,用于請求的時候給服務器來驗證身份
  2. client.truststore:客戶端證書庫入录,用于驗證服務器端身份,防止釣魚

SSLSocketFactory方式進行SSL認證

將兩個證書添加到android項目的assets目標下面佳镜,建立SSL驗證工具僚稿,代碼如下:

public class SSLHelper {
    private static final String KEY_STORE_TYPE_BKS = "bks";
    private static final String KEY_STORE_TYPE_P12 = "PKCS12";
    public static final String KEY_STORE_CLIENT_PATH = "server.pfx";//P12文件
    private static final String KEY_STORE_TRUST_PATH = "client.truststore";//truststore文件
    public static final String KEY_STORE_PASSWORD = "123456";//P12文件密碼
    private static final String KEY_STORE_TRUST_PASSWORD = "123456";//truststore文件密碼

    public static SSLSocketFactory getSSLSocketFactory(Context context) {
        SSLSocketFactory factory = null;

        try {
            // 服務器端需要驗證的客戶端證書
            KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_P12);
            // 客戶端信任的服務器端證書
            KeyStore trustStore = KeyStore.getInstance(KEY_STORE_TYPE_BKS);

            InputStream ksIn = context.getResources().getAssets()
              .open(KEY_STORE_CLIENT_PATH);
            InputStream tsIn = context.getResources().getAssets()
              .open(KEY_STORE_TRUST_PATH);
            try {
                keyStore.load(ksIn, KEY_STORE_PASSWORD.toCharArray());
                trustStore.load(tsIn, KEY_STORE_TRUST_PASSWORD.toCharArray());
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    ksIn.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                try {
                    tsIn.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            //信任管理器
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
              TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(trustStore);
            //密鑰管理器
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("X509");
            keyManagerFactory.init(keyStore, KEY_STORE_PASSWORD.toCharArray());
            //初始化SSLContext
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(keyManagerFactory.getKeyManagers(), 
                            trustManagerFactory.getTrustManagers(), null);
            factory =  sslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        }

        return factory;
    }

}

OkHttpClient配置

OkHttpClient client = new OkHttpClient.Builder()
  .sslSocketFactory(SSLHelper.getSSLSocketFactory(getApplication()))

主機名驗證

如果出現主機名驗證錯誤hostname error,需要添加hostnameVerifier

OkHttpClient client = new OkHttpClient.Builder()
  .sslSocketFactory(SSLHelper.getSSLSocketFactory(getApplication()))
  .hostnameVerifier(new UnSafeHostnameVerifier());
public class UnSafeHostnameVerifier implements HostnameVerifier {
    @Override
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
}

至此蟀伸,Https雙向驗證的配置全部完成了蚀同。

本文基于筆者項目實踐,關于SSL認證筆者還沒有了解透徹啊掏,如有錯誤蠢络,請多多指出。

參考

Android HTTPS SSL雙向驗證

keytool 錯誤: java.lang.Exception: 所輸入的不是 X.509 證書

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末迟蜜,一起剝皮案震驚了整個濱河市刹孔,隨后出現的幾起案子,更是在濱河造成了極大的恐慌娜睛,老刑警劉巖髓霞,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卦睹,死亡現場離奇詭異,居然都是意外死亡酸茴,警方通過查閱死者的電腦和手機分预,發(fā)現死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門兢交,熙熙樓的掌柜王于貴愁眉苦臉地迎上來薪捍,“玉大人,你說我怎么就攤上這事配喳±掖” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵晴裹,是天一觀的道長被济。 經常有香客問我,道長涧团,這世上最難降的妖魔是什么只磷? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮泌绣,結果婚禮上钮追,老公的妹妹穿的比我還像新娘。我一直安慰自己阿迈,他們只是感情好元媚,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著苗沧,像睡著了一般刊棕。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上待逞,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天甥角,我揣著相機與錄音,去河邊找鬼识樱。 笑死嗤无,一個胖子當著我的面吹牛,可吹牛的內容都是我干的牺荠。 我是一名探鬼主播翁巍,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼休雌!你這毒婦竟也來了灶壶?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤杈曲,失蹤者是張志新(化名)和其女友劉穎驰凛,沒想到半個月后胸懈,有當地人在樹林里發(fā)現了一具尸體,經...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡恰响,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年趣钱,在試婚紗的時候發(fā)現自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片胚宦。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡首有,死狀恐怖,靈堂內的尸體忽然破棺而出枢劝,到底是詐尸還是另有隱情井联,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布您旁,位于F島的核電站烙常,受9級特大地震影響,放射性物質發(fā)生泄漏鹤盒。R本人自食惡果不足惜蚕脏,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望侦锯。 院中可真熱鬧驼鞭,春花似錦、人聲如沸率触。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽葱蝗。三九已至穴张,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間两曼,已是汗流浹背皂甘。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留悼凑,地道東北人偿枕。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像户辫,于是被迫代替她去往敵國和親渐夸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354