浅析 OkHttp 的 TLS 连接过程(3)- 应用实例

本文目录:

6 应用实例

6.1 信任所有证书

  • 跳过系统检验,不再使用系统默认的 SSLSocketFactory
  • 自定义 TrustManager,信任所有证书
            X509TrustManager trustManager = new 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];
                }
            };

            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{trustManager}, null);
            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

            OkHttpClient client = new OkHttpClient.Builder()
                    .sslSocketFactory(sslSocketFactory, trustManager)
                    .build();

            Request request = new Request.Builder()
                    .url("https://kyfw.12306.cn/otn/")
                    .build();

            Call call = client.newCall(request);
            Response response = call.execute();

            Logger.d("response " + response.code());

            response.close();

6.2 信任自签名证书

还是以 12306 来进行测试,先从官网上下载证书 srca.cer

  • 将自签名证书,比如 12306 的 srca.cer,保存到 assets
  • 读取自签名证书集合,保存到 KeyStore 中
  • 使用 KeyStore 构建 X509TrustManager
  • 使用 X509TrustManager 初始化 SSLContext
  • 使用 SSLContext 创建 SSLSocketFactory
            // 获取自签名证书集合,由证书工厂管理
            InputStream inputStream = HttpsActivity.this.getAssets().open("srca.cer");
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            Collection<? extends java.security.cert.Certificate> certificates = certificateFactory.generateCertificates(inputStream);
            if (certificates.isEmpty()) {
                throw new IllegalArgumentException("expected non-empty set of trusted certificates");
            }
            // 将证书保存到 KeyStore 中
            char[] password = "password".toCharArray();
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null, password);
            int index = 0;
            for (Certificate certificate : certificates) {
                String certificateAlias = String.valueOf(index++);
                keyStore.setCertificateEntry(certificateAlias, certificate);
            }
            // 使用包含自签名证书的 KeyStore 构建一个 X509TrustManager
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(keyStore, password);
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keyStore);

            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
                throw new IllegalStateException("Unexpected default trust managers:"
                        + Arrays.toString(trustManagers));
            }

            // 使用 X509TrustManager 初始化 SSLContext
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{trustManagers[0]}, null);
            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

            OkHttpClient client = new OkHttpClient.Builder()
                    .sslSocketFactory(sslSocketFactory, (X509TrustManager) trustManagers[0])
                    .build();

            Request request = new Request.Builder()
                    .url("https://kyfw.12306.cn/otn/")
                    .build();

            Call call = client.newCall(request);
            Response response = call.execute();

            Logger.d("response " + response.code());

            response.close();

6.3 自定义TLS连接规格

比如使用三个安全级别很高的密码套件,并且限制 TLS 版本为 1_2

ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)  
    .tlsVersions(TlsVersion.TLS_1_2)
    .cipherSuites(
          CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
          CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
          CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)
    .build();

OkHttpClient client = new OkHttpClient.Builder() 
    .connectionSpecs(Collections.singletonList(spec))
    .build();

该连接规格的配置是否能够生效,还需要和 SSLSocket 的支持情况取交集,SSLSocket 不支持也就用不了

所以这三个密码套件只能在 Android 5.0 以上的机子生效了

6.4 使用证书锁定

比如锁定了指定 publicobject.com 的证书

pin 的取值为,先对证书公钥信息使用 SHA-256 或者 SHA-1 取哈希,然后进行 Base64 编码,再加上 sha256 或者 sha1 的前缀

这样 publicobject.com 只能使用指定公钥的证书了,安全性进一步提高,但灵活性降低

CertificatePinner certificatePinner = new CertificatePinner.Builder()
    .add("publicobject.com", "sha256/afwiKY3RxoMmLkuRW1l7QsPZTJPwDS2pdDROQjXw8ig=")
    .add("publicobject.com", "sha256/klO23nT2ehFDXCfx3eHTDRESMz3asj1muO+4aIdjiuY=")
    .add("publicobject.com", "sha256/grX4Ta9HpZx6tSHkmCrvpApTQGo67CYDnvprLg5yRME=")
    .add("publicobject.com", "sha256/lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=")
    .build();

OkHttpClient client = new OkHttpClient.Builder()
    .certificatePinner(certificatePinner)
    .build();

资料

猜你喜欢

转载自blog.csdn.net/firefile/article/details/80578239
TLS