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. 不支持动态调整,证书变化需重新部署客户端。
相关推荐
历程里程碑4 分钟前
普通数组----合并区间
java·数据结构·python·算法·leetcode·职场和发展·tornado
程序员泠零澪回家种桔子23 分钟前
Spring AI框架全方位详解
java·人工智能·后端·spring·ai·架构
CodeCaptain32 分钟前
nacos-2.3.2-OEM与nacos3.1.x的差异分析
java·经验分享·nacos·springcloud
Anastasiozzzz1 小时前
Java Lambda 揭秘:从匿名内部类到底层原理的深度解析
java·开发语言
骇客野人1 小时前
通过脚本推送Docker镜像
java·docker·容器
铁蛋AI编程实战2 小时前
通义千问 3.5 Turbo GGUF 量化版本地部署教程:4G 显存即可运行,数据永不泄露
java·人工智能·python
晚霞的不甘2 小时前
CANN 编译器深度解析:UB、L1 与 Global Memory 的协同调度机制
java·后端·spring·架构·音视频
SunnyDays10112 小时前
使用 Java 冻结 Excel 行和列:完整指南
java·冻结excel行和列
摇滚侠2 小时前
在 SpringBoot 项目中,开发工具使用 IDEA,.idea 目录下的文件需要提交吗
java·spring boot·intellij-idea
云姜.2 小时前
java多态
java·开发语言·c++