From 555abf50c7461c79b0c8601812e1b56354dd32d1 Mon Sep 17 00:00:00 2001 From: M66B Date: Fri, 8 Dec 2023 08:01:25 +0100 Subject: [PATCH] Refactoring --- .../java/eu/faircode/email/SSLHelper.java | 111 ++++++++++++++++++ .../java/eu/faircode/email/SSLHelper.java | 111 ++++++++++++++++++ .../java/eu/faircode/email/EmailService.java | 95 +-------------- 3 files changed, 228 insertions(+), 89 deletions(-) create mode 100644 app/src/dummy/java/eu/faircode/email/SSLHelper.java create mode 100644 app/src/extra/java/eu/faircode/email/SSLHelper.java diff --git a/app/src/dummy/java/eu/faircode/email/SSLHelper.java b/app/src/dummy/java/eu/faircode/email/SSLHelper.java new file mode 100644 index 0000000000..7a1cf5bd11 --- /dev/null +++ b/app/src/dummy/java/eu/faircode/email/SSLHelper.java @@ -0,0 +1,111 @@ +package eu.faircode.email; + +import androidx.annotation.NonNull; + +import java.security.Principal; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.X509TrustManager; + +public class SSLHelper { + static X509TrustManager getTrustManager(X509TrustManager rtm, + boolean secure, boolean cert_strict, + String trustedFingerprint, + ITrust intf) { + return new X509TrustManager() { + // openssl s_client -connect + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + if (secure) + rtm.checkClientTrusted(chain, authType); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + if (intf != null) + intf.checkServerTrusted(chain); + + if (secure) { + // Check if selected fingerprint + if (trustedFingerprint != null && matches(chain[0], trustedFingerprint)) { + Log.i("Trusted selected fingerprint"); + return; + } + + // Check certificates + try { + Log.i("Auth type=" + authType); + rtm.checkServerTrusted(chain, authType); + } catch (CertificateException ex) { + Principal principal = chain[0].getSubjectDN(); + if (principal == null) + throw ex; + else if (cert_strict) + throw new CertificateException(principal.getName(), ex); + else if (noAnchor(ex) || isExpired(ex)) { + if (BuildConfig.PLAY_STORE_RELEASE) + Log.i(ex); + else + Log.w(ex); + } else + throw new CertificateException(principal.getName(), ex); + } + } + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return rtm.getAcceptedIssuers(); + } + + private boolean noAnchor(Throwable ex) { + while (ex != null) { + if (ex instanceof CertPathValidatorException && + "Trust anchor for certification path not found." + .equals(ex.getMessage())) + return true; + ex = ex.getCause(); + } + return false; + } + + private boolean isExpired(Throwable ex) { + while (ex != null) { + if (ex instanceof CertPathValidatorException && + "timestamp check failed" + .equals(ex.getMessage())) + return true; + + ex = ex.getCause(); + } + return false; + } + }; + } + + private static boolean matches(X509Certificate certificate, @NonNull String trustedFingerprint) { + // Get certificate fingerprint + try { + String fingerprint = EntityCertificate.getFingerprintSha1(certificate); + int slash = trustedFingerprint.indexOf('/'); + if (slash < 0) + return trustedFingerprint.equals(fingerprint); + else { + String keyId = EntityCertificate.getKeyId(certificate); + if (trustedFingerprint.substring(slash + 1).equals(keyId)) + return true; + return trustedFingerprint.substring(0, slash).equals(fingerprint); + } + } catch (Throwable ex) { + Log.w(ex); + return false; + } + } + + interface ITrust { + void checkServerTrusted(X509Certificate[] chain); + } +} diff --git a/app/src/extra/java/eu/faircode/email/SSLHelper.java b/app/src/extra/java/eu/faircode/email/SSLHelper.java new file mode 100644 index 0000000000..7a1cf5bd11 --- /dev/null +++ b/app/src/extra/java/eu/faircode/email/SSLHelper.java @@ -0,0 +1,111 @@ +package eu.faircode.email; + +import androidx.annotation.NonNull; + +import java.security.Principal; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.X509TrustManager; + +public class SSLHelper { + static X509TrustManager getTrustManager(X509TrustManager rtm, + boolean secure, boolean cert_strict, + String trustedFingerprint, + ITrust intf) { + return new X509TrustManager() { + // openssl s_client -connect + + @Override + public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { + if (secure) + rtm.checkClientTrusted(chain, authType); + } + + @Override + public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { + if (intf != null) + intf.checkServerTrusted(chain); + + if (secure) { + // Check if selected fingerprint + if (trustedFingerprint != null && matches(chain[0], trustedFingerprint)) { + Log.i("Trusted selected fingerprint"); + return; + } + + // Check certificates + try { + Log.i("Auth type=" + authType); + rtm.checkServerTrusted(chain, authType); + } catch (CertificateException ex) { + Principal principal = chain[0].getSubjectDN(); + if (principal == null) + throw ex; + else if (cert_strict) + throw new CertificateException(principal.getName(), ex); + else if (noAnchor(ex) || isExpired(ex)) { + if (BuildConfig.PLAY_STORE_RELEASE) + Log.i(ex); + else + Log.w(ex); + } else + throw new CertificateException(principal.getName(), ex); + } + } + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return rtm.getAcceptedIssuers(); + } + + private boolean noAnchor(Throwable ex) { + while (ex != null) { + if (ex instanceof CertPathValidatorException && + "Trust anchor for certification path not found." + .equals(ex.getMessage())) + return true; + ex = ex.getCause(); + } + return false; + } + + private boolean isExpired(Throwable ex) { + while (ex != null) { + if (ex instanceof CertPathValidatorException && + "timestamp check failed" + .equals(ex.getMessage())) + return true; + + ex = ex.getCause(); + } + return false; + } + }; + } + + private static boolean matches(X509Certificate certificate, @NonNull String trustedFingerprint) { + // Get certificate fingerprint + try { + String fingerprint = EntityCertificate.getFingerprintSha1(certificate); + int slash = trustedFingerprint.indexOf('/'); + if (slash < 0) + return trustedFingerprint.equals(fingerprint); + else { + String keyId = EntityCertificate.getKeyId(certificate); + if (trustedFingerprint.substring(slash + 1).equals(keyId)) + return true; + return trustedFingerprint.substring(0, slash).equals(fingerprint); + } + } catch (Throwable ex) { + Log.w(ex); + return false; + } + } + + interface ITrust { + void checkServerTrusted(X509Certificate[] chain); + } +} diff --git a/app/src/main/java/eu/faircode/email/EmailService.java b/app/src/main/java/eu/faircode/email/EmailService.java index 36d51d7f15..784cdab42a 100644 --- a/app/src/main/java/eu/faircode/email/EmailService.java +++ b/app/src/main/java/eu/faircode/email/EmailService.java @@ -61,9 +61,7 @@ import java.net.SocketException; import java.net.UnknownHostException; import java.security.GeneralSecurityException; import java.security.KeyStore; -import java.security.Principal; import java.security.PrivateKey; -import java.security.cert.CertPathValidatorException; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; @@ -1078,75 +1076,13 @@ public class EmailService implements AutoCloseable { for (TrustManager tm : tms) Log.e("Trust manager " + tm.getClass()); - X509TrustManager tm = new X509TrustManager() { - // openssl s_client -connect - - @Override - public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { - if (secure) - rtm.checkClientTrusted(chain, authType); - } - - @Override - public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { - certificate = chain[0]; - - if (secure) { - // Check if selected fingerprint - if (trustedFingerprint != null && matches(certificate, trustedFingerprint)) { - Log.i("Trusted selected fingerprint"); - return; - } - - // Check certificates - try { - Log.i("Auth type=" + authType); - rtm.checkServerTrusted(chain, authType); - } catch (CertificateException ex) { - Principal principal = certificate.getSubjectDN(); - if (principal == null) - throw ex; - else if (cert_strict) - throw new CertificateException(principal.getName(), ex); - else if (noAnchor(ex) || isExpired(ex)) { - if (BuildConfig.PLAY_STORE_RELEASE) - Log.i(ex); - else - Log.w(ex); - } else - throw new CertificateException(principal.getName(), ex); + X509TrustManager tm = SSLHelper.getTrustManager(rtm, secure, cert_strict, trustedFingerprint, + new SSLHelper.ITrust() { + @Override + public void checkServerTrusted(X509Certificate[] chain) { + certificate = chain[0]; } - } - } - - @Override - public X509Certificate[] getAcceptedIssuers() { - return rtm.getAcceptedIssuers(); - } - - private boolean noAnchor(Throwable ex) { - while (ex != null) { - if (ex instanceof CertPathValidatorException && - "Trust anchor for certification path not found." - .equals(ex.getMessage())) - return true; - ex = ex.getCause(); - } - return false; - } - - private boolean isExpired(Throwable ex) { - while (ex != null) { - if (ex instanceof CertPathValidatorException && - "timestamp check failed" - .equals(ex.getMessage())) - return true; - - ex = ex.getCause(); - } - return false; - } - }; + }); KeyManager[] km = null; if (key != null && chain != null) @@ -1345,25 +1281,6 @@ public class EmailService implements AutoCloseable { public String[] getSupportedCipherSuites() { return factory.getSupportedCipherSuites(); } - - private static boolean matches(X509Certificate certificate, @NonNull String trustedFingerprint) { - // Get certificate fingerprint - try { - String fingerprint = EntityCertificate.getFingerprintSha1(certificate); - int slash = trustedFingerprint.indexOf('/'); - if (slash < 0) - return trustedFingerprint.equals(fingerprint); - else { - String keyId = EntityCertificate.getKeyId(certificate); - if (trustedFingerprint.substring(slash + 1).equals(keyId)) - return true; - return trustedFingerprint.substring(0, slash).equals(fingerprint); - } - } catch (Throwable ex) { - Log.w(ex); - return false; - } - } } private static void configureSocketOptions(Socket socket) throws SocketException {