26 votes

Désactiver la vérification du certificat SSL dans la bibliothèque de rétrofit

J'utilise retrofit dans Android pour me connecter au serveur.

public class ApiClient {
    public static final String BASE_URL = "https://example.com/";
    private static Retrofit retrofit = null;

    public static Retrofit getClient() {
        if (retrofit==null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }
}

C'est mon serveur de développement et je veux désactiver la vérification des certificats. Comment puis-je l'implémenter dans ce code ?

ERROR : javax.net.ssl.SSLHandshakeException : java.security.cert.CertPathValidatorException : Ancrage de confiance pour le chemin de certification non trouvé.

43voto

Hitesh Sahu Points 12561

Utilisez cette classe pour obtenir une instance de Retrofit non sécurisée. J'ai inclus les importations pour éviter toute confusion.

import java.security.cert.CertificateException;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import view.utils.AppConstants;

/**
 * Created by Hitesh.Sahu on 11/23/2016.
 */

public class NetworkHandler {

    public static Retrofit getRetrofit() {

        return new Retrofit.Builder()
                .baseUrl(AppConstants.BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(getUnsafeOkHttpClient())
                .build();
    }

    private static OkHttpClient getUnsafeOkHttpClient() {
        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) throws CertificateException {
                        }

                        @Override
                        public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                        }

                        @Override
                        public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                            return new java.security.cert.X509Certificate[]{};
                        }
                    }
            };

            // 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.hostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });

            OkHttpClient okHttpClient = builder.build();
            return okHttpClient;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

Et puis simplement utiliser retrofit sans vérification ssl comme ceci

    private void postFeedbackOnServer() {

        MyApiEndpointInterface apiService =
                NetworkHandler.getRetrofit().create(MyApiEndpointInterface.class);

        Call<ResponseBE> call = apiService.submitFeedbackToServer(requestObject);

        Log.e(TAG ,  "Request is" + new Gson().toJson(requestObject).toString() );

        call.enqueue(new Callback<ResponseBE>() {
            @Override
            public void onResponse(Call<ResponseBE> call, Response<ResponseBE> response) {
                int statusCode = response.code();

                if (statusCode == HttpURLConnection.HTTP_OK) {

              ......

                } else {
                    Toast.makeText(FeedbackActivity.this, "Failed to submit Data" + statusCode, Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onFailure(Call<ResponseBE> call, Throwable t) {

                // Log error here since request failed
                Toast.makeText(FeedbackActivity.this, "Failure" + t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();

            }
        });
    }

17voto

whirlwin Points 3018

La syntaxe a un peu changé depuis la publication de la réponse de Hitesh Sahu. Vous pouvez désormais utiliser des lambdas pour certaines méthodes, supprimer certaines clauses throw et enchaîner les invocations de méthodes de construction.

private static OkHttpClient createOkHttpClient() {
    try {
        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[]{};
                    }
                }
        };
        final SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
        return new OkHttpClient.Builder()
                .sslSocketFactory(sslContext.getSocketFactory())
                .hostnameVerifier((hostname, session) -> true)
                .build();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

6voto

BNK Points 7177

IMO, vous pouvez lire Documentation de Google - Sécurité avec HTTPS et SSL .

A propos de l'exemple de code pour utiliser Retrofit avec votre certificat auto-signé, veuillez essayer ce qui suit, j'espère que cela vous aidera !

...
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    try{
        OkHttpClient client = new OkHttpClient.Builder()
                .sslSocketFactory(getSSLSocketFactory())
                .hostnameVerifier(getHostnameVerifier())
                .build();

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(API_URL_BASE)
                .addConverterFactory(GsonConverterFactory.create())
                .client(client)
                .build();

        WebAPIService service = retrofit.create(WebAPIService.class);

        Call<JsonObject> jsonObjectCall = service.getData(...);
        ...
    } catch (Exception e) {
        e.printStackTrace();
    }
}

// for SSL...    
// Read more at https://developer.android.com/training/articles/security-ssl.html#CommonHostnameProbs
private HostnameVerifier getHostnameVerifier() {
    return new HostnameVerifier() {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true; // verify always returns true, which could cause insecure network traffic due to trusting TLS/SSL server certificates for wrong hostnames
            //HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
            //return hv.verify("localhost", session);
        }
    };
}        

private TrustManager[] getWrappedTrustManagers(TrustManager[] trustManagers) {
    final X509TrustManager originalTrustManager = (X509TrustManager) trustManagers[0];
    return new TrustManager[]{
            new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return originalTrustManager.getAcceptedIssuers();
                }

                public void checkClientTrusted(X509Certificate[] certs, String authType) {
                    try {
                        if (certs != null && certs.length > 0){
                            certs[0].checkValidity();
                        } else {
                            originalTrustManager.checkClientTrusted(certs, authType);
                        }
                    } catch (CertificateException e) {
                        Log.w("checkClientTrusted", e.toString());
                    }
                }

                public void checkServerTrusted(X509Certificate[] certs, String authType) {
                    try {
                        if (certs != null && certs.length > 0){
                            certs[0].checkValidity();
                        } else {
                            originalTrustManager.checkServerTrusted(certs, authType);
                        }
                    } catch (CertificateException e) {
                        Log.w("checkServerTrusted", e.toString());
                    }
                }
            }
    };
}

private SSLSocketFactory getSSLSocketFactory()
        throws CertificateException, KeyStoreException, IOException,
        NoSuchAlgorithmException, KeyManagementException {
    CertificateFactory cf = CertificateFactory.getInstance("X.509");
    InputStream caInput = getResources().openRawResource(R.raw.your_cert); // File path: app\src\main\res\raw\your_cert.cer
    Certificate ca = cf.generateCertificate(caInput);
    caInput.close();
    KeyStore keyStore = KeyStore.getInstance("BKS");
    keyStore.load(null, null);
    keyStore.setCertificateEntry("ca", ca);
    String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
    TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
    tmf.init(keyStore);
    TrustManager[] wrappedTrustManagers = getWrappedTrustManagers(tmf.getTrustManagers());
    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(null, wrappedTrustManagers, null);
    return sslContext.getSocketFactory();
}
...

3voto

The_GM Points 31

Je déconseille fortement de faire ça

Réponse courte - sous-classez HostNameVerifier, remplacez verify() pour qu'il renvoie toujours true.

Ceci a de meilleures options

Longue réponse - consultez mon blog (qui commence à dater) ici : Faire fonctionner Android et SSL ensemble

Peut-être la meilleure option pour votre scénario

Remplacez https par http pour votre serveur de test, la logique n'a pas à être modifiée.

HTH

0voto

mwo Points 1

La mise en œuvre d'une telle solution de contournement dans le code, même à des fins de test, est une mauvaise pratique.

Tu peux :

  1. Générez votre AC.
  2. Signer votre certificat avec l'AC.
  3. Ajoutez votre CA comme étant de confiance.

Quelques liens qui peuvent être utiles :

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X