Android中https單向認(rèn)證的總結(jié)

  • 中間人攻擊

我們都知道使用fiddler抓取app的數(shù)據(jù)包,不管是http還是https請求,都能輕松抓取,此時(shí)對客戶端來說,fiddler是一個(gè)服務(wù)端,對服務(wù)端來說,fiddler就變成了一個(gè)客戶端,查了下資料,這種方式稱為“中間人攻擊”,怎么才能防止https中的“中間人攻擊”呢,工作中也用到了https,所以想深入研究一下這個(gè)問題,當(dāng)然每個(gè)問題如果深挖的話,都需要很多知識的支持,所以這個(gè)過程有些地方是自己的理解,難免有些偏差,有問題,咱們討論區(qū)見。

  • 單向認(rèn)證

平時(shí)我們說的單項(xiàng)認(rèn)證,一般指的是客戶端對服務(wù)端的認(rèn)證,當(dāng)客戶端向服務(wù)端發(fā)送請求時(shí),服務(wù)端會把自己的證書信息發(fā)給客戶端,這個(gè)證書信息包括服務(wù)端的公鑰、有效時(shí)間、有效地址和CA的數(shù)字簽名等信息,所以客戶端需要與預(yù)埋一個(gè)證書,這樣我們可以拿本地證書和服務(wù)端發(fā)送的證書進(jìn)行信息匹配,完成認(rèn)證的過程(在使用fiddler抓包時(shí),需要事先安裝的證書就是為了完成這個(gè)客戶端對服務(wù)端的認(rèn)證過程,而且這個(gè)證書應(yīng)該不是正規(guī)的CA機(jī)構(gòu)頒發(fā)的證書,而是fiddler自己生成的證書)。

  • android中單向認(rèn)證的過程

1.獲取客戶端預(yù)埋的服務(wù)器端的證書對象

InputStream mCaInputStream =context.getResources().openRawResource(R.raw.server_cert);

2.生成符合x509標(biāo)準(zhǔn)的證書

CertificateFactory mCertificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate certificate =(X509Certificate) mCertificateFactory.generateCertificate(mCaInputStream);
if (mCaInputStream != null) {
      mCaInputStream.close();
  }

3.將證書導(dǎo)入到本地的證書密鑰庫中去

KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
//這幾行代碼,貌似沒有導(dǎo)入的操作?
keyStore.load(null, null);
keyStore.setCertificateEntry("123", certificate);

4.使用本地密鑰庫初始化信任管理器中去

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

5.使用信任管理器得到X509TrustManager

TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
 //這個(gè)位置,我直接取了數(shù)組的第一個(gè)元素,貌似不妥。
X509TrustManager x509TrustManager = (X509TrustManager) trustManagers[0];

6.使用X509TrustManager校驗(yàn)服務(wù)端的證書,此方法不報(bào)異常即使校驗(yàn)成功

x509TrustManager.checkServerTrusted(chain, authType);
  • 分析過程

    • 主要分析第6步,這步中的x509TrustManager對象,是我使用客戶端預(yù)埋的證書生成的,用這個(gè)證書去校驗(yàn)服務(wù)器返回來的證書,如果校驗(yàn)成功則客戶端對服務(wù)端認(rèn)證成功,否則,失敗。
    • 這里如果失敗了還有個(gè)處理,就是在預(yù)埋證書校驗(yàn)失敗的情況下,我們重新初始化了一個(gè)系統(tǒng)默認(rèn)的信任管理器TrustManagerFactory,加載手機(jī)端的所有證書到信任管理器中,再去校驗(yàn)服務(wù)端的證書是否是信任的,如果校驗(yàn)成功則客戶端對服務(wù)端認(rèn)證成功,否則,校驗(yàn)失敗,此時(shí)才是真正的認(rèn)證失敗。不過這個(gè)處理比較危險(xiǎn),具體分析見下一條:
    • 我的實(shí)際情況是這樣的,服務(wù)器使用的是阿里云提供的CA證書,這種證書是經(jīng)過專門的CA機(jī)構(gòu)頒發(fā)的,理論上講不需要客戶端代碼認(rèn)證,此時(shí)如果客戶端證書跟這個(gè)阿里云證書不匹配,因?yàn)槭褂昧耸謾C(jī)中默認(rèn)的信任管理器TrustManagerFactory參與校驗(yàn),校驗(yàn)也是會通過校驗(yàn)的,同理,fiddler抓包也是能夠抓取的,也就是我們這種認(rèn)證失去了作用了。
....
 } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (CertificateException e) {
//                發(fā)生異常,就用設(shè)備本身默認(rèn)的信任管理器進(jìn)行校驗(yàn)
//                這樣可能存在危險(xiǎn),經(jīng)過測試如果加上這句話,fiddler可以正常抓包的
//                這是因?yàn)閒iddler抓包之前,會在客戶端安裝一個(gè)證書,如果指定的證書校驗(yàn)失敗
//                就是默認(rèn)使用這個(gè)證書匹配,結(jié)果能夠和fiddler匹配成功。
                try {
                    TrustManagerFactory trustManagerFactory
                            = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
                    //初始化手機(jī)本身默認(rèn)的證書信任管理器用于認(rèn)證。
                    trustManagerFactory.init((KeyStore) null);
                    TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
                    X509TrustManager x509TrustManager = chooseTrustManager(trustManagers);
                    x509TrustManager.checkServerTrusted(chain, authType);
                } catch (NoSuchAlgorithmException e1) {
                    e1.printStackTrace();
                } catch (KeyStoreException e1) {
                    e1.printStackTrace();
                }
  • 所以在實(shí)際中,需要保證認(rèn)證的有效期,一般不會啟動本地默認(rèn)的TrustManagerFactory對象參與校驗(yàn)
  • OkHttp中的ssl配置

    • 校驗(yàn)域名
public class UnSafeHostnameVerifier implements HostnameVerifier {
    @Override
    public boolean verify(String hostname, SSLSession session) {
        if (AppConstants.HOST_NAME.equals(hostname)) {
            //第一請求時(shí),會返回域名hostname,用于和本地域名校驗(yàn)
            return true;
        }
        return false;
    }
}
  • 調(diào)用SSLSocketFactory和X509TrustManager對象
SSLHelper5 sslHelper5 = new SSLHelper5(App.getContext());
OkHttpClient.Builder builder = new OkHttpClient.Builder()
                            .sslSocketFactory(sslHelper5.provideSSLSocketFactory()
                                    , sslHelper5.provideX509TrustManager())
                            .hostnameVerifier(new UnSafeHostnameVerifier())
  • 項(xiàng)目文件

GitHub,SSLHelper5.java

  • 完成(關(guān)于Https的雙向認(rèn)證,近期完成)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容