web-dev-qa-db-fra.com

Android Java.security.cert.CertPathValidatorException: ancre de confiance pour le chemin de certification introuvable

Il y a trois hôtes qu'une application Android fait l'authentification et l'autorisation. L'hôte final est l'API REST. Pour la première fois en utilisant Oauth processus d'authentification et d'autorisation cela fonctionne sans problème.

Mais si l'utilisateur tue l'application après s'être connecté et avoir accédé aux services fournis par REST API, puis à nouveau ouvrir l'application, En ce moment, le processus d'authentification et d'autorisation ne se produit pas, seule l'API REST. Java.security.cert.CertPathValidatorException mais cela fonctionnait lors de la première utilisation (connexion puis utilisation de l'application).

Quelqu'un peut-il expliquer le scénario derrière cette exception et ce qui ne va pas avec l'application. Cela fonctionne si les exceptions de certification sont ignorées comme ci-dessous selon this SO answer .

SSLSocketFactory sslSocketFactory = null;

        try {
            TrustManagerFactory tmf = TrustManagerFactory.getInstance(
                    TrustManagerFactory.getDefaultAlgorithm());
            // Initialise the TMF as you normally would, for example:
            try {
                tmf.init((KeyStore)null);
            } catch(KeyStoreException e) {
                e.printStackTrace();
            }
            TrustManager[] trustManagers = tmf.getTrustManagers();

            final X509TrustManager origTrustmanager = (X509TrustManager)trustManagers[0];

            // Create a trust manager that does not validate certificate chains
            TrustManager[] wrappedTrustManagers = new TrustManager[]{
                    new X509TrustManager() {
                        public Java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return origTrustmanager.getAcceptedIssuers();
                        }

                        public void checkClientTrusted(X509Certificate[] certs, String authType) {
                            try {
                                origTrustmanager.checkClientTrusted(certs, authType);
                            } catch(CertificateException e) {
                                e.printStackTrace();
                            }
                        }

                        public void checkServerTrusted(X509Certificate[] certs, String authType) {
                            try {
                                origTrustmanager.checkServerTrusted(certs, authType);
                            } catch(CertificateException e) {
                                e.printStackTrace();
                            }
                        }
                    }
            };
            //TrustManager[] trustAllCerts = TrustManagerFactory.getInstance("SSL").getTrustManagers();

            // Install the all-trusting trust manager
            final SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, wrappedTrustManagers, new Java.security.SecureRandom());
            // Create an ssl socket factory with our all-trusting manager
            sslSocketFactory = sslContext.getSocketFactory();
        } catch (NoSuchAlgorithmException | KeyManagementException e) {
            e.printStackTrace();
        }
        return sslSocketFactory;

J'utilise Okhttp 3 pour les requêtes http. Toute suggestion aiderait à résoudre le problème. Et s'il vous plaît faites le moi savoir si j'utilise l'extrait de code ci-dessus, est-ce une violation de la sécurité? cela affectera-t-il la sécurité de l'application?

11
Ruwanka Madhushan

Je réponds à cela pour donner une idée du scénario et de la solution selon le Android pour les autres. J'ai résolu cela en utilisant un gestionnaire de confiance personnalisé.

Le problème était avec le certificat du serveur, il manque l'autorité de certification intermédiaire. Cependant, le premier chemin de certificat de flux est terminé d'une manière ou d'une autre et le résultat a été une validation réussie du chemin de certificat.

Il existe une solution pour cela dans site des développeurs Android . il suggère d'utiliser un gestionnaire d'approbation personnalisé qui approuve ce certificat de serveur ou il suggère au serveur d'inclure l'autorité de certification intermédiaire dans la chaîne de serveurs.

gestionnaire de confiance personnalisé. source: https://developer.Android.com/training/articles/security-ssl.html#UnknownCa

// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory.getInstance("X.509");
// From https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = new BufferedInputStream(new FileInputStream("load-der.crt"));
Certificate ca;
try {
    ca = cf.generateCertificate(caInput);
    System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
} finally {
    caInput.close();
}

// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore.getDefaultType();
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);

// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);

// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(null, tmf.getTrustManagers(), null);
// Tell the okhttp to use a SocketFactory from our SSLContext
OkHttpClient okHttpClient client = new OkHttpClient.Builder().sslSocketFactory(context.getSocketFactory()).build();

MISE À JOUR: Mon problème a été résolu après que l'autorité de certification intermédiaire a été ajoutée à la chaîne de certificats du côté serveur. C'est la meilleure solution, le regroupement du certificat avec l'application nécessite que l'application soit mise à jour à l'expiration du certificat ou tout autre problème lié à la gestion des certificats.

MISE À JOUR: 03/09/2017 Le moyen le plus simple de charger le fichier de certificat que j'ai trouvé est l'utilisation des ressources brutes.

InputStream caInput = new BufferedInputStream(context
                .getResources().openRawResource(R.raw.certfilename));

où certfilename est le fichier de certificat placé dans le dossier resources/raw. De même, okhttp sslSocketFactory(SSLSocketFactory sslSocketFactory) a été déprécié et l'approche suggérée dans le doc api okhttp peut être utilisée.

De plus, lors de l'obtention du certificat du serveur, il est préférable d'utiliser openssl.

openssl s_client -connect {server-address}:{port} -showcerts

Parce que j'avais l'habitude de récupérer cela dans Firefox et de faire face à une situation où il était modifié par la protection antivirus.

22
Ruwanka Madhushan