web-dev-qa-db-fra.com

Poignée de main Android 4.4.2 SSL abandonnée

Le code fonctionne sur mon émulateur Genymotion Android 4.4.4 mais pas sur le périphérique que j'utilise (4.4.2).

J'ai essayé beaucoup de solutions de contournement "faire confiance à tous les certificats" mais en vain (je ne pense pas que ce soit le problème, car le certificat est AOK).

Je pense avoir identifié le chiffre (en utilisant une trace wirehark de mon bureau); TLS 0x00 0x1E qui semble être assez rare?

Une idée pour régler le problème?

Voici mon code

StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
CloseableHttpClient client = HttpClientBuilder.create().setSSLSocketFactory(sslsf).build();
String baseURL = "https://mysite.co.uk/api/";
HttpGetHC4 request = new HttpGetHC4(baseURL + "/authenticate?user=abcd&password=1234");
CloseableHttpResponse response = client.execute(request);

Et erreur;

 javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6abff398: Failure in SSL library, usually a protocol error
 error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:744 0x684dfce0:0x00000000)
     at com.Android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.Java:449)
     at org.Apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.Java:340)
     at org.Apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.Java:281)
     at org.Apache.http.impl.conn.HttpClientConnectionOperator.connect(HttpClientConnectionOperator.Java:124)
     at org.Apache.http.impl.conn.BasicHttpClientConnectionManager.connect(BasicHttpClientConnectionManager.Java:322)
     at org.Apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.Java:373)
     at org.Apache.http.impl.execchain.MainClientExec.execute(MainClientExec.Java:225)
     at org.Apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.Java:195)
     at org.Apache.http.impl.execchain.RetryExec.execute(RetryExec.Java:86)
     at org.Apache.http.impl.execchain.RedirectExec.execute(RedirectExec.Java:108)
     at org.Apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.Java:178)
     at org.Apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.Java:82)
     at org.Apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.Java:106)
     at com.example.lee.printticket.Main$OrderAsyncTask.onPostExecute(Main.Java:239)
     at com.example.lee.printticket.Main$OrderAsyncTask.onPostExecute(Main.Java:189)
     at Android.os.AsyncTask.finish(AsyncTask.Java:632)
     at Android.os.AsyncTask.access$600(AsyncTask.Java:177)
     at Android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.Java:645)
     at Android.os.Handler.dispatchMessage(Handler.Java:102)
     at Android.os.Looper.loop(Looper.Java:136)
     at Android.app.ActivityThread.main(ActivityThread.Java:5017)
     at Java.lang.reflect.Method.invokeNative(Native Method)
     at Java.lang.reflect.Method.invoke(Method.Java:515)
     at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:785)
     at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:601)
     at dalvik.system.NativeStart.main(Native Method)
 Caused by: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6abff398: Failure in SSL library, usually a protocol error
 error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:744 0x684dfce0:0x00000000)
     at com.Android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
     at com.Android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.Java:406)
    ... 25 more

MODIFIER

Essayer d'utiliser une technique/bibliothèque différente;

        RequestQueue queue = Volley.newRequestQueue(this);
        String url ="https://mysite.co.uk/api/authenticate?user=abcd&password=1234";

        // Request a string response from the provided URL.
        StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        // Display the first 500 characters of the response string.
                        Log.d("response: ", response);
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.d("response: ", error.toString());
            }
        });
        // Add the request to the RequestQueue.
        queue.add(stringRequest);

Résultats;

D/response:: com.Android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6ad51be0: Failure in SSL library, usually a protocol error
D/response:: error:140740B5:SSL routines:SSL23_CLIENT_HELLO:no ciphers available (external/openssl/ssl/s23_clnt.c:486 0x684dfce0:0x00000000)

Ou avec le piratage NoSSLv3SocketFactory de Javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: négociation SSL interrompue: échec dans la bibliothèque SSL, généralement une erreur de protocole ;

        HttpStack stack = new HurlStack(null, new NoSSLv3SocketFactory());
        RequestQueue queue = Volley.newRequestQueue(this, stack);

Résultats;

D/response:: com.Android.volley.NoConnectionError: javax.net.ssl.SSLHandshakeException: javax.net.ssl.SSLProtocolException: SSL handshake aborted: ssl=0x6ae51d30: Failure in SSL library, usually a protocol error
D/response:: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure (external/openssl/ssl/s23_clnt.c:744 0x684dfce0:0x00000000)
22
Lee Tickett

Android utilise différents protocoles pour les opérations réseau.

Configuration par défaut pour différentes versions d'Android.

J'ai trouvé la solution pour OkHttpClient.

Protocol    Supported (API Levels)  Enabled by default (API Levels)

SSLv3       1–25                    1–22
TLSv1       1+                      1+
TLSv1.1     16+                     20+
TLSv1.2     16+                     20+

Nous devons donc modifier le protocole de connexion dans Android VERSION> = 16 et VERSION <22.

Créer un fichier Java Tls12SocketFactory.Java

/**
 * Enables TLS v1.2 when creating SSLSockets.
 * <p/>
 * For some reason, Android supports TLS v1.2 from API 16, but enables it by
 * default only from API 20.
 * @link https://developer.Android.com/reference/javax/net/ssl/SSLSocket.html
 * @see SSLSocketFactory
 */
public class Tls12SocketFactory extends SSLSocketFactory {
    private static final String[] TLS_V12_ONLY = {"TLSv1.2"};

    final SSLSocketFactory delegate;

    public Tls12SocketFactory(SSLSocketFactory base) {
        this.delegate = base;
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return delegate.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return delegate.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket s, String Host, int port, boolean autoClose) throws IOException {
        return patch(delegate.createSocket(s, Host, port, autoClose));
    }

    @Override
    public Socket createSocket(String Host, int port) throws IOException, UnknownHostException {
        return patch(delegate.createSocket(Host, port));
    }

    @Override
    public Socket createSocket(String Host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        return patch(delegate.createSocket(Host, port, localHost, localPort));
    }

    @Override
    public Socket createSocket(InetAddress Host, int port) throws IOException {
        return patch(delegate.createSocket(Host, port));
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return patch(delegate.createSocket(address, port, localAddress, localPort));
    }

    private Socket patch(Socket s) {
        if (s instanceof SSLSocket) {
            ((SSLSocket) s).setEnabledProtocols(TLS_V12_ONLY);
        }
        return s;
    }
}

Mettez cette méthode quelque part dans votre code.

public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) {
        if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) {
            try {
                SSLContext sc = SSLContext.getInstance("TLSv1.2");
                sc.init(null, null, null);
                client.sslSocketFactory(new Tls12SocketFactory(sc.getSocketFactory()));

                ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
                        .tlsVersions(TlsVersion.TLS_1_2).build();

                List<ConnectionSpec> specs = new ArrayList<>();
                specs.add(cs);
                specs.add(ConnectionSpec.COMPATIBLE_TLS);
                specs.add(ConnectionSpec.CLEARTEXT);

                client.connectionSpecs(specs);
            } catch (Exception exc) {
                Log.e("OkHttpTLSCompat", "Error while setting TLS 1.2", exc);
            }
        }

        return client;
    }

    public OkHttpClient getNewHttpClient() {
        OkHttpClient.Builder client = new OkHttpClient.Builder().followRedirects(true).followSslRedirects(true)
                .retryOnConnectionFailure(true).cache(null).connectTimeout(5, TimeUnit.SECONDS)
                .writeTimeout(5, TimeUnit.SECONDS).readTimeout(5, TimeUnit.SECONDS);

        return enableTls12OnPreLollipop(client).build();
    }

Créez une instance OkHttp comme ci-dessous:

private OkHttpClient getNewHttpClient() {
    OkHttpClient.Builder client = new OkHttpClient.Builder()
            .followRedirects(true)
            .followSslRedirects(true)
            .retryOnConnectionFailure(true)
            .cache(null)
            .connectTimeout(5, TimeUnit.SECONDS)
            .writeTimeout(5, TimeUnit.SECONDS)
            .readTimeout(5, TimeUnit.SECONDS);

    return enableTls12OnPreLollipop(client).build();
}

Utilisez un objet client comme ceci:

OkHttpClient client = getNewHttpClient();
Request.Builder requestBuilder = new Request.Builder();
URL url = new URL("YOUR_URL_LINK");
Request request = requestBuilder.url(url).build();
Call call = client.newCall(request);
Response response = call.execute();

MERCI

2
user9453341

Essaye ça:

public class ClientSSLSocketFactory {

    public static SSLSocketFactory getSocketFactory(Context context) {
        try {
            X509TrustManager tm = new X509TrustManager() {
                public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
                }

                public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
                }

                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[0];
                }
            };
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(null, new TrustManager[]{tm}, new SecureRandom());

            SSLSocketFactory ssf = SSLCertificateSocketFactory.getDefault(10000, new SSLSessionCache(context));
            return ssf;
        } catch (Exception ex) {
            Log.e("ssl", "Error during the getSocketFactory");
            ex.printStackTrace();
            return null;
        }
    }
}

Et lors de la création de la file d'attente:

sRequestQueue = Volley.newRequestQueue(context, new HurlStack(null, ClientSSLSocketFactory.getSocketFactory()));

J'espère que ça aide.

0
GuilhE

Je crois que le problème est que SSL v3 est basé sur des chaînes comme celle-ci, ce qui suggère qu’il essaie d’utiliser SSL v3: SSL23_GET_SERVER_HELLO: sslv3 

SSL v3 est considéré comme non sécurisé et est donc désactivé sur la plupart des logiciels modernes (même dans des endroits où vous vous attendriez à ce qu'il fonctionne, de nombreuses entreprises l'ont tout simplement retiré). La trace de la pile indique également que le code utilise le client http Apache: Org.Apache.http.impl.client

Vous devez donc empêcher le client http Apache d’utiliser ssl v3. Le client HTTP Apache existe séparément des bibliothèques Java SSL/TLS standard. 

Il y a une autre question de stackoverflow qui n'a pas reçu de réponse: Comment désactiver SSLv3 dans Apache HttpClient

Ce lien semble le plus prometteur: https://discretemkt.wordpress.com/2014/11/16/commons-httpclient-can-disable-sslv3/

Ici, la ligne de clé est 

Protocol.registerProtocol(scheme, customHttps);

Cet appel semble vous permettre de contourner l’usine ssl existante. Si vous exécutez ce code en premier, cela peut fonctionner si les versions du client HTTP Apache sont compatibles.

Notez également que TLS 1.0 est également considéré comme non sécurisé de nos jours. TLS 1.2 et 1.3 et la norme maintenant.

0
Sean F