Ship our own CA certificates for old devices

This commit is contained in:
ByteHamster 2020-10-05 12:44:47 +02:00
parent e1ff4c8763
commit 2e0a191d5c
6 changed files with 313 additions and 154 deletions

View File

@ -1,38 +1,14 @@
package de.danoeh.antennapod.core.service.download; package de.danoeh.antennapod.core.service.download;
import android.os.Build; import android.os.Build;
import androidx.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import androidx.annotation.NonNull;
import de.danoeh.antennapod.core.service.BasicAuthorizationInterceptor;
import java.io.File;
import java.io.IOException;
import java.net.CookieManager;
import java.net.CookiePolicy;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Socket;
import java.net.SocketAddress;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
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 de.danoeh.antennapod.core.preferences.UserPreferences; 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.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.storage.DBWriter;
import de.danoeh.antennapod.core.util.Flavors; import de.danoeh.antennapod.core.util.Flavors;
import okhttp3.Cache; import okhttp3.Cache;
@ -46,6 +22,19 @@ import okhttp3.Request;
import okhttp3.Response; import okhttp3.Response;
import okhttp3.internal.http.StatusLine; import okhttp3.internal.http.StatusLine;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.net.CookieManager;
import java.net.CookiePolicy;
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;
/** /**
* Provides access to a HttpClient singleton. * Provides access to a HttpClient singleton.
*/ */
@ -155,17 +144,15 @@ public class AntennapodHttpClient {
// The Free flavor bundles a modern conscrypt (security provider), so CustomSslSocketFactory // 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 // 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. // that old, deprecated, protocols (like SSLv3, TLSv1.0 and TLSv1.1) are disabled.
builder.sslSocketFactory(new CustomSslSocketFactory(), trustManager()); X509TrustManager trustManager = BackportTrustManager.create();
builder.sslSocketFactory(new NoV1SslSocketFactory(trustManager), trustManager);
} else if (Build.VERSION.SDK_INT < 21) { } else if (Build.VERSION.SDK_INT < 21) {
// The Play flavor can not be assumed to have a modern security provider, so for Android X509TrustManager trustManager = BackportTrustManager.create();
// older than 5.0 CustomSslSocketFactory is used to enable all possible protocols (modern builder.sslSocketFactory(new NoV1SslSocketFactory(trustManager), trustManager);
// and deprecated). And we explicitly enable deprecated cipher suites disabled by default.
builder.sslSocketFactory(new CustomSslSocketFactory(), trustManager());
// workaround for Android 4.x for certain web sites. // workaround for Android 4.x for certain web sites.
// see: https://github.com/square/okhttp/issues/4053#issuecomment-402579554 // see: https://github.com/square/okhttp/issues/4053#issuecomment-402579554
List<CipherSuite> cipherSuites = new ArrayList<>(); List<CipherSuite> cipherSuites = new ArrayList<>(ConnectionSpec.MODERN_TLS.cipherSuites());
cipherSuites.addAll(ConnectionSpec.MODERN_TLS.cipherSuites());
cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA); cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA); cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA);
@ -178,125 +165,7 @@ public class AntennapodHttpClient {
return builder; return builder;
} }
/**
* Closes expired connections. This method should be called by the using class once has finished its work with
* the HTTP client.
*/
public static synchronized void cleanup() {
if (httpClient != null) {
// does nothing at the moment
}
}
/**
* Reimplements default trust manager (required for calling sslSocketFactory).
*/
private static X509TrustManager trustManager() {
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));
}
return (X509TrustManager) trustManagers[0];
} catch (Exception e) {
Log.e(TAG, Log.getStackTraceString(e));
return null;
}
}
public static void setCacheDirectory(File cacheDirectory) { public static void setCacheDirectory(File cacheDirectory) {
AntennapodHttpClient.cacheDirectory = cacheDirectory; AntennapodHttpClient.cacheDirectory = cacheDirectory;
} }
/**
* Used to disable deprecated protocols and explicitly enable TLSv1.3 and TLSv1.2, or to enable
* all protocols (including deprecated) up to TLSv1.2, depending on build flavor (Free or Play).
*/
private static class CustomSslSocketFactory extends SSLSocketFactory {
private SSLSocketFactory factory;
public CustomSslSocketFactory() {
try {
SSLContext sslContext;
if (Flavors.FLAVOR == Flavors.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, null, 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 (Flavors.FLAVOR == Flavors.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.
s.setEnabledProtocols(new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" });
}
}
}
} }

View File

@ -259,7 +259,6 @@ public class HttpDownloader extends Downloader {
onFail(DownloadError.ERROR_CONNECTION_ERROR, request.getSource()); onFail(DownloadError.ERROR_CONNECTION_ERROR, request.getSource());
} finally { } finally {
IOUtils.closeQuietly(out); IOUtils.closeQuietly(out);
AntennapodHttpClient.cleanup();
IOUtils.closeQuietly(responseBody); IOUtils.closeQuietly(responseBody);
} }
} }

View File

@ -0,0 +1,73 @@
package de.danoeh.antennapod.core.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-----";
}

View File

@ -0,0 +1,58 @@
package de.danoeh.antennapod.core.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")))));
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;
}
}
}

View File

@ -0,0 +1,60 @@
package de.danoeh.antennapod.core.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]);
}
}

View File

@ -0,0 +1,100 @@
package de.danoeh.antennapod.core.ssl;
import de.danoeh.antennapod.core.util.Flavors;
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 (Flavors.FLAVOR == Flavors.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 (Flavors.FLAVOR == Flavors.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" });
}
}
}