mirror of
https://github.com/M66B/FairEmail.git
synced 2026-04-12 03:54:28 +02:00
Export/import certificates
This commit is contained in:
@@ -549,6 +549,11 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
|
||||
for (EntityAnswer answer : db.answer().getAnswers(true))
|
||||
janswers.put(answer.toJSON());
|
||||
|
||||
// Certificates
|
||||
JSONArray jcertificates = new JSONArray();
|
||||
for (EntityCertificate certificate : db.certificate().getCertificates())
|
||||
jcertificates.put(certificate.toJSON());
|
||||
|
||||
// Settings
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
JSONArray jsettings = new JSONArray();
|
||||
@@ -576,6 +581,7 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
|
||||
JSONObject jexport = new JSONObject();
|
||||
jexport.put("accounts", jaccounts);
|
||||
jexport.put("answers", janswers);
|
||||
jexport.put("certificates", jcertificates);
|
||||
jexport.put("settings", jsettings);
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
@@ -880,6 +886,17 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
|
||||
db.account().updateAccount(account);
|
||||
}
|
||||
|
||||
JSONArray jcertificates = jimport.getJSONArray("certificates");
|
||||
for (int c = 0; c < jcertificates.length(); c++) {
|
||||
JSONObject jcertificate = (JSONObject) jcertificates.get(c);
|
||||
EntityCertificate certificate = EntityCertificate.fromJSON(jcertificate);
|
||||
EntityCertificate record = db.certificate().getCertificate(certificate.fingerprint, certificate.email);
|
||||
if (record == null) {
|
||||
db.certificate().insertCertificate(certificate);
|
||||
Log.i("Imported certificate=" + certificate.email);
|
||||
}
|
||||
}
|
||||
|
||||
// Settings
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
SharedPreferences.Editor editor = prefs.edit();
|
||||
@@ -1044,9 +1061,9 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
|
||||
}
|
||||
}
|
||||
|
||||
String fingerprint = Helper.getFingerprint(cert);
|
||||
List<String> emails = Helper.getAltSubjectName(cert);
|
||||
String subject = Helper.getSubject(cert);
|
||||
String fingerprint = EntityCertificate.getFingerprint(cert);
|
||||
List<String> emails = EntityCertificate.getAltSubjectName(cert);
|
||||
String subject = EntityCertificate.getSubject(cert);
|
||||
|
||||
DB db = DB.getInstance(context);
|
||||
for (String email : emails) {
|
||||
@@ -1056,7 +1073,7 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
|
||||
record.fingerprint = fingerprint;
|
||||
record.email = email;
|
||||
record.subject = subject;
|
||||
record.setEncoded(cert.getEncoded());
|
||||
record.setCertificate(cert);
|
||||
record.id = db.certificate().insertCertificate(record);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ import java.util.List;
|
||||
|
||||
@Dao
|
||||
public interface DaoCertificate {
|
||||
@Query("SELECT * FROM certificate")
|
||||
List<EntityCertificate> getCertificates();
|
||||
|
||||
@Query("SELECT * FROM certificate" +
|
||||
" ORDER BY email, subject")
|
||||
LiveData<List<EntityCertificate>> liveCertificates();
|
||||
|
||||
@@ -26,8 +26,24 @@ import androidx.room.Entity;
|
||||
import androidx.room.Index;
|
||||
import androidx.room.PrimaryKey;
|
||||
|
||||
import org.bouncycastle.asn1.x509.GeneralName;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateEncodingException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.CertificateParsingException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
@Entity(
|
||||
tableName = EntityCertificate.TABLE_NAME,
|
||||
foreignKeys = {
|
||||
@@ -50,14 +66,69 @@ public class EntityCertificate {
|
||||
@NonNull
|
||||
public String data;
|
||||
|
||||
void setEncoded(byte[] encoded) {
|
||||
private void setEncoded(byte[] encoded) {
|
||||
this.data = Base64.encodeToString(encoded, Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
byte[] getEncoded() {
|
||||
private byte[] getEncoded() {
|
||||
return Base64.decode(this.data, Base64.NO_WRAP);
|
||||
}
|
||||
|
||||
void setCertificate(X509Certificate certificate) throws CertificateEncodingException {
|
||||
setEncoded(certificate.getEncoded());
|
||||
}
|
||||
|
||||
X509Certificate getCertificate() throws CertificateException {
|
||||
return (X509Certificate) CertificateFactory.getInstance("X.509")
|
||||
.generateCertificate(new ByteArrayInputStream(getEncoded()));
|
||||
}
|
||||
|
||||
static String getFingerprint(X509Certificate certificate) throws CertificateEncodingException, NoSuchAlgorithmException {
|
||||
return Helper.sha256(certificate.getEncoded());
|
||||
}
|
||||
|
||||
static String getSubject(X509Certificate certificate) {
|
||||
return certificate.getSubjectX500Principal().getName(X500Principal.RFC2253);
|
||||
}
|
||||
|
||||
static List<String> getAltSubjectName(X509Certificate certificate) {
|
||||
List<String> result = new ArrayList<>();
|
||||
try {
|
||||
Collection<List<?>> altNames = certificate.getSubjectAlternativeNames();
|
||||
if (altNames != null)
|
||||
for (List altName : altNames)
|
||||
if (altName.get(0).equals(GeneralName.rfc822Name))
|
||||
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 result;
|
||||
}
|
||||
|
||||
public JSONObject toJSON() throws JSONException {
|
||||
JSONObject json = new JSONObject();
|
||||
json.put("id", id);
|
||||
json.put("email", email);
|
||||
json.put("data", data);
|
||||
return json;
|
||||
}
|
||||
|
||||
public static EntityCertificate fromJSON(JSONObject json) throws JSONException, CertificateException, NoSuchAlgorithmException {
|
||||
EntityCertificate certificate = new EntityCertificate();
|
||||
certificate.id = json.getLong("id");
|
||||
certificate.email = json.getString("email");
|
||||
certificate.data = json.getString("data");
|
||||
|
||||
X509Certificate cert = certificate.getCertificate();
|
||||
certificate.fingerprint = getFingerprint(cert);
|
||||
certificate.subject = getSubject(cert);
|
||||
|
||||
return certificate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof EntityCertificate) {
|
||||
|
||||
@@ -126,7 +126,6 @@ import org.openintents.openpgp.util.OpenPgpApi;
|
||||
import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -136,7 +135,6 @@ import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
@@ -2029,11 +2027,8 @@ public class FragmentCompose extends FragmentBase {
|
||||
List<EntityCertificate> acertificates = db.certificate().getCertificateByEmail(email);
|
||||
if (acertificates == null || acertificates.size() == 0)
|
||||
throw new IllegalArgumentException(context.getString(R.string.title_certificate_missing, email), new IllegalStateException());
|
||||
for (EntityCertificate acertificate : acertificates) {
|
||||
X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X.509")
|
||||
.generateCertificate(new ByteArrayInputStream(acertificate.getEncoded()));
|
||||
certs.add(cert);
|
||||
}
|
||||
for (EntityCertificate acertificate : acertificates)
|
||||
certs.add(acertificate.getCertificate());
|
||||
}
|
||||
|
||||
// Build signature
|
||||
|
||||
@@ -4420,8 +4420,8 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
|
||||
try {
|
||||
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
|
||||
boolean known = true;
|
||||
String fingerprint = Helper.getFingerprint(cert);
|
||||
List<String> emails = Helper.getAltSubjectName(cert);
|
||||
String fingerprint = EntityCertificate.getFingerprint(cert);
|
||||
List<String> emails = EntityCertificate.getAltSubjectName(cert);
|
||||
for (String email : emails) {
|
||||
EntityCertificate record = db.certificate().getCertificate(fingerprint, email);
|
||||
if (record == null)
|
||||
@@ -4558,7 +4558,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
|
||||
boolean known = args.getBoolean("known");
|
||||
|
||||
boolean match = false;
|
||||
List<String> emails = (cert == null ? Collections.emptyList() : Helper.getAltSubjectName(cert));
|
||||
List<String> emails = (cert == null ? Collections.emptyList() : EntityCertificate.getAltSubjectName(cert));
|
||||
for (String email : emails)
|
||||
if (Objects.equals(sender, email)) {
|
||||
match = true;
|
||||
@@ -4580,7 +4580,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
|
||||
tvSender.setText(sender);
|
||||
tvEmail.setText(TextUtils.join(",", emails));
|
||||
tvEmailInvalid.setVisibility(match ? View.GONE : View.VISIBLE);
|
||||
tvSubject.setText(Helper.getSubject(cert));
|
||||
tvSubject.setText(EntityCertificate.getSubject(cert));
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext())
|
||||
.setView(dview)
|
||||
@@ -4607,16 +4607,19 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
|
||||
if (message == null)
|
||||
return null;
|
||||
|
||||
String fingerprint = Helper.getFingerprint(cert);
|
||||
List<String> emails = Helper.getAltSubjectName(cert);
|
||||
String subject = Helper.getSubject(cert);
|
||||
String fingerprint = EntityCertificate.getFingerprint(cert);
|
||||
List<String> emails = EntityCertificate.getAltSubjectName(cert);
|
||||
String subject = EntityCertificate.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);
|
||||
EntityCertificate record = db.certificate().getCertificate(fingerprint, email);
|
||||
if (record == null) {
|
||||
record = new EntityCertificate();
|
||||
record.fingerprint = fingerprint;
|
||||
record.email = email;
|
||||
record.subject = subject;
|
||||
record.setCertificate(cert);
|
||||
record.id = db.certificate().insertCertificate(record);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
@@ -84,8 +84,6 @@ import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.bottomnavigation.BottomNavigationView;
|
||||
|
||||
import org.bouncycastle.asn1.x509.GeneralName;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -97,14 +95,10 @@ 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;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -118,8 +112,6 @@ import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
|
||||
import static androidx.browser.customtabs.CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION;
|
||||
|
||||
@@ -816,31 +808,6 @@ 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 List<String> getAltSubjectName(X509Certificate certificate) {
|
||||
List<String> result = new ArrayList<>();
|
||||
try {
|
||||
Collection<List<?>> altNames = certificate.getSubjectAlternativeNames();
|
||||
if (altNames != null)
|
||||
for (List altName : altNames)
|
||||
if (altName.get(0).equals(GeneralName.rfc822Name))
|
||||
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 result;
|
||||
}
|
||||
|
||||
static void selectKeyAlias(final Activity activity, final String email, final IKeyAlias intf) {
|
||||
final Context context = activity.getApplicationContext();
|
||||
final Handler handler = new Handler();
|
||||
|
||||
Reference in New Issue
Block a user