summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorByteHamster <info@bytehamster.com>2021-03-01 16:21:39 +0100
committerByteHamster <info@bytehamster.com>2021-03-01 16:21:39 +0100
commit0e94aa5d9d02dd9b8a88b3b03eb279cb386b3dc3 (patch)
treeb1282edeb33eab01a5ad2ced49d40fa015c768af /net
parentddd6a12354b776bf0ae28b2d0ea2f1f067daf341 (diff)
downloadantennapod-0e94aa5d9d02dd9b8a88b3b03eb279cb386b3dc3.zip
Moved SSL providers to new module
Diffstat (limited to 'net')
-rw-r--r--net/README.md3
-rw-r--r--net/ssl/README.md3
-rw-r--r--net/ssl/build.gradle64
-rw-r--r--net/ssl/src/free/java/de/danoeh/antennapod/net/ssl/SslProviderInstaller.java13
-rw-r--r--net/ssl/src/main/AndroidManifest.xml1
-rw-r--r--net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/BackportCaCerts.java105
-rw-r--r--net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/BackportTrustManager.java60
-rw-r--r--net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/CompositeX509TrustManager.java60
-rw-r--r--net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/NoV1SslSocketFactory.java98
-rw-r--r--net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/SslClientSetup.java37
-rw-r--r--net/ssl/src/play/java/de/danoeh/antennapod/net/ssl/SslProviderInstaller.java20
11 files changed, 464 insertions, 0 deletions
diff --git a/net/README.md b/net/README.md
new file mode 100644
index 000000000..4d578407c
--- /dev/null
+++ b/net/README.md
@@ -0,0 +1,3 @@
+# :net
+
+This folder contains modules that directly interact with the network.
diff --git a/net/ssl/README.md b/net/ssl/README.md
new file mode 100644
index 000000000..bf01f3ab6
--- /dev/null
+++ b/net/ssl/README.md
@@ -0,0 +1,3 @@
+# :net:ssl
+
+This module provides SSL backports and security provider implementations.
diff --git a/net/ssl/build.gradle b/net/ssl/build.gradle
new file mode 100644
index 000000000..92fb6cbd9
--- /dev/null
+++ b/net/ssl/build.gradle
@@ -0,0 +1,64 @@
+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 {
+ 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"
+}
diff --git a/net/ssl/src/free/java/de/danoeh/antennapod/net/ssl/SslProviderInstaller.java b/net/ssl/src/free/java/de/danoeh/antennapod/net/ssl/SslProviderInstaller.java
new file mode 100644
index 000000000..48b5690cc
--- /dev/null
+++ b/net/ssl/src/free/java/de/danoeh/antennapod/net/ssl/SslProviderInstaller.java
@@ -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);
+ }
+}
diff --git a/net/ssl/src/main/AndroidManifest.xml b/net/ssl/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..2acf91510
--- /dev/null
+++ b/net/ssl/src/main/AndroidManifest.xml
@@ -0,0 +1 @@
+<manifest package="de.danoeh.antennapod.net.ssl" />
diff --git a/net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/BackportCaCerts.java b/net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/BackportCaCerts.java
new file mode 100644
index 000000000..ecfc99e15
--- /dev/null
+++ b/net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/BackportCaCerts.java
@@ -0,0 +1,105 @@
+package de.danoeh.antennapod.net.ssl;
+
+public class BackportCaCerts {
+ public static final String SECTIGO_USER_TRUST = "-----BEGIN CERTIFICATE-----\n"
+ + "MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB\n"
+ + "iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl\n"
+ + "cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV\n"
+ + "BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw\n"
+ + "MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV\n"
+ + "BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU\n"
+ + "aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy\n"
+ + "dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n"
+ + "AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B\n"
+ + "3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY\n"
+ + "tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/\n"
+ + "Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2\n"
+ + "VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT\n"
+ + "79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6\n"
+ + "c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT\n"
+ + "Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l\n"
+ + "c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee\n"
+ + "UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE\n"
+ + "Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd\n"
+ + "BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G\n"
+ + "A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF\n"
+ + "Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO\n"
+ + "VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3\n"
+ + "ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs\n"
+ + "8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR\n"
+ + "iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze\n"
+ + "Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ\n"
+ + "XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/\n"
+ + "qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB\n"
+ + "VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB\n"
+ + "L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG\n"
+ + "jjxDah2nGN59PRbxYvnKkKj9\n"
+ + "-----END CERTIFICATE-----\n";
+
+ public static final String COMODO = "-----BEGIN CERTIFICATE-----\n"
+ + "MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB\n"
+ + "hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\n"
+ + "A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV\n"
+ + "BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5\n"
+ + "MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT\n"
+ + "EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR\n"
+ + "Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh\n"
+ + "dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR\n"
+ + "6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X\n"
+ + "pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC\n"
+ + "9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV\n"
+ + "/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf\n"
+ + "Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z\n"
+ + "+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w\n"
+ + "qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah\n"
+ + "SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC\n"
+ + "u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf\n"
+ + "Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq\n"
+ + "crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E\n"
+ + "FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB\n"
+ + "/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl\n"
+ + "wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM\n"
+ + "4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV\n"
+ + "2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna\n"
+ + "FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ\n"
+ + "CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK\n"
+ + "boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke\n"
+ + "jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL\n"
+ + "S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb\n"
+ + "QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl\n"
+ + "0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB\n"
+ + "NVOFBkpdn627G190\n"
+ + "-----END CERTIFICATE-----";
+
+ public static final String LETSENCRYPT_ISRG = "-----BEGIN CERTIFICATE-----\n"
+ + "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\n"
+ + "-----END CERTIFICATE-----";
+}
diff --git a/net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/BackportTrustManager.java b/net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/BackportTrustManager.java
new file mode 100644
index 000000000..3a188b47a
--- /dev/null
+++ b/net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/BackportTrustManager.java
@@ -0,0 +1,60 @@
+package de.danoeh.antennapod.net.ssl;
+
+import android.util.Log;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+import java.io.ByteArrayInputStream;
+import java.nio.charset.Charset;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.CertificateFactory;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * SSL trust manager that allows old Android systems to use modern certificates.
+ */
+public class BackportTrustManager {
+ private static final String TAG = "BackportTrustManager";
+
+ private static X509TrustManager getSystemTrustManager(KeyStore keystore) {
+ TrustManagerFactory factory;
+ try {
+ factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ factory.init(keystore);
+ for (TrustManager manager : factory.getTrustManagers()) {
+ if (manager instanceof X509TrustManager) {
+ return (X509TrustManager) manager;
+ }
+ }
+ } catch (NoSuchAlgorithmException | KeyStoreException e) {
+ e.printStackTrace();
+ }
+ throw new IllegalStateException("Unexpected default trust managers");
+ }
+
+ public static X509TrustManager create() {
+ try {
+ KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
+ keystore.load(null); // Clear
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ keystore.setCertificateEntry("BACKPORT_COMODO_ROOT_CA", cf.generateCertificate(
+ new ByteArrayInputStream(BackportCaCerts.COMODO.getBytes(Charset.forName("UTF-8")))));
+ keystore.setCertificateEntry("SECTIGO_USER_TRUST_CA", cf.generateCertificate(
+ new ByteArrayInputStream(BackportCaCerts.SECTIGO_USER_TRUST.getBytes(Charset.forName("UTF-8")))));
+ keystore.setCertificateEntry("LETSENCRYPT_ISRG_CA", cf.generateCertificate(
+ new ByteArrayInputStream(BackportCaCerts.LETSENCRYPT_ISRG.getBytes(Charset.forName("UTF-8")))));
+
+ List<X509TrustManager> managers = new ArrayList<>();
+ managers.add(getSystemTrustManager(keystore));
+ managers.add(getSystemTrustManager(null));
+ return new CompositeX509TrustManager(managers);
+ } catch (Exception e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ return null;
+ }
+ }
+}
diff --git a/net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/CompositeX509TrustManager.java b/net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/CompositeX509TrustManager.java
new file mode 100644
index 000000000..16b2f0931
--- /dev/null
+++ b/net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/CompositeX509TrustManager.java
@@ -0,0 +1,60 @@
+package de.danoeh.antennapod.net.ssl;
+
+import javax.net.ssl.X509TrustManager;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Represents an ordered list of {@link X509TrustManager}s with additive trust. If any one of the composed managers
+ * trusts a certificate chain, then it is trusted by the composite manager.
+ * Based on https://stackoverflow.com/a/16229909
+ */
+public class CompositeX509TrustManager implements X509TrustManager {
+ private final List<X509TrustManager> trustManagers;
+
+ public CompositeX509TrustManager(List<X509TrustManager> trustManagers) {
+ this.trustManagers = trustManagers;
+ }
+
+ @Override
+ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ CertificateException reason = null;
+ for (X509TrustManager trustManager : trustManagers) {
+ try {
+ trustManager.checkClientTrusted(chain, authType);
+ return; // someone trusts them. success!
+ } catch (CertificateException e) {
+ // maybe someone else will trust them
+ reason = e;
+ }
+ }
+ throw reason;
+ }
+
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
+ CertificateException reason = null;
+ for (X509TrustManager trustManager : trustManagers) {
+ try {
+ trustManager.checkServerTrusted(chain, authType);
+ return; // someone trusts them. success!
+ } catch (CertificateException e) {
+ // maybe someone else will trust them
+ reason = e;
+ }
+ }
+ throw reason;
+ }
+
+ @Override
+ public X509Certificate[] getAcceptedIssuers() {
+ List<X509Certificate> certificates = new ArrayList<>();
+ for (X509TrustManager trustManager : trustManagers) {
+ certificates.addAll(Arrays.asList(trustManager.getAcceptedIssuers()));
+ }
+ return certificates.toArray(new X509Certificate[0]);
+ }
+}
diff --git a/net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/NoV1SslSocketFactory.java b/net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/NoV1SslSocketFactory.java
new file mode 100644
index 000000000..0e31cda68
--- /dev/null
+++ b/net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/NoV1SslSocketFactory.java
@@ -0,0 +1,98 @@
+package de.danoeh.antennapod.net.ssl;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.security.GeneralSecurityException;
+
+/**
+ * SSLSocketFactory that does not use TLS 1.0
+ * This fixes issues with old Android versions that abort if the server does not know TLS 1.0
+ */
+public class NoV1SslSocketFactory extends SSLSocketFactory {
+ private SSLSocketFactory factory;
+
+ public NoV1SslSocketFactory(TrustManager trustManager) {
+ try {
+ SSLContext sslContext;
+
+ if (BuildConfig.FLAVOR.equals("free")) {
+ // Free flavor (bundles modern conscrypt): support for TLSv1.3 is guaranteed.
+ sslContext = SSLContext.getInstance("TLSv1.3");
+ } else {
+ // Play flavor (security provider can vary): only TLSv1.2 is guaranteed.
+ sslContext = SSLContext.getInstance("TLSv1.2");
+ }
+
+ sslContext.init(null, new TrustManager[] {trustManager}, null);
+ factory = sslContext.getSocketFactory();
+ } catch (GeneralSecurityException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public String[] getDefaultCipherSuites() {
+ return factory.getDefaultCipherSuites();
+ }
+
+ @Override
+ public String[] getSupportedCipherSuites() {
+ return factory.getSupportedCipherSuites();
+ }
+
+ public Socket createSocket() throws IOException {
+ SSLSocket result = (SSLSocket) factory.createSocket();
+ configureSocket(result);
+ return result;
+ }
+
+ public Socket createSocket(String var1, int var2) throws IOException {
+ SSLSocket result = (SSLSocket) factory.createSocket(var1, var2);
+ configureSocket(result);
+ return result;
+ }
+
+ public Socket createSocket(Socket var1, String var2, int var3, boolean var4) throws IOException {
+ SSLSocket result = (SSLSocket) factory.createSocket(var1, var2, var3, var4);
+ configureSocket(result);
+ return result;
+ }
+
+ public Socket createSocket(InetAddress var1, int var2) throws IOException {
+ SSLSocket result = (SSLSocket) factory.createSocket(var1, var2);
+ configureSocket(result);
+ return result;
+ }
+
+ public Socket createSocket(String var1, int var2, InetAddress var3, int var4) throws IOException {
+ SSLSocket result = (SSLSocket) factory.createSocket(var1, var2, var3, var4);
+ configureSocket(result);
+ return result;
+ }
+
+ public Socket createSocket(InetAddress var1, int var2, InetAddress var3, int var4) throws IOException {
+ SSLSocket result = (SSLSocket) factory.createSocket(var1, var2, var3, var4);
+ configureSocket(result);
+ return result;
+ }
+
+ private void configureSocket(SSLSocket s) {
+ 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" });
+ } else {
+ // Play flavor (security provider can vary): only TLSv1.2 is guaranteed, supported
+ // cipher suites may vary. Old protocols might be necessary to keep things working.
+
+ // TLS 1.0 is enabled by default on some old systems, which causes connection errors.
+ // This disables that.
+ s.setEnabledProtocols(new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" });
+ }
+ }
+} \ No newline at end of file
diff --git a/net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/SslClientSetup.java b/net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/SslClientSetup.java
new file mode 100644
index 000000000..666010d2f
--- /dev/null
+++ b/net/ssl/src/main/java/de/danoeh/antennapod/net/ssl/SslClientSetup.java
@@ -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));
+ }
+ }
+}
diff --git a/net/ssl/src/play/java/de/danoeh/antennapod/net/ssl/SslProviderInstaller.java b/net/ssl/src/play/java/de/danoeh/antennapod/net/ssl/SslProviderInstaller.java
new file mode 100644
index 000000000..6c89df5ec
--- /dev/null
+++ b/net/ssl/src/play/java/de/danoeh/antennapod/net/ssl/SslProviderInstaller.java
@@ -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();
+ }
+ }
+}