Application: On Android 4.4 (KitKat) with Google Play Services installed, manually enable TLS 1.2.

This has two steps:

- Use SafetyNet ProviderInstaller.insertProvider() to load a recent version of Conscrypt.
- Enable TLS 1.2 in the socket factory by wrapping it.
This commit is contained in:
Andreas Schildbach 2019-11-22 18:00:48 +01:00
parent 2ffe808276
commit ad2a0d6891
2 changed files with 143 additions and 0 deletions

View file

@ -18,6 +18,7 @@
package de.schildbach.oeffi;
import java.io.File;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
@ -89,6 +90,13 @@ public class Application extends android.app.Application {
});
interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
builder.addNetworkInterceptor(interceptor);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
log.info("manually enabling TLS 1.2 on API level {}", Build.VERSION.SDK_INT);
if (safetyNetInsertProvider()) {
final Tls12SocketFactory socketFactory = new Tls12SocketFactory();
builder.sslSocketFactory(socketFactory, socketFactory.getTrustManager());
}
}
okHttpClient = builder.build();
initMaps();
@ -154,6 +162,23 @@ public class Application extends android.app.Application {
initNotificationManager();
}
private boolean safetyNetInsertProvider() {
// This piece of code uses SafetyNet (Google Play Services) to insert a recent version of Conscrypt, if
// available. We use reflection to avoid the proprietary Google Play Services client library.
try {
final Stopwatch watch = Stopwatch.createStarted();
final Context remoteContext = createPackageContext("com.google.android.gms", 3);
final Method insertProvider = remoteContext.getClassLoader().loadClass("com.google.android.gms.common" +
".security.ProviderInstallerImpl").getMethod("insertProvider", new Class[] { Context.class });
insertProvider.invoke(null, new Object[] { remoteContext });
log.info("insertProvider successful, took {}", watch.stop());
return true;
} catch (final Exception x) {
log.warn("insertProvider failed", x);
return false;
}
}
private void initLogging() {
final File logDir = new File(getFilesDir(), "log");
final File logFile = new File(logDir, "oeffi.log");

View file

@ -0,0 +1,118 @@
/*
* Copyright the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.schildbach.oeffi;
import androidx.annotation.Nullable;
import okhttp3.TlsVersion;
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 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.Arrays;
import java.util.Set;
import java.util.TreeSet;
public final class Tls12SocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
private final TrustManager[] trustManagers;
public Tls12SocketFactory() {
try {
final TrustManagerFactory trustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
trustManagers = trustManagerFactory.getTrustManagers();
final SSLContext context = SSLContext.getInstance(TlsVersion.TLS_1_2.javaName());
context.init(null, trustManagers, null);
delegate = context.getSocketFactory();
} catch (final NoSuchAlgorithmException | KeyManagementException | KeyStoreException x) {
throw new RuntimeException(x);
}
}
@Nullable
public X509TrustManager getTrustManager() {
for (final TrustManager tm : trustManagers)
if (tm instanceof X509TrustManager)
return (X509TrustManager) tm;
return null;
}
@Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
@Override
public Socket createSocket() throws IOException {
return patchForTls12(delegate.createSocket());
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return patchForTls12(delegate.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
return patchForTls12(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException,
UnknownHostException {
return patchForTls12(delegate.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return patchForTls12(delegate.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return patchForTls12(delegate.createSocket(address, port, localAddress, localPort));
}
private Socket patchForTls12(final Socket socket) {
if (socket != null && (socket instanceof SSLSocket)) {
final SSLSocket sslSocket = (SSLSocket) socket;
final Set<String> protocols = new TreeSet<>();
protocols.addAll(Arrays.asList(sslSocket.getEnabledProtocols()));
protocols.add(TlsVersion.TLS_1_2.javaName());
sslSocket.setEnabledProtocols(protocols.toArray(new String[0]));
}
return socket;
}
}