Export/import certificates

pull/169/head
M66B 5 years ago
parent 45e621d3ae
commit 3ade2e002f

@ -549,6 +549,11 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
for (EntityAnswer answer : db.answer().getAnswers(true)) for (EntityAnswer answer : db.answer().getAnswers(true))
janswers.put(answer.toJSON()); janswers.put(answer.toJSON());
// Certificates
JSONArray jcertificates = new JSONArray();
for (EntityCertificate certificate : db.certificate().getCertificates())
jcertificates.put(certificate.toJSON());
// Settings // Settings
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
JSONArray jsettings = new JSONArray(); JSONArray jsettings = new JSONArray();
@ -576,6 +581,7 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
JSONObject jexport = new JSONObject(); JSONObject jexport = new JSONObject();
jexport.put("accounts", jaccounts); jexport.put("accounts", jaccounts);
jexport.put("answers", janswers); jexport.put("answers", janswers);
jexport.put("certificates", jcertificates);
jexport.put("settings", jsettings); jexport.put("settings", jsettings);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 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); 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 // Settings
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
SharedPreferences.Editor editor = prefs.edit(); SharedPreferences.Editor editor = prefs.edit();
@ -1044,9 +1061,9 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
} }
} }
String fingerprint = Helper.getFingerprint(cert); String fingerprint = EntityCertificate.getFingerprint(cert);
List<String> emails = Helper.getAltSubjectName(cert); List<String> emails = EntityCertificate.getAltSubjectName(cert);
String subject = Helper.getSubject(cert); String subject = EntityCertificate.getSubject(cert);
DB db = DB.getInstance(context); DB db = DB.getInstance(context);
for (String email : emails) { for (String email : emails) {
@ -1056,7 +1073,7 @@ public class ActivitySetup extends ActivityBase implements FragmentManager.OnBac
record.fingerprint = fingerprint; record.fingerprint = fingerprint;
record.email = email; record.email = email;
record.subject = subject; record.subject = subject;
record.setEncoded(cert.getEncoded()); record.setCertificate(cert);
record.id = db.certificate().insertCertificate(record); record.id = db.certificate().insertCertificate(record);
} }
} }

@ -28,6 +28,9 @@ import java.util.List;
@Dao @Dao
public interface DaoCertificate { public interface DaoCertificate {
@Query("SELECT * FROM certificate")
List<EntityCertificate> getCertificates();
@Query("SELECT * FROM certificate" + @Query("SELECT * FROM certificate" +
" ORDER BY email, subject") " ORDER BY email, subject")
LiveData<List<EntityCertificate>> liveCertificates(); LiveData<List<EntityCertificate>> liveCertificates();

@ -26,8 +26,24 @@ import androidx.room.Entity;
import androidx.room.Index; import androidx.room.Index;
import androidx.room.PrimaryKey; 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 java.util.Objects;
import javax.security.auth.x500.X500Principal;
@Entity( @Entity(
tableName = EntityCertificate.TABLE_NAME, tableName = EntityCertificate.TABLE_NAME,
foreignKeys = { foreignKeys = {
@ -50,14 +66,69 @@ public class EntityCertificate {
@NonNull @NonNull
public String data; public String data;
void setEncoded(byte[] encoded) { private void setEncoded(byte[] encoded) {
this.data = Base64.encodeToString(encoded, Base64.NO_WRAP); this.data = Base64.encodeToString(encoded, Base64.NO_WRAP);
} }
byte[] getEncoded() { private byte[] getEncoded() {
return Base64.decode(this.data, Base64.NO_WRAP); 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 @Override
public boolean equals(Object obj) { public boolean equals(Object obj) {
if (obj instanceof EntityCertificate) { if (obj instanceof EntityCertificate) {

@ -126,7 +126,6 @@ import org.openintents.openpgp.util.OpenPgpApi;
import org.openintents.openpgp.util.OpenPgpServiceConnection; import org.openintents.openpgp.util.OpenPgpServiceConnection;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -136,7 +135,6 @@ import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.security.PrivateKey; import java.security.PrivateKey;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
@ -2029,11 +2027,8 @@ public class FragmentCompose extends FragmentBase {
List<EntityCertificate> acertificates = db.certificate().getCertificateByEmail(email); List<EntityCertificate> acertificates = db.certificate().getCertificateByEmail(email);
if (acertificates == null || acertificates.size() == 0) if (acertificates == null || acertificates.size() == 0)
throw new IllegalArgumentException(context.getString(R.string.title_certificate_missing, email), new IllegalStateException()); throw new IllegalArgumentException(context.getString(R.string.title_certificate_missing, email), new IllegalStateException());
for (EntityCertificate acertificate : acertificates) { for (EntityCertificate acertificate : acertificates)
X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X.509") certs.add(acertificate.getCertificate());
.generateCertificate(new ByteArrayInputStream(acertificate.getEncoded()));
certs.add(cert);
}
} }
// Build signature // Build signature

@ -4420,8 +4420,8 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
try { try {
if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) { if (signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))) {
boolean known = true; boolean known = true;
String fingerprint = Helper.getFingerprint(cert); String fingerprint = EntityCertificate.getFingerprint(cert);
List<String> emails = Helper.getAltSubjectName(cert); List<String> emails = EntityCertificate.getAltSubjectName(cert);
for (String email : emails) { for (String email : emails) {
EntityCertificate record = db.certificate().getCertificate(fingerprint, email); EntityCertificate record = db.certificate().getCertificate(fingerprint, email);
if (record == null) if (record == null)
@ -4558,7 +4558,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
boolean known = args.getBoolean("known"); boolean known = args.getBoolean("known");
boolean match = false; 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) for (String email : emails)
if (Objects.equals(sender, email)) { if (Objects.equals(sender, email)) {
match = true; match = true;
@ -4580,7 +4580,7 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
tvSender.setText(sender); tvSender.setText(sender);
tvEmail.setText(TextUtils.join(",", emails)); tvEmail.setText(TextUtils.join(",", emails));
tvEmailInvalid.setVisibility(match ? View.GONE : View.VISIBLE); tvEmailInvalid.setVisibility(match ? View.GONE : View.VISIBLE);
tvSubject.setText(Helper.getSubject(cert)); tvSubject.setText(EntityCertificate.getSubject(cert));
AlertDialog.Builder builder = new AlertDialog.Builder(getContext()) AlertDialog.Builder builder = new AlertDialog.Builder(getContext())
.setView(dview) .setView(dview)
@ -4607,16 +4607,19 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
if (message == null) if (message == null)
return null; return null;
String fingerprint = Helper.getFingerprint(cert); String fingerprint = EntityCertificate.getFingerprint(cert);
List<String> emails = Helper.getAltSubjectName(cert); List<String> emails = EntityCertificate.getAltSubjectName(cert);
String subject = Helper.getSubject(cert); String subject = EntityCertificate.getSubject(cert);
for (String email : emails) { for (String email : emails) {
EntityCertificate record = new EntityCertificate(); EntityCertificate record = db.certificate().getCertificate(fingerprint, email);
record.fingerprint = fingerprint; if (record == null) {
record.email = email; record = new EntityCertificate();
record.subject = subject; record.fingerprint = fingerprint;
record.setEncoded(encoded); record.email = email;
record.id = db.certificate().insertCertificate(record); record.subject = subject;
record.setCertificate(cert);
record.id = db.certificate().insertCertificate(record);
}
} }
return null; return null;

@ -84,8 +84,6 @@ import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomnavigation.BottomNavigationView;
import org.bouncycastle.asn1.x509.GeneralName;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -97,14 +95,10 @@ import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; 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.DateFormat;
import java.text.DecimalFormat; import java.text.DecimalFormat;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -118,8 +112,6 @@ import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import javax.security.auth.x500.X500Principal;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND; import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static androidx.browser.customtabs.CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION; import static androidx.browser.customtabs.CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION;
@ -816,31 +808,6 @@ public class Helper {
prefs.edit().remove("last_authentication").apply(); 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) { static void selectKeyAlias(final Activity activity, final String email, final IKeyAlias intf) {
final Context context = activity.getApplicationContext(); final Context context = activity.getApplicationContext();
final Handler handler = new Handler(); final Handler handler = new Handler();

Loading…
Cancel
Save