0


【网络安全】https与证书原理 | SSL Pinning及其绕过

SSL Pinning

1 HTTPS协议流程

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

HTTPS=HTTP+TLS,其它的协议也类似,如FTPS=FTP+TLS
在这里插入图片描述
1) ClientHello

  • Client 首先发送本地的 TLS 版本、支持的加密算法套件,并且生成一个随机数 R1 。

2)Server Hello

  • Server 端确认 TLS 版本号。从 Client 端支持的加密套件中选取一个,并生成一个随机数 R2 一起发送给 Client。
  • Server 向 Client 发送自己的CA证书(包含公钥、证书签名)。

3)证书校验

  • Client 判断证书签名与CA证书是否合法有效
  • Client 生成随机数pre-master secret,并使用Server发过来的公钥对pre-master secret进行加密,将加密后的pre-master secret送给Server。这一步结束后,Client 与 Server 就都有 R1、R2、pre-master secret 了,两端便可以使用这 3 个随机数独立生成 对称会话密钥了,避免了对称密钥的传输,同时可以 根据会话密钥生成 6 个密钥(P1~P6) 用作后续身份验证

Client端和Server端,最终都会用相同的算法将pre-master secret(预主密钥)转换成master secret(主密钥),通过主密钥可以生成session key。两者后续的通信交互数据,将通过session key进行加密。
在这里插入图片描述
参考:https://www.laoqingcai.com/tls1.2-premasterkey/

4)Client 握手结束通知

  • Client 使用 P1 将之前的握手信息的 hash 值加密并发送给 Server
  • Client 发送握手结束消息

5)Server 握手结束通知

  • Server 计算之前的握手信息的 hash 值,并与 P1 解密客户端发送的握手信息的 hash 对比校验
  • 验证通过后,使用 P2 将之前的握手信息的 hash 值加密并发送给 Client

6)Client 开始HTTPS通讯

  • Client 计算之前的握手信息的 hash 值,并与 P2 解密 Server 发送的握手信息的 hash 对比校验
  • 验证通过后,开始发起 HTTPS 请求。

两者后续的通信交互数据,将通过session key进行加密。所以中间人即使截获数据,也无法解析。

2 证书相关

证书文件

证书=公钥+(公钥+元信息)的签名

图片名称

其中的元信息包括:

  • Subject(主体信息): - Common Name(CN)通用名称- SAN- Organization- Organization Unit(OU)- Country- State- City- Address- Postal code
  • Issuer(签发者信息): - Common Name(CN)通用名称- Organization- Organization Unit(OU)- Country- State- City- Address- Postal code
  • Validity(有效期): - Not Before(签发日期)- Not After(过期时间)
  • Signature Algorithm
  • Serial Number
  • Version
  • Extensions(扩展信息):只在证书版本2、3中才有

因此,证书的结构大致如下:
在这里插入图片描述

CA

签名 = 计算摘要 + 对摘要值私钥加密
CA:Certificate Authority,专门用自己的私钥 给别人进行签名的机构

签发证书的过程

注意,计算签名时,是对整个证书文件计算签名,也就是对【元信息+公钥】计算签名,而不只是对公钥计算签名。
在这里插入图片描述
(参考:https://blog.csdn.net/bluishglc/article/details/123617558)

证书的验证过程

关键过程:用信任CA库里CA证书(公钥),验证网站的证书文件里的签名

  1. 在TLS握手的过程中,客户端得到了网站的证书
  2. 客户端打开证书,查看是哪个CA签名的这个证书
  3. 在自己信任的CA库中,找相应CA的证书(包含CA的公钥),
  4. CA证书里面的公钥解密网站证书上的签名,取出网站证书的摘要,然后用同样的算法(比如sha256)算出网站证书的摘要,如果摘要和签名中的摘要对的上,说明这个证书是合法的,且没被人篡改过
  5. 读出里面的CN,对于网站的证书,里面一般包含的是域名
  6. 检查里面的域名和自己访问网站的域名对不对的上,对的上,就说明这个证书确实是颁发给这个网站的
  7. 到此为止检查通过

证书链的验证

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

我们使用End-user Certificates来确保加密传输数据的公钥(public key)不被篡改,而又如何确保end-user certificates的合法性呢?

这个认证过程跟公钥的认证过程类似,首先获取颁布end-user certificates的CA的证书,然后验证end-user certificates的signature。一般来说,root CAs不会直接颁布end-user certificates的,而是授权给多个二级CA,而二级CA又可以授权给多个三级CA,这些中间的CA就是Intermediates CAs,它们才会颁布end-user certificates。

但是Intermediates Certificates的可靠性又如何保证呢?这就是涉及到证书链,Certificate Chain ,链式向上验证证书,直到Root Certificates,如下图:

在这里插入图片描述
中间CA的证书怎么获取?
以百度的TLS证书进行举例,百度服务器证书 签发者公钥(中间机构公钥)通过下图中的URI获取:
在这里插入图片描述

3 SSL Pinning

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

3.1 原理

默认情况下,只要网站证书的Root CA,属于系统信任的Root CA集合(例如,安卓中系统默认信任 /system/etc/security/ 中CA证书对应的CA)。

对于www.example.com,可能出现以下情况:

(1)情况A:

某个系统信任的Root CA,授权给了可靠的Intermediate CA 1,Intermediate CA 1给www.example.com颁发了一个合法的证书1;

同时该Root CA也授权给了不可靠的Intermediate CA 2(不可靠的原因可能是私钥被泄露),Intermediate CA 2给 www.example.com颁发了一个证书2。

这时候我们希望只信任证书1而不信任证书2,否则一些中间人拿到了证书2,就可以伪装成合法的www.example.com。

这通过修改信任CA集合是较难实现的,因为两个证书的根信任锚是相同的Root CA。当然,可以从信任集中删除Root CA,再添加Intermediate CA 1而不添加Intermediate CA 2。但这意味着我们需要移除Root CA。通常,一个Root CA会作为成千上万个证书的根信任锚,移除Root CA可能引发过大的影响。

(2)情况B:

系统的可信CA集合被篡改。例如,安卓系统在被Root的情况下,用户可以修改系统信任证书(方法例如:https://github.com/doug-leith/cydia)。

这种情况下,app可能需要只信任特定的某个(某些)证书。

原理:

可以采用证书固定。只有当网站的证书链中,至少有一个节点的证书全部内容/证书公钥,跟客户端预埋的证书的内容相匹配,我们的客户端才信任此证书链。

证书固定 与 限制可信CA 的关系

如果把某个Root CA的证书固定起来,那就相当于设置该Root CA为唯一可信的Root CA。

被固定的证书可以是(一般是)某个中间CA的证书。这样,不再是所有以trusted Root CA为根的证书链都仍旧可信了。只有子节点包含该中间CA的证书链才可信。

被固定的证书的Root CA可以不在系统trusted Root CA集合中。

3.2 实现方案

具体实现技术上,SSL Pinning可以分为Certificate Pinning(证书固定)和Public Key Pinning(公钥固定)

3.2.1 证书固定

把证书文件打包进安装包,将app设置为仅接受指定的内置证书,而不接受操作系统内置的CA根证书对应的任何证书。

3.2.2 公钥固定

提取证书中的公钥并内置到App中,通过与服务器对比公钥值,来验证连接的合法性。我们在申请证书时,公钥在证书的续期前后可以保持不变,所以可以解决证书有效期问题。

3.3 实例

3.3.1 证书固定实例:基于TrustManagerFactory

// kotlin语法// 加载证书文件val cf: CertificateFactory = CertificateFactory.getInstance("X.509")val caInput: InputStream =BufferedInputStream(FileInputStream("load-der.crt"))// 使用CertificateFactory生成一个X509Certificate的实例val ca: X509Certificate = caInput.use{
    cf.generateCertificate(it)as X509Certificate
}
System.out.println("ca="+ ca.subjectDN)// 创建一个KeyStore实例,并把前边的X509Certificate实例加进去,并起一个别名"ca"val keyStoreType = KeyStore.getDefaultType()val keyStore = KeyStore.getInstance(keyStoreType).apply{load(null,null)setCertificateEntry("ca", ca)}// 创建一个TrustManagerFactory实例,并且使用前边的KeyStore实例进行初始化val tmfAlgorithm: String = TrustManagerFactory.getDefaultAlgorithm()val tmf: TrustManagerFactory = TrustManagerFactory.getInstance(tmfAlgorithm).apply{init(keyStore)}// 创建一个SSLContext实例,并且使用前面的TrustManagerFactory实例的trustManagers进行初始化val context: SSLContext = SSLContext.getInstance("TLS").apply{init(null, tmf.trustManagers,null)}// 创建HttpsURLConnection实例urlConnectionval url =URL("https://certs.cac.washington.edu/CAtest/")val urlConnection = url.openConnection()as HttpsURLConnection

// 将SSLContext实例context的socketFactory属性,赋值给urlConnection
urlConnection.sslSocketFactory = context.socketFactory

val inputStream: InputStream = urlConnection.inputStream
copyInputStreamToOutputStream(inputStream, System.out)

3.3.2 证书固定实例:基于NSC配置文件

需要在Manifest文件的android:networkSecurityConfig属性加上对应的配置内容,示例如下:

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

关于NSC的详细内容,可以参考论文:
[USENIX Sec’21] Why Eve and Mallory Still Love Android: Revisiting TLS (In)Security in Android Applications

或者直接参考Google的官网文档:
https://developer.android.com/training/articles/security-config

计划后续写一篇博客详细介绍Google Android的NSC。

4 安卓中的SSL Pinning

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

SSL Pinning机制中,客户端将特定域名的证书与特定的签发者绑定。即,对某个域名,客户端只承认特定CA为该域名签发的证书,而不承认其它 CA 为该域名签发的证书。

4.1 Android 的根证书管理

AOSP 源码库中,CA 根证书主要存放在 system/ca-certificates 目录下,而在 Android 系统中,则存放在 /system/etc/security/ 目录下:

在这里插入图片描述
cacerts_google 目录下的根证书,主要用于 system/update_engine、external/libbrillo 和 system/core/crash_reporter 等模块

cacerts 目录下的根证书则用于所有的应用。cacerts 目录下的根证书,即 Android 系统的根证书库,像下面这样:
在这里插入图片描述
它们都是 PEM 格式的 X.509 证书。

Android 系统通过 SystemCertificateSource、DirectoryCertificateSource 和 CertificateSource 等类管理系统根证书库。

  • CertificateSource定义了可以对根证书库执行的操作,主要是对根证书的获取和查找: 位于frameworks/base/core/java/android/security/net/config/CertificateSource.java
  • DirectoryCertificateSource 类提供证书的创建、获取和查找操作: 位于frameworks/base/core/java/android/security/net/config/DirectoryCertificateSource.java 获取根证书库的 getCertificates() 操作在第一次被调用时,遍历文件系统,并加载系统所有的根证书文件,并缓存起来,以备后面访问。 根证书的查找操作,主要依据证书文件的文件名进行,证书文件被要求以 [SubjectName 的哈希值].[Index] 的形式命名。
  • SystemCertificateSource 类定义了系统根证书库的路径,以及无效一个根证书的机制: 位于frameworks/base/core/java/android/security/net/config/SystemCertificateSource.java Android 系统的根证书位于 /system/etc/security/cacerts/ 目录下。用户可以通过将特定根证书复制到用户配置目录的 cacerts-removed 目录下来无效一个根证书。

4.2 证书链合法性验证

OpenSSLSocketImpl.startHandshake() 通过 NativeCrypto 类SSL_do_handshake() 方法执行握手操作:

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

SSL_do_handshake() 方法的第三参数是一个接口:SSLHandshakeCallbacks
在这里插入图片描述

SSLHandshakeCallbacks是NativeCrypto 类定义的接口,其中包含一组回调函数;这组回调函数,是SSL_do_handshake() 的参数,在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.
 */publicinterfaceSSLHandshakeCallbacks{/**
     * 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
     */publicvoidverifyCertificateChain(long sslSessionNativePtr,long[] certificateChainRefs,String authMethod)throwsCertificateException;

verifyCertificateChain()的参数:

  • 指向一个sslSession的指针
  • X.509 证书链
  • 认证算法名称

SSLHandshakeCallbacks中的回调方法的实现在 OpenSSLSocketImpl 。 OpenSSLSocketImpl中,verifyCertificateChain()的实现如下:

@SuppressWarnings("unused")// used by NativeCrypto.SSLHandshakeCallbacks@OverridepublicvoidverifyCertificateChain(long sslSessionNativePtr,long[] certRefs,String authMethod)throwsCertificateException{try{X509TrustManager x509tm = sslParameters.getX509TrustManager();if(x509tm ==null){thrownewCertificateException("No X.509 TrustManager");}if(certRefs ==null|| certRefs.length ==0){thrownewSSLException("Peer sent no certificate");}OpenSSLX509Certificate[] peerCertChain =newOpenSSLX509Certificate[certRefs.length];for(int i =0; i < certRefs.length; i++){
            peerCertChain[i]=newOpenSSLX509Certificate(certRefs[i]);}// Used for verifyCertificateChain callback
        handshakeSession =newOpenSSLSessionImpl(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){thrownewCertificateException("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){thrownewCertificateException(e);}finally{// Clear this before notifying handshake completed listeners
        handshakeSession =null;}}

这里面,verifyCertificateChain() 从 OpenSSLSocketImpl的 sslParameters 获得 X509TrustManager:

X509TrustManager x509tm = sslParameters.getX509TrustManager();

然后在 Platform.checkServerTrusted() 中执行服务端证书合法有效性的检查:

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

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

publicstaticvoidcheckServerTrusted(X509TrustManager tm,X509Certificate[] chain,String authType,OpenSSLSocketImpl socket)throwsCertificateException{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()会调用X509TrustManager.checkServerTrusted()来完成检查。
其中的X509TrustManager实例来源于OpenSSLSocketImpl 的sslParameters,如前文所述:

X509TrustManager x509tm = sslParameters.getX509TrustManager();

那OpenSSLSocketImpl 的 sslParameters 又来自于哪里呢?来源于构造函数,例如:

protectedOpenSSLSocketImpl(SSLParametersImpl sslParameters)throwsIOException{this.socket =this;this.peerHostname =null;this.peerPort =-1;this.autoClose =false;this.sslParameters = sslParameters;}

而OpenSSLSocketFactoryImpl类会实例化OpenSSLSocketImpl:

packageorg.conscrypt;importjava.io.IOException;importjava.net.InetAddress;importjava.net.Socket;importjava.net.UnknownHostException;importjava.security.KeyManagementException;publicclassOpenSSLSocketFactoryImplextendsjavax.net.ssl.SSLSocketFactory{privatefinalSSLParametersImpl sslParameters;privatefinalIOException instantiationException;

    …………

    @OverridepublicSocketcreateSocket()throwsIOException{if(instantiationException !=null){throw instantiationException;}returnnewOpenSSLSocketImpl((SSLParametersImpl) sslParameters.clone());}@OverridepublicSocketcreateSocket(String hostname,int port)throwsIOException,UnknownHostException{returnnewOpenSSLSocketImpl(hostname, port,(SSLParametersImpl) sslParameters.clone());}@OverridepublicSocketcreateSocket(String hostname,int port,InetAddress localHost,int localPort)throwsIOException,UnknownHostException{returnnewOpenSSLSocketImpl(hostname,
                                     port,
                                     localHost,
                                     localPort,(SSLParametersImpl) sslParameters.clone());}@OverridepublicSocketcreateSocket(InetAddress address,int port)throwsIOException{returnnewOpenSSLSocketImpl(address, port,(SSLParametersImpl) sslParameters.clone());}@OverridepublicSocketcreateSocket(InetAddress address,int port,InetAddress localAddress,int localPort)throwsIOException{returnnewOpenSSLSocketImpl(address,
                                     port,
                                     localAddress,
                                     localPort,(SSLParametersImpl) sslParameters.clone());}}

后面的细节暂时略过不看。

总结:

OpenSSLSocketImpl.startHandshake() 和 NativeCrypto.SSL_do_handshake() 执行完整的 SSL/TLS 握手过程。

证书合法性验证是 SSL/TLS 握手的一个重要步骤。该过程通过 native层调用Java 层的回调方法 SSLHandshakeCallbacks.verifyCertificateChain() 来完成。

回调方法的实现在OpenSSLSocketImpl。

OpenSSLSocketImpl.verifyCertificateChain()调用Platform.checkServerTrusted(),调用RootTrustManager.checkServerTrusted() ,调用NetworkSecurityTrustManager.checkServerTrusted() ,将真正根据系统根证书库执行证书合法性验证的 TrustManagerImpl 和 SSL/TLS 握手过程结合起来。

OpenSSLSocketFactoryImpl 将 OpenSSLSocketImpl 和 SSLParametersImpl 粘起来。

SSLParametersImpl 将 OpenSSLSocketImpl 和 RootTrustManager 粘起来。

NetworkSecurityConfig 将 RootTrustManager 和 NetworkSecurityTrustManager
粘起来。

NetworkSecurityConfig、NetworkSecurityTrustManager 和
TrustedCertificateStoreAdapter 将 TrustManagerImpl 和管理系统根证书库的
SystemCertificateSource 粘起来。

TrustManagerImpl 是证书合法性验证的核心,它会查找系统根证书库,并验证服务端证书的合法性做。

这个过程的调用栈如下:

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)

在实际的开发过程中,有时为了节省昂贵的购买证书的费用,而想要自己给自己的服务器的域名签发域名证书,这即是私有 CA 签名的证书。为了能够使用这种证书,需要在客户端预埋根证书,并对客户端证书合法性验证的过程进行干预,通过我们预埋的根证书为服务端的证书做合法性验证,而不依赖系统的根证书库。

要想定制 OpenSSLSocketImpl 的证书验证过程,必然要改变 SSLParametersImpl;要改变 OpenSSLSocketImpl 的 SSLParametersImpl,则必然需要修改 SSLSocketFactory。修改 SSLSocketFactory 常常是一个不错的方法。

两种实现手段:
(1)自己实现 X509TrustManager
像下面这样:

privatefinalclassHelloX509TrustManagerimplementsX509TrustManager{privateX509TrustManager mSystemDefaultTrustManager;privateX509Certificate mCertificate;privateHelloX509TrustManager(){
        mCertificate =loadRootCertificate();
        mSystemDefaultTrustManager =systemDefaultTrustManager();}privateX509CertificateloadRootCertificate(){String certName ="netease.crt";X509Certificate certificate =null;InputStream certInput =null;try{
            certInput =newBufferedInputStream(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;}privateX509TrustManagersystemDefaultTrustManager(){try{TrustManagerFactory trustManagerFactory =TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init((KeyStore)null);TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();if(trustManagers.length !=1||!(trustManagers[0]instanceofX509TrustManager)){thrownewIllegalStateException("Unexpected default trust managers:"+Arrays.toString(trustManagers));}return(X509TrustManager) trustManagers[0];}catch(GeneralSecurityException e){thrownewAssertionError();// The system has no TLS. Just give up.}}@OverridepublicvoidcheckClientTrusted(X509Certificate[] chain,String authType)throwsCertificateException{
        mSystemDefaultTrustManager.checkClientTrusted(chain, authType);}@OverridepublicvoidcheckServerTrusted(X509Certificate[] chain,String authType)throwsCertificateException{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);}@OverridepublicX509Certificate[]getAcceptedIssuers(){return mSystemDefaultTrustManager.getAcceptedIssuers();}}

(2)仅修改 X509TrustManager 所用的根证书库

privateTrustManager[]createX509TrustManager(){CertificateFactory cf =null;InputStream in =null;TrustManager[] trustManagers =nulltry{
        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 双向认证

服务端也可能校验客户端的证书(来确保客户端是合法的客户端),这种情况下需要把客户端预存的证书导入中间人抓包工具中。

可以参考:
https://www.anquanke.com/post/id/272672

标签: web安全

本文转载自: https://blog.csdn.net/qq_39441603/article/details/126183031
版权归原作者 Jouzzy 所有, 如有侵权,请联系我们删除。

“【网络安全】https与证书原理 | SSL Pinning及其绕过”的评论:

还没有评论