diff --git a/app/src/main/java/eu/faircode/email/ActivitySetup.java b/app/src/main/java/eu/faircode/email/ActivitySetup.java index e0ac70fe61..30a8d5f83e 100644 --- a/app/src/main/java/eu/faircode/email/ActivitySetup.java +++ b/app/src/main/java/eu/faircode/email/ActivitySetup.java @@ -1023,18 +1023,21 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac CertificateFactory fact = CertificateFactory.getInstance("X.509"); X509Certificate cert = (X509Certificate) fact.generateCertificate(bis); - String fingerprint = Helper.sha256(cert.getEncoded()); - String email = Helper.getAltSubjectName(cert); + String fingerprint = Helper.getFingerprint(cert); + List emails = Helper.getAltSubjectName(cert); + String subject = Helper.getSubject(cert); DB db = DB.getInstance(context); - EntityCertificate record = db.certificate().getCertificate(fingerprint, email); - if (record == null) { - record = new EntityCertificate(); - record.fingerprint = fingerprint; - record.email = email; - record.subject = Helper.getSubject(cert); - record.setEncoded(cert.getEncoded()); - record.id = db.certificate().insertCertificate(record); + for (String email : emails) { + EntityCertificate record = db.certificate().getCertificate(fingerprint, email); + if (record == null) { + record = new EntityCertificate(); + record.fingerprint = fingerprint; + record.email = email; + record.subject = subject; + record.setEncoded(cert.getEncoded()); + record.id = db.certificate().insertCertificate(record); + } } return null; diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java index 42af08517a..b0139eea55 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessages.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java @@ -141,6 +141,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.StandardCharsets; import java.security.PrivateKey; +import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.text.Collator; import java.text.DateFormat; @@ -4340,9 +4341,9 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. } private void onSmime(Bundle args) { - new SimpleTask() { + new SimpleTask() { @Override - protected Boolean onExecute(Context context, Bundle args) throws Throwable { + protected X509Certificate onExecute(Context context, Bundle args) throws Throwable { long id = args.getLong("id"); int type = args.getInt("type"); @@ -4388,22 +4389,23 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. .getCertificate(certHolder); try { if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) { - String fingerprint = Helper.sha256(cert.getEncoded()); - String email = Helper.getAltSubjectName(cert); - EntityCertificate record = db.certificate().getCertificate(fingerprint, email); + boolean known = true; + String fingerprint = Helper.getFingerprint(cert); + List emails = Helper.getAltSubjectName(cert); + for (String email : emails) { + EntityCertificate record = db.certificate().getCertificate(fingerprint, email); + if (record == null) + known = false; + } String sender = null; if (message.from != null && message.from.length == 1) sender = ((InternetAddress) message.from[0]).getAddress(); args.putString("sender", sender); - args.putString("fingerprint", fingerprint); - args.putString("email", email); - args.putString("subject", Helper.getSubject(cert)); - args.putByteArray("encoded", cert.getEncoded()); - args.putBoolean("known", record != null); + args.putBoolean("known", known); - return true; + return cert; } } catch (CMSVerifierCertificateNotValidException ex) { Log.w(ex); @@ -4519,18 +4521,21 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. } @Override - protected void onExecuted(final Bundle args, Boolean valid) { + protected void onExecuted(final Bundle args, X509Certificate cert) { int type = args.getInt("type"); if (EntityMessage.SMIME_SIGNONLY.equals(type)) { String sender = args.getString("sender"); - String fingerprint = args.getString("fingerprint"); - String email = args.getString("email"); - String subject = args.getString("subject"); - byte[] encoded = args.getByteArray("encoded"); boolean known = args.getBoolean("known"); - boolean match = Objects.equals(sender, email); - if (valid == null || !valid) + boolean match = false; + List emails = (cert == null ? Collections.emptyList() : Helper.getAltSubjectName(cert)); + for (String email : emails) + if (Objects.equals(sender, email)) { + match = true; + break; + } + + if (cert == null) Snackbar.make(view, R.string.title_signature_invalid, Snackbar.LENGTH_LONG).show(); else if (known && match) Snackbar.make(view, R.string.title_signature_valid, Snackbar.LENGTH_LONG).show(); @@ -4543,44 +4548,58 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. TextView tvSubject = dview.findViewById(R.id.tvSubject); tvSender.setText(sender); - tvEmail.setText(email); + tvEmail.setText(TextUtils.join(",", emails)); tvEmailInvalid.setVisibility(match ? View.GONE : View.VISIBLE); - tvSubject.setText(subject); + tvSubject.setText(Helper.getSubject(cert)); AlertDialog.Builder builder = new AlertDialog.Builder(getContext()) .setView(dview) .setNegativeButton(android.R.string.cancel, null); - if (!TextUtils.isEmpty(sender) && !known) + if (!TextUtils.isEmpty(sender) && !known && emails.size() > 0) builder.setPositiveButton(R.string.title_signature_store, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { - new SimpleTask() { - @Override - protected Void onExecute(Context context, Bundle args) throws Throwable { - long id = args.getLong("id"); + try { + args.putByteArray("encoded", cert.getEncoded()); - DB db = DB.getInstance(context); + new SimpleTask() { + @Override + protected Void onExecute(Context context, Bundle args) throws Throwable { + long id = args.getLong("id"); + byte[] encoded = args.getByteArray("encoded"); + + X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X.509") + .generateCertificate(new ByteArrayInputStream(encoded)); + + DB db = DB.getInstance(context); + EntityMessage message = db.message().getMessage(id); + if (message == null) + return null; + + String fingerprint = Helper.getFingerprint(cert); + List emails = Helper.getAltSubjectName(cert); + String subject = Helper.getSubject(cert); + for (String email : emails) { + EntityCertificate record = new EntityCertificate(); + record.fingerprint = fingerprint; + record.email = email; + record.subject = subject; + record.setEncoded(encoded); + record.id = db.certificate().insertCertificate(record); + } - EntityMessage message = db.message().getMessage(id); - if (message == null) return null; + } - EntityCertificate record = new EntityCertificate(); - record.fingerprint = fingerprint; - record.email = email; - record.subject = subject; - record.setEncoded(encoded); - record.id = db.certificate().insertCertificate(record); - - return null; - } - - @Override - protected void onException(Bundle args, Throwable ex) { - Helper.unexpectedError(getParentFragmentManager(), ex); - } - }.execute(FragmentMessages.this, args, "certificate:store"); + @Override + protected void onException(Bundle args, Throwable ex) { + Helper.unexpectedError(getParentFragmentManager(), ex); + } + }.execute(FragmentMessages.this, args, "certificate:store"); + } catch (Throwable ex) { + Helper.unexpectedError(getParentFragmentManager(), ex); + } } }); diff --git a/app/src/main/java/eu/faircode/email/Helper.java b/app/src/main/java/eu/faircode/email/Helper.java index 4a77dc8f0d..ef8adbf686 100644 --- a/app/src/main/java/eu/faircode/email/Helper.java +++ b/app/src/main/java/eu/faircode/email/Helper.java @@ -102,6 +102,7 @@ import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; import java.text.DateFormat; @@ -958,24 +959,29 @@ public class Helper { prefs.edit().remove("last_authentication").apply(); } + static String getFingerprint(X509Certificate certificate) throws CertificateEncodingException, NoSuchAlgorithmException { + return sha256(certificate.getEncoded()); + } + static String getSubject(X509Certificate certificate) { return certificate.getSubjectX500Principal().getName(X500Principal.RFC2253); } - static String getAltSubjectName(X509Certificate certificate) { + static List getAltSubjectName(X509Certificate certificate) { + List result = new ArrayList<>(); try { Collection> altNames = certificate.getSubjectAlternativeNames(); if (altNames != null) for (List altName : altNames) if (altName.get(0).equals(GeneralName.rfc822Name)) - return (String) altName.get(1); + result.add((String) altName.get(1)); else Log.i("Alt type=" + altName.get(0) + " data=" + altName.get(1)); } catch (CertificateParsingException ex) { Log.w(ex); } - return "?"; + return result; } static void selectKeyAlias(final Activity activity, final String email, final IKeyAlias intf) {