Android java.security.cert.CertPathValidatorException:未找到证书路径的信任锚
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/39264056/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android java.security.cert.CertPathValidatorException: Trust anchor for certification path not found
提问by Ruwanka Madhushan
There are three hosts that an android app do the authentication and authorization. Final host is the REST API. For the first time using Oauth authentication and authorization process it works without issue.
android 应用程序有三个主机进行身份验证和授权。最终主机是 REST API。第一次使用 Oauth 身份验证和授权过程,它可以正常工作。
But if user kills the appafter login and accessing the services provided by REST API and then again open the app, this issue arise. In this time authentication and authorization process is not happening, only the REST API.It caused to java.security.cert.CertPathValidatorException
but it was working during the first use (login and then use the app).
但是如果用户在登录并访问 REST API 提供的服务后杀死该应用程序,然后再次打开该应用程序,则会出现此问题。此时没有发生身份验证和授权过程,只有 REST API。它导致java.security.cert.CertPathValidatorException
但它在第一次使用(登录然后使用该应用程序)期间工作正常。
Can someone explains the scenario behind this exception and whats wrong with the app. This works if certification exceptions are ignored as bellow according to this SO answer.
有人可以解释这个异常背后的场景以及应用程序有什么问题。如果根据此 SO answer如下所示忽略认证例外,则此方法有效。
SSLSocketFactory sslSocketFactory = null;
try {
TrustManagerFactory tmf = TrustManagerFactory.getInstance(
TrustManagerFactory.getDefaultAlgorithm());
// Initialise the TMF as you normally would, for example:
try {
tmf.init((KeyStore)null);
} catch(KeyStoreException e) {
e.printStackTrace();
}
TrustManager[] trustManagers = tmf.getTrustManagers();
final X509TrustManager origTrustmanager = (X509TrustManager)trustManagers[0];
// Create a trust manager that does not validate certificate chains
TrustManager[] wrappedTrustManagers = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return origTrustmanager.getAcceptedIssuers();
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
try {
origTrustmanager.checkClientTrusted(certs, authType);
} catch(CertificateException e) {
e.printStackTrace();
}
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
try {
origTrustmanager.checkServerTrusted(certs, authType);
} catch(CertificateException e) {
e.printStackTrace();
}
}
}
};
//TrustManager[] trustAllCerts = TrustManagerFactory.getInstance("SSL").getTrustManagers();
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, wrappedTrustManagers, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
sslSocketFactory = sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException | KeyManagementException e) {
e.printStackTrace();
}
return sslSocketFactory;
I am using Okhttp 3 for the http requests. Any suggestion would help to solve the issue. And please let me know if I use above code snippet, is it a security violation? will it effect to the security of the app?
我对 http 请求使用 Okhttp 3。任何建议都有助于解决问题。如果我使用上面的代码片段,请告诉我,这是否违反安全?它会影响应用程序的安全性吗?
采纳答案by Ruwanka Madhushan
I am answering to this to give an idea about the scenario and solution as per the android developer site for others benefit. I have solved this using custom trust manager.
我正在回答这个问题,以根据 android 开发人员网站为其他人的利益提供有关场景和解决方案的想法。我已经使用自定义信任管理器解决了这个问题。
The problem was with the server certificate, it misses intermediate certificate authority. However with the first flow certificate path is completed somehow and result was successful certificate path validation.
问题出在服务器证书上,它缺少中间证书颁发机构。然而,第一个流证书路径以某种方式完成,结果是成功的证书路径验证。
There is a solution for this in android developer site. it suggest to use custom trust manager that trusts this server certificate or it suggest to server to include the intermediate CA in the server chain.
在android developer site 中有一个解决方案。它建议使用信任此服务器证书的自定义信任管理器,或者建议服务器将中间 CA 包含在服务器链中。
custom trust manager. source: https://developer.android.com/training/articles/security-ssl.html#UnknownCa
自定义信任管理器。来源:https: //developer.android.com/training/articles/security-ssl.html#UnknownCa
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Tell the okhttp to use a SocketFactory from our SSLContext
OkHttpClient okHttpClient client = new OkHttpClient.Builder().sslSocketFactory(context.getSocketFactory()).build();
UPDATE:My problem was solved after intermediate certificate authority added to the certificate chain from the server side. It is the best solution, Bundling the certificate with the app requires app to be updated on certificate expiring or any other issues related with certificate management.
更新:在从服务器端将中间证书颁发机构添加到证书链后,我的问题得到了解决。这是最好的解决方案,将证书与应用程序捆绑在一起需要在证书到期或与证书管理相关的任何其他问题时更新应用程序。
UPDATE:03/09/2017Easiest way to load certificate file I found is use of raw resource.
更新:03/09/2017我发现加载证书文件的最简单方法是使用原始资源。
InputStream caInput = new BufferedInputStream(context
.getResources().openRawResource(R.raw.certfilename));
where certfilename is the certificate file placed in resources/raw folder. Also okhttp's sslSocketFactory(SSLSocketFactory sslSocketFactory)
has been deprecated and suggested approach in the okhttp api doc can be used.
其中 certfilename 是放置在资源/原始文件夹中的证书文件。另外 okhttpsslSocketFactory(SSLSocketFactory sslSocketFactory)
已被弃用,可以使用 okhttp api 文档中的建议方法。
Also when getting the certificate from the server it is better to use openssl.
此外,从服务器获取证书时,最好使用 openssl。
openssl s_client -connect {server-address}:{port} -showcerts
Because I used to grab that from firefox and faced situation where it was altered by the virus guard.
因为我曾经从 Firefox 中获取它并面临被病毒防护更改的情况。