Merge pull request #4976 from ByteHamster/net-ssl

Moved SSL providers to new module
This commit is contained in:
ByteHamster 2021-03-01 20:07:36 +01:00 committed by GitHub
commit 3f6cb57ffe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 161 additions and 69 deletions

View File

@ -73,6 +73,7 @@ project.ext {
// Google Play build
wearableSupportVersion = "2.6.0"
playServicesVersion = "8.4.0"
//Tests
awaitilityVersion = "3.1.6"

View File

@ -71,6 +71,7 @@ android {
}
dependencies {
implementation project(':net:ssl')
implementation project(':ui:app-start-intent')
implementation project(':ui:common')
@ -102,13 +103,10 @@ dependencies {
// Non-free dependencies:
playApi 'com.google.android.libraries.cast.companionlibrary:ccl:2.9.1'
playApi 'androidx.mediarouter:mediarouter:1.0.0'
playApi 'com.google.android.gms:play-services-cast:8.4.0'
playApi "com.google.android.gms:play-services-cast:$playServicesVersion"
playApi "com.google.android.support:wearable:$wearableSupportVersion"
compileOnly "com.google.android.wearable:wearable:$wearableSupportVersion"
// bundle conscrypt with free builds
freeImplementation "org.conscrypt:conscrypt-android:$conscryptVersion"
testImplementation "org.awaitility:awaitility:$awaitilityVersion"
testImplementation 'junit:junit:4.13'
testImplementation 'org.mockito:mockito-inline:3.5.13'

View File

@ -1,8 +1,8 @@
package de.danoeh.antennapod.core;
import android.content.Context;
import java.security.Security;
import org.conscrypt.Conscrypt;
import de.danoeh.antennapod.net.ssl.SslProviderInstaller;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
@ -42,16 +42,11 @@ public class ClientConfig {
UserPreferences.init(context);
UsageStatistics.init(context);
PlaybackPreferences.init(context);
installSslProvider(context);
SslProviderInstaller.install(context);
NetworkUtils.init(context);
AntennapodHttpClient.setCacheDirectory(new File(context.getCacheDir(), "okhttp"));
SleepTimerPreferences.init(context);
NotificationUtils.createChannels(context);
initialized = true;
}
private static void installSslProvider(Context context) {
// Insert bundled conscrypt as highest security provider (overrides OS version).
Security.insertProviderAt(Conscrypt.newProvider(), 1);
}
}

View File

@ -1,19 +1,14 @@
package de.danoeh.antennapod.core.service.download;
import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.BasicAuthorizationInterceptor;
import de.danoeh.antennapod.core.service.UserAgentInterceptor;
import de.danoeh.antennapod.core.ssl.BackportTrustManager;
import de.danoeh.antennapod.core.ssl.NoV1SslSocketFactory;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.net.ssl.SslClientSetup;
import okhttp3.Cache;
import okhttp3.CipherSuite;
import okhttp3.ConnectionSpec;
import okhttp3.Credentials;
import okhttp3.HttpUrl;
import okhttp3.JavaNetCookieJar;
@ -21,8 +16,6 @@ import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.internal.http.StatusLine;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.net.CookieManager;
import java.net.CookiePolicy;
@ -30,9 +23,6 @@ import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
@ -140,28 +130,7 @@ public class AntennapodHttpClient {
}
}
if (Flavors.FLAVOR == Flavors.FREE) {
// The Free flavor bundles a modern conscrypt (security provider), so CustomSslSocketFactory
// is only used to make sure that modern protocols (TLSv1.3 and TLSv1.2) are enabled and
// that old, deprecated, protocols (like SSLv3, TLSv1.0 and TLSv1.1) are disabled.
X509TrustManager trustManager = BackportTrustManager.create();
builder.sslSocketFactory(new NoV1SslSocketFactory(trustManager), trustManager);
} else if (Build.VERSION.SDK_INT < 21) {
X509TrustManager trustManager = BackportTrustManager.create();
builder.sslSocketFactory(new NoV1SslSocketFactory(trustManager), trustManager);
// workaround for Android 4.x for certain web sites.
// see: https://github.com/square/okhttp/issues/4053#issuecomment-402579554
List<CipherSuite> cipherSuites = new ArrayList<>(ConnectionSpec.MODERN_TLS.cipherSuites());
cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA);
ConnectionSpec legacyTls = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.cipherSuites(cipherSuites.toArray(new CipherSuite[0]))
.build();
builder.connectionSpecs(Arrays.asList(legacyTls, ConnectionSpec.CLEARTEXT));
}
SslClientSetup.installCertificates(builder);
return builder;
}

View File

@ -2,10 +2,6 @@ package de.danoeh.antennapod.core;
import android.content.Context;
import android.util.Log;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.common.GooglePlayServicesRepairableException;
import com.google.android.gms.security.ProviderInstaller;
import de.danoeh.antennapod.core.cast.CastManager;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
@ -15,6 +11,7 @@ import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import de.danoeh.antennapod.net.ssl.SslProviderInstaller;
import java.io.File;
@ -48,7 +45,7 @@ public class ClientConfig {
UserPreferences.init(context);
UsageStatistics.init(context);
PlaybackPreferences.init(context);
installSslProvider(context);
SslProviderInstaller.install(context);
NetworkUtils.init(context);
// Don't initialize Cast-related logic unless it is enabled, to avoid the unnecessary
// Google Play Service usage.
@ -64,15 +61,4 @@ public class ClientConfig {
NotificationUtils.createChannels(context);
initialized = true;
}
private static void installSslProvider(Context context) {
try {
ProviderInstaller.installIfNeeded(context);
} catch (GooglePlayServicesRepairableException e) {
e.printStackTrace();
GoogleApiAvailability.getInstance().showErrorNotification(context, e.getConnectionStatusCode());
} catch (GooglePlayServicesNotAvailableException e) {
e.printStackTrace();
}
}
}

3
net/README.md Normal file
View File

@ -0,0 +1,3 @@
# :net
This folder contains modules that directly interact with the network.

3
net/ssl/README.md Normal file
View File

@ -0,0 +1,3 @@
# :net:ssl
This module provides SSL backports and security provider implementations.

65
net/ssl/build.gradle Normal file
View File

@ -0,0 +1,65 @@
apply plugin: "com.android.library"
android {
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled false
testApplicationId "de.danoeh.antennapod.core.tests"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile("proguard-android.txt")
}
debug {
// debug build has method count over 64k single-dex threshold.
// For building debug build to use on Android < 21 (pre-Android 5) devices,
// you need to manually change class
// de.danoeh.antennapod.PodcastApp to extend MultiDexApplication .
// See Issue #2813
multiDexEnabled true
}
}
flavorDimensions "market"
productFlavors {
free {
dimension "market"
}
play {
dimension "market"
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
testOptions {
unitTests {
includeAndroidResources = true
}
}
lintOptions {
disable 'GradleDependency'
warningsAsErrors true
abortOnError true
}
}
dependencies {
annotationProcessor "androidx.annotation:annotation:$annotationVersion"
implementation "androidx.appcompat:appcompat:$appcompatVersion"
implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
playImplementation "com.google.android.gms:play-services-base:$playServicesVersion"
freeImplementation "org.conscrypt:conscrypt-android:$conscryptVersion"
}

View File

@ -0,0 +1,13 @@
package de.danoeh.antennapod.net.ssl;
import android.content.Context;
import org.conscrypt.Conscrypt;
import java.security.Security;
public class SslProviderInstaller {
public static void install(Context context) {
// Insert bundled conscrypt as highest security provider (overrides OS version).
Security.insertProviderAt(Conscrypt.newProvider(), 1);
}
}

View File

@ -0,0 +1 @@
<manifest package="de.danoeh.antennapod.net.ssl" />

View File

@ -1,4 +1,4 @@
package de.danoeh.antennapod.core.ssl;
package de.danoeh.antennapod.net.ssl;
public class BackportCaCerts {
public static final String SECTIGO_USER_TRUST = "-----BEGIN CERTIFICATE-----\n"

View File

@ -1,4 +1,4 @@
package de.danoeh.antennapod.core.ssl;
package de.danoeh.antennapod.net.ssl;
import android.util.Log;

View File

@ -1,4 +1,4 @@
package de.danoeh.antennapod.core.ssl;
package de.danoeh.antennapod.net.ssl;
import javax.net.ssl.X509TrustManager;
import java.security.cert.CertificateException;

View File

@ -1,6 +1,4 @@
package de.danoeh.antennapod.core.ssl;
import de.danoeh.antennapod.core.util.Flavors;
package de.danoeh.antennapod.net.ssl;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocket;
@ -22,7 +20,7 @@ public class NoV1SslSocketFactory extends SSLSocketFactory {
try {
SSLContext sslContext;
if (Flavors.FLAVOR == Flavors.FREE) {
if (BuildConfig.FLAVOR.equals("free")) {
// Free flavor (bundles modern conscrypt): support for TLSv1.3 is guaranteed.
sslContext = SSLContext.getInstance("TLSv1.3");
} else {
@ -84,7 +82,7 @@ public class NoV1SslSocketFactory extends SSLSocketFactory {
}
private void configureSocket(SSLSocket s) {
if (Flavors.FLAVOR == Flavors.FREE) {
if (BuildConfig.FLAVOR.equals("free")) {
// Free flavor (bundles modern conscrypt): TLSv1.3 and modern cipher suites are
// guaranteed. Protocols older than TLSv1.2 are now deprecated and can be disabled.
s.setEnabledProtocols(new String[] { "TLSv1.3", "TLSv1.2" });

View File

@ -0,0 +1,37 @@
package de.danoeh.antennapod.net.ssl;
import android.os.Build;
import okhttp3.CipherSuite;
import okhttp3.ConnectionSpec;
import okhttp3.OkHttpClient;
import javax.net.ssl.X509TrustManager;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class SslClientSetup {
public static void installCertificates(OkHttpClient.Builder builder) {
if (BuildConfig.FLAVOR.equals("free")) {
// The Free flavor bundles a modern conscrypt (security provider), so CustomSslSocketFactory
// is only used to make sure that modern protocols (TLSv1.3 and TLSv1.2) are enabled and
// that old, deprecated, protocols (like SSLv3, TLSv1.0 and TLSv1.1) are disabled.
X509TrustManager trustManager = BackportTrustManager.create();
builder.sslSocketFactory(new NoV1SslSocketFactory(trustManager), trustManager);
} else if (Build.VERSION.SDK_INT < 21) {
X509TrustManager trustManager = BackportTrustManager.create();
builder.sslSocketFactory(new NoV1SslSocketFactory(trustManager), trustManager);
// workaround for Android 4.x for certain web sites.
// see: https://github.com/square/okhttp/issues/4053#issuecomment-402579554
List<CipherSuite> cipherSuites = new ArrayList<>(ConnectionSpec.MODERN_TLS.cipherSuites());
cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA);
ConnectionSpec legacyTls = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
.cipherSuites(cipherSuites.toArray(new CipherSuite[0]))
.build();
builder.connectionSpecs(Arrays.asList(legacyTls, ConnectionSpec.CLEARTEXT));
}
}
}

View File

@ -0,0 +1,20 @@
package de.danoeh.antennapod.net.ssl;
import android.content.Context;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.common.GooglePlayServicesRepairableException;
import com.google.android.gms.security.ProviderInstaller;
public class SslProviderInstaller {
public static void install(Context context) {
try {
ProviderInstaller.installIfNeeded(context);
} catch (GooglePlayServicesRepairableException e) {
e.printStackTrace();
GoogleApiAvailability.getInstance().showErrorNotification(context, e.getConnectionStatusCode());
} catch (GooglePlayServicesNotAvailableException e) {
e.printStackTrace();
}
}
}

View File

@ -1,4 +1,5 @@
include ':app'
include ':core'
include ':net:ssl'
include ':ui:app-start-intent'
include ':ui:common'

View File

@ -39,6 +39,7 @@ android {
}
lintOptions {
disable 'GradleDependency'
warningsAsErrors true
abortOnError true
}

View File

@ -39,6 +39,7 @@ android {
}
lintOptions {
disable 'GradleDependency'
warningsAsErrors true
abortOnError true
}