java.security.cert.CertPathValidatorException:未找到证书路径的信任锚。安卓 2.3
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25122287/
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
java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. Android 2.3
提问by joseph
In my server (production server), I have a goDaddy ssl certificate. I have both iOS and Android apps connecting with the server, iOS connecting with no problems, android with versions 4.* everything is good, but with devices with 2.3.* I always get a SSLHandshakeException.
在我的服务器(生产服务器)中,我有一个 goDaddy ssl 证书。我有 iOS 和 Android 应用程序与服务器连接,iOS 连接没有问题,android 版本 4.* 一切都很好,但是使用 2.3.* 的设备我总是得到 SSLHandshakeException。
I did exactly like on Android Developers page (https://developer.android.com/training/articles/security-ssl.html).
我在 Android 开发者页面(https://developer.android.com/training/articles/security-ssl.html)上完全一样。
I already saw similar threads here in stackoverflow (here) but none is helping.
我已经在 stackoverflow (这里) 中看到了类似的线程,但没有任何帮助。
Then I saw thisthread talking about Extended Key Usage, but when debugging I get the following information:
然后我看到这个线程谈论扩展密钥用法,但是在调试时我得到以下信息:
[2]: OID: 2.5.29.37, Critical: false
Extended Key Usage: [ "1.3.6.1.5.5.7.3.1", "1.3.6.1.5.5.7.3.2" ]
So I guess the certificate is not "forcing" Extended Key Usage.
所以我猜证书不是“强制”扩展密钥使用。
Also on thisthread there are some other possible causes such as date/time being completly wrong, wich are all not existent.
此外,在此线程上还有一些其他可能的原因,例如日期/时间完全错误,但都不存在。
Taking that into consideration, I now don't know where the problem might be.
考虑到这一点,我现在不知道问题出在哪里。
Any suggestions?
有什么建议?
EDIT:StackTrace below:
编辑:下面的堆栈跟踪:
08-04 16:54:30.139: W/System.err(4832): Caused by: java.security.cert.CertificateException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
08-04 16:54:30.149: W/System.err(4832): at org.apache.harmony.xnet.provider.jsse.TrustManagerImpl.checkServerTrusted(TrustManagerImpl.java:161)
08-04 16:54:30.149: W/System.err(4832): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.verifyCertificateChain(OpenSSLSocketImpl.java:664)
08-04 16:54:30.149: W/System.err(4832): at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_do_handshake(Native Method)
08-04 16:54:30.159: W/System.err(4832): at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:474)
采纳答案by userM1433372
It looks like the issuer of your certificate is not in the trust store of the 2.3 devices.
您的证书颁发者似乎不在 2.3 设备的信任库中。
Take a look at the root and intermediate ca's of your GoDaddy certificate and check whether the certificates are present on your 2.3 device.
查看 GoDaddy 证书的根证书和中间证书,并检查证书是否存在于 2.3 设备上。
See http://www.andreabaccega.com/blog/2010/09/23/android-root-certification-authorities-list/for obtaining a list of 2.3 certificates.
参见http://www.andreabaccega.com/blog/2010/09/23/android-root-certification-authorities-list/获取 2.3 证书列表。
When only the root CA is available make sure that your webserver also serves the intermediate certificates upon request.
当只有根 CA 可用时,请确保您的网络服务器也根据请求提供中间证书。
回答by Josh
I went through LOTS of places in SO and the web to solve this thing. This is the code that worked for me (Android 21):
我在 SO 和网络上浏览了很多地方来解决这个问题。这是对我有用的代码(Android 21):
ByteArrayInputStream derInputStream = new ByteArrayInputStream(app.certificateString.getBytes());
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509","BC");
X509Certificate cert = (X509Certificate) certificateFactory.generateCertificate(derInputStream);
String alias = "alias";//cert.getSubjectX500Principal().getName();
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null);
trustStore.setCertificateEntry(alias, cert);
KeyManagerFactory kmf = KeyManagerFactory.getInstance("X509");
kmf.init(trustStore, null);
KeyManager[] keyManagers = kmf.getKeyManagers();
TrustManagerFactory tmf = TrustManagerFactory.getInstance("X509");
tmf.init(trustStore);
TrustManager[] trustManagers = tmf.getTrustManagers();
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagers, trustManagers, null);
URL url = new URL(someURL);
conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(sslContext.getSocketFactory());
app.certificateString
is a String that contains the Certificate, for example:
app.certificateString
是一个包含证书的字符串,例如:
static public String certificateString=
"-----BEGIN CERTIFICATE-----\n" +
"MIIGQTCCBSmgAwIBAgIHBcg1dAivUzANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UE" +
"BhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsTIlNlY3VyZSBE" +
... a bunch of characters...
"5126sfeEJMRV4Fl2E5W1gDHoOd6V==\n" +
"-----END CERTIFICATE-----";
I have tested that you can put any characters in the certificate string, if it is self signed, as long as you keep the exact structure above. I obtained the certificate string with my laptop's Terminal command line. I you need to know more details, let me know.
我已经测试过您可以在证书字符串中放置任何字符,如果它是自签名的,只要您保持上面的确切结构。我使用笔记本电脑的终端命令行获得了证书字符串。我你需要知道更多的细节,让我知道。
回答by Newbie009
In case someone need the answer, I finally found the answer after 2 days of google. Basically we need to use custom TrustManager to trusts the CAs in our KeyStore, this is because in Android 2.3.x, the keystore is not being properly used. Credit to https://github.com/delgurthfor the CustomTrustManager.
如果有人需要答案,经过 2 天的谷歌搜索,我终于找到了答案。基本上我们需要使用自定义 TrustManager 来信任 KeyStore 中的 CA,这是因为在 Android 2.3.x 中,密钥库没有被正确使用。感谢https://github.com/delgurth为CustomTrustManager。
Please refer: https://github.com/ikust/hello-pinnedcerts/issues/2
请参考:https: //github.com/ikust/hello-pinnedcerts/issues/2
KeyPinStore.java
密钥存储库
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
public class KeyPinStore {
private static final String[] certificates = {"certificate1.crt", "certificate2.crt", "certificate3.crt", "certificate4.crt"};
private static KeyPinStore instance = null;
private SSLContext sslContext = SSLContext.getInstance("TLS");
public static synchronized KeyPinStore getInstance() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
if (instance == null) {
instance = new KeyPinStore();
}
return instance;
}
private KeyPinStore() throws CertificateException, IOException, KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
for (int i = 0; i < certificates.length; i++) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = new BufferedInputStream(Application.context.getAssets().open("certificate/" + certificates[i]));
Certificate ca;
try {
ca = cf.generateCertificate(caInput);
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
caInput.close();
}
// Create a KeyStore containing our trusted CAs
keyStore.setCertificateEntry("ca" + i, ca);
}
// Use custom trust manager to trusts the CAs in our KeyStore
TrustManager[] trustManagers = {new CustomTrustManager(keyStore)};
// Create an SSLContext that uses our TrustManager
// SSLContext context = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagers, null);
}
public SSLContext getContext() {
return sslContext;
}
}
CustomTrustManager.java
自定义信任管理器
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.List;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
/**
* A custom X509TrustManager implementation that trusts a specified server certificate in addition
* to those that are in the system TrustStore.
* Also handles an out-of-order certificate chain, as is often produced by Apache's mod_ssl
*/
public class CustomTrustManager implements X509TrustManager {
private final TrustManager[] originalTrustManagers;
private final KeyStore trustStore;
/**
* @param trustStore A KeyStore containing the server certificate that should be trusted
* @throws NoSuchAlgorithmException
* @throws KeyStoreException
*/
public CustomTrustManager(KeyStore trustStore) throws NoSuchAlgorithmException, KeyStoreException {
this.trustStore = trustStore;
final TrustManagerFactory originalTrustManagerFactory = TrustManagerFactory.getInstance("X509");
originalTrustManagerFactory.init(trustStore);
originalTrustManagers = originalTrustManagerFactory.getTrustManagers();
}
/**
* No-op. Never invoked by client, only used in server-side implementations
* @return
*/
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
/**
* No-op. Never invoked by client, only used in server-side implementations
* @return
*/
public void checkClientTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {
}
/**
* Given the partial or complete certificate chain provided by the peer,
* build a certificate path to a trusted root and return if it can be validated and is trusted
* for client SSL authentication based on the authentication type. The authentication type is
* determined by the actual certificate used. For instance, if RSAPublicKey is used, the authType should be "RSA".
* Checking is case-sensitive.
* Defers to the default trust manager first, checks the cert supplied in the ctor if that fails.
* @param chain the server's certificate chain
* @param authType the authentication type based on the client certificate
* @throws java.security.cert.CertificateException
*/
public void checkServerTrusted(X509Certificate[] chain, String authType) throws java.security.cert.CertificateException {
try {
for (TrustManager originalTrustManager : originalTrustManagers) {
((X509TrustManager) originalTrustManager).checkServerTrusted(chain, authType);
}
} catch(CertificateException originalException) {
try {
// Ordering issue?
X509Certificate[] reorderedChain = reorderCertificateChain(chain);
if (! Arrays.equals(chain, reorderedChain)) {
checkServerTrusted(reorderedChain, authType);
return;
}
for (int i = 0; i < chain.length; i++) {
if (validateCert(reorderedChain[i])) {
return;
}
}
throw originalException;
} catch(Exception ex) {
ex.printStackTrace();
throw originalException;
}
}
}
/**
* Checks if we have added the certificate in the trustStore, if that's the case we trust the certificate
* @param x509Certificate the certificate to check
* @return true if we know the certificate, false otherwise
* @throws KeyStoreException on problems accessing the key store
*/
private boolean validateCert(final X509Certificate x509Certificate) throws KeyStoreException {
return trustStore.getCertificateAlias(x509Certificate) != null;
}
/**
* Puts the certificate chain in the proper order, to deal with out-of-order
* certificate chains as are sometimes produced by Apache's mod_ssl
* @param chain the certificate chain, possibly with bad ordering
* @return the re-ordered certificate chain
*/
private X509Certificate[] reorderCertificateChain(X509Certificate[] chain) {
X509Certificate[] reorderedChain = new X509Certificate[chain.length];
List<X509Certificate> certificates = Arrays.asList(chain);
int position = chain.length - 1;
X509Certificate rootCert = findRootCert(certificates);
reorderedChain[position] = rootCert;
X509Certificate cert = rootCert;
while((cert = findSignedCert(cert, certificates)) != null && position > 0) {
reorderedChain[--position] = cert;
}
return reorderedChain;
}
/**
* A helper method for certificate re-ordering.
* Finds the root certificate in a possibly out-of-order certificate chain.
* @param certificates the certificate change, possibly out-of-order
* @return the root certificate, if any, that was found in the list of certificates
*/
private X509Certificate findRootCert(List<X509Certificate> certificates) {
X509Certificate rootCert = null;
for(X509Certificate cert : certificates) {
X509Certificate signer = findSigner(cert, certificates);
if(signer == null || signer.equals(cert)) { // no signer present, or self-signed
rootCert = cert;
break;
}
}
return rootCert;
}
/**
* A helper method for certificate re-ordering.
* Finds the first certificate in the list of certificates that is signed by the sigingCert.
*/
private X509Certificate findSignedCert(X509Certificate signingCert, List<X509Certificate> certificates) {
X509Certificate signed = null;
for(X509Certificate cert : certificates) {
Principal signingCertSubjectDN = signingCert.getSubjectDN();
Principal certIssuerDN = cert.getIssuerDN();
if(certIssuerDN.equals(signingCertSubjectDN) && !cert.equals(signingCert)) {
signed = cert;
break;
}
}
return signed;
}
/**
* A helper method for certificate re-ordering.
* Finds the certificate in the list of certificates that signed the signedCert.
*/
private X509Certificate findSigner(X509Certificate signedCert, List<X509Certificate> certificates) {
X509Certificate signer = null;
for(X509Certificate cert : certificates) {
Principal certSubjectDN = cert.getSubjectDN();
Principal issuerDN = signedCert.getIssuerDN();
if(certSubjectDN.equals(issuerDN)) {
signer = cert;
break;
}
}
return signer;
}
}
To use it, just get the SSLSocketFactory and apply it, eg:
要使用它,只需获取 SSLSocketFactory 并应用它,例如:
with HttpsURLConnection
使用 HttpsURLConnection
KeyPinStore keystore = KeyPinStore.getInstance();
SSLSocketFactory sslSF = keystore.getContext().getSocketFactory();
URL url = new URL("https://certs.cac.washington.edu/CAtest/");
HttpsURLConnection urlConnection = (HttpsURLConnection)url.openConnection();
urlConnection.setSSLSocketFactory(sslSF);
InputStream in = urlConnection.getInputStream();
copyInputStreamToOutputStream(in, System.out);
with Volley
与凌空
KeyPinStore keystore = KeyPinStore.getInstance();
SSLSocketFactory sslSF = keystore.getContext().getSocketFactory();
RequestQueue mRequestQueue = Volley.newRequestQueue(context, new HurlStack(null, sslSF));
回答by Arun Prajapati
If you are Using AndroidNetworking / Rx2AndroidNetworking Library, This Solution is work for me after waste 4 Hr.
如果您使用的是 AndroidNetworking / Rx2AndroidNetworking 库,那么浪费 4 小时后,此解决方案对我有用。
AndroidNetworking.initialize(getApplicationContext(), myUnsafeHttpClient());
private static OkHttpClient myUnsafeHttpClient() {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {
}
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
//Using TLS 1_2 & 1_1 for HTTP/2 Server requests
// Note : The following is suitable for my Server. Please change accordingly
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.COMPATIBLE_TLS)
.tlsVersions(TlsVersion.TLS_1_2, TlsVersion.TLS_1_1, TlsVersion.TLS_1_0)
.cipherSuites(
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,
CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,
CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
.build();
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory);
builder.connectionSpecs(Collections.singletonList(spec));
builder.hostnameVerifier((hostname, session) -> true);
return builder.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}