开发者

Android OKHttp源码解析Https安全处理

目录
  • Https
    • 加密知识
      • 秘钥
      • 对称加密
      • 非对称加密
      • Https的方案
    • TSL握手
      • 随机数计算
      • OkHttp的设计
        • 涉及的类
          • OkHttpClient配置阶段
            • 单独配置SSLSocketFactory
            • 同时配置SSLSocketFactory和X509TrustManager
            • 配置HostnameVerifier
            • 配置CertificatePinner
          • OkHttpClient参数处理阶段
            • X509TrustManager trustManager = Util.platformTrustManager();
            • this.sslSocketFactory = newSslSocketFactory(trustManager);
            • this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
          • OkHttp连接Https阶段
            • 信任所有证书
            • 配置自签名证书
        • 配置自签名证书

          Https

          Https是Http协议加上下一层的SSL/TSL协议组成的,TSL是SSL的后继版本,差别很小,可以理解为一个东西。进行Https连接时,会先进行TSL的握手,完成证书认证操作,产生对称加密的公钥、加密套件等参数。之后就可以使用这个公钥进行对称加密了。

          Https的加密方式同时使用了非对称加密和对称加密:

          • 使用反向的非对称加密对证书进行签名
          • 在检查通过的证书公钥基础上,利用非对称加密产生对称加密的公钥
          • 使用产生的公钥,利用对称加密交互传输的数据。

          上面就是Https工作的大致流程,下面详细介绍下加密的知识和握手。

          加密知识

          对于加密这种技术,很早很早之前就有了。没有加密的数据,称为明文,经过加密的叫做密文。Http默认都是明文传输,这种方式很容易被监听或者修改。加密的最终目的,是保证机密性、完整性、可用性。

          密码是一套加密算法,使用计算机之前都是使用机械式后者密码本进行操作。在使用计算机之后,加密的安全程度愈来越高,但是被解密也愈来愈容易。

          秘钥

          如果光有密码和原始数据,那么破解会简单很多,因为只要知道了密码的加密方式,反向操作即可拿到明文数据。所以为了增加难度,增加了秘钥。

          现在密码就需要两个参数进行计算了,秘钥+明文+密码==密文。只拿到密码和密文,是不能获取原始明文,还需要秘钥。破解的难度就更大了。

          用秘钥加密的技术,又因为加密和解密秘钥的情况分两种。

          对称加密

          加密和解密的秘钥是相同的,这种加密方式被称为对称加密,使用的秘钥被称为公钥。

          对称加密的速度很快,但是服务器需要把自己的公钥传到客户端,客户端使用这个公钥对数据进行加密,服务器使用同样的公钥进行解密。

          但是这种方式没有办法防止中间人攻击,如果中间人篡改了传输的公钥,使用自己的公钥代替他,那么接可以截取发送方的数据,使用自己的公钥进行解密。

          非对称加密

          加密和解密的秘钥是不同的,这种加密方式被称为非对称加密,进行加密的是公共的公钥,而进行解密的叫做私钥。这样只有私钥的拥有者才可以解密数据。

          反向的非对称加密是数字签名,也就是使用私钥进行加密,使用公共的公钥的进行解密,这样就可以鉴定发送者的身份。也就是只有发送者才有私钥。

          非对称加密的缺点是速度很慢,同样也没有办法防止中间人攻击,中间人截获了服务器的公钥。并用自己的公钥代替,这样也可以获取发送者的数据。

          Https的方案

          因为对称和非对称加密都有自己的问题,都是因为公钥的传递没法保证安全性,中间人可以通过替换成自己的公钥,完成截取的工作。

          Https使用了混合的方式,同时使用了两种方式,使用非对称加密产生对称加密的公钥,再通过对称加密进行处理。首先对称加密比较快速相对于非对称加密。所以还是使用对称加密比较好,那么对称加密的缺点时怎么保证公钥能够安全的交换呢。

          这里可以使用非对称加密传输这段公钥。这样这段公钥就可以被安全的传输。因为只有服务器的私钥才可以进行解密。非对称加密有什么问题呢,就是不能判断收到的公钥是否就是真正的公钥,是不是被篡改或者替换。怎么保证受到的公钥就是合法的公钥呢?

          那就需要一个机构来给这个公钥背书,可以通过它保证这个公钥是合法的,而承载公钥的载体就是证书。客户端通过证书进行验证,完成公钥的获取。之后就可以通过这个公钥完成非对称加密传输。协商对称加密所用的公钥。

          Https的方案大体就是这样。 以上就是Https的基础知识。下面分析下TSL的握手细节。

          TSL握手

          TSL的握手主要的目的要协商加密的算法、对称加密的公钥、TSL/SSL版本。

          首先通过连接到服务器的443端口,通过TCP连接,这段是明文传输,用于沟通上面所说的参数。建立完成连接后就可以开始进行握手操作了。

          Android OKHttp源码解析Https安全处理

          握手如上图所示,逐条分析下

          • 客户端发送 client hello的报文,发送了客户端支持的协议版本、密码套件、随机数、压缩算法等,服务器要在这之中选中一个自己支持的,如果自己都不支持,那么就会断开连接。
          • 服务器返回 server hello报文,内含选中的版本、密码套件、随机数、压缩算法等。并会返回自己的证书。
          • 客户端收到证书后,检验这个证书,检验分四步:时间有效性检查、签发的颁发者可信度检测、签名检测、站点名称检测。如果四项检测都通过了,那么就会取出证书中的公钥。
          • 通过上面产生的随机数,产生了Pre-master secret,该报文使用从证书中解密获得的公钥进行加密(其实就是服务器的公钥),并通过公钥加密传输到服务端。通过这个数通过DH算法计算出MAC报文摘要和对称加密的公钥。 上面的方式就产生了可以进行对称加密的公钥。下面发送的数据就可以通过这个公钥开始对称加密了。

          没有用到数字证书? 传输的证书使用了数字证书也就是反向的对称加密,当收到证书,检验通过后,会使用CA的公钥进行检测,也就是CA使用了自己的私钥进行了加密,只有CA知道私钥。

          随机数怎么计算的?可以参考这里

          随机数计算

          传输过程中,会涉及3个随机数,客户端产生的/服务端产生的/Pre-master secret。 在传输Pre-master secret时,会使用从证书获取的公开秘钥,只有服务器才可以解密,对于客户端:

          当其生成了Pre-master secret之后,会结合原来的A、B随机数,用DH算法计算出一个master secret,紧接着根据这个master secret推导出hash secretsession secret

          对于服务端:

          当其解密获得了Pre-master secret之后,会结合原来的A、B随机数,用DH算法计算出一个master secret,紧接着根据这个master secret推导出hash secretsession secret

          在客户端和服务端的master secret是依据三个随机数推导出来的,它是不会在网络上传输的,只有双方知道,不会有第三者知道。同时,客户端推导出来的session secrethash secret与服务端也是完全一样的。

          那么现在双方如果开始使用对称算法加密来进行通讯,使用哪个作为共享的密钥呢?过程是这样子的:

          双方使用对称加密算法进行加密,用hash secret对HTTP报文 做一次运算生成一个MAC,附在HTTP报文的后面,然后用session-secret加密所有数据(HTTP+MAC),然后发送。

          接收方则先用session-secret解密数据,然后得到HTTP+MAC,再用相同的算法计算出自己的MAC,如果两个MAC相等,证明数据没有被篡改。

          OkHttp的设计

          OkHttp是支持自动的Https连接的,也就是我们默认访问一个Https的网站,会自动的完成TSL的握手和加密。但是对于自签名的证书还是需要我们进行配置的。

          涉及的类

          • ConnectionSpec :连接的参数配置,包括SSL/TLS的版本、密码套件等,这个在OkHttpClient#connectionSpecs进行配置,默认是具有SNI和ALPN等扩展功能的现代TLS和clear text即明文传输。SSL握手的前两部就是沟通这部分参数的。
          • CertificateChainCleaner:证书链清理工具,用于省略无用的证书,过滤出一个列表,最后一个链结是受信任的证书。
          • X509TrustManager:此接口的实例管理哪些 X509 证书可用于验证安全套接字的远程端。 决策可能基于受信任的证书颁发机构、证书撤销列表、在线状态检查或其他方式。这个类对应上面证书检测的签发的颁发者可信度检测、签名检测、时间有效性检查。
          • HostnameVerifier:验证主机名是否与服务器的身份验证方案匹配。可以基于证书,也可以基于其他方式。这个用于上面说的验证证书的站点名称检测。
          • X509Certificate:X.509 证书的抽象类。 这提供了一种访问 X.509 证书所有属性的标准方法。现有的证书都是X509类型的,这时一个标准。
          • SSLSocketFactory:这个是jdk提供的工具,负责SSLSocketSSLSocket可以调用handShake进行ssl的握手。
          • CertificatePinner:固定证书配置,用于对握手通过的证书做固定验证,也就是证书必须满足固定证书的配置。 上面的类不但有jdk还有OkHttp的工具,共同完成了Https的工作。OkHttp大部分利用了jdk关注Https的支持。

          OkHttpClient配置阶段

          OkHttpClient作为OkHttp的入口,可以对上面的类进行配置。看下在buidler里是怎么进行配置的。

          单独配置SSLSocketFactory

          设置用于保护 HTTPS 连接的套接字工厂。如果未设置,将使用系统默认值。

          public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory) {
            if (sslSocketFactory == null) throw new NullPointerException("sslSocketFactory == null");
            this.sslSocketFactory = sslSocketFactory;
            this.certificateChainCleaner = Platform.get().buildCertificateChainCleaner(sslSocketFactory);
            return this;
          }
          

          同时配置SSLSocketFactory和X509TrustManager

          可以通过sslSocketFactory方法,配置上面的两个参数,正常情况下,我们不需要配置,只需要采用系统默认的配置即可。

          public Builder sslSocketFactory(
              SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) {
            if (sslSocketFactory == null) throw new NullPointerException("sslSocketFactory == null");
            if (trustManager == null) throw new NullPointerException("trustManager == null");
            this.sslSocketFactory = sslSocketFactory;
            this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
            return this;
          }
          

          配置HostnameVerifier

          可以通过hostnameVerifier()配置hostnameVerifier,以达到我们检测证书的站点名称。

          public Builder hostnameVerifier(HostnameVerifier hostnameVerifier) {
            if (hostnameVerifier == null) throw new NullPointerException("hostnameVerifier == null");
            this.hostnameVerifier = hostnameVerifier;
            return this;
          }
          

          HostnameVerifier是一个接口,我们只要调用它的verify方法就可以完成校验,这个操作发生在Https握手完成后,系统提供了AbstractVerifier骨架类进行配置。默认是OkHostnameVerifier

          配置CertificatePinner

          设置固定证书,我们可以创建一个CertificatePinner,CertificatePinner是一个实现好的类,我们只要传入主机名称和证书的SHA-256或者SHA-1 hashes即可。握手守信的证书必须通过配置的固定证书。如果不满足,就会抛出异常,停止链接。

          public Builder certificatePinner(CertificatePinner certificatePinner) {
            if (certificatePinner == null) throw new NullPointerException("certificatePinner == null");
            this.certificatePinner = certificatePinner;
            return this;
          }
          

          OkHttpClient参数处理阶段

          上面我们可以通过builder配置参数,那么参数是如何进行处理的呢。我们配置不配置一个参数又有什么不同呢?

          boolean isTLS = false;
          for (ConnectionSpec spec : connectionSpecs) {
            isTLS = isTLS || spec.isTls();
          }
          if (builder.sslSocketFactory != null || !isTLS) {
            // 自定义或不使用Https
            this.sslSocketFactory = builder.sslSocketFactory;
            this.certificateChainCleaner = builder.certificateChainCleaner;
          } else {
            // 默认配置
            X509TrustManager trustManager = Util.platformTrustManager();
            this.sslSocketFactory = newSslSocketFactory(trustManager);
            this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
          }
          if (sslSocketFactory != null) {
            Platform.get().configureSslSocketFactory(sslSocketFactory);
          }
          

          参数的处理代码如上所示,先获取链接的配置是否是TSL,除了明文连接外,都是使用TSL的。如果我们配置了自己的sslSocketFactory或者不是TSL连接(没有配置sslSocketFactory),那么都会使用builde人内部的sslSocketFactory。也就是说配置了,就使用配置的,没有配置,如果当前不支持TSL,那么sslSocketFactory就为空。

          如果没有配置并且是TSL连接的话,这里就会使用默认的配置。这里的逻辑是先获取X509TrustManager,再通过X509TrustManager获取SslSocketFactory,通过SslSocketFactory再获取CertificateChainCleaner。整体的依赖关系就是这样。后面配置自定义证书时,也会使用这个依赖链。 依次看下每个过程:

          X509TrustManager trustManager = Util.platformTrustManager();

          public static X509TrustManager platformTrustManager() {
            try {
              TrustManagerFactory trustManagerF编程客栈actory = 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 assertionError("No System TLS", e); // The system has no TLS. Just give up.
            }
          }
          

          通过TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( TrustManagerFactory.getDefaultAlgorithm());获取默认的TrustManager工厂,调用init方法,这个方法传入秘钥库,并使用证书颁发机构和相关信任材料的来源初始化此工厂。通常使用传入的KeyStore作为做出信任决策的基础。我们自签名的证书也会通过这个方法进行配置。

          后面只饿极取出trustManagerFactory.getTrustManagers(),拿数组第一个作为最终的X509TrustManager。

          this.sslSocketFactory = newSslSocketFactory(trustManager);

          private static SSLSocketFactory newSslSocketFactory(X509TrustManager trustManager) {
            try {
              SSLContext sslContext = Platform.get().getSSLContext();
              sslContext.init(null, new TrustManager[] { trustManager }, null);
              return sslContext.getSocketFactory();
            } catch (GeneralSecurityException e) {
              throw assertionError("No System TLS", e); // The system has no TLS. Just give up.
            }
          }
          

          获取X509TrustManager后,这里通过Platform.get().getSSLContext()获取SSLContext。

          Platform.get()通过反射获取了不同平台的配置工具,这样OKHttp就可以运行在不同的平台上。获取SSLContext后,就可以init方法,对SSLContext进行配置,调用getSocketFactory获取最终的SSLSocketFactory。

          this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);

          这里配置了CertificateChainCleaner,获取trustManager中的可以用于验证对等方的证书,之后创建一个BasicCertificateChainCleaner

          通过上面的两个配置的步骤,就完成了配置阶段,看看在连接时是怎么使用Https的。

          OkHttp连接Https阶段

          Https的握手发生在Http连接之后,在ConnectInterceptor这个连接拦截器中。在调用完connectSocket后,就开始进行SSL的握手。因为Https需要默认连接443 端口,但是Http会连接80端口,这个逻辑是在哪儿配置的呢。在我们构建请求的Request传入的HttpUrl中,有一个port字段就是用于确定端口的。在获取端口时,如果没有进行显式的配置。就会根据defaultPort()进行配置。逻辑也比较简单。所以connectSocket会直接连接443端口,为下面的SSL握手做了准备。

          public static int defaultPort(String scheme) {
            if (sjavascriptcheme.equals("http")) {
              return 80;
            } else if (scheme.equals("http://www.devze.comhttps")) {
              return 443;
            } else {
              return -1;
            }
          }
          

          进行SSL连接主要通过connectTls进行。 通过下面的方法进行判断。如果sslSocketFactory不为null,那么就会使用Https进行连接。

          if (route.address().sslSocketFactory() == null)
          
          private void connectTls(ConnectionSpecSelector connectionSpecSelector) throws IOException {
            Address address = route.address();
            SSLSocketFactory sslSocketFactory = address.sslSocketFactory();
            boolean success = false;
            SSLSocket sslSocket = null;
            try {
              // 创建SSLSocket,是对原始Socke的包装
              sslSocket = (SSLSocket) sslSocketFactory.createSocandroidket(
                  rawSocket, address.url().host(), address.url().port(), true /* autoClose */);
              // 配置SSL版本和密码套件
              ConnectionSpec connectionSpec = connectionSpecSelector.configureSecureSocket(sslSocket);
              if (connectionSpec.supportsTlsExtensions()) {
                // 配置SSL扩展
                Platform.get().configureTlsExtensions(
                    sslSocket, address.url().host(), address.protocols());
              }
              // 进行握手
              sslSocket.startHandshake();
              // 等待握手完成
              SSLSession sslSocketSess编程客栈ion = sslSocket.getSession();
              Handshake unverifiedHandshake = Handshake.get(sslSocketSession);
              // 进行证书域名确定
              if (!address.hostnameVerifier().verify(address.url().host(), sslSocketSession)) {
                List<Certificate> peerCertificates = unverifiedHandshake.peerCertificates();
                if (!peerCertificates.isEmpty()) {
                  X509Certificate cert = (X509Certificate) peerCertificates.get(0);
                  throw new SSLPeerUnverifiedException(
                      "Hostname " + address.url().host() + " not verified:"
                          + "\n    certificate: " + CertificatePinner.pin(cert)
                          + "\n    DN: " + cert.getSubjectDN().getName()
                          + "\n    subjectAltNames: " + OkHostnameVerifier.allSubjectAltNames(cert));
                } else {
                  throw new SSLPeerUnverifiedException(
                      "Hostname " + address.url().host() + " not verified (no certificates)");
                }
              }
              // 检测固定证书
              address.certificatePinner().check(address.url().host(),
                  unverifiedHandshake.peerCertificates());
              // 握手成功,获取Http协议
              String maybeProtocol = connectionSpec.supportsTlsExtensions()
                  ? Platform.get().getSelectedProtocol(sslSocket)
                  : null;
              socket = sslSocket;
              source = Okio.buffer(Okio.source(socket));
              sink = Okio.buffer(Okio.sink(socket));
              handshake = unverifiedHandshake;
              protocol = maybeProtocol != null
                  ? Protocol.get(maybeProtocol)
                  : Protocol.HTTP_1_1;
              success = true;
            } catch (AssertionError e) {
              if (Util.isandroidGetsocknameError(e)) throw new IOException(e);
              throw e;
            } finally {
              if (sslSocket != null) {
                Platform.get().afterHandshake(sslSocket);
              }
              if (!success) {
                closeQuietly(sslSocket);
              }
            }
          }
          

          逻辑比较清晰,逐条分析下

          • 通过sslSocketFactory创建SSLSocket。通过SSLSocket可以直接进行执行SSL的握手。
          • 传入上面讲到的TSL版本和密码套件
          • 配置SSL的扩展,这里如果ALPN的扩展,会写上使用的Http版本,握完手后会取这个配置,并判断是否使用Http2.0版本。
          • 调用sslSocket.startHandshake(),进行握手。这时一个同步的操作,会阻塞当前线程,直到握手成功,如果中间出了什么问题,那么会直接抛出异常。
          • 完成握手后会获取Handshake数据,执行到这里说明握手已经成功了,服务器的证书已经被信任了。证实的信息就在Handshake中。

          通过我们传域名检测完成域名检测,也就是hostnameVerifier类,调用它的verify方法,通过返回的boolean值,进行判断。默认的值时OkHostnameVerifierverify方法实现如下。这里检测了host和ip的值。如果不一致,可能证书被替换了。

          public boolean verify(String host, X509Certificate certificate) {
            return verifyAsIpAddress(host)
                ? verifyIpAddress(host, certificate)
                : verifyHostname(host, certificate);
          }
          
          • 通过CertificatePinner固定证书检测,调用check进行检测。如果当前受信的证书不满足固定的配置,那么就不能继续请求。固定证书的威力很大,如果配置了,那么后续的版本必须满足这个固定的配置,所以一直要商量好。
          • 所有的检查通过,握手成功。这时就是获取配置的时候了。比如商议的Http版本和连接成功的输入输出流,之后的传输,也会通过SSlSocket的输入输出进行配置了。 以上就完成了SSL的握手和配置。

          在实际应用中我们可能需要配置自己的证书,如果完全使用CA的证书,我们是不需要配置什么的,使用默认配置即可,但是还是有些场景需要自己动手配置Https。最常见的情形就是配置自签名的证书,服务器给我们一个根证书,我们配置在本地,在握手阶段,服务器给出的证书,会受这个根证书的认证。这样既完成了自签名证书的配置。下面是一些场景和常用的OkHttp的代码配置。

          配置自签名证书

          信任所有证书

          这是一种非常不安全的配置,这么配置,会导致毫无安全性可言。但是有些场景还是可以暂时使用的。

          static class HttpsTrustAllCertsTrustManager implements X509TrustManager {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            }
            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            }
            @Override
            public X509Certificate[] getAcceptedIssuers() {
              return new X509Certificate[0]; //返回长度为0的数组,相当于return null
            }
            public static SSLSocketFactory createSSLSocketFactory() {
              SSLSocketFactory sSLSocketFactory = null;
              try {
                SSLContext sc = Platform.get().getSSLContext();
                sc.init(null, new TrustManager[]{new HttpsTrustAllCertsTrustManager()},new SecureRandom());
                sSLSocketFactory = sc.getSocketFactory();
              } catch (Exception e) {
              }
              return sSLSocketFactory;
            }
          }
          static class TrustAllHostnameVerifier implements HostnameVerifier {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
              return true;
            }
          }
          //构建OkHttpClient
          OkHttpClient mClient =
              new OkHttpClient.Builder()
                  .sslSocketFactory(HttpsTrustAllCertsTrustManager
                      .createSSLSocketFactory(), new HttpsTrustAllCertsTrustManager())
                  .hostnameVerifier(new TrustAllHostnameVerifier())
                  .build();
          

          上面共配置了两个变量,SslSocketFactory和HostnameVerifier。

          • 第一个变量依赖X509TrustManager。这个认证中心,我们需要给一个空实现,这样就会信任所有的证书,创建的模式和OkHttpClient创建默认的配置套路一样。
          • 第二个HostnameVerifier,如果我们不进行配置,会走一个默认的OkHostnameVerifier,如果不设置也会验证域名。所以还需要实现一个自定义的验证期,永远返回true。

          这样经过两个两步的设置,就完成了所有证书的配置工作。这种模式可以配合固定证书使用,也就是服务器的证书只能满足固定的规则才可以,也不失是一种策略。

          配置自签名证书

          对于自签名开发者_开发教程的证书,一般都是一个根证书,服务器返回的证书,使用这个根证书就可以进行认证。我们的任务就是配置这个默认的自签名证书进入OkHttp的配置。

          try {
            CertificateFactory cf = CertificateFactory.getInstance("X.509");
            //获取证书输入流
            InputStream caInput = null;
            Certificate ca;
            try {
                ca = cf.generateCertificate(caInput);
            } finally {
                caInput.close();
            }
            // 创建KeyStore,穿入证书
            String keyStoreType = KeyStore.getDefaultType();
            KeyStore keyStore = KeyStore.getInstance(keyStoreType);
            keyStore.load(null, null);
            keyStore.setCertificateEntry("ca", ca);
            // 创建TrustManagerFactory,用于生成TrustManager
            TrustManagerFactory tmf =
                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            tmf.init(keyStore);
            SSLContext s = SSLContext.getInstance("TLSv1", "AndroidOpenSSL");
            s.init(null, tmf.getTrustManagers(), null);
            return s.getSocketFactory();
          } catch (Exception e) {
            e.printStackTrace();
          } 
          

          上面信任所有证书,我们只是自己实现了一个TrustManager,但是在配置自签名证书的时候,就需要通过TrustManagerFactory获取了。和上面配置的主要区别,也在于TrustManager的创建。

          • 获取证书的输入流,构建Certificate
          • 获取KeyStore,通过传入证书Certificate
          • 创建TrustManagerFactory,并调用init,初始化KeyStore
          • 通过SSLContext的init方法获取SSLSocketFactory

          通过传入的SSLSocketFactory,传入OkHttpClient就可以了。整体逻辑还是比较简单的。

          以上就是Android OKHttp源码解析Https安全处理的详细内容,更多关于Android OKHttp Https安全处理的资料请关注我们其它相关文章!

          0

          上一篇:

          下一篇:

          精彩评论

          暂无评论...
          验证码 换一张
          取 消

          最新开发

          开发排行榜