diff --git a/app/src/main/java/eu/faircode/email/EmailService.java b/app/src/main/java/eu/faircode/email/EmailService.java index 678c32fd0b..1a05f34a46 100644 --- a/app/src/main/java/eu/faircode/email/EmailService.java +++ b/app/src/main/java/eu/faircode/email/EmailService.java @@ -484,7 +484,7 @@ public class EmailService implements AutoCloseable { Throwable ce = ex; while (ce != null) { if (factory != null && ce instanceof CertificateException) - throw new UntrustedException(factory.certificate, ex); + throw new UntrustedException(ex, factory.certificate); if (ce instanceof IOException) ioError = true; ce = ce.getCause(); @@ -1037,10 +1037,10 @@ public class EmailService implements AutoCloseable { } } - class UntrustedException extends MessagingException { + static class UntrustedException extends MessagingException { private X509Certificate certificate; - UntrustedException(@NonNull X509Certificate certificate, @NonNull Exception cause) { + UntrustedException(@NonNull Exception cause, @NonNull X509Certificate certificate) { super("Untrusted", cause); this.certificate = certificate; } @@ -1049,23 +1049,10 @@ public class EmailService implements AutoCloseable { return certificate; } - String getFingerprint() { - try { - if (certificate == null) - return null; - String keyId = EntityCertificate.getKeyId(certificate); - String fingerPrint = EntityCertificate.getFingerprintSha1(certificate); - return fingerPrint + (keyId == null ? "" : "/" + keyId); - } catch (Throwable ex) { - Log.e(ex); - return null; - } - } - @NonNull @Override public synchronized String toString() { return getCause().toString(); } } -} \ No newline at end of file +} diff --git a/app/src/main/java/eu/faircode/email/EntityCertificate.java b/app/src/main/java/eu/faircode/email/EntityCertificate.java index 4659a50cc7..da3763a25f 100644 --- a/app/src/main/java/eu/faircode/email/EntityCertificate.java +++ b/app/src/main/java/eu/faircode/email/EntityCertificate.java @@ -143,6 +143,19 @@ public class EntityCertificate { } } + static String getKeyFingerprint(X509Certificate certificate) { + if (certificate == null) + return null; + try { + String keyId = getKeyId(certificate); + String fingerPrint = getFingerprintSha1(certificate); + return fingerPrint + (keyId == null ? "" : "/" + keyId); + } catch (Throwable ex) { + Log.e(ex); + return null; + } + } + static String getSubject(X509Certificate certificate) { return certificate.getSubjectX500Principal().getName(X500Principal.RFC2253); } diff --git a/app/src/main/java/eu/faircode/email/FragmentAccount.java b/app/src/main/java/eu/faircode/email/FragmentAccount.java index dd8ae9ac30..f10109d125 100644 --- a/app/src/main/java/eu/faircode/email/FragmentAccount.java +++ b/app/src/main/java/eu/faircode/email/FragmentAccount.java @@ -69,6 +69,7 @@ import com.sun.mail.imap.IMAPFolder; import java.io.FileNotFoundException; import java.net.UnknownHostException; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; import java.util.Date; @@ -1331,9 +1332,10 @@ public class FragmentAccount extends FragmentBase { grpError.setVisibility(View.VISIBLE); if (ex instanceof EmailService.UntrustedException) { - EmailService.UntrustedException e = (EmailService.UntrustedException) ex; - cbTrust.setTag(e.getFingerprint()); - cbTrust.setText(getString(R.string.title_trust, e.getFingerprint())); + X509Certificate certificate = ((EmailService.UntrustedException) ex).getCertificate(); + String fingerprint = EntityCertificate.getKeyFingerprint(certificate); + cbTrust.setTag(fingerprint); + cbTrust.setText(getString(R.string.title_trust, fingerprint)); cbTrust.setVisibility(View.VISIBLE); } diff --git a/app/src/main/java/eu/faircode/email/FragmentIdentity.java b/app/src/main/java/eu/faircode/email/FragmentIdentity.java index a9d1e9f1c2..b141deab87 100644 --- a/app/src/main/java/eu/faircode/email/FragmentIdentity.java +++ b/app/src/main/java/eu/faircode/email/FragmentIdentity.java @@ -66,6 +66,7 @@ import com.google.android.material.textfield.TextInputLayout; import java.io.FileNotFoundException; import java.net.UnknownHostException; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -1044,9 +1045,10 @@ public class FragmentIdentity extends FragmentBase { grpError.setVisibility(View.VISIBLE); if (ex instanceof EmailService.UntrustedException) { - EmailService.UntrustedException e = (EmailService.UntrustedException) ex; - cbTrust.setTag(e.getFingerprint()); - cbTrust.setText(getString(R.string.title_trust, e.getFingerprint())); + X509Certificate certificate = ((EmailService.UntrustedException) ex).getCertificate(); + String fingerprint = EntityCertificate.getKeyFingerprint(certificate); + cbTrust.setTag(fingerprint); + cbTrust.setText(getString(R.string.title_trust, fingerprint)); cbTrust.setVisibility(View.VISIBLE); } diff --git a/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java b/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java index 1f34f088d7..7ba01e003f 100644 --- a/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java +++ b/app/src/main/java/eu/faircode/email/FragmentQuickSetup.java @@ -50,6 +50,8 @@ import androidx.fragment.app.FragmentActivity; import com.google.android.material.textfield.TextInputLayout; import java.net.UnknownHostException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -77,13 +79,18 @@ public class FragmentQuickSetup extends FragmentBase { private Button btnSupport; private TextView tvImap; - private TextView tvImapFingerprint; private TextView tvSmtp; + + private TextView tvImapFingerprint; + private TextView tvImapDnsNames; private TextView tvSmtpFingerprint; + private TextView tvSmtpDnsNames; + private Button btnSave; private ContentLoadingProgressBar pbSave; private Group grpSetup; + private Group grpCertificate; private Group grpError; private static final String PRIVACY_URI = "https://www.mozilla.org/privacy/"; @@ -114,13 +121,18 @@ public class FragmentQuickSetup extends FragmentBase { btnSupport = view.findViewById(R.id.btnSupport); tvImap = view.findViewById(R.id.tvImap); - tvImapFingerprint = view.findViewById(R.id.tvImapFingerprint); tvSmtp = view.findViewById(R.id.tvSmtp); + + tvImapFingerprint = view.findViewById(R.id.tvImapFingerprint); + tvImapDnsNames = view.findViewById(R.id.tvImapDnsNames); tvSmtpFingerprint = view.findViewById(R.id.tvSmtpFingerprint); + tvSmtpDnsNames = view.findViewById(R.id.tvSmtpDnsNames); + btnSave = view.findViewById(R.id.btnSave); pbSave = view.findViewById(R.id.pbSave); grpSetup = view.findViewById(R.id.grpSetup); + grpCertificate = view.findViewById(R.id.grpCertificate); grpError = view.findViewById(R.id.grpError); // Wire controls @@ -209,6 +221,7 @@ public class FragmentQuickSetup extends FragmentBase { btnHelp.setVisibility(View.GONE); btnSave.setVisibility(View.GONE); grpSetup.setVisibility(View.GONE); + grpCertificate.setVisibility(View.GONE); grpError.setVisibility(View.GONE); return view; @@ -219,8 +232,6 @@ public class FragmentQuickSetup extends FragmentBase { args.putString("name", etName.getText().toString().trim()); args.putString("email", etEmail.getText().toString().trim()); args.putString("password", tilPassword.getEditText().getText().toString()); - args.putString("imap_fingerprint", tvImapFingerprint.getText().toString()); - args.putString("smtp_fingerprint", tvSmtpFingerprint.getText().toString()); args.putBoolean("check", check); new SimpleTask() { @@ -237,6 +248,8 @@ public class FragmentQuickSetup extends FragmentBase { btnHelp.setVisibility(View.GONE); btnSave.setVisibility(check ? View.GONE : View.VISIBLE); grpSetup.setVisibility(check ? View.GONE : View.VISIBLE); + if (check) + grpCertificate.setVisibility(View.GONE); } @Override @@ -253,8 +266,6 @@ public class FragmentQuickSetup extends FragmentBase { String email = args.getString("email"); String password = args.getString("password"); boolean check = args.getBoolean("check"); - String imap_fingerprint = args.getString("imap_fingerprint"); - String smtp_fingerprint = args.getString("smtp_fingerprint"); if (TextUtils.isEmpty(name)) throw new IllegalArgumentException(context.getString(R.string.title_no_name)); @@ -264,10 +275,6 @@ public class FragmentQuickSetup extends FragmentBase { throw new IllegalArgumentException(context.getString(R.string.title_email_invalid, email)); if (TextUtils.isEmpty(password)) throw new IllegalArgumentException(context.getString(R.string.title_no_password)); - if (TextUtils.isEmpty(imap_fingerprint)) - imap_fingerprint = null; - if (TextUtils.isEmpty(smtp_fingerprint)) - smtp_fingerprint = null; ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo ani = (cm == null ? null : cm.getActiveNetworkInfo()); @@ -284,6 +291,10 @@ public class FragmentQuickSetup extends FragmentBase { Log.i("User type=" + provider.user + " name=" + user); List folders; + String imap_fingerprint = null; + String smtp_fingerprint = null; + X509Certificate imap_certificate = null; + X509Certificate smtp_certificate = null; String aprotocol = (provider.imap.starttls ? "imap" : "imaps"); int aencryption = (provider.imap.starttls ? EmailService.ENCRYPTION_STARTTLS : EmailService.ENCRYPTION_SSL); @@ -294,17 +305,15 @@ public class FragmentQuickSetup extends FragmentBase { provider.imap.host, provider.imap.port, AUTH_TYPE_PASSWORD, null, user, password, - null, imap_fingerprint); + null, null); } catch (EmailService.UntrustedException ex) { - if (check) { - imap_fingerprint = ex.getFingerprint(); - iservice.connect( - provider.imap.host, provider.imap.port, - AUTH_TYPE_PASSWORD, null, - user, password, - null, imap_fingerprint); - } else - throw ex; + imap_certificate = ex.getCertificate(); + imap_fingerprint = EntityCertificate.getKeyFingerprint(imap_certificate); + iservice.connect( + provider.imap.host, provider.imap.port, + AUTH_TYPE_PASSWORD, null, + user, password, + null, imap_fingerprint); } catch (Throwable ex) { Log.w(ex); // Why not AuthenticationFailedException? @@ -348,29 +357,35 @@ public class FragmentQuickSetup extends FragmentBase { } } - Long max_size = null; + Long max_size; String iprotocol = (provider.smtp.starttls ? "smtp" : "smtps"); int iencryption = (provider.smtp.starttls ? EmailService.ENCRYPTION_STARTTLS : EmailService.ENCRYPTION_SSL); try (EmailService iservice = new EmailService( context, iprotocol, null, iencryption, false, EmailService.PURPOSE_CHECK, true)) { iservice.setUseIp(provider.useip, null); - iservice.connect( - provider.smtp.host, provider.smtp.port, - AUTH_TYPE_PASSWORD, null, - user, password, - null, smtp_fingerprint); + try { + iservice.connect( + provider.smtp.host, provider.smtp.port, + AUTH_TYPE_PASSWORD, null, + user, password, + null, null); + } catch (EmailService.UntrustedException ex) { + smtp_certificate = ex.getCertificate(); + smtp_fingerprint = EntityCertificate.getKeyFingerprint(smtp_certificate); + iservice.connect( + provider.smtp.host, provider.smtp.port, + AUTH_TYPE_PASSWORD, null, + user, password, + null, smtp_fingerprint); + } + max_size = iservice.getMaxSize(); - } catch (EmailService.UntrustedException ex) { - if (check) - smtp_fingerprint = ex.getFingerprint(); - else - throw ex; } if (check) { - args.putString("imap_fingerprint", imap_fingerprint); - args.putString("smtp_fingerprint", smtp_fingerprint); + args.putSerializable("imap_certificate", imap_certificate); + args.putSerializable("smtp_certificate", smtp_certificate); return provider; } @@ -468,11 +483,38 @@ public class FragmentQuickSetup extends FragmentBase { boolean check = args.getBoolean("check"); if (check) { tvImap.setText(result == null ? null : result.imap.toString()); - tvImapFingerprint.setText(args.getString("imap_fingerprint")); tvSmtp.setText(result == null ? null : result.smtp.toString()); - tvSmtpFingerprint.setText(args.getString("smtp_fingerprint")); - btnSave.setVisibility(result == null ? View.GONE : View.VISIBLE); grpSetup.setVisibility(result == null ? View.GONE : View.VISIBLE); + + X509Certificate imap_certificate = + (X509Certificate) args.getSerializable("imap_certificate"); + X509Certificate smtp_certificate = + (X509Certificate) args.getSerializable("smtp_certificate"); + + List imapNames = new ArrayList<>(); + if (imap_certificate != null) + try { + imapNames = EntityCertificate.getDnsNames(imap_certificate); + } catch (Throwable ignored) { + } + + List smtpNames = new ArrayList<>(); + if (smtp_certificate != null) + try { + smtpNames = EntityCertificate.getDnsNames(smtp_certificate); + } catch (Throwable ignored) { + } + + tvImapFingerprint.setText(EntityCertificate.getKeyFingerprint(imap_certificate)); + tvImapDnsNames.setText(TextUtils.join(", ", imapNames)); + tvSmtpFingerprint.setText(EntityCertificate.getKeyFingerprint(smtp_certificate)); + tvSmtpDnsNames.setText(TextUtils.join(", ", smtpNames)); + + grpCertificate.setVisibility( + imap_certificate == null && smtp_certificate == null + ? View.GONE : View.VISIBLE); + + btnSave.setVisibility(result == null ? View.GONE : View.VISIBLE); } else { FragmentDialogAccount fragment = new FragmentDialogAccount(); fragment.setArguments(args); @@ -527,10 +569,9 @@ public class FragmentQuickSetup extends FragmentBase { if (provider != null && provider.imap != null && provider.smtp != null) { tvImap.setText(provider.imap.toString()); - tvImapFingerprint.setText(null); tvSmtp.setText(provider.smtp.toString()); - tvSmtpFingerprint.setText(null); grpSetup.setVisibility(View.VISIBLE); + grpCertificate.setVisibility(View.GONE); } getMainHandler().post(new Runnable() { diff --git a/app/src/main/res/layout/fragment_quick_setup.xml b/app/src/main/res/layout/fragment_quick_setup.xml index 7e24198fe8..b6b330ac80 100644 --- a/app/src/main/res/layout/fragment_quick_setup.xml +++ b/app/src/main/res/layout/fragment_quick_setup.xml @@ -253,6 +253,37 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/tvImapTitle" /> + + + + + + + app:layout_constraintTop_toBottomOf="@id/tvCertificateInfo" /> + app:layout_constraintTop_toBottomOf="@id/tvImapDnsNames" /> + app:layout_constraintTop_toBottomOf="@id/tvSmtpFingerprint" />