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. 不支持动态调整,证书变化需重新部署客户端。
相关推荐
老友@8 分钟前
Java Excel 导出:EasyExcel 使用详解
java·开发语言·excel·easyexcel·excel导出
Full Stack Developme20 分钟前
java.net.http 包详解
java·http·.net
Meteors.35 分钟前
安卓进阶——RxJava
android·rxjava
daidaidaiyu1 小时前
Spring BeanPostProcessor接口
java·spring
weixin_436525071 小时前
SpringBoot 单体服务集成 Zipkin 实现链路追踪
java·spring boot·后端
她说..1 小时前
Redis实现未读消息计数
java·数据库·redis·缓存
book多得1 小时前
Redis 大 Key 问题:识别、危害与最优处理方案
java·redis·mybatis
任子菲阳2 小时前
学Java第四十三天——Map双列集合
java·开发语言
zeijiershuai2 小时前
Java 会话技术、Cookie、JWT令牌、过滤器Filter、拦截器Interceptor
java·开发语言
fury_1232 小时前
tsfile.raw提示
java·前端·javascript