证书固定不适用于 Android 上的 OkHttp
Posted
技术标签:
【中文标题】证书固定不适用于 Android 上的 OkHttp【英文标题】:Certificate pinning not working with OkHttp on Android 【发布时间】:2015-11-24 16:37:45 【问题描述】:在 android 应用上使用 com.squareup.okhttp:okhttp:2.4.0
和 com.squareup.retrofit:retrofit:1.9.0
,尝试通过 HTTPS 与使用自签名证书的服务器 REST API 通信。
服务器密钥库有一个私钥和 2 个证书,即服务器证书和根证书。 openssl s_client
输出 -
Certificate chain
0 s:/C=...OU=Dev/CN=example.com
i:/C=... My CA/emailAddress=info@example.com
1 s:/C=... My CA/emailAddress=info@example.com
i:/C=... My CA/emailAddress=info@example.com
在 Android 应用中,OkHttp 使用根证书的 SHA1 签名进行初始化 -
CertificatePinner certificatePinner = new CertificatePinner.Builder()
.add("example.com", "sha1/5d...3b=")
.build();
OkHttpClient client = new OkHttpClient();
client.setCertificatePinner(certificatePinner);
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://example.com")
.setClient(new OkClient(client))
.build();
但是当尝试发送请求失败并出现异常 -
retrofit.RetrofitError: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at retrofit.RestAdapter$RestHandler.invokeRequest(RestAdapter.java:395)
at retrofit.RestAdapter$RestHandler.invoke(RestAdapter.java:240)
at java.lang.reflect.Proxy.invoke(Proxy.java:397)
at $Proxy1.report(Unknown Source)
...
at android.os.AsyncTask$2.call(AsyncTask.java:288)
at java.util.concurrent.FutureTask.run(FutureTask.java:237)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:818)
Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:306)
at com.squareup.okhttp.internal.http.SocketConnector.connectTls(SocketConnector.java:103)
at com.squareup.okhttp.Connection.connect(Connection.java:143)
at com.squareup.okhttp.Connection.connectAndSetOwner(Connection.java:185)
at com.squareup.okhttp.OkHttpClient$1.connectAndSetOwner(OkHttpClient.java:128)
at com.squareup.okhttp.internal.http.HttpEngine.nextConnection(HttpEngine.java:341)
在尝试sslSocket.startHandshake()
时,甚至在CertificatePinner
用于检查收到的证书之前,它就会被抛出com.squareup.okhttp.internal.http.SocketConnector
。
我已确保服务器已使用openssl
和curl --cacert root.pem
正确安装了证书。
那么为什么 OkHttp 在尝试检查提供的证书是否正常之前就抛出异常?
【问题讨论】:
【参考方案1】:OkHttp 不支持自签名证书。
使用由已知 CA 签名的证书时,握手成功,然后CertificatePinner
确保证书链至少包含一个提供的签名。如果没有出现,它会抛出异常,停止请求。
因此,可以使用由已知 CA 签名的证书并固定其中一个证书,以确保我们正在与正确的服务器通信。
【讨论】:
是的。证书固定不会取代现有的证书验证;它补充了它。您需要配置您的客户端以信任您的证书;测试中有很多例子! @JesseWilson 和@Kof:我能问一个问题吗,a known CA
是指成功安装在 Android 手机中的 CA(我可以在 Trusted credentials - User
中看到它吗?谢谢
可能是设备在其受信任的 CA 数据库中安装了签名的 CA。
我们如何知道我们的 CA 是否可信?
@Mr.Jay 在浏览器中他们有一个受信任的 CA 列表,可以在 Internet 上查找列表,如果它是您的 CA(用于自签名),您可以相信自己,相信我。
【参考方案2】:
OkHttp 确实支持自签名证书。
请查看this answer,了解如何创建仅信任您的证书的SslSocket
。
【讨论】:
另一个查看示例代码的地方是 OkHttp 的配方存储库:CustomTrust.java。它提供了更通用的工作代码。以上是关于证书固定不适用于 Android 上的 OkHttp的主要内容,如果未能解决你的问题,请参考以下文章
APNS 不适用于 Google App Engine 上的 AdHoc 分发配置文件
我正在使用 `fcm` gem 发送推送通知,它适用于 android 但不适用于 IOS
主题不适用于 Android 上的 DialogFragment
为啥这个 CSS 不适用于 Android 上的 Chrome,但适用于其他任何地方?
使用 Compose 导航不适用于 Android 上的 Google 地图
为啥 shadowColor: '...' - 不适用于“react-native-drawer”组件的 Android 上的抽屉样式?