OkHttp源码学习之CertificateChainCleaner

235 阅读3分钟

OkHttp 中,CertificateChainCleaner 是一个工具类,用于清理和验证服务器证书链(Certificate Chain)。它主要用于确保客户端接收到的证书链是可信的,并且能够正确地通过本地的信任管理器(TrustManager)的验证。

1. 什么是证书链?

证书链是服务器证书与其信任根证书之间的一系列中间证书的集合,用于建立服务器的可信性。证书链通常包括:

  • 服务器证书:由服务器提供。
  • 中间证书:由中间 CA 签发,用于连接服务器证书和根证书。
  • 根证书:由受信任的根 CA 签发,是信任链的起点。

证书链清理 的目的是:

  • 确保证书链中的每个证书都能正确链接到受信任的根证书。
  • 删除不必要或无效的中间证书。

2. CertificateChainCleaner 的作用

CertificateChainCleaner 提供了以下功能:

  1. 验证服务器证书链的合法性。
  2. 清理不必要的证书,确保只包含有效的证书链。
  3. 确保证书链能够与客户端信任的根证书匹配。

在 HTTPS 连接中,CertificateChainCleaner 是建立安全连接的一部分,通常由 OkHttp 内部使用,开发者很少直接与它交互。


3. CertificateChainCleaner 的工作机制

CertificateChainCleaner 的核心方法是 clean()


fun clean(chain: List<Certificate>, hostname: String): List<Certificate>
  • 参数

    • chain:服务器提供的证书链。
    • hostname:当前连接的主机名。
  • 返回值

    • 清理后合法的证书链。

内部工作原理:

  1. 遍历证书链中的每个证书,检查其签名是否由链中的上一个证书或根证书签名。
  2. 验证最后一个证书是否由客户端信任的根 CA 签发。
  3. 返回清理后完整的合法证书链。

4. 使用 CertificateChainCleaner 的场景

虽然 OkHttp 自动处理证书链清理,但以下场景可能需要了解或使用它:

  1. 自定义信任管理器

    • 当使用自定义 TrustManager 时,可能需要手动清理证书链。
  2. 调试和排查证书问题

    • 如果遇到证书验证失败的问题,可以通过清理证书链查看是否存在错误的中间证书。
  3. 高级 HTTPS 配置

    • 在一些高安全性场景中,需要更灵活地控制证书链验证流程。

5. CertificateChainCleaner 的实现

CertificateChainCleaner 是一个抽象类,主要有两个实现:

  1. BasicCertificateChainCleaner

    • 使用标准的 Java TrustManager 验证证书链。
    • 默认情况下,OkHttp 使用此实现。
  2. AndroidCertificateChainCleaner(仅 Android 平台):

    • 针对 Android 平台的优化,利用 Android 的证书验证机制。

6. 自定义 CertificateChainCleaner

在一些场景中,可能需要自定义 CertificateChainCleaner。可以通过继承 CertificateChainCleaner 实现自己的清理逻辑:

示例:

public class CustomCertificateChainCleaner extends CertificateChainCleaner {
    @Override
    public List<Certificate> clean(List<Certificate> chain, String hostname) throws SSLPeerUnverifiedException {
        // 自定义证书链清理逻辑
        return chain; // 返回清理后的证书链
    }
}

将自定义的 CertificateChainCleaner 应用于 OkHttp:

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

7. CertificateChainCleaner 的典型使用场景

场景 1:信任所有证书(仅限测试环境)

在测试环境中,可以跳过证书验证和清理:

HostnameVerifier hostnameVerifier = (hostname, session) -> true;
OkHttpClient client = new OkHttpClient.Builder()
    .hostnameVerifier(hostnameVerifier)
    .build();

场景 2:调试证书链

调试时可以打印和检查证书链,确认其合法性:


Response response = client.newCall(request).execute();
Handshake handshake = response.handshake();
List<Certificate> certificates = handshake.peerCertificates();

for (Certificate cert : certificates) {
    System.out.println(cert.toString());
}