Encryption improvements

This commit is contained in:
M66B
2019-12-04 20:36:33 +01:00
parent 66e7d57cbe
commit 32781bda24
31 changed files with 475 additions and 170 deletions

View File

@@ -74,12 +74,16 @@ import com.microsoft.identity.client.IPublicClientApplication;
import com.microsoft.identity.client.PublicClientApplication;
import com.microsoft.identity.client.exception.MsalException;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -88,9 +92,13 @@ import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.cert.CertificateFactory;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.security.spec.KeySpec;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -106,6 +114,7 @@ import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.security.auth.x500.X500Principal;
public class ActivitySetup extends ActivityBase implements FragmentManager.OnBackStackChangedListener {
private View view;
@@ -127,6 +136,7 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
static final int REQUEST_IMPORT_OAUTH = 5;
static final int REQUEST_CHOOSE_ACCOUNT = 6;
static final int REQUEST_DONE = 7;
static final int REQUEST_IMPORT_CERTIFICATE = 7;
static final String ACTION_QUICK_GMAIL = BuildConfig.APPLICATION_ID + ".ACTION_QUICK_GMAIL";
static final String ACTION_QUICK_OUTLOOK = BuildConfig.APPLICATION_ID + ".ACTION_QUICK_OUTLOOK";
@@ -135,7 +145,9 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
static final String ACTION_VIEW_IDENTITIES = BuildConfig.APPLICATION_ID + ".ACTION_VIEW_IDENTITIES";
static final String ACTION_EDIT_ACCOUNT = BuildConfig.APPLICATION_ID + ".EDIT_ACCOUNT";
static final String ACTION_EDIT_IDENTITY = BuildConfig.APPLICATION_ID + ".EDIT_IDENTITY";
static final String ACTION_MANAGE_LOCAL_CONTACTS = BuildConfig.APPLICATION_ID + ".LOCAL_CONTACTS";
static final String ACTION_MANAGE_LOCAL_CONTACTS = BuildConfig.APPLICATION_ID + ".MANAGE_LOCAL_CONTACTS";
static final String ACTION_MANAGE_CERTIFICATES = BuildConfig.APPLICATION_ID + ".MANAGE_CERTIFICATES";
static final String ACTION_IMPORT_CERTIFICATE = BuildConfig.APPLICATION_ID + ".IMPORT_CERTIFICATE";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -303,6 +315,8 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
iff.addAction(ACTION_EDIT_ACCOUNT);
iff.addAction(ACTION_EDIT_IDENTITY);
iff.addAction(ACTION_MANAGE_LOCAL_CONTACTS);
iff.addAction(ACTION_MANAGE_CERTIFICATES);
iff.addAction(ACTION_IMPORT_CERTIFICATE);
lbm.registerReceiver(receiver, iff);
}
@@ -366,6 +380,10 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
case REQUEST_IMPORT_OAUTH:
ServiceSynchronize.reload(this, "oauth");
break;
case REQUEST_IMPORT_CERTIFICATE:
if (resultCode == RESULT_OK && data != null)
handleImportCertificate(data);
break;
}
} catch (Throwable ex) {
Log.e(ex);
@@ -989,6 +1007,63 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
}.execute(this, args, "setup:import");
}
private void handleImportCertificate(Intent data) {
Uri uri = data.getData();
if (uri != null) {
Bundle args = new Bundle();
args.putParcelable("uri", uri);
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) throws Throwable {
Uri uri = args.getParcelable("uri");
PemObject pem;
try (InputStream is = context.getContentResolver().openInputStream(uri)) {
pem = new PemReader(new InputStreamReader(is)).readPemObject();
}
ByteArrayInputStream bis = new ByteArrayInputStream(pem.getContent());
CertificateFactory fact = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) fact.generateCertificate(bis);
String email = "?";
try {
Collection<List<?>> altNames = cert.getSubjectAlternativeNames();
if (altNames != null)
for (List altName : altNames)
if (altName.get(0).equals(GeneralName.rfc822Name))
email = (String) altName.get(1);
else
Log.i("Alt type=" + altName.get(0) + " data=" + altName.get(1));
} catch (CertificateParsingException ex) {
Log.w(ex);
}
String fingerprint = Helper.sha256(cert.getEncoded());
DB db = DB.getInstance(context);
EntityCertificate record = db.certificate().getCertificate(fingerprint, email);
if (record == null) {
record = new EntityCertificate();
record.fingerprint = Helper.sha256(cert.getEncoded());
record.email = email;
record.subject = cert.getSubjectX500Principal().getName(X500Principal.RFC2253);
record.setEncoded(cert.getEncoded());
record.id = db.certificate().insertCertificate(record);
}
return null;
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(getSupportFragmentManager(), ex);
}
}.execute(this, args, "setup:cert");
}
}
@RequiresApi(api = Build.VERSION_CODES.O)
private JSONObject channelToJSON(NotificationChannel channel) throws JSONException {
JSONObject jchannel = new JSONObject();
@@ -1302,6 +1377,23 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
fragmentTransaction.commit();
}
private void onManageCertificates(Intent intent) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.content_frame, new FragmentCertificates()).addToBackStack("certificates");
fragmentTransaction.commit();
}
private void onImportCertificate(Intent intent) {
Intent open = new Intent(Intent.ACTION_OPEN_DOCUMENT);
open.addCategory(Intent.CATEGORY_OPENABLE);
open.setType("*/*");
Helper.openAdvanced(open);
if (open.resolveActivity(getPackageManager()) == null)
ToastEx.makeText(this, R.string.title_no_saf, Toast.LENGTH_LONG).show();
else
startActivityForResult(Helper.getChooser(this, open), REQUEST_IMPORT_CERTIFICATE);
}
private static Intent getIntentExport() {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
@@ -1397,6 +1489,10 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
onEditIdentity(intent);
else if (ACTION_MANAGE_LOCAL_CONTACTS.equals(action))
onManageLocalContacts(intent);
else if (ACTION_MANAGE_CERTIFICATES.equals(action))
onManageCertificates(intent);
else if (ACTION_IMPORT_CERTIFICATE.equals(action))
onImportCertificate(intent);
}
}
};

View File

@@ -23,11 +23,14 @@ import android.content.Context;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.PopupMenu;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.LifecycleOwner;
import androidx.recyclerview.widget.DiffUtil;
@@ -52,6 +55,8 @@ public class AdapterCertificate extends RecyclerView.Adapter<AdapterCertificate.
private TextView tvEmail;
private TextView tvSubject;
private TwoStateOwner powner = new TwoStateOwner(owner, "CertificatePopup");
ViewHolder(View itemView) {
super(itemView);
@@ -76,38 +81,65 @@ public class AdapterCertificate extends RecyclerView.Adapter<AdapterCertificate.
if (pos == RecyclerView.NO_POSITION)
return false;
EntityCertificate certificate = items.get(pos);
final EntityCertificate certificate = items.get(pos);
Bundle args = new Bundle();
args.putLong("id", certificate.id);
PopupMenuLifecycle popupMenu = new PopupMenuLifecycle(context, powner, view);
new SimpleTask<Void>() {
popupMenu.getMenu().add(Menu.NONE, 0, 0, certificate.email).setEnabled(false);
popupMenu.getMenu().add(Menu.NONE, R.string.title_delete, 1, R.string.title_delete);
popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
protected Void onExecute(Context context, Bundle args) throws Throwable {
long id = args.getLong("id");
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.string.title_delete:
onActionDelete();
return true;
DB db = DB.getInstance(context);
db.certificate().deleteCertificate(id);
return null;
default:
return false;
}
}
@Override
protected void onException(Bundle args, Throwable ex) {
// TODO: report error
private void onActionDelete() {
Bundle args = new Bundle();
args.putLong("id", certificate.id);
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) throws Throwable {
long id = args.getLong("id");
DB db = DB.getInstance(context);
db.certificate().deleteCertificate(id);
return null;
}
@Override
protected void onException(Bundle args, Throwable ex) {
// TODO: report error
}
}.execute(context, owner, args, "certificate:delete");
}
}.execute(context, owner, args, "certificate:delete");
});
popupMenu.show();
return true;
}
private void wire() {
view.setOnClickListener(this);
if (intf != null)
view.setOnClickListener(this);
view.setOnLongClickListener(this);
}
private void unwire() {
view.setOnClickListener(null);
if (intf != null)
view.setOnClickListener(null);
view.setOnLongClickListener(null);
}
private void bindTo(EntityCertificate certificate) {
@@ -221,6 +253,11 @@ public class AdapterCertificate extends RecyclerView.Adapter<AdapterCertificate.
holder.wire();
}
@Override
public void onViewDetachedFromWindow(@NonNull ViewHolder holder) {
holder.powner.recreate();
}
interface ICertificate {
void onSelected(EntityCertificate certificate);
}

View File

@@ -0,0 +1,110 @@
package eu.faircode.email;
/*
This file is part of FairEmail.
FairEmail is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
FairEmail is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with FairEmail. If not, see <http://www.gnu.org/licenses/>.
Copyright 2018-2019 by Marcel Bokhorst (M66B)
*/
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.constraintlayout.widget.Group;
import androidx.lifecycle.Observer;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
import java.util.List;
public class FragmentCertificates extends FragmentBase {
private RecyclerView rvCertificate;
private ContentLoadingProgressBar pbWait;
private Group grpReady;
private FloatingActionButton fab;
private AdapterCertificate adapter;
@Override
@Nullable
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
setSubtitle(R.string.title_advanced_manage_certificates);
setHasOptionsMenu(true);
View view = inflater.inflate(R.layout.fragment_certificates, container, false);
// Get controls
rvCertificate = view.findViewById(R.id.rvCertificate);
pbWait = view.findViewById(R.id.pbWait);
grpReady = view.findViewById(R.id.grpReady);
fab = view.findViewById(R.id.fab);
// Wire controls
rvCertificate.setHasFixedSize(false);
LinearLayoutManager llm = new LinearLayoutManager(getContext());
rvCertificate.setLayoutManager(llm);
DividerItemDecoration itemDecorator = new DividerItemDecoration(getContext(), llm.getOrientation());
itemDecorator.setDrawable(getContext().getDrawable(R.drawable.divider));
rvCertificate.addItemDecoration(itemDecorator);
adapter = new AdapterCertificate(this, null);
rvCertificate.setAdapter(adapter);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext());
lbm.sendBroadcast(new Intent(ActivitySetup.ACTION_IMPORT_CERTIFICATE));
}
});
// Initialize
grpReady.setVisibility(View.GONE);
pbWait.setVisibility(View.VISIBLE);
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
DB db = DB.getInstance(getContext());
db.certificate().liveCertificates(null).observe(getViewLifecycleOwner(), new Observer<List<EntityCertificate>>() {
@Override
public void onChanged(List<EntityCertificate> certificates) {
if (certificates == null)
certificates = new ArrayList<>();
adapter.set(null, certificates);
pbWait.setVisibility(View.GONE);
grpReady.setVisibility(View.VISIBLE);
}
});
}
}

View File

@@ -71,7 +71,6 @@ import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
@@ -94,6 +93,7 @@ import androidx.documentfile.provider.DocumentFile;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.Observer;
import androidx.preference.PreferenceManager;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -120,8 +120,6 @@ import org.bouncycastle.operator.OutputEncryptor;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.bouncycastle.util.Store;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemReader;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.TextNode;
@@ -138,7 +136,6 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.UnknownHostException;
import java.security.PrivateKey;
@@ -173,7 +170,6 @@ import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMultipart;
import javax.mail.internet.ParseException;
import javax.mail.util.ByteArrayDataSource;
import javax.security.auth.x500.X500Principal;
import static android.app.Activity.RESULT_CANCELED;
import static android.app.Activity.RESULT_OK;
@@ -966,10 +962,10 @@ public class FragmentCompose extends FragmentBase {
int colorEncrypt = Helper.resolveColor(getContext(), R.attr.colorEncrypt);
ImageButton ib = (ImageButton) menu.findItem(R.id.menu_encrypt).getActionView();
ib.setEnabled(!busy);
if (EntityMessage.PGP_SIGNONLY.equals(encrypt)) {
if (EntityMessage.PGP_SIGNONLY.equals(encrypt) || EntityMessage.SMIME_SIGNONLY.equals(encrypt)) {
ib.setImageResource(R.drawable.baseline_gesture_24);
ib.setImageTintList(null);
} else if (EntityMessage.PGP_SIGNENCRYPT.equals(encrypt)) {
} else if (EntityMessage.PGP_SIGNENCRYPT.equals(encrypt) || EntityMessage.SMIME_SIGNENCRYPT.equals(encrypt)) {
ib.setImageResource(R.drawable.baseline_lock_24);
ib.setImageTintList(ColorStateList.valueOf(colorEncrypt));
} else {
@@ -1037,12 +1033,25 @@ public class FragmentCompose extends FragmentBase {
}
private void onMenuEncrypt() {
if (EntityMessage.PGP_SIGNENCRYPT.equals(encrypt))
encrypt = EntityMessage.PGP_SIGNONLY;
else if (EntityMessage.PGP_SIGNONLY.equals(encrypt))
encrypt = EntityMessage.ENCRYPT_NONE;
else
encrypt = EntityMessage.PGP_SIGNENCRYPT;
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
String encrypt_method = prefs.getString("default_encrypt_method", "pgp");
if ("pgp".equals(encrypt_method)) {
if (EntityMessage.ENCRYPT_NONE.equals(encrypt) || encrypt == null)
encrypt = EntityMessage.PGP_SIGNENCRYPT;
else if (EntityMessage.PGP_SIGNENCRYPT.equals(encrypt))
encrypt = EntityMessage.PGP_SIGNONLY;
else
encrypt = EntityMessage.ENCRYPT_NONE;
} else {
if (EntityMessage.ENCRYPT_NONE.equals(encrypt) || encrypt == null)
encrypt = EntityMessage.SMIME_SIGNENCRYPT;
else if (EntityMessage.SMIME_SIGNENCRYPT.equals(encrypt))
encrypt = EntityMessage.SMIME_SIGNONLY;
else
encrypt = EntityMessage.ENCRYPT_NONE;
}
getActivity().invalidateOptionsMenu();
Bundle args = new Bundle();
@@ -2481,6 +2490,7 @@ public class FragmentCompose extends FragmentBase {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean plain_only = prefs.getBoolean("plain_only", false);
String encrypt_method = prefs.getString("default_encrypt_method", "pgp");
boolean sign_default = prefs.getBoolean("sign_default", false);
boolean encrypt_default = prefs.getBoolean("encrypt_default", false);
boolean receipt_default = prefs.getBoolean("receipt_default", false);
@@ -2517,9 +2527,15 @@ public class FragmentCompose extends FragmentBase {
if (plain_only)
data.draft.plain_only = true;
if (encrypt_default)
data.draft.encrypt = EntityMessage.PGP_SIGNENCRYPT;
if ("s/mime".equals(encrypt_method))
data.draft.encrypt = EntityMessage.SMIME_SIGNENCRYPT;
else
data.draft.encrypt = EntityMessage.PGP_SIGNENCRYPT;
else if (sign_default)
data.draft.encrypt = EntityMessage.PGP_SIGNONLY;
if ("s/mime".equals(encrypt_method))
data.draft.encrypt = EntityMessage.SMIME_SIGNONLY;
else
data.draft.encrypt = EntityMessage.PGP_SIGNONLY;
if (receipt_default)
data.draft.receipt_request = true;
@@ -3957,7 +3973,6 @@ public class FragmentCompose extends FragmentBase {
View dview = LayoutInflater.from(getContext()).inflate(R.layout.dialog_certificate, null);
final RecyclerView rvCertificate = dview.findViewById(R.id.rvCertificate);
final Button btnImport = dview.findViewById(R.id.btnImport);
final ProgressBar pbWait = dview.findViewById(R.id.pbWait);
final Dialog dialog = new AlertDialog.Builder(getContext())
@@ -3969,6 +3984,12 @@ public class FragmentCompose extends FragmentBase {
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) {
@@ -3979,22 +4000,6 @@ public class FragmentCompose extends FragmentBase {
});
rvCertificate.setAdapter(adapter);
btnImport.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
Helper.openAdvanced(intent);
PackageManager pm = getContext().getPackageManager();
if (intent.resolveActivity(pm) == null)
ToastEx.makeText(getContext(), R.string.title_no_saf, Toast.LENGTH_LONG).show();
else
startActivityForResult(Helper.getChooser(getContext(), intent), 1);
}
});
btnImport.setEnabled(email != null);
rvCertificate.setVisibility(View.GONE);
pbWait.setVisibility(View.VISIBLE);
@@ -4010,54 +4015,6 @@ public class FragmentCompose extends FragmentBase {
return dialog;
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (resultCode == RESULT_OK && data != null) {
Uri uri = data.getData();
if (uri != null) {
Bundle args = new Bundle();
args.putParcelable("uri", uri);
new SimpleTask<Void>() {
@Override
protected Void onExecute(Context context, Bundle args) throws Throwable {
Uri uri = args.getParcelable("uri");
PemObject pem;
try (InputStream is = context.getContentResolver().openInputStream(uri)) {
pem = new PemReader(new InputStreamReader(is)).readPemObject();
}
ByteArrayInputStream bis = new ByteArrayInputStream(pem.getContent());
CertificateFactory fact = CertificateFactory.getInstance("X.509");
X509Certificate cert = (X509Certificate) fact.generateCertificate(bis);
String fingerprint = Helper.sha256(cert.getEncoded());
DB db = DB.getInstance(context);
EntityCertificate record = db.certificate().getCertificate(fingerprint, email);
if (record == null) {
record = new EntityCertificate();
record.fingerprint = Helper.sha256(cert.getEncoded());
record.email = email;
record.subject = cert.getSubjectX500Principal().getName(X500Principal.RFC2253);
record.setEncoded(cert.getEncoded());
record.id = db.certificate().insertCertificate(record);
}
// TODO: report exists
return null;
}
@Override
protected void onException(Bundle args, Throwable ex) {
Helper.unexpectedError(getParentFragmentManager(), ex);
}
}.execute(this, args, "compose:cert");
}
}
}
}
public static class FragmentDialogSend extends FragmentDialogBase {

View File

@@ -51,6 +51,7 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.widget.SwitchCompat;
import androidx.lifecycle.Lifecycle;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager;
import org.openintents.openpgp.util.OpenPgpApi;
@@ -62,6 +63,7 @@ import java.util.List;
public class FragmentOptionsPrivacy extends FragmentBase implements SharedPreferences.OnSharedPreferenceChangeListener {
private SwitchCompat swDisableTracking;
private SwitchCompat swDisplayHidden;
private Spinner spEncryptMethod;
private Spinner spOpenPgp;
private SwitchCompat swSign;
private SwitchCompat swEncrypt;
@@ -70,6 +72,7 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer
private Button btnBiometrics;
private Button btnPin;
private Spinner spBiometricsTimeout;
private Button btnManageCertificates;
private Button btnImportKey;
private TextView tvKeySize;
@@ -77,7 +80,7 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer
private final static String[] RESET_OPTIONS = new String[]{
"disable_tracking", "display_hidden",
"openpgp_provider", "sign_default", "encrypt_default", "auto_decrypt",
"default_encrypt_method", "openpgp_provider", "sign_default", "encrypt_default", "auto_decrypt",
"secure",
"biometrics", "pin", "biometrics_timeout"
};
@@ -94,6 +97,7 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer
swDisableTracking = view.findViewById(R.id.swDisableTracking);
swDisplayHidden = view.findViewById(R.id.swDisplayHidden);
spEncryptMethod = view.findViewById(R.id.spEncryptMethod);
spOpenPgp = view.findViewById(R.id.spOpenPgp);
swSign = view.findViewById(R.id.swSign);
swEncrypt = view.findViewById(R.id.swEncrypt);
@@ -102,6 +106,7 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer
btnBiometrics = view.findViewById(R.id.btnBiometrics);
btnPin = view.findViewById(R.id.btnPin);
spBiometricsTimeout = view.findViewById(R.id.spBiometricsTimeout);
btnManageCertificates = view.findViewById(R.id.btnManageCertificates);
btnImportKey = view.findViewById(R.id.btnImportKey);
tvKeySize = view.findViewById(R.id.tvKeySize);
@@ -136,6 +141,21 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer
}
});
spEncryptMethod.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (position == 1)
prefs.edit().putString("default_encrypt_method", "s/mime").apply();
else
onNothingSelected(parent);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
prefs.edit().remove("default_encrypt_method").apply();
}
});
spOpenPgp.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long id) {
@@ -225,6 +245,14 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer
}
});
btnManageCertificates.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext());
lbm.sendBroadcast(new Intent(ActivitySetup.ACTION_MANAGE_CERTIFICATES));
}
});
final Intent importKey = KeyChain.createInstallIntent();
btnImportKey.setEnabled(importKey.resolveActivity(getContext().getPackageManager()) != null);
btnImportKey.setOnClickListener(new View.OnClickListener() {
@@ -236,7 +264,7 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer
try {
int maxKeySize = javax.crypto.Cipher.getMaxAllowedKeyLength("AES");
tvKeySize.setText(getString(R.string.title_aes_key_size, maxKeySize));
tvKeySize.setText(getString(R.string.title_advanced_aes_key_size, maxKeySize));
} catch (NoSuchAlgorithmException ex) {
tvKeySize.setText(Helper.formatThrowable(ex));
}
@@ -290,6 +318,10 @@ public class FragmentOptionsPrivacy extends FragmentBase implements SharedPrefer
swDisableTracking.setChecked(prefs.getBoolean("disable_tracking", true));
swDisplayHidden.setChecked(prefs.getBoolean("display_hidden", false));
String encrypt_method = prefs.getString("default_encrypt_method", "pgp");
if ("s/mime".equals(encrypt_method))
spEncryptMethod.setSelection(1);
String provider = prefs.getString("openpgp_provider", "org.sufficientlysecure.keychain");
for (int pos = 0; pos < openPgpProvider.size(); pos++)
if (provider.equals(openPgpProvider.get(pos))) {