upgrade OkHttp, add Conscrypt (#1083)

* upgrade OkHttp, add Conscrypt

* fix tests
This commit is contained in:
Konrad Pozniak 2019-03-04 19:25:51 +01:00 committed by GitHub
parent a9524508e6
commit 2b490dd4f3
4 changed files with 21 additions and 87 deletions

View File

@ -94,8 +94,9 @@ dependencies {
implementation 'com.squareup.retrofit2:converter-gson:2.5.0' implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
implementation 'com.squareup.picasso:picasso:2.5.2' implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.squareup.okhttp3:okhttp:3.12.0' implementation 'com.squareup.okhttp3:okhttp:3.13.1'
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.13.1'
implementation "org.conscrypt:conscrypt-android:2.0.0"
implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0' implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0'
implementation 'com.github.connyduck:sparkbutton:2.0.0' implementation 'com.github.connyduck:sparkbutton:2.0.0'
implementation 'com.github.chrisbanes:PhotoView:2.3.0' implementation 'com.github.chrisbanes:PhotoView:2.3.0'

View File

@ -32,6 +32,10 @@ import com.keylesspalace.tusky.util.EmojiCompatFont;
import com.keylesspalace.tusky.util.NotificationPullJobCreator; import com.keylesspalace.tusky.util.NotificationPullJobCreator;
import com.squareup.picasso.Picasso; import com.squareup.picasso.Picasso;
import org.conscrypt.Conscrypt;
import java.security.Security;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.android.AndroidInjector; import dagger.android.AndroidInjector;
@ -62,6 +66,8 @@ public class TuskyApplication extends Application implements HasActivityInjector
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
initSecurityProvider();
appDatabase = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "tuskyDB") appDatabase = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "tuskyDB")
.allowMainThreadQueries() .allowMainThreadQueries()
.addMigrations(AppDatabase.MIGRATION_2_3, AppDatabase.MIGRATION_3_4, AppDatabase.MIGRATION_4_5, .addMigrations(AppDatabase.MIGRATION_2_3, AppDatabase.MIGRATION_3_4, AppDatabase.MIGRATION_4_5,
@ -93,6 +99,10 @@ public class TuskyApplication extends Application implements HasActivityInjector
} }
protected void initSecurityProvider() {
Security.insertProviderAt(Conscrypt.newProvider(), 1);
}
/** /**
* This method will load the EmojiCompat font which has been selected. * This method will load the EmojiCompat font which has been selected.
* If this font does not work or if the user hasn't selected one (yet), it will use a * If this font does not work or if the user hasn't selected one (yet), it will use a

View File

@ -19,49 +19,21 @@ import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Build; import android.os.Build;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import androidx.annotation.NonNull;
import android.util.Log;
import com.keylesspalace.tusky.BuildConfig; import com.keylesspalace.tusky.BuildConfig;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.Proxy; import java.net.Proxy;
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 java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext; import androidx.annotation.NonNull;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.Cache; import okhttp3.Cache;
import okhttp3.ConnectionSpec;
import okhttp3.Interceptor; import okhttp3.Interceptor;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.Request; import okhttp3.Request;
public class OkHttpUtils { public class OkHttpUtils {
private static final String TAG = "OkHttpUtils"; // logging tag
/**
* Makes a Builder with the maximum range of TLS versions and cipher suites enabled.
* <p>
* It first tries the "approved" list of cipher suites given in OkHttp (the default in
* ConnectionSpec.MODERN_TLS) and if that doesn't work falls back to the set of ALL enabled,
* then falls back to plain http.
* <p>
* API level 24 has a regression in elliptic curves where it only supports secp256r1, so this
* first tries a fallback without elliptic curves at all, and then tries them after.
* <p>
* TLS 1.1 and 1.2 have to be manually enabled on API levels 16-20.
*/
@NonNull @NonNull
public static OkHttpClient.Builder getCompatibleClientBuilder(@NonNull Context context) { public static OkHttpClient.Builder getCompatibleClientBuilder(@NonNull Context context) {
@ -77,25 +49,13 @@ public class OkHttpUtils {
httpPort = -1; httpPort = -1;
} }
ConnectionSpec fallback = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.allEnabledCipherSuites()
.supportsTlsExtensions(true)
.build();
List<ConnectionSpec> specList = new ArrayList<>();
specList.add(ConnectionSpec.MODERN_TLS);
addNougatFixConnectionSpec(specList);
specList.add(fallback);
specList.add(ConnectionSpec.CLEARTEXT);
int cacheSize = 25*1024*1024; // 25 MiB int cacheSize = 25*1024*1024; // 25 MiB
OkHttpClient.Builder builder = new OkHttpClient.Builder() OkHttpClient.Builder builder = new OkHttpClient.Builder()
.addInterceptor(getUserAgentInterceptor()) .addInterceptor(getUserAgentInterceptor())
.readTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS) .writeTimeout(30, TimeUnit.SECONDS)
.cache(new Cache(context.getCacheDir(), cacheSize)) .cache(new Cache(context.getCacheDir(), cacheSize));
.connectionSpecs(specList);
if (httpProxyEnabled && !httpServer.isEmpty() && (httpPort > 0) && (httpPort < 65535)) { if (httpProxyEnabled && !httpServer.isEmpty() && (httpPort > 0) && (httpPort < 65535)) {
InetSocketAddress address = InetSocketAddress.createUnresolved(httpServer, httpPort); InetSocketAddress address = InetSocketAddress.createUnresolved(httpServer, httpPort);
@ -121,47 +81,6 @@ public class OkHttpUtils {
}; };
} }
/**
* Android version Nougat has a regression where elliptic curve cipher suites are supported, but
* only the curve secp256r1 is allowed. So, first it's best to just disable all elliptic
* ciphers, try the connection, and fall back to the all cipher suites enabled list after.
*/
private static void addNougatFixConnectionSpec(List<ConnectionSpec> specList) {
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.N) {
return;
}
SSLSocketFactory socketFactory;
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);
socketFactory = sslContext.getSocketFactory();
} catch (NoSuchAlgorithmException|KeyStoreException|KeyManagementException e) {
Log.e(TAG, "Failed obtaining the SSL socket factory.");
return;
}
String[] cipherSuites = socketFactory.getDefaultCipherSuites();
ArrayList<String> allowedList = new ArrayList<>();
for (String suite : cipherSuites) {
if (!suite.contains("ECDH")) {
allowedList.add(suite);
}
}
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.cipherSuites(allowedList.toArray(new String[0]))
.supportsTlsExtensions(true)
.build();
specList.add(spec);
}
} }

View File

@ -8,6 +8,10 @@ class FakeTuskyApplication : TuskyApplication() {
lateinit var locator: ServiceLocator lateinit var locator: ServiceLocator
override fun initSecurityProvider() {
// No-op
}
override fun initAppInjector() { override fun initAppInjector() {
// No-op // No-op
} }