summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorMartin Fietz <Martin.Fietz@gmail.com>2015-02-27 14:22:37 +0100
committerMartin Fietz <marf@hadiko-99-4.hadiko.uni-karlsruhe.de>2015-03-09 10:29:59 +0100
commitca3cebb463a136082493f0bda8f090d1bc130e20 (patch)
tree1796128f23dfff3fd8e87550f0c0ed74082462d1 /core
parent4a789221806f316a64f81ca5fe50a22c4b920a2a (diff)
downloadAntennaPod-ca3cebb463a136082493f0bda8f090d1bc130e20.zip
Fix for AntennaPod/AntennaPod#629
On older devices, crucial intermediate certificates are missing. In this fix, we create a custom trustmanager. The algorithm follows the certificate chain and checks validity and issuer signature. In the last step, we take the root CA's certificate from the system's keystore and verify the chain's last certificate thus validating the whole chain.
Diffstat (limited to 'core')
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetService.java95
1 files changed, 93 insertions, 2 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetService.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetService.java
index 5ee40186f..791ccd5ec 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetService.java
@@ -1,5 +1,8 @@
package de.danoeh.antennapod.core.gpoddernet;
+import android.os.Build;
+import android.util.Log;
+
import com.squareup.okhttp.Credentials;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.OkHttpClient;
@@ -18,16 +21,27 @@ import org.json.JSONObject;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.net.CookieManager;
-import java.net.CookiePolicy;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
+import java.security.KeyStore;
+import java.security.Principal;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+import javax.security.auth.x500.X500Principal;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetDevice;
@@ -43,6 +57,8 @@ import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
*/
public class GpodnetService {
+ private static final String TAG = "GpodnetService";
+
private static final String BASE_SCHEME = "https";
public static final String DEFAULT_BASE_HOST = "gpodder.net";
@@ -56,9 +72,84 @@ public class GpodnetService {
public GpodnetService() {
httpClient = AntennapodHttpClient.getHttpClient();
+ if (Build.VERSION.SDK_INT <= 10) {
+ Log.d(TAG, "Use custom SSL factory");
+ SSLSocketFactory factory = getCustomSslSocketFactory();
+ httpClient.setSslSocketFactory(factory);
+ }
BASE_HOST = GpodnetPreferences.getHostname();
}
+ private synchronized static SSLSocketFactory getCustomSslSocketFactory() {
+ try {
+ TrustManagerFactory defaultTrustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ defaultTrustManagerFactory.init((KeyStore) null); // use system keystore
+ final X509TrustManager defaultTrustManager = (X509TrustManager) defaultTrustManagerFactory.getTrustManagers()[0];
+ TrustManager[] customTrustManagers = new TrustManager[]{new X509TrustManager() {
+ @Override
+ public java.security.cert.X509Certificate[] getAcceptedIssuers() {
+ return null;
+ }
+ @Override
+ public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
+ }
+ @Override
+ public void checkServerTrusted(X509Certificate[] chain, String authType)
+ throws CertificateException {
+ // chain may out of order - construct data structures to walk from server certificate to root certificate
+ Map<Principal, X509Certificate> certificates = new HashMap<Principal, X509Certificate>(chain.length - 1);
+ X509Certificate subject = null;
+ for (X509Certificate cert : chain) {
+ cert.checkValidity();
+ if (cert.getSubjectDN().toString().startsWith("CN=" + DEFAULT_BASE_HOST)) {
+ subject = cert;
+ } else {
+ certificates.put(cert.getSubjectDN(), cert);
+ }
+ }
+ if (subject == null) {
+ throw new CertificateException("Chain does not contain a certificate for " + DEFAULT_BASE_HOST);
+ }
+ // follow chain to root CA
+ while (certificates.get(subject.getIssuerDN()) != null) {
+ subject.checkValidity();
+ X509Certificate issuer = certificates.get(subject.getIssuerDN());
+ try {
+ subject.verify(issuer.getPublicKey());
+ } catch (Exception e) {
+ Log.d(TAG, "failed: " + issuer.getSubjectDN() + " -> " + subject.getSubjectDN());
+ throw new CertificateException("Could not verify certificate");
+ }
+ subject = issuer;
+ }
+ X500Principal rootAuthority = subject.getIssuerX500Principal();
+ boolean accepted = false;
+ for (X509Certificate cert :
+ defaultTrustManager.getAcceptedIssuers()) {
+ if (cert.getSubjectX500Principal().equals(rootAuthority)) {
+ try {
+ subject.verify(cert.getPublicKey());
+ accepted = true;
+ } catch (Exception e) {
+ Log.d(TAG, "failed: " + cert.getSubjectDN() + " -> " + subject.getSubjectDN());
+ throw new CertificateException("Could not verify root certificate");
+ }
+ }
+ }
+ if (accepted == false) {
+ throw new CertificateException("Could not verify root certificate");
+ }
+ }
+ }};
+ SSLContext sslContext = SSLContext.getInstance("TLS");
+ sslContext.init(null, customTrustManagers, null);
+ return sslContext.getSocketFactory();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
/**
* Returns the [count] most used tags.
*/