From c8092220dfe7da2c417688969e83097ecfc0e2aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20K=C3=B6ster?= Date: Mon, 25 Apr 2016 14:12:53 +0200 Subject: [PATCH] Enable TLS 1.2 on Android 4.1-4.4 (See Issue #7192) Credits to Alex Gotev (@gotev) for the nice implementation. --- .../modules/network/OkHttpClientProvider.java | 46 ++++++++++- .../modules/network/TLSSocketFactory.java | 78 +++++++++++++++++++ 2 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/modules/network/TLSSocketFactory.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/network/OkHttpClientProvider.java b/ReactAndroid/src/main/java/com/facebook/react/modules/network/OkHttpClientProvider.java index dcd8972cf6e157..7c154f9cf9f416 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/modules/network/OkHttpClientProvider.java +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/network/OkHttpClientProvider.java @@ -9,11 +9,19 @@ package com.facebook.react.modules.network; +import android.os.Build; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.TimeUnit; import javax.annotation.Nullable; +import javax.net.ssl.SSLContext; +import okhttp3.ConnectionSpec; import okhttp3.OkHttpClient; +import okhttp3.TlsVersion; /** * Helper class that provides the same OkHttpClient instance that will be used for all networking @@ -39,11 +47,43 @@ public static void replaceOkHttpClient(OkHttpClient client) { private static OkHttpClient createClient() { // No timeouts by default - return new OkHttpClient.Builder() + OkHttpClient.Builder client = new OkHttpClient.Builder() .connectTimeout(0, TimeUnit.MILLISECONDS) .readTimeout(0, TimeUnit.MILLISECONDS) .writeTimeout(0, TimeUnit.MILLISECONDS) - .cookieJar(new ReactCookieJarContainer()) - .build(); + .cookieJar(new ReactCookieJarContainer()); + + return enableTls12OnPreLollipop(client).build(); } + + /* + On Android 4.1-4.4 (API level 16 to 19) TLS 1.1 and 1.2 are + available but not enabled by default. The following method + enables it. + + See also https://github.com/facebook/react-native/issues/7192 + */ + public static OkHttpClient.Builder enableTls12OnPreLollipop(OkHttpClient.Builder client) { + if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 20) { + try { + client.sslSocketFactory(new TLSSocketFactory()); + + ConnectionSpec cs = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) + .tlsVersions(TlsVersion.TLS_1_2) + .build(); + + List specs = new ArrayList<>(); + specs.add(cs); + specs.add(ConnectionSpec.COMPATIBLE_TLS); + specs.add(ConnectionSpec.CLEARTEXT); + + client.connectionSpecs(specs); + } catch (Exception exc) { + Log.e("OkHttpClientProvider", "Error while enabling TLS 1.2", exc); + } + } + + return client; + } + } diff --git a/ReactAndroid/src/main/java/com/facebook/react/modules/network/TLSSocketFactory.java b/ReactAndroid/src/main/java/com/facebook/react/modules/network/TLSSocketFactory.java new file mode 100644 index 00000000000000..428fd882c7a075 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/modules/network/TLSSocketFactory.java @@ -0,0 +1,78 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +package com.facebook.react.modules.network; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; + +/** + * + * This class is needed for TLS 1.2 support on Android 4.x + * + * Source: http://blog.dev-area.net/2015/08/13/android-4-1-enable-tls-1-1-and-tls-1-2/ + */ +public class TLSSocketFactory extends SSLSocketFactory { + private SSLSocketFactory internalSSLSocketFactory; + + public TLSSocketFactory() throws KeyManagementException, NoSuchAlgorithmException { + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, null, null); + internalSSLSocketFactory = context.getSocketFactory(); + } + + @Override + public String[] getDefaultCipherSuites() { + return internalSSLSocketFactory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return internalSSLSocketFactory.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(s, host, port, autoClose)); + } + + @Override + public Socket createSocket(String host, int port) throws IOException, UnknownHostException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port, localHost, localPort)); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + return enableTLSOnSocket(internalSSLSocketFactory.createSocket(address, port, localAddress, localPort)); + } + + private Socket enableTLSOnSocket(Socket socket) { + if(socket != null && (socket instanceof SSLSocket)) { + ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.1", "TLSv1.2"}); + } + return socket; + } +}