Possible fix to enable connections using TLS 1.1 and 1.2 on pre-Lollipop android versions. Also expands to the enabled list of cipher suites.
parent
bfc89b26bd
commit
fd472fbe1f
@ -0,0 +1,157 @@ |
|||||||
|
/* Copyright 2017 Andrew Dawson |
||||||
|
* |
||||||
|
* This file is part of Tusky. |
||||||
|
* |
||||||
|
* Tusky is free software: you can redistribute it and/or modify it under the terms of the GNU |
||||||
|
* Lesser General Public License as published by the Free Software Foundation, either version 3 of |
||||||
|
* the License, or (at your option) any later version. |
||||||
|
* |
||||||
|
* Tusky is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even |
||||||
|
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser |
||||||
|
* General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU Lesser General Public License along with Tusky. If |
||||||
|
* not, see <http://www.gnu.org/licenses/>. */
|
||||||
|
|
||||||
|
package com.keylesspalace.tusky; |
||||||
|
|
||||||
|
import android.os.Build; |
||||||
|
import android.support.annotation.NonNull; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.net.InetAddress; |
||||||
|
import java.net.Socket; |
||||||
|
import java.net.UnknownHostException; |
||||||
|
import java.security.KeyManagementException; |
||||||
|
import java.security.KeyStore; |
||||||
|
import java.security.KeyStoreException; |
||||||
|
import java.security.NoSuchAlgorithmException; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import javax.net.ssl.SSLContext; |
||||||
|
import javax.net.ssl.SSLSocket; |
||||||
|
import javax.net.ssl.SSLSocketFactory; |
||||||
|
import javax.net.ssl.TrustManager; |
||||||
|
import javax.net.ssl.TrustManagerFactory; |
||||||
|
import javax.net.ssl.X509TrustManager; |
||||||
|
|
||||||
|
import okhttp3.ConnectionSpec; |
||||||
|
import okhttp3.OkHttpClient; |
||||||
|
import okhttp3.logging.HttpLoggingInterceptor; |
||||||
|
|
||||||
|
class OkHttpUtils { |
||||||
|
private static final String TAG = "OkHttpUtils"; // logging tag
|
||||||
|
|
||||||
|
@NonNull |
||||||
|
static OkHttpClient.Builder getCompatibleClientBuilder() { |
||||||
|
ConnectionSpec fallback = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS) |
||||||
|
.allEnabledCipherSuites() |
||||||
|
.supportsTlsExtensions(true) |
||||||
|
.build(); |
||||||
|
|
||||||
|
List<ConnectionSpec> specList = new ArrayList<>(); |
||||||
|
specList.add(ConnectionSpec.MODERN_TLS); |
||||||
|
specList.add(fallback); |
||||||
|
specList.add(ConnectionSpec.CLEARTEXT); |
||||||
|
|
||||||
|
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); |
||||||
|
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); |
||||||
|
|
||||||
|
OkHttpClient.Builder builder = new OkHttpClient.Builder() |
||||||
|
.connectionSpecs(specList) |
||||||
|
.addInterceptor(loggingInterceptor); |
||||||
|
|
||||||
|
return enableHigherTlsOnPreLollipop(builder); |
||||||
|
} |
||||||
|
|
||||||
|
@NonNull |
||||||
|
static OkHttpClient getCompatibleClient() { |
||||||
|
return getCompatibleClientBuilder().build(); |
||||||
|
} |
||||||
|
|
||||||
|
private static OkHttpClient.Builder enableHigherTlsOnPreLollipop(OkHttpClient.Builder builder) { |
||||||
|
if (Build.VERSION.SDK_INT >= 16 && Build.VERSION.SDK_INT < 22) { |
||||||
|
try { |
||||||
|
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance( |
||||||
|
TrustManagerFactory.getDefaultAlgorithm()); |
||||||
|
trustManagerFactory.init((KeyStore) null); |
||||||
|
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers(); |
||||||
|
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) { |
||||||
|
throw new IllegalStateException("Unexpected default trust managers:" |
||||||
|
+ Arrays.toString(trustManagers)); |
||||||
|
} |
||||||
|
|
||||||
|
X509TrustManager trustManager = (X509TrustManager) trustManagers[0]; |
||||||
|
|
||||||
|
SSLContext sslContext = SSLContext.getInstance("TLS"); |
||||||
|
sslContext.init(null, new TrustManager[] { trustManager }, null); |
||||||
|
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory(); |
||||||
|
|
||||||
|
builder.sslSocketFactory(new Tls11And12SocketFactory(sslSocketFactory), |
||||||
|
trustManager); |
||||||
|
} catch (NoSuchAlgorithmException|KeyStoreException|KeyManagementException e) { |
||||||
|
Log.e(TAG, "Failed enabling TLS 1.1 & 1.2. " + e.getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return builder; |
||||||
|
} |
||||||
|
|
||||||
|
private static class Tls11And12SocketFactory extends SSLSocketFactory { |
||||||
|
private static final String[] TLS_VERSIONS = { "TLSv1.1", "TLSv1.2" }; |
||||||
|
|
||||||
|
final SSLSocketFactory delegate; |
||||||
|
|
||||||
|
Tls11And12SocketFactory(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 socket) { |
||||||
|
if (socket instanceof SSLSocket) { |
||||||
|
SSLSocket sslSocket = (SSLSocket) socket; |
||||||
|
sslSocket.setEnabledProtocols(TLS_VERSIONS); |
||||||
|
} |
||||||
|
return socket; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue