ssl java.security.cert.CertPathValidatorException:找不到证书路径的信任锚,Android 2.3

zynd9foi  于 2023-11-18  发布在  Java
关注(0)|答案(7)|浏览(122)

在我的服务器(生产服务器),我有一个goDaddy ssl证书。我有iOS和Android应用程序连接到服务器,iOS连接没有问题,Android版本4.* 一切都很好,但与2.3.* 我总是得到一个SSLHandshakeException设备。
我做了完全一样的Android开发者页面(https://developer.android.com/training/articles/security-ssl.html).
我已经在堆栈溢出(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线程上,还有一些其他可能的原因,如日期/时间完全错误,这些都是不存在的。
考虑到这一点,我现在不知道问题可能出在哪里。
有什么建议吗?

**编辑:**堆栈跟踪如下:

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)

mfpqipee

mfpqipee1#

我在SO和Web中经历了很多地方来解决这个问题。这是为我工作的代码(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是一个包含证书的字符串,例如:

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


我已经测试过,你可以把任何字符的证书字符串,如果它是自签名的,只要你保持上面的确切结构。我用我的笔记本电脑的终端命令行获得的证书字符串。我你需要知道更多的细节,让我知道。

eiee3dmh

eiee3dmh2#

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

字符串

bbmckpt7

bbmckpt73#

证书的颁发者似乎不在2.3设备的信任存储中。
查看GoDaddy证书的根证书和中间证书,并检查证书是否存在于2.3设备上。
请参阅http://www.andreabaccega.com/blog/2010/09/23/android-root-certification-authorities-list/获取2.3证书列表。
当只有根CA可用时,请确保您的Web服务器也根据请求提供中间证书。

e5nszbig

e5nszbig4#

如果有人需要答案,我终于找到了答案后2谷歌天.基本上我们需要使用自定义TrustManager信任CA在我们的密钥库,这是因为在Android 2.3.x,密钥库没有被正确使用.信贷https://github.com/delgurth为CustomTrustManager.
请参阅: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并应用它,例如:

使用HttpURL连接

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


关于Volley

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

laik7k3q

laik7k3q5#

如果你正在使用AndroidNetworking /Rx 2AndroidNetworking库,这个解决方案是为我工作后浪费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);
    }
}

字符串

eqqqjvef

eqqqjvef6#

运行此命令以验证代码。keystore具有系统证书:

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

字符串

ykejflvf

ykejflvf7#

我从网站上下载了证书并放在res/raw folderx 1c 0d1x上



并将此添加到网络配置文件中。

<base-config cleartextTrafficPermitted="true">
    <trust-anchors>

        <certificates src="system"></certificates>
        <certificates src="user"></certificates>

    </trust-anchors>
</base-config>

<domain-config cleartextTrafficPermitted="true">
    <domain includeSubdomains="true">enci.it</domain>
    <domain includeSubdomains="true">sport.enci.it</domain>
    <trust-anchors>
        <certificates src="@raw/enci"/>
    </trust-anchors>
</domain-config>

字符串

相关问题