简体   繁体   English

java.security.cert.CertPathValidatorException:未找到证书路径的信任锚。 安卓 2.3

[英]java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. Android 2.3

In my server (production server), I have a goDaddy ssl certificate.在我的服务器(生产服务器)中,我有一个 goDaddy ssl 证书。 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.我有 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 Stack Overflow ( here ) but none is helping.我已经在 Stack Overflow( 这里)中看到了类似的线程,但没有任何帮助。

Then I saw this thread 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 this thread there are some other possible causes such as date/time being completely wrong, which 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)

I went through LOTS of places in SO and the web to solve this thing.我在 SO 和网络上浏览了很多地方来解决这个问题。 This is the code that worked for me (Android 21):这是对我有用的代码(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.我你需要知道更多的细节,让我知道。

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 可用时,请确保您的网络服务器也根据请求提供中间证书。

If above all answers not work and you get any kind of issue in publish app when using android 30 as compile and target sdk version.如果以上所有答案都不起作用,并且您在使用 android 30 作为编译和目标 sdk 版本时在发布应用程序中遇到任何类型的问题。 Please download your ssl .cert file from server.请从服务器下载您的 ssl .cert 文件。 and just put it in raw folder.并将其放在原始文件夹中。 create network_security_config in XML folder.在 XML 文件夹中创建 network_security_config。 use line in menifest inside application tag在应用程序标签内的清单中使用行

android:networkSecurityConfig="@xml/network_security_config" android:networkSecurityConfig="@xml/network_security_config"

and use below mention code in network_security_config file.并在 network_security_config 文件中使用下面提到的代码。

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config>
    <domain includeSubdomains="true">your server domain</domain>
    <trust-anchors>
        <certificates src="@raw/your server cerificate file name"/>
    </trust-anchors>
</domain-config>
</network-security-config>

In case someone need the answer, I finally found the answer after 2 days of google.如果有人需要答案,经过 2 天的谷歌搜索,我终于找到了答案。 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.基本上我们需要使用自定义 TrustManager 来信任 KeyStore 中的 CA,这是因为在 Android 2.3.x 中,密钥库没有被正确使用。 Credit to https://github.com/delgurth for the CustomTrustManager. CustomTrustManager 归功于https://github.com/delgurth

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));

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);
    }
}

运行此命令以验证 code.keystore 是否具有系统证书:

keytool -list -stotetype JKS -keystore config\code.keystore

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 java.security.cert.CertPathValidatorException:找不到证书路径的信任锚 - java.security.cert.CertPathValidatorException: Trust anchor for certification path not found Android-Retrofit2-java.security.cert.CertPathValidatorException:找不到证书路径的信任锚 - Android - Retrofit2 - java.security.cert.CertPathValidatorException: Trust anchor for certification path not found Hostgator 启用的免费 SSL - Android 错误:java.security.cert.CertPathValidatorException:找不到证书路径的信任锚 - Free SSL Enabled by Hostgator - Android Error : java.security.cert.CertPathValidatorException: Trust anchor for certification path not found Android-原因:java.security.cert.CertPathValidatorException:找不到证书路径的信任锚 - Android - Caused by: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found Android java.security.cert.CertPathValidatorException:未找到证书路径的信任锚 - Android java.security.cert.CertPathValidatorException: Trust anchor for certification path not found javax.net.ssl.SSLHandshakeException:java.security.cert.CertPathValidatorException:找不到证书路径的信任锚 - javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found 调试javax.net.ssl.SSLHandshakeException:java.security.cert.CertPathValidatorException:找不到证书路径的信任锚 - Debugging javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found 带有客户端证书(crt,p12)的OkHttp:java.security.cert.CertPathValidatorException:找不到证书路径的信任锚 - OkHttp with Client Certificate (crt, p12): java.security.cert.CertPathValidatorException: Trust anchor for certification path not found IOException错误:javax.net.ssl.SSLHandshakeException:java.security.cert.CertPathValidatorException:找不到证书路径的信任锚 - IOException Error: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found Android:CertPathValidatorException:找不到证书路径的信任锚 - Android: CertPathValidatorException: Trust anchor for certification path not found
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM