Simplify importing S/MIME certificates

pull/194/merge
M66B 3 years ago
parent 76d833be02
commit 0136bfe0a5

@ -19,6 +19,8 @@ package eu.faircode.email;
Copyright 2018-2021 by Marcel Bokhorst (M66B) Copyright 2018-2021 by Marcel Bokhorst (M66B)
*/ */
import static android.app.Activity.RESULT_OK;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
@ -26,6 +28,7 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.provider.Settings; import android.provider.Settings;
@ -48,6 +51,7 @@ import android.widget.CompoundButton;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.Spinner; import android.widget.Spinner;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
@ -62,6 +66,8 @@ import org.openintents.openpgp.IOpenPgpService2;
import org.openintents.openpgp.util.OpenPgpApi; import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection; import org.openintents.openpgp.util.OpenPgpServiceConnection;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.Principal; import java.security.Principal;
@ -103,6 +109,8 @@ public class FragmentOptionsEncryption extends FragmentBase implements SharedPre
private OpenPgpServiceConnection pgpService; private OpenPgpServiceConnection pgpService;
private List<String> openPgpProvider = new ArrayList<>(); private List<String> openPgpProvider = new ArrayList<>();
static final int REQUEST_IMPORT_CERTIFICATE = 1;
private final static String[] RESET_OPTIONS = new String[]{ private final static String[] RESET_OPTIONS = new String[]{
"sign_default", "encrypt_default", "auto_decrypt", "auto_undecrypt", "sign_default", "encrypt_default", "auto_decrypt", "auto_undecrypt",
"openpgp_provider", "autocrypt", "autocrypt_mutual", "encrypt_subject", "openpgp_provider", "autocrypt", "autocrypt_mutual", "encrypt_subject",
@ -314,11 +322,18 @@ public class FragmentOptionsEncryption extends FragmentBase implements SharedPre
final Intent importKey = KeyChain.createInstallIntent(); final Intent importKey = KeyChain.createInstallIntent();
btnImportKey.setEnabled(importKey.resolveActivity(pm) != null); // system whitelisted btnImportKey.setEnabled(importKey.resolveActivity(pm) != null); // system whitelisted
btnImportKey.setVisibility(Build.VERSION.SDK_INT < Build.VERSION_CODES.R ? View.VISIBLE : View.GONE);
btnImportKey.setOnClickListener(new View.OnClickListener() { btnImportKey.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
startActivity(importKey); Context context = v.getContext();
PackageManager pm = context.getPackageManager();
Intent open = new Intent(Intent.ACTION_GET_CONTENT);
open.addCategory(Intent.CATEGORY_OPENABLE);
open.setType("*/*");
if (open.resolveActivity(pm) == null) // system whitelisted
ToastEx.makeText(context, R.string.title_no_saf, Toast.LENGTH_LONG).show();
else
startActivityForResult(Helper.getChooser(context, open), REQUEST_IMPORT_CERTIFICATE);
} }
}); });
@ -472,6 +487,61 @@ public class FragmentOptionsEncryption extends FragmentBase implements SharedPre
super.onDestroyView(); super.onDestroyView();
} }
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
try {
switch (requestCode) {
case REQUEST_IMPORT_CERTIFICATE:
if (resultCode == RESULT_OK && data != null)
handleImportCertificate(data);
break;
}
} catch (Throwable ex) {
Log.e(ex);
}
}
private void handleImportCertificate(Intent data) {
Uri uri = data.getData();
if (uri != null) {
Bundle args = new Bundle();
args.putParcelable("uri", uri);
new SimpleTask<byte[]>() {
@Override
protected byte[] onExecute(Context context, Bundle args) throws Throwable {
Uri uri = args.getParcelable("uri");
Log.i("Import certificate uri=" + uri);
if (uri == null)
throw new FileNotFoundException();
try (InputStream is = context.getContentResolver().openInputStream(uri)) {
return Helper.readBytes(is);
}
}
@Override
protected void onExecuted(Bundle args, byte[] data) {
Intent intent = KeyChain.createInstallIntent();
intent.putExtra(KeyChain.EXTRA_PKCS12, data);
startActivity(intent);
}
@Override
protected void onException(Bundle args, Throwable ex) {
boolean expected =
(ex instanceof IllegalArgumentException ||
ex instanceof FileNotFoundException ||
ex instanceof SecurityException);
Log.unexpectedError(getParentFragmentManager(), ex, !expected);
}
}.execute(this, args, "setup:cert");
}
}
@Override @Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED))

@ -1653,6 +1653,14 @@ public class Helper {
} }
} }
static byte[] readBytes(InputStream is) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream(Math.max(BUFFER_SIZE, is.available()));
byte[] buffer = new byte[BUFFER_SIZE];
for (int len = is.read(buffer); len != -1; len = is.read(buffer))
os.write(buffer, 0, len);
return os.toByteArray();
}
static void copy(File src, File dst) throws IOException { static void copy(File src, File dst) throws IOException {
try (InputStream is = new FileInputStream(src)) { try (InputStream is = new FileInputStream(src)) {
try (OutputStream os = new FileOutputStream(dst)) { try (OutputStream os = new FileOutputStream(dst)) {

Loading…
Cancel
Save