CertPathValidatorException:未find证书path的信任锚 – 更新Android
我正在创build一个使用https
与服务器通信的android应用程序。 我正在使用OkHttp
和OkHttp
发出请求。 这些工作正常的标准http
请求。 以下是我所遵循的步骤。
步骤1:使用该命令从服务器获取证书文件
echo -n | openssl s_client -connect api.****.tk:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > gtux.cert
步骤2:使用以下命令将证书转换为BKS格式
keytool -importcert -v -trustcacerts -file "gtux.cert" -alias imeto_alias -keystore "my_keystore.bks" -provider org.bouncycastle.jce.provider.BouncyCastleProvider -providerpath "bcprov-jdk16-146.jar" -storetype BKS
它问我的密码,并成功创build文件。
第3步:
创build一个OkHttpClient,并使用相同的方式进行https请求
public class MySSLTrust { public static OkHttpClient trustcert(Context context){ OkHttpClient okHttpClient = new OkHttpClient(); try { KeyStore ksTrust = KeyStore.getInstance("BKS"); InputStream instream = context.getResources().openRawResource(R.raw.my_keystore); ksTrust.load(instream, "secret".toCharArray()); // TrustManager decides which certificate authorities to use. TrustManagerFactory tmf = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ksTrust); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); okHttpClient.setSslSocketFactory(sslContext.getSocketFactory()); } catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | KeyManagementException e) { e.printStackTrace(); } return okHttpClient; } }
步骤4:
RestAdapter必须被创build
RestAdapter.Builder() .setRequestInterceptor(intercept) .setEndpoint("https://api.****.tk") .setClient(new OkClient(this)) .setLogLevel(RestAdapter.LogLevel.FULL) .setLog(new AndroidLog("RETROFIT")) .build();
但最后,当运行应用程序是扔我CertPathValidatorException : Trust anchor for certificate path not found
。 请帮我解决这个问题。 谢谢。
其他失败尝试:试图安装在我的Xperia Z2的证书,它说文件已安装,但是当我运行应用程序相同的exception被抛出。
错误日志这里是执行中的错误日志…
错误日志
粘贴在那里,这样就很容易阅读
免责声明:这个答案是从2015年7月 ,并从那时起使用Retrofit和OkHttp 。
查看这个链接了解更多关于Retrofit v2的信息,以及关于当前OkHttp方法的信息。
好的,我使用Android开发者指南开始工作。
就像OP,我试图使用Retrofit和OkHttp连接到一个自签名的SSL启用服务器。
这里是使事情工作的代码(我已经删除了try / catch块):
public static RestAdapter createAdapter(Context context) { // loading CAs from an InputStream CertificateFactory cf = CertificateFactory.getInstance("X.509"); InputStream cert = context.getResources().openRawResource(R.raw.my_cert); Certificate ca; try { ca = cf.generateCertificate(cert); } finally { cert.close(); } // creating a KeyStore containing our trusted CAs String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); keyStore.setCertificateEntry("ca", ca); // creating a TrustManager that trusts the CAs in our KeyStore String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); // creating an SSLSocketFactory that uses our TrustManager SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); // creating an OkHttpClient that uses our SSLSocketFactory OkHttpClient okHttpClient = new OkHttpClient(); okHttpClient.setSslSocketFactory(sslContext.getSocketFactory()); // creating a RestAdapter that uses this custom client return new RestAdapter.Builder() .setEndpoint(UrlRepository.API_BASE) .setClient(new OkClient(okHttpClient)) .build(); }
为了帮助debugging,我还将.setLogLevel(RestAdapter.LogLevel.FULL)
添加到RestAdapter创build命令中,我可以看到它连接并从服务器获取响应。
它只需要保存在main/res/raw
原始.crt文件。 .crt文件,即证书,是使用openssl
创build证书时创build的两个文件之一。 通常,它是.crt或.cert文件,而另一个是.key文件。
Afaik,.crt文件是您的公钥 ,.key文件是您的私钥。
正如我所看到的,你已经有一个.cert文件,这是相同的,所以尝试使用它。
PS:对于那些将来阅读并且只有一个.pem文件的人来说,根据这个答案 ,你只需要把它转换成另一个:
openssl x509 -outform der -in your-cert.pem -out your-cert.crt
PS²:对于那些根本没有任何文件的用户,可以使用下面的命令(bash)从任何服务器提取公钥(aka证书):
echo -n | openssl s_client -connect your.server.com:443 | \ sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ~/my_cert.crt
只需replaceyour.server.com
和端口(如果它不是标准的HTTPS),并为您的输出文件select一个有效的path来创build。
我不使用Retrofit,对于OkHttp,这里是自签名证书的唯一解决scheme:
-
从Gowtham的问题中获得我们网站的证书,并将其放入项目的res / raw目录中:
echo -n | openssl s_client -connect elkews.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ./res/raw/elkews_cert.crt
-
使用Paulo答案设置SSL工厂(现在使用OkHttpClient.Builder() ),但没有RestAdapter创build。
-
然后添加以下解决scheme来解决: SSLPeerUnverifiedException:主机名未validation
所以在我工作的Paulo代码(在sslContext初始化之后)的结尾如下所示:
... OkHttpClient.Builder builder = new OkHttpClient.Builder().sslSocketFactory(sslContext.getSocketFactory()); builder.hostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return "secure.elkews.com".equalsIgnoreCase(hostname); }); OkHttpClient okHttpClient = builder.build();
您正在转换证书到BKS密钥库,为什么不直接使用.cert
,从https://developer.android.com/training/articles/security-ssl.html :
CertificateFactory cf = CertificateFactory.getInstance("X.509"); InputStream instream = context.getResources().openRawResource(R.raw.gtux_cert); Certificate ca; try { ca = cf.generateCertificate(instream); } finally { caInput.close(); } KeyStore kStore = KeyStore.getInstance(KeyStore.getDefaultType()); kStore.load(null, null); kStore.setCertificateEntry("ca", ca); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm();); tmf.init(kStore); SSLContext context = SSLContext.getInstance("TLS"); context.init(null, tmf.getTrustManagers(), null); okHttpClient.setSslSocketFactory(context.getSocketFactory());
如果你有证书,那么你可以提供,但很less有networking服务将没有他们的证书,请按照下面。
// creating a KeyStore containing our trusted CAs String keyStoreType = KeyStore.getDefaultType(); KeyStore keyStore = KeyStore.getInstance(keyStoreType); keyStore.load(null, null); // creating a TrustManager that trusts the CAs in our KeyStore String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm(); TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm); tmf.init(keyStore); // creating an SSLSocketFactory that uses our TrustManager SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, tmf.getTrustManagers(), null); okHttpClient.setSslSocketFactory(sslContext.getSocketFactory()); // creating a RestAdapter using the custom client return new RestAdapter.Builder() .setEndpoint(UrlRepository.API_BASE) .setClient(new OkClient(okHttpClient)) .build();
经过漫长的研究和深入挖掘,我发现在Android的证书钉扎的解决scheme,是的,它不同于iOS我们需要一个证书本身,但在Android我们只需要一个哈希引脚,就是这样。
如何获得证书的哈希引脚?
最初只是使用一个错误的哈希引脚,你的Java类会抛出一个错误的正确的哈希引脚或引脚链,只是复制并粘贴到你的代码就是这样。
这个解决scheme解决了我的问题: https : //stackoverflow.com/a/45853669/3448003
- 使用Retrofit 2.0和RxJava获取响应状态代码
- 实施Retrofitcallback重新创build活动的最佳做法?
- 不支持的操作:Android,Retrofit,OkHttp。 在OkHttpClient中添加拦截器
- 在Android中使用Retrofit
- 使用Rxjava Schedulers.newThread()与Schedulers.io()进行改造
- 添加标题的所有请求与改造2
- Android Retrofit – onProgressUpdate显示进度通知
- 什么时候应该使用RxJava Observable,并在Android上进行简单的callback?
- 改进callback获得响应正文