diff --git a/app/src/main/java/eu/faircode/email/AdapterCertificate.java b/app/src/main/java/eu/faircode/email/AdapterCertificate.java index 03365b5320..9b78314f3e 100644 --- a/app/src/main/java/eu/faircode/email/AdapterCertificate.java +++ b/app/src/main/java/eu/faircode/email/AdapterCertificate.java @@ -42,7 +42,6 @@ import java.util.List; import java.util.Objects; public class AdapterCertificate extends RecyclerView.Adapter { - private ICertificate intf; private Context context; private LifecycleOwner owner; private LayoutInflater inflater; @@ -50,7 +49,7 @@ public class AdapterCertificate extends RecyclerView.Adapter items = new ArrayList<>(); - public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener { + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener { private View view; private TextView tvEmail; private TextView tvSubject; @@ -65,16 +64,6 @@ public class AdapterCertificate extends RecyclerView.Adapter() { @Override - protected Void onExecute(Context context, Bundle args) throws Throwable { + protected Void onExecute(Context context, Bundle args) { long id = args.getLong("id"); DB db = DB.getInstance(context); @@ -131,14 +120,10 @@ public class AdapterCertificate extends RecyclerView.Adapter 0) + sender = ((InternetAddress) draft.from[0]).getAddress(); + Log.i("Alias sender=" + sender); + + Bundle args = new Bundle(); + args.putLong("id", draft.id); + args.putInt("type", draft.encrypt); + args.putString("sender", sender); + + Helper.selectKeyAlias(getActivity(), sender, new Helper.IKeyAlias() { + @Override + public void onSelected(String alias) { + args.putString("alias", alias); + onSmime(args); + } + + @Override + public void onNothingSelected() { + Snackbar snackbar = Snackbar.make(view, R.string.title_invalid_key, Snackbar.LENGTH_LONG); + final Intent intent = KeyChain.createInstallIntent(); + if (intent.resolveActivity(getContext().getPackageManager()) != null) + snackbar.setAction(R.string.title_fix, new View.OnClickListener() { + @Override + public void onClick(View v) { + startActivity(intent); } - } - }, - null, null, null, -1, null); + }); + snackbar.show(); + } + }); } else { if (pgpService.isBound()) try { @@ -1886,7 +1879,6 @@ public class FragmentCompose extends FragmentBase { long id = args.getLong("id"); int type = args.getInt("type"); String alias = args.getString("alias"); - long cid = args.getLong("certificate", -1); DB db = DB.getInstance(context); @@ -1897,9 +1889,6 @@ public class FragmentCompose extends FragmentBase { EntityIdentity identity = db.identity().getIdentity(draft.identity); if (identity == null) throw new IllegalArgumentException(getString(R.string.title_from_missing)); - EntityCertificate certificate = db.certificate().getCertificate(cid); - if (certificate == null && EntityMessage.SMIME_SIGNENCRYPT.equals(type)) - throw new IllegalArgumentException("Certificate missing"); // Get/clean attachments List attachments = db.attachment().getAttachments(id); @@ -2008,8 +1997,24 @@ public class FragmentCompose extends FragmentBase { return null; } - X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X.509") - .generateCertificate(new ByteArrayInputStream(certificate.getEncoded())); + List
addresses = new ArrayList<>(); + if (draft.to != null) + addresses.addAll(Arrays.asList(draft.to)); + if (draft.cc != null) + addresses.addAll(Arrays.asList(draft.cc)); + if (draft.bcc != null) + addresses.addAll(Arrays.asList(draft.bcc)); + + List certs = new ArrayList<>(); + for (Address address : addresses) { + String email = ((InternetAddress) address).getAddress(); + List e = db.certificate().getCertificateByEmail(email); + if (e == null || e.size() < 1) + throw new IllegalArgumentException(context.getString(R.string.title_certificate_missing, email)); + X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X.509") + .generateCertificate(new ByteArrayInputStream(e.get(0).getEncoded())); + certs.add(cert); + } // Build signature BodyPart bpSignature = new MimeBodyPart(); @@ -2032,8 +2037,10 @@ public class FragmentCompose extends FragmentBase { // Encrypt CMSEnvelopedDataGenerator cmsEnvelopedDataGenerator = new CMSEnvelopedDataGenerator(); - RecipientInfoGenerator gen = new JceKeyTransRecipientInfoGenerator(cert); - cmsEnvelopedDataGenerator.addRecipientInfoGenerator(gen); + for (X509Certificate cert : certs) { + RecipientInfoGenerator gen = new JceKeyTransRecipientInfoGenerator(cert); + cmsEnvelopedDataGenerator.addRecipientInfoGenerator(gen); + } ByteArrayOutputStream osMessage = new ByteArrayOutputStream(); imessage.writeTo(osMessage); @@ -3963,60 +3970,6 @@ public class FragmentCompose extends FragmentBase { } } - public static class FragmentDialogCertificate extends FragmentDialogBase { - private String email; - - @NonNull - @Override - public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { - email = getArguments().getString("email"); - - View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_select_certificate, null); - final RecyclerView rvCertificate = dview.findViewById(R.id.rvCertificate); - final ProgressBar pbWait = dview.findViewById(R.id.pbWait); - - final Dialog dialog = new AlertDialog.Builder(getContext()) - .setTitle(R.string.title_select_certificate) - .setView(dview) - .setNegativeButton(android.R.string.cancel, null).create(); - - rvCertificate.setHasFixedSize(false); - LinearLayoutManager llm = new LinearLayoutManager(getContext()); - rvCertificate.setLayoutManager(llm); - - DividerItemDecoration itemDecorator = new DividerItemDecoration(getContext(), llm.getOrientation()); - Drawable divider = getContext().getDrawable(R.drawable.divider); - divider.mutate().setTint(getContext().getResources().getColor(R.color.lightColorSeparator)); - itemDecorator.setDrawable(divider); - rvCertificate.addItemDecoration(itemDecorator); - - final AdapterCertificate adapter = new AdapterCertificate(this, new AdapterCertificate.ICertificate() { - @Override - public void onSelected(EntityCertificate certificate) { - dialog.dismiss(); - getArguments().putLong("certificate", certificate.id); - sendResult(RESULT_OK); - } - }); - rvCertificate.setAdapter(adapter); - - rvCertificate.setVisibility(View.GONE); - pbWait.setVisibility(View.VISIBLE); - - DB db = DB.getInstance(getContext()); - db.certificate().liveCertificates(email).observe(getViewLifecycleOwner(), new Observer>() { - @Override - public void onChanged(List certificates) { - pbWait.setVisibility(View.GONE); - rvCertificate.setVisibility(View.VISIBLE); - adapter.set(email, certificates); - } - }); - - return dialog; - } - } - public static class FragmentDialogSend extends FragmentDialogBase { @NonNull @Override diff --git a/app/src/main/java/eu/faircode/email/FragmentMessages.java b/app/src/main/java/eu/faircode/email/FragmentMessages.java index bc535fface..d91c34621e 100644 --- a/app/src/main/java/eu/faircode/email/FragmentMessages.java +++ b/app/src/main/java/eu/faircode/email/FragmentMessages.java @@ -49,7 +49,6 @@ import android.print.PrintAttributes; import android.print.PrintDocumentAdapter; import android.print.PrintManager; import android.security.KeyChain; -import android.security.KeyChainAliasCallback; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.LongSparseArray; @@ -3896,6 +3895,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. long id = intent.getLongExtra("id", -1); boolean auto = intent.getBooleanExtra("auto", false); int type = intent.getIntExtra("type", EntityMessage.ENCRYPT_NONE); + String recipient = intent.getStringExtra("recipient"); final Bundle args = new Bundle(); args.putLong("id", id); @@ -3904,27 +3904,27 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences. if (EntityMessage.SMIME_SIGNONLY.equals(type)) onSmime(args); else if (EntityMessage.SMIME_SIGNENCRYPT.equals(type)) { - Handler handler = new Handler(); - KeyChain.choosePrivateKeyAlias(getActivity(), new KeyChainAliasCallback() { - @Override - public void alias(@Nullable String alias) { - Log.i("Selected key alias=" + alias); - if (alias != null) { - args.putString("alias", alias); - handler.post(new Runnable() { - @Override - public void run() { - try { - onSmime(args); - } catch (Throwable ex) { - Log.e(ex); - } - } - }); + Helper.selectKeyAlias(getActivity(), recipient, new Helper.IKeyAlias() { + @Override + public void onSelected(String alias) { + args.putString("alias", alias); + onSmime(args); + } + + @Override + public void onNothingSelected() { + Snackbar snackbar = Snackbar.make(view, R.string.title_invalid_key, Snackbar.LENGTH_LONG); + final Intent intent = KeyChain.createInstallIntent(); + if (intent.resolveActivity(getContext().getPackageManager()) != null) + snackbar.setAction(R.string.title_fix, new View.OnClickListener() { + @Override + public void onClick(View v) { + startActivity(intent); } - } - }, - null, null, null, -1, null); + }); + snackbar.show(); + } + }); } else { if (pgpService.isBound()) { Intent data = new Intent(); diff --git a/app/src/main/java/eu/faircode/email/Helper.java b/app/src/main/java/eu/faircode/email/Helper.java index 512c9b9c07..4a77dc8f0d 100644 --- a/app/src/main/java/eu/faircode/email/Helper.java +++ b/app/src/main/java/eu/faircode/email/Helper.java @@ -19,6 +19,7 @@ package eu.faircode.email; Copyright 2018-2019 by Marcel Bokhorst (M66B) */ +import android.app.Activity; import android.app.Dialog; import android.app.KeyguardManager; import android.content.ActivityNotFoundException; @@ -42,6 +43,9 @@ import android.os.LocaleList; import android.os.Parcel; import android.os.PowerManager; import android.os.StatFs; +import android.security.KeyChain; +import android.security.KeyChainAliasCallback; +import android.security.KeyChainException; import android.text.Spannable; import android.text.Spanned; import android.text.TextUtils; @@ -974,6 +978,62 @@ public class Helper { return "?"; } + static void selectKeyAlias(final Activity activity, final String email, final IKeyAlias intf) { + final Context context = activity.getApplicationContext(); + final Handler handler = new Handler(); + + new Thread(new Runnable() { + @Override + public void run() { + if (email != null) + try { + if (KeyChain.getPrivateKey(context, email) != null) { + Log.i("Private key available alias=" + email); + handler.post(new Runnable() { + @Override + public void run() { + intf.onSelected(email); + } + }); + return; + } + } catch (KeyChainException ex) { + Log.w(ex); + } catch (Throwable ex) { + Log.e(ex); + } + + handler.post(new Runnable() { + @Override + public void run() { + KeyChain.choosePrivateKeyAlias(activity, new KeyChainAliasCallback() { + @Override + public void alias(@Nullable final String alias) { + Log.i("Selected key alias=" + alias); + handler.post(new Runnable() { + @Override + public void run() { + if (alias == null) + intf.onNothingSelected(); + else + intf.onSelected(alias); + } + }); + } + }, + null, null, null, -1, email); + } + }); + } + }).start(); + } + + interface IKeyAlias { + void onSelected(String alias); + + void onNothingSelected(); + } + // Miscellaneous static List> chunkList(List list, int size) { diff --git a/app/src/main/res/layout/dialog_select_certificate.xml b/app/src/main/res/layout/dialog_select_certificate.xml deleted file mode 100644 index ff77d6a8e4..0000000000 --- a/app/src/main/res/layout/dialog_select_certificate.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - \ No newline at end of file