TL;DR
- 直接解法:服务端换成权威 CA 签发的证书,客户端删除自定义 TrustManager,让 OkHttp 使用默认信任链,然后保留 CertificatePinner 做证书钉扎。
- 不要用“全部信任”的 TrustManager 绕过自签名证书错误:自定义 TrustManager 后 OkHttp 拿不到预期的证书链指纹,CertificatePinner 仍会报 SSLPeerUnverifiedException。
最近在写一些用 HTTPS 通讯的程序,最初因为使用了 Cloudflare 而受到免费计划的 SSL 限制。 更换到国内 CDN 后想用自签名证书给 API 接口用,发现不行,客户端会报错。
查询后发现是 OkHttp 的特别限制:
Exception in thread "main" javax.net.ssl.SSLPeerUnverifiedException: Certificate pinning failure!
Peer certificate chain: # 自定义TrustManagers后,不回报任何指纹
Pinned certificates for api.mic.run:
sha256/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
at okhttp3.CertificatePinner.check$okhttp(CertificatePinner.kt:200)
at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.kt:410)
at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.kt:337)
at okhttp3.internal.connection.RealConnection.connect(RealConnection.kt:209)
at okhttp3.internal.connection.ExchangeFinder.findConnection(ExchangeFinder.kt:226)
at okhttp3.internal.connection.ExchangeFinder.findHealthyConnection(ExchangeFinder.kt:106)
at okhttp3.internal.connection.ExchangeFinder.find(ExchangeFinder.kt:74)
at okhttp3.internal.connection.RealCall.initExchange$okhttp(RealCall.kt:255)txt不自定义 TrustManager 的话,JDK 又会报 SSLHandshakeException,因为自签名证书不被信任。
目前我想到的解决办法就是把自己验证的逻辑丢到里面,但把 @Overwrite 那里覆盖掉就解决了验证…