summaryrefslogtreecommitdiff
path: root/app/src/main
diff options
context:
space:
mode:
authorcyBerta <cyberta@riseup.net>2021-12-16 23:45:41 +0100
committercyBerta <cyberta@riseup.net>2021-12-17 01:09:57 +0100
commit8411cd82c0572e0e871c1cf93e0d4c05b35fb999 (patch)
treecb659af65a1b3baef5285a395c3cdfa2251c8187 /app/src/main
parent8e5ce3e312f03035314b6ab036c625f83a515fc7 (diff)
allow to parse and handle multiple certs in a pem file
Diffstat (limited to 'app/src/main')
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java32
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/KeyStoreHelper.java78
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java20
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java50
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/TLSCompatSocketFactory.java10
5 files changed, 75 insertions, 115 deletions
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
index dccb5678..0a81b9cb 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.os.Looper;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -37,6 +38,7 @@ import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
+import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
@@ -44,10 +46,13 @@ import java.security.cert.X509Certificate;
import java.security.interfaces.RSAPrivateKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.ArrayList;
import java.util.Calendar;
+import java.util.Collection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import de.blinkt.openvpn.core.VpnStatus;
import se.leap.bitmaskclient.BuildConfig;
import se.leap.bitmaskclient.R;
import se.leap.bitmaskclient.providersetup.ProviderAPI;
@@ -99,25 +104,28 @@ public class ConfigHelper {
return ret;
}
- public static X509Certificate parseX509CertificateFromString(String certificateString) {
- java.security.cert.Certificate certificate = null;
+ public static ArrayList<X509Certificate> parseX509CertificatesFromString(String certificateString) {
+ Collection<? extends Certificate> certificates;
CertificateFactory cf;
try {
cf = CertificateFactory.getInstance("X.509");
certificateString = certificateString.replaceAll("-----BEGIN CERTIFICATE-----", "").trim().replaceAll("-----END CERTIFICATE-----", "").trim();
- byte[] cert_bytes = Base64.decode(certificateString);
- InputStream caInput = new ByteArrayInputStream(cert_bytes);
- try {
- certificate = cf.generateCertificate(caInput);
- System.out.println("ca=" + ((X509Certificate) certificate).getSubjectDN());
- } finally {
- caInput.close();
+ byte[] certBytes = Base64.decode(certificateString);
+ try (InputStream caInput = new ByteArrayInputStream(certBytes)) {
+ certificates = cf.generateCertificates(caInput);
+ if (certificates != null) {
+ for (Certificate cert : certificates) {
+ System.out.println("ca=" + ((X509Certificate) cert).getSubjectDN());
+ }
+ return (ArrayList<X509Certificate>) certificates;
+ }
}
- } catch (NullPointerException | CertificateException | IOException | IllegalArgumentException e) {
- return null;
+ } catch (NullPointerException | CertificateException | IOException | IllegalArgumentException | ClassCastException e) {
+ e.printStackTrace();
}
- return (X509Certificate) certificate;
+
+ return null;
}
public static RSAPrivateKey parseRsaKeyFromString(String rsaKeyString) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/KeyStoreHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/KeyStoreHelper.java
deleted file mode 100644
index b0b28993..00000000
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/KeyStoreHelper.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package se.leap.bitmaskclient.base.utils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-
-/**
- * Created by cyberta on 18.03.18.
- */
-
-public class KeyStoreHelper {
- private static KeyStore trustedKeystore;
-
- /**
- * Adds a new X509 certificate given its input stream and its provider name
- *
- * @param provider used to store the certificate in the keystore
- * @param inputStream from which X509 certificate must be generated.
- */
- public static void addTrustedCertificate(String provider, InputStream inputStream) {
- CertificateFactory cf;
- try {
- cf = CertificateFactory.getInstance("X.509");
- X509Certificate cert =
- (X509Certificate) cf.generateCertificate(inputStream);
- trustedKeystore.setCertificateEntry(provider, cert);
- } catch (CertificateException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (KeyStoreException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- /**
- * Adds a new X509 certificate given in its string from and using its provider name
- *
- * @param provider used to store the certificate in the keystore
- * @param certificate
- */
- public static void addTrustedCertificate(String provider, String certificate) {
-
- try {
- X509Certificate cert = ConfigHelper.parseX509CertificateFromString(certificate);
- if (trustedKeystore == null) {
- trustedKeystore = KeyStore.getInstance("BKS");
- trustedKeystore.load(null);
- }
- trustedKeystore.setCertificateEntry(provider, cert);
- } catch (KeyStoreException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (NoSuchAlgorithmException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (CertificateException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
-
- /**
- * @return class wide keystore
- */
- public static KeyStore getKeystore() {
- return trustedKeystore;
- }
-
-}
diff --git a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
index c747b731..16d1c5ad 100644
--- a/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/eip/VpnCertificateValidator.java
@@ -16,9 +16,12 @@
*/
package se.leap.bitmaskclient.eip;
+import androidx.annotation.VisibleForTesting;
+
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
@@ -27,7 +30,7 @@ import se.leap.bitmaskclient.base.utils.ConfigHelper;
public class VpnCertificateValidator {
public final static String TAG = VpnCertificateValidator.class.getSimpleName();
- private String certificate;
+ private final String certificate;
private CalendarProviderInterface calendarProvider;
public VpnCertificateValidator(String certificate) {
@@ -35,21 +38,30 @@ public class VpnCertificateValidator {
this.calendarProvider = new CalendarProvider();
}
+ @VisibleForTesting
public void setCalendarProvider(CalendarProviderInterface calendarProvider) {
this.calendarProvider = calendarProvider;
}
/**
*
- * @return true if there's a certificate that is valid for more than 15 more days
+ * @return true if all certificates are valid for more than 15 more days
*/
public boolean isValid() {
if (certificate.isEmpty()) {
return false;
}
- X509Certificate x509Certificate = ConfigHelper.parseX509CertificateFromString(certificate);
- return isValid(x509Certificate);
+ ArrayList<X509Certificate> x509Certificates = ConfigHelper.parseX509CertificatesFromString(certificate);
+ if (x509Certificates == null) {
+ return false;
+ }
+ for (X509Certificate cert : x509Certificates) {
+ if (!isValid(cert)) {
+ return false;
+ }
+ }
+ return true;
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
index 808d9e75..63cf03cf 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/ProviderApiManagerBase.java
@@ -849,18 +849,24 @@ public abstract class ProviderApiManagerBase {
protected boolean validCertificate(Provider provider, String certString) {
boolean result = false;
if (!ConfigHelper.checkErroneousDownload(certString)) {
- X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(certString);
+ ArrayList<X509Certificate> certificates = ConfigHelper.parseX509CertificatesFromString(certString);
try {
- if (certificate != null) {
- JSONObject providerJson = provider.getDefinition();
- String fingerprint = providerJson.getString(Provider.CA_CERT_FINGERPRINT);
- String encoding = fingerprint.split(":")[0];
- String expectedFingerprint = fingerprint.split(":")[1];
- String realFingerprint = getFingerprintFromCertificate(certificate, encoding);
-
- result = realFingerprint.trim().equalsIgnoreCase(expectedFingerprint.trim());
- } else
+ if (certificates != null) {
+ if (certificates.size() == 1) {
+ JSONObject providerJson = provider.getDefinition();
+ String fingerprint = providerJson.getString(Provider.CA_CERT_FINGERPRINT);
+ String encoding = fingerprint.split(":")[0];
+ String expectedFingerprint = fingerprint.split(":")[1];
+ String realFingerprint = getFingerprintFromCertificate(certificates.get(0), encoding);
+ result = realFingerprint.trim().equalsIgnoreCase(expectedFingerprint.trim());
+ } else {
+ // otherwise we assume the provider is transitioning the CA certs and thus shipping multiple CA certs
+ // in that case we don't do cert pinning
+ result = true;
+ }
+ } else {
result = false;
+ }
} catch (JSONException | NoSuchAlgorithmException | CertificateEncodingException e) {
result = false;
}
@@ -910,18 +916,24 @@ public abstract class ProviderApiManagerBase {
return result;
}
- X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(caCert);
- if (certificate == null) {
+ ArrayList<X509Certificate> certificates = ConfigHelper.parseX509CertificatesFromString(caCert);
+ if (certificates == null) {
return setErrorResult(result, warning_corrupted_provider_cert, ERROR_INVALID_CERTIFICATE.toString());
}
try {
- certificate.checkValidity();
String encoding = provider.getCertificatePinEncoding();
String expectedFingerprint = provider.getCertificatePin();
- String realFingerprint = getFingerprintFromCertificate(certificate, encoding);
- if (!realFingerprint.trim().equalsIgnoreCase(expectedFingerprint.trim())) {
- return setErrorResult(result, warning_corrupted_provider_cert, ERROR_CERTIFICATE_PINNING.toString());
+ // Do certificate pinning only if we have 1 cert, otherwise we assume some transitioning of
+ // X509 certs, therefore we cannot do cert pinning
+ if (certificates.size() == 1) {
+ String realFingerprint = getFingerprintFromCertificate(certificates.get(0), encoding);
+ if (!realFingerprint.trim().equalsIgnoreCase(expectedFingerprint.trim())) {
+ return setErrorResult(result, warning_corrupted_provider_cert, ERROR_CERTIFICATE_PINNING.toString());
+ }
+ }
+ for (X509Certificate certificate : certificates) {
+ certificate.checkValidity();
}
if (!canConnect(provider, result)) {
@@ -1073,9 +1085,9 @@ public abstract class ProviderApiManagerBase {
keyString = Base64.encodeToString(key.getEncoded(), Base64.DEFAULT);
provider.setPrivateKey( "-----BEGIN RSA PRIVATE KEY-----\n" + keyString + "-----END RSA PRIVATE KEY-----");
- X509Certificate certificate = ConfigHelper.parseX509CertificateFromString(certificateString);
- certificate.checkValidity();
- certificateString = Base64.encodeToString(certificate.getEncoded(), Base64.DEFAULT);
+ ArrayList<X509Certificate> certificates = ConfigHelper.parseX509CertificatesFromString(certificateString);
+ certificates.get(0).checkValidity();
+ certificateString = Base64.encodeToString(certificates.get(0).getEncoded(), Base64.DEFAULT);
provider.setVpnCertificate( "-----BEGIN CERTIFICATE-----\n" + certificateString + "-----END CERTIFICATE-----");
result.putBoolean(BROADCAST_RESULT_KEY, true);
} catch (CertificateException | NullPointerException e) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/TLSCompatSocketFactory.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/TLSCompatSocketFactory.java
index 5357fd74..cc68b5a8 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/TLSCompatSocketFactory.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/TLSCompatSocketFactory.java
@@ -12,6 +12,8 @@ import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
import java.util.Arrays;
import javax.net.ssl.SSLContext;
@@ -55,8 +57,12 @@ public class TLSCompatSocketFactory extends SSLSocketFactory {
KeyStore keyStore = KeyStore.getInstance(defaultType);
keyStore.load(null, null);
if (!TextUtils.isEmpty(trustedSelfSignedCaCert)) {
- java.security.cert.Certificate provider_certificate = ConfigHelper.parseX509CertificateFromString(trustedSelfSignedCaCert);
- keyStore.setCertificateEntry("provider_ca_certificate", provider_certificate);
+ ArrayList<X509Certificate> x509Certificates = ConfigHelper.parseX509CertificatesFromString(trustedSelfSignedCaCert);
+ if (x509Certificates != null) {
+ for (int i = 0; i < x509Certificates.size(); i++) {
+ keyStore.setCertificateEntry("provider_ca_certificate"+i, x509Certificates.get(i));
+ }
+ }
}
// Create a TrustManager that trusts the CAs in our KeyStore