Ship our own CA certificates for old devices
This commit is contained in:
parent
e1ff4c8763
commit
2e0a191d5c
|
@ -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" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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-----";
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue