【问题标题】:java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. Android 2.3java.security.cert.CertPathValidatorException:找不到证书路径的信任锚。安卓2.3
【发布时间】:2014-09-27 03:21:12
【问题描述】:

在我的服务器(生产服务器)中,我有一个 goDaddy ssl 证书。 我有 iOS 和 Android 应用程序与服务器连接,iOS 连接没有问题,android 版本 4.* 一切都很好,但使用 2.3.* 的设备我总是得到 SSLHandshakeException。

我在 Android 开发者页面 (https://developer.android.com/training/articles/security-ssl.html) 上所做的完全一样。

我已经在 Stack Overflow (here) 中看到过类似的主题,但没有任何帮助。

然后我看到this 线程在谈论扩展密钥用法,但是在调试时我得到以下信息:

[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" ]

所以我猜证书不是“强制”扩展密钥使用。

this 线程上还有一些其他可能的原因,例如日期/时间完全错误,这些都是不存在的。

考虑到这一点,我现在不知道问题可能出在哪里。

有什么建议吗?

编辑: StackTrace 如下:

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)

【问题讨论】:

  • 你能提供堆栈跟踪吗?
  • 服务器的 URL 是什么?
  • 此外,您可以获得免费的StartCom 1 类服务器证书,该证书受到大多数桌面和移动浏览器的信任。它包括 iOS 2.0 和 Ansdroid 2.2。请参阅StartSSL Comparison ChartList of browser versions with StartCom certs。如果您需要通配符,那么您将不得不购买它。
  • @jww 抱歉,我不想表明我自己和/或我的公司,所以我觉得展示它很不自在。但是你能告诉我这个想法是什么吗?我已经买了一个 goDaddy 证书,所以再买一个也没有意义。
  • @j01101101 - StartcomCAcert 1 类证书是免费的。我想查看openssl s_client -connect <server>:<port> 的输出,以确保您发送的是有效链。

标签: java android security ssl android-sdk-2.3


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    如果以上所有答案都不起作用,并且您在使用 android 30 作为编译和目标 sdk 版本时在发布应用程序中遇到任何问题。 请从服务器下载您的 ssl .cert 文件。 并将其放在原始文件夹中。 在 XML 文件夹中创建 network_security_config。 在应用程序标签内的清单中使用行

    android:networkSecurityConfig="@xml/network_security_config"

    并在 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>
    

    【讨论】:

    • 谢谢,唯一的方法至少在 android 29 中有效。
    【解决方案3】:

    如果您使用的是 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);
        }
    }
    

    【讨论】:

      【解决方案4】:

      我浏览了 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是一个包含证书的String,例如:

      static public String certificateString=
          "-----BEGIN CERTIFICATE-----\n" +
          "MIIGQTCCBSmgAwIBAgIHBcg1dAivUzANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UE" +
          "BhMCSUwxFjAUBgNVBAoTDVN0YXJ0Q29tIEx0ZC4xKzApBgNVBAsTIlNlY3VyZSBE" +
          ... a bunch of characters...
          "5126sfeEJMRV4Fl2E5W1gDHoOd6V==\n" +
          "-----END CERTIFICATE-----";
      

      我已经测试过你可以在证书字符串中放入任何字符,如果它是自签名的,只要你保持上面的确切结构。 我使用笔记本电脑的终端命令行获得了证书字符串。如果您需要了解更多详情,请告诉我。

      【讨论】:

      • 嗨!您愿意帮助解决这个问题吗? stackoverflow.com/questions/39553999/… 我看到很多关于自签名 SSL 证书的问题,但大多数问题都已过时(例如,Android 不推荐使用的库),而我正在使用 Android Volley。我还看到大量的问题,例如“核对所有 SSL 证书”之类的答案,这真的让我感到不安(至少)。我想解决这个问题,也想解决这个问题,停止这种关于核销 ssl 证书的“废话”。
      • 很好的答案,效果很好。谢谢@Josh
      【解决方案5】:

      如果有人需要答案,我在google 2天后终于找到了答案。基本上我们需要使用自定义的 TrustManager 来信任 KeyStore 中的 CA,这是因为在 Android 2.3.x 中,没有正确使用密钥库。归功于 CustomTrustManager 的 https://github.com/delgurth

      请参考: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;
        }
      }
      

      要使用它,只需获取 SSLSocketFactory 并应用它,例如:

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

      齐射

      KeyPinStore keystore = KeyPinStore.getInstance();
      SSLSocketFactory sslSF = keystore.getContext().getSocketFactory();
      RequestQueue mRequestQueue = Volley.newRequestQueue(context, new HurlStack(null, sslSF));
      

      【讨论】:

      • 在哪里可以获得 certificate1.crt、certificate2.crt、certificate3.crt?
      【解决方案6】:

      您的证书颁发者似乎不在 2.3 设备的信任库中。

      查看您的 GoDaddy 证书的根证书和中间证书,并检查证书是否存在于您的 2.3 设备上。

      请参阅http://www.andreabaccega.com/blog/2010/09/23/android-root-certification-authorities-list/ 获取 2.3 证书列表。

      如果只有根 CA 可用,请确保您的网络服务器也应请求提供中间证书。

      【讨论】:

      • 我的网络服务器也提供中间证书,但它不在该列表中。根据文档,通过使用我的证书创建一个密钥库并添加一个信任管理器(我正在这样做)将起作用,但目前它显然不起作用。有什么建议吗?
      • 您的网络服务器的网址是什么?我可以访问它吗?您是指服务器上的信任管理器还是您的 Android 应用程序中的信任管理器?
      • 看起来问题是从 Android 开发文档(在“未知证书颁发机构”中)运行代码时导致错误。我删除了它,它现在可以在所有设备上运行。我将此答案标记为正确答案,因为实际上代码正在更改我的设备 CA。 @jww 正如我所说,我的代码与 Android 文档中的代码完全相同,并提供了链接。我可以不给你链接,提供不正确的信息与不提供一些信息有很大的不同。社区仍然能够帮助我,就像 userM1433372 所做的那样。
      猜你喜欢
      • 2016-03-31
      • 2017-01-08
      • 2016-12-26
      • 1970-01-01
      • 2018-01-06
      • 2023-02-15
      • 2019-08-02
      • 1970-01-01
      相关资源
      最近更新 更多