Store intermediate certificates

This commit is contained in:
M66B 2020-01-30 14:19:15 +01:00
parent ff08ec5667
commit 8fb2409e36
7 changed files with 2190 additions and 7 deletions

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,7 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
@ -57,6 +58,7 @@ public class AdapterCertificate extends RecyclerView.Adapter<AdapterCertificate.
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnLongClickListener {
private View view;
private TextView tvEmail;
private ImageView ivIntermediate;
private TextView tvSubject;
private TextView tvAfter;
private TextView tvBefore;
@ -69,6 +71,7 @@ public class AdapterCertificate extends RecyclerView.Adapter<AdapterCertificate.
view = itemView.findViewById(R.id.clItem);
tvEmail = itemView.findViewById(R.id.tvEmail);
ivIntermediate = itemView.findViewById(R.id.ivIntermediate);
tvSubject = itemView.findViewById(R.id.tvSubject);
tvAfter = itemView.findViewById(R.id.tvAfter);
tvBefore = itemView.findViewById(R.id.tvBefore);
@ -143,6 +146,7 @@ public class AdapterCertificate extends RecyclerView.Adapter<AdapterCertificate.
private void bindTo(EntityCertificate certificate) {
tvEmail.setText(certificate.email);
ivIntermediate.setVisibility(certificate.intermediate ? View.VISIBLE : View.INVISIBLE);
tvSubject.setText(certificate.subject);
tvAfter.setText(certificate.after == null ? null : TF.format(certificate.after));
tvBefore.setText(certificate.before == null ? null : TF.format(certificate.before));

View File

@ -60,7 +60,7 @@ import io.requery.android.database.sqlite.SQLiteDatabase;
// https://developer.android.com/topic/libraries/architecture/room.html
@Database(
version = 135,
version = 136,
entities = {
EntityIdentity.class,
EntityAccount.class,
@ -1321,6 +1321,13 @@ public abstract class DB extends RoomDatabase {
db.execSQL("CREATE VIEW IF NOT EXISTS `identity_view` AS " + TupleIdentityView.query);
db.execSQL("CREATE VIEW IF NOT EXISTS `folder_view` AS " + TupleFolderView.query);
}
})
.addMigrations(new Migration(135, 136) {
@Override
public void migrate(@NonNull SupportSQLiteDatabase db) {
Log.i("DB migration from version " + startVersion + " to " + endVersion);
db.execSQL("ALTER TABLE `certificate` ADD COLUMN `intermediate` INTEGER NOT NULL DEFAULT 0");
}
});
}

View File

@ -32,7 +32,7 @@ public interface DaoCertificate {
List<EntityCertificate> getCertificates();
@Query("SELECT * FROM certificate" +
" ORDER BY email, subject")
" ORDER BY intermediate, email, subject")
LiveData<List<EntityCertificate>> liveCertificates();
@Query("SELECT * FROM certificate" +
@ -44,6 +44,10 @@ public interface DaoCertificate {
" WHERE email = :email COLLATE NOCASE")
List<EntityCertificate> getCertificateByEmail(String email);
@Query("SELECT * FROM certificate" +
" WHERE intermediate")
List<EntityCertificate> getIntermediateCertificate();
@Insert
long insertCertificate(EntityCertificate certificate);

View File

@ -62,6 +62,8 @@ public class EntityCertificate {
@NonNull
public String fingerprint;
@NonNull
public boolean intermediate;
@NonNull
public String email;
public String subject;
public Long after;
@ -70,8 +72,13 @@ public class EntityCertificate {
public String data;
static EntityCertificate from(X509Certificate certificate, String email) throws CertificateEncodingException, NoSuchAlgorithmException {
return from(certificate, false, email);
}
static EntityCertificate from(X509Certificate certificate, boolean intermediate, String email) throws CertificateEncodingException, NoSuchAlgorithmException {
EntityCertificate record = new EntityCertificate();
record.fingerprint = getFingerprint(certificate);
record.intermediate = intermediate;
record.email = email;
record.subject = getSubject(certificate);
@ -131,6 +138,7 @@ public class EntityCertificate {
public JSONObject toJSON() throws JSONException {
JSONObject json = new JSONObject();
json.put("id", id);
json.put("intermediate", intermediate);
json.put("email", email);
json.put("data", data);
return json;
@ -139,6 +147,7 @@ public class EntityCertificate {
public static EntityCertificate fromJSON(JSONObject json) throws JSONException, CertificateException, NoSuchAlgorithmException {
EntityCertificate certificate = new EntityCertificate();
certificate.id = json.getLong("id");
certificate.intermediate = json.optBoolean("intermediate");
certificate.email = json.getString("email");
certificate.data = json.getString("data");
@ -160,9 +169,9 @@ public class EntityCertificate {
if (obj instanceof EntityCertificate) {
EntityCertificate other = (EntityCertificate) obj;
return (this.fingerprint.equals(other.fingerprint) &&
this.intermediate == other.intermediate &&
Objects.equals(this.email, other.email) &&
Objects.equals(this.subject, other.subject) &&
this.data.equals(other.data));
Objects.equals(this.subject, other.subject));
} else
return false;
}

View File

@ -4835,8 +4835,43 @@ public class FragmentMessages extends FragmentBase implements SharedPreferences.
X509CertSelector target = new X509CertSelector();
target.setCertificate(cert);
// Load/store intermediate certificates
List<X509Certificate> local = new ArrayList<>();
try {
List<EntityCertificate> ecs = db.certificate().getIntermediateCertificate();
for (EntityCertificate ec : ecs)
local.add(ec.getCertificate());
for (X509Certificate c : certs) {
boolean[] usage = c.getKeyUsage();
boolean root = (usage != null && usage[5]);
if (root) {
boolean found = false;
String issuer = (c.getIssuerDN() == null ? "" : c.getIssuerDN().getName());
EntityCertificate record = EntityCertificate.from(c, true, issuer);
for (EntityCertificate ec : ecs)
if (ec.fingerprint.equals(record.fingerprint)) {
found = true;
break;
}
if (!found) {
Log.i("Storing certificate subject=" + record.subject);
local.add(record.getCertificate());
db.certificate().insertCertificate(record);
}
}
}
} catch (Throwable ex) {
Log.e(ex);
local = certs;
}
// Intermediate certificates
Log.i("Intermediate certificates=" + local.size());
PKIXBuilderParameters params = new PKIXBuilderParameters(ks, target);
CertStoreParameters intermediates = new CollectionCertStoreParameters(certs);
CertStoreParameters intermediates = new CollectionCertStoreParameters(local);
params.addCertStore(CertStore.getInstance("Collection", intermediates));
params.setRevocationEnabled(false);
params.setDate(signingTime);

View File

@ -18,10 +18,25 @@
android:layout_height="wrap_content"
android:text="test@example.com"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintEnd_toStartOf="@+id/ivIntermediate"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/ivIntermediate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/baseline_security_24" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="tvEmail,ivIntermediate" />
<TextView
android:id="@+id/tvSubject"
android:layout_width="0dp"
@ -30,7 +45,7 @@
android:textAppearance="@android:style/TextAppearance.Small"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tvEmail" />
app:layout_constraintTop_toBottomOf="@+id/barrier" />
<TextView
android:id="@+id/tvAfter"