Java.sécurité.cert.CertPathValidatorException: Ancre de confiance pour le chemin de certification introuvable. Android 2.3


Dans mon serveur (serveur de production), j'ai un certificat ssl GoDaddy. J'ai des applications iOS et Android qui se connectent au serveur, iOS qui se connecte sans problème, Android avec les versions 4.* tout est bon, mais avec des appareils avec 2.3.* Je reçois toujours une exception SSLHandshakeException.

J'ai fait exactement comme sur la page des développeurs Android (https://developer.android.com/training/articles/security-ssl.html).

J'ai déjà vu des threads similaires ici dans stackoverflow (ici ) mais aucun n'aide.

Ensuite, j'ai vu ce thread parler de l'utilisation étendue des clés, mais lors du débogage, j'obtiens les informations suivantes:

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

Donc je suppose que le certificat ne "force" pas l'utilisation étendue de la clé.

Aussi sur ce thread il y a d'autres causes possibles telles que la date/heure complètement fausse, qui sont toutes inexistantes.

En tenant compte de cela, je ne sais maintenant pas où le problème pourrait être.

Des suggestions?

MODIFIER: StackTrace ci-dessous:

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)
Author: Community, 2014-08-04

3 answers

Il semble que l'émetteur de votre certificat ne soit pas dans le magasin de confiance des périphériques 2.3.

Jetez un œil aux CA racine et intermédiaire de votre certificat GoDaddy et vérifiez si les certificats sont présents sur votre appareil 2.3.

Voir http://www.andreabaccega.com/blog/2010/09/23/android-root-certification-authorities-list/ pour obtenir une liste de 2.3 certificats.

Lorsque seule l'autorité de certification racine est disponible, assurez-vous que votre serveur web sert aussi les certificats intermédiaires sur demande.

 2
Author: userM1433372, 2014-08-04 16:04:25

J'ai parcouru BEAUCOUP d'endroits dans SO et sur le Web pour résoudre ce problème. C'est le code qui a fonctionné pour moi (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 est une Chaîne qui contient le Certificat, par exemple:

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

J'ai testé que vous pouvez mettre n'importe quel caractère dans la chaîne de certificat, si elle est auto-signée, tant que vous conservez la structure exacte ci-dessus. J'ai obtenu la chaîne de certificat avec la ligne de commande du terminal de mon ordinateur portable. J'ai besoin de connaître plus de détails, faites-le moi savoir.

 5
Author: Josh, 2018-06-25 11:29:41

Au cas où quelqu'un aurait besoin de la réponse, j'ai finalement trouvé la réponse après 2 jours de Google. Fondamentalement, nous devons utiliser TrustManager personnalisé pour faire confiance au CAs dans notre magasin de clés, c'est parce que dans Android 2.3.x, le keystore n'est pas utilisé correctement. Crédit à https://github.com/delgurth pour le CustomTrustManager.

Veuillez vous référer: 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;
  }
}

Pour l'utiliser, il suffit d'obtenir le SSLSocketFactory et de l'appliquer, par exemple:

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

Avec Terrain De Volley -

KeyPinStore keystore = KeyPinStore.getInstance();
SSLSocketFactory sslSF = keystore.getContext().getSocketFactory();
RequestQueue mRequestQueue = Volley.newRequestQueue(context, new HurlStack(null, sslSF));
 1
Author: Newbie009, 2017-09-28 09:38:27