CertPathValidatorException:未find证书path的信任锚 – 更新Android

我正在创build一个使用https与服务器通信的android应用程序。 我正在使用OkHttpOkHttp发出请求。 这些工作正常的标准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月 ,并从那时起使用RetrofitOkHttp
查看这个链接了解更多关于Retrofit v2的信息,以及关于当前OkHttp方法的信息。

好的,我使用Android开发者指南开始工作。

就像OP,我试图使用RetrofitOkHttp连接到一个自签名的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:

  1. 从Gowtham的问题中获得我们网站的证书,并将其放入项目的res / raw目录中:

     echo -n | openssl s_client -connect elkews.com:443 | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' > ./res/raw/elkews_cert.crt 
  2. 使用Paulo答案设置SSL工厂(现在使用OkHttpClient.Builder() ),但没有RestAdapter创build。

  3. 然后添加以下解决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