From ec01a211376acea6c1e20a0c2f3436f22b2dc833 Mon Sep 17 00:00:00 2001 From: davigonz Date: Wed, 20 Feb 2019 18:16:37 +0100 Subject: [PATCH] Include TLSv1.2 support and make it work on 16 to 19 devices --- .../android/lib/common/http/HttpClient.java | 32 +++++++- .../lib/common/http/TLSSocketFactory.java | 82 +++++++++++++++++++ 2 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/TLSSocketFactory.java diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java index 566d98e9..e9f77fe4 100644 --- a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/HttpClient.java @@ -26,6 +26,7 @@ package com.owncloud.android.lib.common.http; import android.content.Context; +import android.os.Build; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.http.interceptors.HttpInterceptor; import com.owncloud.android.lib.common.http.interceptors.RequestHeaderInterceptor; @@ -39,8 +40,10 @@ import okhttp3.OkHttpClient; import okhttp3.Protocol; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -67,9 +70,34 @@ public class HttpClient { try { final X509TrustManager trustManager = new AdvancedX509TrustManager( NetworkUtils.getKnownServersStore(sContext)); - final SSLContext sslContext = SSLContext.getInstance("TLS"); + + SSLContext sslContext; + + try { + sslContext = SSLContext.getInstance("TLSv1.2"); + } catch (NoSuchAlgorithmException tlsv12Exception) { + try { + Log_OC.w(TAG, "TLSv1.2 is not supported in this device; falling through TLSv1.1"); + sslContext = SSLContext.getInstance("TLSv1.1"); + } catch (NoSuchAlgorithmException tlsv11Exception) { + Log_OC.w(TAG, "TLSv1.1 is not supported in this device; falling through TLSv1.0"); + sslContext = SSLContext.getInstance("TLSv1"); + // should be available in any device; see reference of supported protocols in + // http://developer.android.com/reference/javax/net/ssl/SSLSocket.html + } + } + sslContext.init(null, new TrustManager[]{trustManager}, null); + SSLSocketFactory sslSocketFactory; + + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + // TLS v1.2 is disabled by default from API 16 to 19, use custom SSLSocketFactory to enable it + sslSocketFactory = new TLSSocketFactory(sslContext.getSocketFactory()); + } else { + sslSocketFactory = sslContext.getSocketFactory(); + } + // Automatic cookie handling, NOT PERSISTENT CookieJar cookieJar = new CookieJar() { @Override @@ -97,7 +125,7 @@ public class HttpClient { .writeTimeout(HttpConstants.DEFAULT_DATA_TIMEOUT, TimeUnit.MILLISECONDS) .connectTimeout(HttpConstants.DEFAULT_CONNECTION_TIMEOUT, TimeUnit.MILLISECONDS) .followRedirects(false) - .sslSocketFactory(sslContext.getSocketFactory(), trustManager) + .sslSocketFactory(sslSocketFactory, trustManager) .hostnameVerifier((asdf, usdf) -> true) .cookieJar(cookieJar); // TODO: Not verifying the hostname against certificate. ask owncloud security human if this is ok. diff --git a/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/TLSSocketFactory.java b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/TLSSocketFactory.java new file mode 100644 index 00000000..9ec8815e --- /dev/null +++ b/owncloudComLibrary/src/main/java/com/owncloud/android/lib/common/http/TLSSocketFactory.java @@ -0,0 +1,82 @@ +/* ownCloud Android Library is available under MIT license + * Copyright (C) 2019 ownCloud GmbH. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +package com.owncloud.android.lib.common.http; + +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import java.io.IOException; +import java.net.InetAddress; +import java.net.Socket; + +public class TLSSocketFactory extends SSLSocketFactory { + private SSLSocketFactory mInternalSSLSocketFactory; + + public TLSSocketFactory(SSLSocketFactory delegate) { + mInternalSSLSocketFactory = delegate; + } + + @Override + public String[] getDefaultCipherSuites() { + return mInternalSSLSocketFactory.getDefaultCipherSuites(); + } + + @Override + public String[] getSupportedCipherSuites() { + return mInternalSSLSocketFactory.getSupportedCipherSuites(); + } + + @Override + public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException { + return enableTLSOnSocket(mInternalSSLSocketFactory.createSocket(s, host, port, autoClose)); + } + + @Override + public Socket createSocket(String host, int port) throws IOException { + return enableTLSOnSocket(mInternalSSLSocketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException { + return enableTLSOnSocket(mInternalSSLSocketFactory.createSocket(host, port, localHost, localPort)); + } + + @Override + public Socket createSocket(InetAddress host, int port) throws IOException { + return enableTLSOnSocket(mInternalSSLSocketFactory.createSocket(host, port)); + } + + @Override + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws + IOException { + return enableTLSOnSocket(mInternalSSLSocketFactory.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; + } +}