国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【網(wǎng)絡(luò)安全】https與證書原理 | SSL Pinning及其繞過

這篇具有很好參考價(jià)值的文章主要介紹了【網(wǎng)絡(luò)安全】https與證書原理 | SSL Pinning及其繞過。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

SSL Pinning

1 HTTPS協(xié)議流程

參考:
https://segmentfault.com/a/1190000009002353?sort=newest
https://zhuanlan.zhihu.com/p/353571366
https://juejin.cn/post/6863295544828444686

HTTPS=HTTP+TLS,其它的協(xié)議也類似,如FTPS=FTP+TLS
【網(wǎng)絡(luò)安全】https與證書原理 | SSL Pinning及其繞過
1) ClientHello

  • Client 首先發(fā)送本地的 TLS 版本、支持的加密算法套件,并且生成一個(gè)隨機(jī)數(shù) R1 。

2)Server Hello

  • Server 端確認(rèn) TLS 版本號(hào)。從 Client 端支持的加密套件中選取一個(gè),并生成一個(gè)隨機(jī)數(shù) R2 一起發(fā)送給 Client。
  • Server 向 Client 發(fā)送自己的CA證書(包含公鑰、證書簽名)。

3)證書校驗(yàn)

  • Client 判斷證書簽名與CA證書是否合法有效
  • Client 生成隨機(jī)數(shù)pre-master secret,并使用Server發(fā)過來的公鑰對(duì)pre-master secret進(jìn)行加密,將加密后的pre-master secret送給Server。這一步結(jié)束后,Client 與 Server 就都有 R1、R2、pre-master secret 了,兩端便可以使用這 3 個(gè)隨機(jī)數(shù)獨(dú)立生成 對(duì)稱會(huì)話密鑰了,避免了對(duì)稱密鑰的傳輸,同時(shí)可以 根據(jù)會(huì)話密鑰生成 6 個(gè)密鑰(P1~P6) 用作后續(xù)身份驗(yàn)證

Client端和Server端,最終都會(huì)用相同的算法將pre-master secret(預(yù)主密鑰)轉(zhuǎn)換成master secret(主密鑰),通過主密鑰可以生成session key。兩者后續(xù)的通信交互數(shù)據(jù),將通過session key進(jìn)行加密。
【網(wǎng)絡(luò)安全】https與證書原理 | SSL Pinning及其繞過
參考:https://www.laoqingcai.com/tls1.2-premasterkey/

4)Client 握手結(jié)束通知

  • Client 使用 P1 將之前的握手信息的 hash 值加密并發(fā)送給 Server
  • Client 發(fā)送握手結(jié)束消息

5)Server 握手結(jié)束通知

  • Server 計(jì)算之前的握手信息的 hash 值,并與 P1 解密客戶端發(fā)送的握手信息的 hash 對(duì)比校驗(yàn)
  • 驗(yàn)證通過后,使用 P2 將之前的握手信息的 hash 值加密并發(fā)送給 Client

6)Client 開始HTTPS通訊

  • Client 計(jì)算之前的握手信息的 hash 值,并與 P2 解密 Server 發(fā)送的握手信息的 hash 對(duì)比校驗(yàn)
  • 驗(yàn)證通過后,開始發(fā)起 HTTPS 請(qǐng)求。

兩者后續(xù)的通信交互數(shù)據(jù),將通過session key進(jìn)行加密。所以中間人即使截獲數(shù)據(jù),也無法解析。

2 證書相關(guān)

證書文件

證書=公鑰+(公鑰+元信息)的簽名

【網(wǎng)絡(luò)安全】https與證書原理 | SSL Pinning及其繞過

其中的元信息包括:

  • Subject(主體信息):
    • Common Name(CN)通用名稱
    • SAN
    • Organization
    • Organization Unit(OU)
    • Country
    • State
    • City
    • Address
    • Postal code
  • Issuer(簽發(fā)者信息):
    • Common Name(CN)通用名稱
    • Organization
    • Organization Unit(OU)
    • Country
    • State
    • City
    • Address
    • Postal code
  • Validity(有效期):
    • Not Before(簽發(fā)日期)
    • Not After(過期時(shí)間)
  • Signature Algorithm
  • Serial Number
  • Version
  • Extensions(擴(kuò)展信息):只在證書版本2、3中才有

因此,證書的結(jié)構(gòu)大致如下:
【網(wǎng)絡(luò)安全】https與證書原理 | SSL Pinning及其繞過

CA

簽名 = 計(jì)算摘要 + 對(duì)摘要值私鑰加密
CA:Certificate Authority,專門用自己的私鑰 給別人進(jìn)行簽名的機(jī)構(gòu)

簽發(fā)證書的過程

注意,計(jì)算簽名時(shí),是對(duì)整個(gè)證書文件計(jì)算簽名,也就是對(duì)【元信息+公鑰】計(jì)算簽名,而不只是對(duì)公鑰計(jì)算簽名。
【網(wǎng)絡(luò)安全】https與證書原理 | SSL Pinning及其繞過
(參考:https://blog.csdn.net/bluishglc/article/details/123617558)

證書的驗(yàn)證過程

關(guān)鍵過程:用信任CA庫里CA證書(公鑰),驗(yàn)證網(wǎng)站的證書文件里的簽名

  1. 在TLS握手的過程中,客戶端得到了網(wǎng)站的證書
  2. 客戶端打開證書,查看是哪個(gè)CA簽名的這個(gè)證書
  3. 在自己信任的CA庫中,找相應(yīng)CA的證書(包含CA的公鑰),
  4. CA證書里面的公鑰解密網(wǎng)站證書上的簽名,取出網(wǎng)站證書的摘要,然后用同樣的算法(比如sha256)算出網(wǎng)站證書的摘要,如果摘要和簽名中的摘要對(duì)的上,說明這個(gè)證書是合法的,且沒被人篡改過
  5. 讀出里面的CN,對(duì)于網(wǎng)站的證書,里面一般包含的是域名
  6. 檢查里面的域名和自己訪問網(wǎng)站的域名對(duì)不對(duì)的上,對(duì)的上,就說明這個(gè)證書確實(shí)是頒發(fā)給這個(gè)網(wǎng)站的
  7. 到此為止檢查通過

證書鏈的驗(yàn)證

參考:
https://www.jianshu.com/p/46e48bc517d0
https://www.cnblogs.com/xiaxveliang/p/13183175.html

我們使用End-user Certificates來確保加密傳輸數(shù)據(jù)的公鑰(public key)不被篡改,而又如何確保end-user certificates的合法性呢?

這個(gè)認(rèn)證過程跟公鑰的認(rèn)證過程類似,首先獲取頒布end-user certificates的CA的證書,然后驗(yàn)證end-user certificates的signature。一般來說,root CAs不會(huì)直接頒布end-user certificates的,而是授權(quán)給多個(gè)二級(jí)CA,而二級(jí)CA又可以授權(quán)給多個(gè)三級(jí)CA,這些中間的CA就是Intermediates CAs,它們才會(huì)頒布end-user certificates。

但是Intermediates Certificates的可靠性又如何保證呢?這就是涉及到證書鏈,Certificate Chain ,鏈?zhǔn)较蛏向?yàn)證證書,直到Root Certificates,如下圖:

【網(wǎng)絡(luò)安全】https與證書原理 | SSL Pinning及其繞過
中間CA的證書怎么獲?。?br> 以百度的TLS證書進(jìn)行舉例,百度服務(wù)器證書 簽發(fā)者公鑰(中間機(jī)構(gòu)公鑰)通過下圖中的URI獲?。?br>【網(wǎng)絡(luò)安全】https與證書原理 | SSL Pinning及其繞過

3 SSL Pinning

參考:
https://shunix.com/ssl-pinning/
https://zhuanlan.zhihu.com/p/58204817

3.1 原理

默認(rèn)情況下,只要網(wǎng)站證書的Root CA,屬于系統(tǒng)信任的Root CA集合(例如,安卓中系統(tǒng)默認(rèn)信任 /system/etc/security/ 中CA證書對(duì)應(yīng)的CA)。

對(duì)于www.example.com,可能出現(xiàn)以下情況:

(1)情況A:

某個(gè)系統(tǒng)信任的Root CA,授權(quán)給了可靠的Intermediate CA 1,Intermediate CA 1給www.example.com頒發(fā)了一個(gè)合法的證書1;

同時(shí)該Root CA也授權(quán)給了不可靠的Intermediate CA 2(不可靠的原因可能是私鑰被泄露),Intermediate CA 2給 www.example.com頒發(fā)了一個(gè)證書2。

這時(shí)候我們希望只信任證書1而不信任證書2,否則一些中間人拿到了證書2,就可以偽裝成合法的www.example.com。

這通過修改信任CA集合是較難實(shí)現(xiàn)的,因?yàn)閮蓚€(gè)證書的根信任錨是相同的Root CA。當(dāng)然,可以從信任集中刪除Root CA,再添加Intermediate CA 1而不添加Intermediate CA 2。但這意味著我們需要移除Root CA。通常,一個(gè)Root CA會(huì)作為成千上萬個(gè)證書的根信任錨,移除Root CA可能引發(fā)過大的影響。

(2)情況B:

系統(tǒng)的可信CA集合被篡改。例如,安卓系統(tǒng)在被Root的情況下,用戶可以修改系統(tǒng)信任證書(方法例如:https://github.com/doug-leith/cydia)。

這種情況下,app可能需要只信任特定的某個(gè)(某些)證書。

原理:

可以采用證書固定。只有當(dāng)網(wǎng)站的證書鏈中,至少有一個(gè)節(jié)點(diǎn)的證書全部?jī)?nèi)容/證書公鑰,跟客戶端預(yù)埋的證書的內(nèi)容相匹配,我們的客戶端才信任此證書鏈。

證書固定 與 限制可信CA 的關(guān)系

如果把某個(gè)Root CA的證書固定起來,那就相當(dāng)于設(shè)置該Root CA為唯一可信的Root CA。

被固定的證書可以是(一般是)某個(gè)中間CA的證書。這樣,不再是所有以trusted Root CA為根的證書鏈都仍舊可信了。只有子節(jié)點(diǎn)包含該中間CA的證書鏈才可信。

被固定的證書的Root CA可以不在系統(tǒng)trusted Root CA集合中。

3.2 實(shí)現(xiàn)方案

具體實(shí)現(xiàn)技術(shù)上,SSL Pinning可以分為Certificate Pinning(證書固定)和Public Key Pinning(公鑰固定)

3.2.1 證書固定

把證書文件打包進(jìn)安裝包,將app設(shè)置為僅接受指定的內(nèi)置證書,而不接受操作系統(tǒng)內(nèi)置的CA根證書對(duì)應(yīng)的任何證書。

3.2.2 公鑰固定

提取證書中的公鑰并內(nèi)置到App中,通過與服務(wù)器對(duì)比公鑰值,來驗(yàn)證連接的合法性。我們?cè)谏暾?qǐng)證書時(shí),公鑰在證書的續(xù)期前后可以保持不變,所以可以解決證書有效期問題。

3.3 實(shí)例

3.3.1 證書固定實(shí)例:基于TrustManagerFactory
// kotlin語法

// 加載證書文件
val cf: CertificateFactory = CertificateFactory.getInstance("X.509")
val caInput: InputStream = BufferedInputStream(FileInputStream("load-der.crt"))

// 使用CertificateFactory生成一個(gè)X509Certificate的實(shí)例
val ca: X509Certificate = caInput.use {
    cf.generateCertificate(it) as X509Certificate
}
System.out.println("ca=" + ca.subjectDN)


// 創(chuàng)建一個(gè)KeyStore實(shí)例,并把前邊的X509Certificate實(shí)例加進(jìn)去,并起一個(gè)別名"ca"
val keyStoreType = KeyStore.getDefaultType()
val keyStore = KeyStore.getInstance(keyStoreType).apply {
    load(null, null)
    setCertificateEntry("ca", ca)
}

// 創(chuàng)建一個(gè)TrustManagerFactory實(shí)例,并且使用前邊的KeyStore實(shí)例進(jìn)行初始化
val tmfAlgorithm: String = TrustManagerFactory.getDefaultAlgorithm()
val tmf: TrustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm).apply {
    init(keyStore)
}

// 創(chuàng)建一個(gè)SSLContext實(shí)例,并且使用前面的TrustManagerFactory實(shí)例的trustManagers進(jìn)行初始化
val context: SSLContext = SSLContext.getInstance("TLS").apply {
    init(null, tmf.trustManagers, null)
}

// 創(chuàng)建HttpsURLConnection實(shí)例urlConnection
val url = URL("https://certs.cac.washington.edu/CAtest/")
val urlConnection = url.openConnection() as HttpsURLConnection

// 將SSLContext實(shí)例context的socketFactory屬性,賦值給urlConnection
urlConnection.sslSocketFactory = context.socketFactory

val inputStream: InputStream = urlConnection.inputStream
copyInputStreamToOutputStream(inputStream, System.out)
3.3.2 證書固定實(shí)例:基于NSC配置文件

需要在Manifest文件的android:networkSecurityConfig屬性加上對(duì)應(yīng)的配置內(nèi)容,示例如下:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
	<!-- Support certificate file, in der or pem format -->
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <trust-anchors>
            <certificates src="@raw/my_ca"/>
        </trust-anchors>
    </domain-config>
    
    <!-- Support sha256 hash of subject public key -->
    <domain-config>
        <domain includeSubdomains="true">example.com</domain>
        <pin-set expiration="2018-01-01">
            <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin>
            <!-- backup pin -->
            <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin>
        </pin-set>
    </domain-config>
</network-security-config>

關(guān)于NSC的詳細(xì)內(nèi)容,可以參考論文:
[USENIX Sec’21] Why Eve and Mallory Still Love Android: Revisiting TLS (In)Security in Android Applications

或者直接參考Google的官網(wǎng)文檔:
https://developer.android.com/training/articles/security-config

計(jì)劃后續(xù)寫一篇博客詳細(xì)介紹Google Android的NSC。

4 安卓中的SSL Pinning

參考:http://hanpfei.github.io/2018/03/20/android_cert_mgr_and_verify/

SSL Pinning機(jī)制中,客戶端將特定域名的證書與特定的簽發(fā)者綁定。即,對(duì)某個(gè)域名,客戶端只承認(rèn)特定CA為該域名簽發(fā)的證書,而不承認(rèn)其它 CA 為該域名簽發(fā)的證書。

4.1 Android 的根證書管理

AOSP 源碼庫中,CA 根證書主要存放在 system/ca-certificates 目錄下,而在 Android 系統(tǒng)中,則存放在 /system/etc/security/ 目錄下:

【網(wǎng)絡(luò)安全】https與證書原理 | SSL Pinning及其繞過
cacerts_google 目錄下的根證書,主要用于 system/update_engine、external/libbrillo 和 system/core/crash_reporter 等模塊

cacerts 目錄下的根證書則用于所有的應(yīng)用。cacerts 目錄下的根證書,即 Android 系統(tǒng)的根證書庫,像下面這樣:
【網(wǎng)絡(luò)安全】https與證書原理 | SSL Pinning及其繞過
它們都是 PEM 格式的 X.509 證書。

Android 系統(tǒng)通過 SystemCertificateSource、DirectoryCertificateSource 和 CertificateSource 等類管理系統(tǒng)根證書庫。

  • CertificateSource定義了可以對(duì)根證書庫執(zhí)行的操作,主要是對(duì)根證書的獲取和查找
    位于frameworks/base/core/java/android/security/net/config/CertificateSource.java

  • DirectoryCertificateSource 類提供證書的創(chuàng)建、獲取和查找操作:
    位于frameworks/base/core/java/android/security/net/config/DirectoryCertificateSource.java
    獲取根證書庫的 getCertificates() 操作在第一次被調(diào)用時(shí),遍歷文件系統(tǒng),并加載系統(tǒng)所有的根證書文件,并緩存起來,以備后面訪問。
    根證書的查找操作,主要依據(jù)證書文件的文件名進(jìn)行,證書文件被要求以 [SubjectName 的哈希值].[Index] 的形式命名。

  • SystemCertificateSource 類定義了系統(tǒng)根證書庫的路徑,以及無效一個(gè)根證書的機(jī)制:
    位于frameworks/base/core/java/android/security/net/config/SystemCertificateSource.java
    Android 系統(tǒng)的根證書位于 /system/etc/security/cacerts/ 目錄下。用戶可以通過將特定根證書復(fù)制到用戶配置目錄的 cacerts-removed 目錄下來無效一個(gè)根證書。

4.2 證書鏈合法性驗(yàn)證

OpenSSLSocketImpl.startHandshake() 通過 NativeCrypto 類SSL_do_handshake() 方法執(zhí)行握手操作:

(NativeCrypto 位于external/conscrypt/src/main/java/org/conscrypt/NativeCrypto.java)

SSL_do_handshake() 方法的第三參數(shù)是一個(gè)接口:SSLHandshakeCallbacks
【網(wǎng)絡(luò)安全】https與證書原理 | SSL Pinning及其繞過

SSLHandshakeCallbacks是NativeCrypto 類定義的接口,其中包含一組回調(diào)函數(shù);這組回調(diào)函數(shù),是SSL_do_handshake() 的參數(shù),在SSL_do_handshake() 中被傳入native層

SSLHandshakeCallbacks 中的方法之一是verifyCertificateChain():

/**
 * A collection of callbacks from the native OpenSSL code that are
 * related to the SSL handshake initiated by SSL_do_handshake.
 */
public interface SSLHandshakeCallbacks {
    /**
     * Verify that we trust the certificate chain is trusted.
     *
     * @param sslSessionNativePtr pointer to a reference of the SSL_SESSION
     * @param certificateChainRefs chain of X.509 certificate references
     * @param authMethod auth algorithm name
     *
     * @throws CertificateException if the certificate is untrusted
     */
    public void verifyCertificateChain(long sslSessionNativePtr, 
    		long[] certificateChainRefs,
    		String authMethod) 
    		throws CertificateException;

verifyCertificateChain()的參數(shù):

  • 指向一個(gè)sslSession的指針
  • X.509 證書鏈
  • 認(rèn)證算法名稱

SSLHandshakeCallbacks中的回調(diào)方法的實(shí)現(xiàn)在 OpenSSLSocketImpl 。 OpenSSLSocketImpl中,verifyCertificateChain()的實(shí)現(xiàn)如下:

@SuppressWarnings("unused") // used by NativeCrypto.SSLHandshakeCallbacks
@Override
public void verifyCertificateChain(long sslSessionNativePtr, long[] certRefs, String authMethod)
        throws CertificateException {
    try {
        X509TrustManager x509tm = sslParameters.getX509TrustManager();
        if (x509tm == null) {
            throw new CertificateException("No X.509 TrustManager");
        }
        if (certRefs == null || certRefs.length == 0) {
            throw new SSLException("Peer sent no certificate");
        }
        OpenSSLX509Certificate[] peerCertChain = new OpenSSLX509Certificate[certRefs.length];
        for (int i = 0; i < certRefs.length; i++) {
            peerCertChain[i] = new OpenSSLX509Certificate(certRefs[i]);
        }
        // Used for verifyCertificateChain callback
        handshakeSession = new OpenSSLSessionImpl(sslSessionNativePtr, null, peerCertChain,
                getHostnameOrIP(), getPort(), null);
        boolean client = sslParameters.getUseClientMode();
        if (client) {
            Platform.checkServerTrusted(x509tm, peerCertChain, authMethod, this);
            if (sslParameters.isCTVerificationEnabled(getHostname())) {
                byte[] tlsData = NativeCrypto.SSL_get_signed_cert_timestamp_list(
                                    sslNativePointer);
                byte[] ocspData = NativeCrypto.SSL_get_ocsp_response(sslNativePointer);
                CTVerifier ctVerifier = sslParameters.getCTVerifier();
                CTVerificationResult result =
                    ctVerifier.verifySignedCertificateTimestamps(peerCertChain, tlsData, ocspData);
                if (result.getValidSCTs().size() == 0) {
                    throw new CertificateException("No valid SCT found");
                }
            }
        } else {
            String authType = peerCertChain[0].getPublicKey().getAlgorithm();
            Platform.checkClientTrusted(x509tm, peerCertChain, authType, this);
        }
    } catch (CertificateException e) {
        throw e;
    } catch (Exception e) {
        throw new CertificateException(e);
    } finally {
        // Clear this before notifying handshake completed listeners
        handshakeSession = null;
    }
}

這里面,verifyCertificateChain() 從 OpenSSLSocketImpl的 sslParameters 獲得 X509TrustManager:

 X509TrustManager x509tm = sslParameters.getX509TrustManager();

然后在 Platform.checkServerTrusted() 中執(zhí)行服務(wù)端證書合法有效性的檢查:

Platform.checkServerTrusted(x509tm, peerCertChain, authMethod, this);

Platform.checkServerTrusted在com.android.org.conscrypt.Platform類(external/conscrypt/src/compat/java/org/conscrypt/Platform.java):

public static void checkServerTrusted(X509TrustManager tm, X509Certificate[] chain,
        String authType, OpenSSLSocketImpl socket) throws CertificateException {
    if (!checkTrusted("checkServerTrusted", tm, chain, authType, Socket.class, socket)
            && !checkTrusted("checkServerTrusted", tm, chain, authType, String.class,
                             socket.getHandshakeSession().getPeerHost())) {
        tm.checkServerTrusted(chain, authType);
    }
}

可以看到,Platform.checkServerTrusted()會(huì)調(diào)用X509TrustManager.checkServerTrusted()來完成檢查。
其中的X509TrustManager實(shí)例來源于OpenSSLSocketImpl 的sslParameters,如前文所述:

 X509TrustManager x509tm = sslParameters.getX509TrustManager();

那OpenSSLSocketImpl 的 sslParameters 又來自于哪里呢?來源于構(gòu)造函數(shù),例如:

protected OpenSSLSocketImpl(SSLParametersImpl sslParameters) throws IOException {
    this.socket = this;
    this.peerHostname = null;
    this.peerPort = -1;
    this.autoClose = false;
    this.sslParameters = sslParameters;
}

而OpenSSLSocketFactoryImpl類會(huì)實(shí)例化OpenSSLSocketImpl:

package org.conscrypt;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
public class OpenSSLSocketFactoryImpl extends javax.net.ssl.SSLSocketFactory {
    private final SSLParametersImpl sslParameters;
    private final IOException instantiationException;

    …………

    @Override
    public Socket createSocket() throws IOException {
        if (instantiationException != null) {
            throw instantiationException;
        }
        return new OpenSSLSocketImpl((SSLParametersImpl) sslParameters.clone());
    }
    @Override
    public Socket createSocket(String hostname, int port) throws IOException, UnknownHostException {
        return new OpenSSLSocketImpl(hostname, port, (SSLParametersImpl) sslParameters.clone());
    }
    @Override
    public Socket createSocket(String hostname, int port, InetAddress localHost, int localPort)
            throws IOException, UnknownHostException {
        return new OpenSSLSocketImpl(hostname,
                                     port,
                                     localHost,
                                     localPort,
                                     (SSLParametersImpl) sslParameters.clone());
    }
    @Override
    public Socket createSocket(InetAddress address, int port) throws IOException {
        return new OpenSSLSocketImpl(address, port, (SSLParametersImpl) sslParameters.clone());
    }
    @Override
    public Socket createSocket(InetAddress address,
                               int port,
                               InetAddress localAddress,
                               int localPort)
            throws IOException {
        return new OpenSSLSocketImpl(address,
                                     port,
                                     localAddress,
                                     localPort,
                                     (SSLParametersImpl) sslParameters.clone());
    }

}

后面的細(xì)節(jié)暫時(shí)略過不看。

總結(jié):

OpenSSLSocketImpl.startHandshake() 和 NativeCrypto.SSL_do_handshake() 執(zhí)行完整的 SSL/TLS 握手過程。

證書合法性驗(yàn)證是 SSL/TLS 握手的一個(gè)重要步驟。該過程通過 native層調(diào)用Java 層的回調(diào)方法 SSLHandshakeCallbacks.verifyCertificateChain() 來完成。

回調(diào)方法的實(shí)現(xiàn)在OpenSSLSocketImpl。

OpenSSLSocketImpl.verifyCertificateChain()調(diào)用Platform.checkServerTrusted(),調(diào)用RootTrustManager.checkServerTrusted() ,調(diào)用NetworkSecurityTrustManager.checkServerTrusted() ,將真正根據(jù)系統(tǒng)根證書庫執(zhí)行證書合法性驗(yàn)證的 TrustManagerImpl 和 SSL/TLS 握手過程結(jié)合起來。

OpenSSLSocketFactoryImpl 將 OpenSSLSocketImpl 和 SSLParametersImpl 粘起來。

SSLParametersImpl 將 OpenSSLSocketImpl 和 RootTrustManager 粘起來。

NetworkSecurityConfig 將 RootTrustManager 和 NetworkSecurityTrustManager
粘起來。

NetworkSecurityConfig、NetworkSecurityTrustManager 和
TrustedCertificateStoreAdapter 將 TrustManagerImpl 和管理系統(tǒng)根證書庫的
SystemCertificateSource 粘起來。

TrustManagerImpl 是證書合法性驗(yàn)證的核心,它會(huì)查找系統(tǒng)根證書庫,并驗(yàn)證服務(wù)端證書的合法性做。

這個(gè)過程的調(diào)用棧如下:

com.android.org.conscrypt.TrustManagerImpl.checkServerTrusted()
android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted()
android.security.net.config.NetworkSecurityTrustManager.checkServerTrusted()
android.security.net.config.RootTrustManager.checkServerTrusted()
com.android.org.conscrypt.Platform.checkServerTrusted()
com.android.org.conscrypt.OpenSSLSocketImpl.verifyCertificateChain()
com.android.org.conscrypt.NativeCrypto.SSL_do_handshake()
com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake()
com.android.okhttp.Connection.connectTls()

4.3 自定義證書(SSL Pinning)

在實(shí)際的開發(fā)過程中,有時(shí)為了節(jié)省昂貴的購買證書的費(fèi)用,而想要自己給自己的服務(wù)器的域名簽發(fā)域名證書,這即是私有 CA 簽名的證書。為了能夠使用這種證書,需要在客戶端預(yù)埋根證書,并對(duì)客戶端證書合法性驗(yàn)證的過程進(jìn)行干預(yù),通過我們預(yù)埋的根證書為服務(wù)端的證書做合法性驗(yàn)證,而不依賴系統(tǒng)的根證書庫。

要想定制 OpenSSLSocketImpl 的證書驗(yàn)證過程,必然要改變 SSLParametersImpl;要改變 OpenSSLSocketImpl 的 SSLParametersImpl,則必然需要修改 SSLSocketFactory。修改 SSLSocketFactory 常常是一個(gè)不錯(cuò)的方法。

兩種實(shí)現(xiàn)手段:
(1)自己實(shí)現(xiàn) X509TrustManager
像下面這樣:

private final class HelloX509TrustManager implements X509TrustManager {
    private X509TrustManager mSystemDefaultTrustManager;
    private X509Certificate mCertificate;
    private HelloX509TrustManager() {
        mCertificate = loadRootCertificate();
        mSystemDefaultTrustManager = systemDefaultTrustManager();
    }
    private X509Certificate loadRootCertificate() {
        String certName = "netease.crt";
        X509Certificate certificate = null;
        InputStream certInput = null;
        try {
            certInput = new BufferedInputStream(MainActivity.this.getAssets().open(certName));
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            certificate = (X509Certificate) certificateFactory.generateCertPath(certInput).getCertificates().get(0);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } finally {
            if (certInput != null) {
                try {
                    certInput.close();
                } catch (IOException e) {
                }
            }
        }
        return certificate;
    }
    private X509TrustManager systemDefaultTrustManager() {
        try {
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                    TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init((KeyStore) null);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
                throw new IllegalStateException("Unexpected default trust managers:"
                        + Arrays.toString(trustManagers));
            }
            return (X509TrustManager) trustManagers[0];
        } catch (GeneralSecurityException e) {
            throw new AssertionError(); // The system has no TLS. Just give up.
        }
    }
    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        mSystemDefaultTrustManager.checkClientTrusted(chain, authType);
    }
    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        for (X509Certificate certificate : chain) {
            try {
                certificate.verify(mCertificate.getPublicKey());
                return;
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (InvalidKeyException e) {
                e.printStackTrace();
            } catch (NoSuchProviderException e) {
                e.printStackTrace();
            } catch (SignatureException e) {
                e.printStackTrace();
            }
        }
        mSystemDefaultTrustManager.checkServerTrusted(chain, authType);
    }
    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return mSystemDefaultTrustManager.getAcceptedIssuers();
    }
}

(2)僅修改 X509TrustManager 所用的根證書庫

private TrustManager[] createX509TrustManager() {
    CertificateFactory cf = null;
    InputStream in = null;
    TrustManager[] trustManagers = null
    try {
        cf = CertificateFactory.getInstance("X.509");
        in = getAssets().open("ca.crt");
        Certificate ca = cf.generateCertificate(in);
        KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
        keystore.load(null, null);
        keystore.setCertificateEntry("ca", ca);
        String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
        tmf.init(keystore);
        trustManagers = tmf.getTrustManagers();
    } catch (CertificateException e) {
        e.printStackTrace();
    } catch (NoSuchAlgorithmException e) {
        e.printStackTrace();
    } catch (KeyStoreException e) {
        e.printStackTrace();
    } catch (IOException e1) {
        e1.printStackTrace();
    } finally {
        if (in != null) {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return trustManagers;
}

4.4 雙向認(rèn)證

服務(wù)端也可能校驗(yàn)客戶端的證書(來確??蛻舳耸呛戏ǖ目蛻舳耍?,這種情況下需要把客戶端預(yù)存的證書導(dǎo)入中間人抓包工具中。

可以參考:
https://www.anquanke.com/post/id/272672文章來源地址http://www.zghlxwxcb.cn/news/detail-446857.html

到了這里,關(guān)于【網(wǎng)絡(luò)安全】https與證書原理 | SSL Pinning及其繞過的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場(chǎng)。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包