OkHttp源码学习之CertificatePinner

OkHttp 中,CertificatePinner 是一个安全功能,用于实现 SSL/TLS 证书固定(Certificate Pinning) 。通过将服务器的公钥或证书与客户端预定义的可信值绑定,可以有效防止中间人攻击(MITM),即使攻击者获得了伪造的可信 CA 证书,也无法欺骗客户端。


1. CertificatePinner 的作用

  • 增强 HTTPS 安全性

    • 即使服务器的证书被替换或签发机构被攻破,客户端依然能够验证预定义的可信证书。
  • 防止中间人攻击

    • 防止攻击者通过伪造的证书截获和篡改数据。
  • 提升安全透明性

    • 明确指定客户端信任的证书或公钥,减少对 CA 的完全信任。

2. CertificatePinner 的基本工作原理

  1. 客户端在发起 HTTPS 请求时,会从服务器获取其证书。
  2. CertificatePinner 将服务器证书的公钥或完整证书与客户端本地预定义的值进行对比。
  3. 如果匹配成功,连接继续;否则,连接失败。

3. CertificatePinner 的使用

1. 创建 CertificatePinner

CertificatePinner 是不可变的,通过 CertificatePinner.Builder 配置:

java 复制代码
CertificatePinner certificatePinner = new CertificatePinner.Builder()
    .add("example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
    .build();
  • add() 方法参数

    • hostname:目标主机名(如 example.com)。
    • pin:证书的指纹(支持 sha256sha1 格式,但推荐使用 sha256)。

2. 将 CertificatePinner 应用于 OkHttpClient

CertificatePinner 设置到 OkHttpClient

java 复制代码
OkHttpClient client = new OkHttpClient.Builder()
    .certificatePinner(certificatePinner)
    .build();

3. 获取服务器证书指纹

要固定证书,需要先获取服务器证书的指纹:

  • 使用 OpenSSL:

    bash 复制代码
    bash
    复制代码
    echo | openssl s_client -connect example.com:443 | openssl x509 -pubkey | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -binary | base64
  • 使用 Java 程序提取:

    java 复制代码
    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder().url("https://example.com").build();
    Response response = client.newCall(request).execute();
    
    Handshake handshake = response.handshake();
    List<Certificate> certificates = handshake.peerCertificates();
    
    for (Certificate certificate : certificates) {
        System.out.println(certificate);
    }

4. 完整示例

java 复制代码
CertificatePinner certificatePinner = new CertificatePinner.Builder()
    .add("example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
    .add("api.example.com", "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=")
    .build();

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

Request request = new Request.Builder()
    .url("https://example.com")
    .build();

try (Response response = client.newCall(request).execute()) {
    System.out.println(response.body().string());
}

5. 注意事项

1. 主机名匹配

  • CertificatePinner 支持通配符:

    java 复制代码
    .add("*.example.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")

2. 动态证书更新

  • 如果服务器的证书更新,客户端需要同步更新 CertificatePinner,否则会导致连接失败。

3. 多个指纹支持

  • 可以为同一个主机配置多个指纹,以应对证书过渡或多 CA 签发的情况:

    java 复制代码
    .add("example.com", 
         "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
         "sha256/BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB=");

4. 生产环境推荐

  • 固定公钥(pin),而非完整证书,以减少证书更换的频率。

5. 禁用证书固定

  • 在开发或测试环境中,可以禁用 CertificatePinner,例如设置一个空实例:

    java 复制代码
    OkHttpClient client = new OkHttpClient.Builder()
        .certificatePinner(CertificatePinner.DEFAULT)
        .build();

6. 常见问题与解决

问题 1:连接失败,提示证书固定错误

  • 确保固定的指纹与服务器证书的实际指纹一致。
  • 检查主机名是否匹配,确保域名正确。

问题 2:证书更换导致服务中断

  • 提前添加新证书的指纹,支持证书过渡。

7. CertificatePinner 的优势与限制

优势

  1. 增强安全性,防止中间人攻击。
  2. 降低对 CA 的信任依赖。

限制

  1. 配置和维护成本较高,特别是证书频繁更换时。
  2. 不支持动态调整,证书变化需重新部署客户端。
相关推荐
木井巳1 小时前
【多线程】并发安全
java·java-ee
It's now7 小时前
Spring AI 基础开发流程
java·人工智能·后端·spring
cxh_陈7 小时前
线程的状态,以及和锁有什么关系
java·线程·线程的状态·线程和锁
计算机毕设VX:Fegn08957 小时前
计算机毕业设计|基于springboot + vue图书商城系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·课程设计
R.lin7 小时前
Java 8日期时间API完全指南
java·开发语言·python
毕设源码-赖学姐7 小时前
【开题答辩全过程】以 高校教学质量监控平台为例,包含答辩的问题和答案
java·eclipse
高山上有一只小老虎8 小时前
翻之矩阵中的行
java·算法
火钳游侠8 小时前
java单行注释,多行注释,文档注释
java·开发语言
code bean8 小时前
【CMake】为什么需要清理 CMake 缓存文件?深入理解 CMake 生成器切换机制
java·spring·缓存
selt7918 小时前
Redisson之RedissonLock源码完全解析
android·java·javascript