From f46cbb38a92ff5281a974ecc0932ba5459c7334e Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Wed, 23 Dec 2015 19:18:53 +0100 Subject: show certificate information --- .../crypto/axolotl/AxolotlService.java | 4 ++ .../crypto/axolotl/SQLiteAxolotlStore.java | 4 ++ .../conversations/persistance/DatabaseBackend.java | 39 ++++++++++++++--- .../conversations/ui/ContactDetailsActivity.java | 44 ++++++++++++++++++- .../conversations/ui/EditAccountActivity.java | 2 +- .../siacs/conversations/ui/TrustKeysActivity.java | 2 + .../eu/siacs/conversations/ui/XmppActivity.java | 10 +++-- .../eu/siacs/conversations/utils/CryptoHelper.java | 50 ++++++++++++++++++++++ 8 files changed, 145 insertions(+), 10 deletions(-) (limited to 'src/main/java/eu/siacs') diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index ad9b8be3..31b23e3c 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -575,6 +575,10 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded { return axolotlStore.getFingerprintTrust(fingerprint); } + public X509Certificate getFingerprintCertificate(String fingerprint) { + return axolotlStore.getFingerprintCertificate(fingerprint); + } + public void setFingerprintTrust(String fingerprint, XmppAxolotlSession.Trust trust) { axolotlStore.setFingerprintTrust(fingerprint, trust); } diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java index 788a391d..3c8cb3c1 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java @@ -219,6 +219,10 @@ public class SQLiteAxolotlStore implements AxolotlStore { mXmppConnectionService.databaseBackend.setIdentityKeyCertificate(account, fingerprint, x509Certificate); } + public X509Certificate getFingerprintCertificate(String fingerprint) { + return mXmppConnectionService.databaseBackend.getIdentityKeyCertifcate(account, fingerprint); + } + public Set getContactKeysWithTrust(String bareJid, XmppAxolotlSession.Trust trust) { return mXmppConnectionService.databaseBackend.loadIdentityKeys(account, bareJid, trust); } diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java index 4882d72b..3077c488 100644 --- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java +++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java @@ -15,13 +15,15 @@ import org.whispersystems.libaxolotl.AxolotlAddress; import org.whispersystems.libaxolotl.IdentityKey; import org.whispersystems.libaxolotl.IdentityKeyPair; import org.whispersystems.libaxolotl.InvalidKeyException; -import org.whispersystems.libaxolotl.state.AxolotlStore; import org.whispersystems.libaxolotl.state.PreKeyRecord; import org.whispersystems.libaxolotl.state.SessionRecord; import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HashSet; @@ -600,16 +602,16 @@ public class DatabaseBackend extends SQLiteOpenHelper { db.delete(Message.TABLENAME, Message.CONVERSATION + "=?", args); } - public Pair getLastMessageReceived(Account account) { + public Pair getLastMessageReceived(Account account) { SQLiteDatabase db = this.getReadableDatabase(); String sql = "select messages.timeSent,messages.serverMsgId from accounts join conversations on accounts.uuid=conversations.accountUuid join messages on conversations.uuid=messages.conversationUuid where accounts.uuid=? and (messages.status=0 or messages.carbon=1 or messages.serverMsgId not null) order by messages.timesent desc limit 1"; String[] args = {account.getUuid()}; Cursor cursor = db.rawQuery(sql, args); - if (cursor.getCount() ==0) { + if (cursor.getCount() == 0) { return null; } else { cursor.moveToFirst(); - return new Pair<>(cursor.getLong(0),cursor.getString(1)); + return new Pair<>(cursor.getLong(0), cursor.getString(1)); } } @@ -1072,11 +1074,38 @@ public class DatabaseBackend extends SQLiteOpenHelper { + SQLiteAxolotlStore.FINGERPRINT + " = ? ", selectionArgs) == 1; } catch (CertificateEncodingException e) { - Log.d(Config.LOGTAG,"could not encode certificate"); + Log.d(Config.LOGTAG, "could not encode certificate"); return false; } } + public X509Certificate getIdentityKeyCertifcate(Account account, String fingerprint) { + SQLiteDatabase db = this.getReadableDatabase(); + String[] selectionArgs = { + account.getUuid(), + fingerprint + }; + String[] colums = {SQLiteAxolotlStore.CERTIFICATE}; + String selection = SQLiteAxolotlStore.ACCOUNT + " = ? AND " + SQLiteAxolotlStore.FINGERPRINT + " = ? "; + Cursor cursor = db.query(SQLiteAxolotlStore.IDENTITIES_TABLENAME, colums, selection, selectionArgs, null, null, null); + if (cursor.getCount() < 1) { + return null; + } else { + cursor.moveToFirst(); + byte[] certificate = cursor.getBlob(cursor.getColumnIndex(SQLiteAxolotlStore.CERTIFICATE)); + if (certificate == null || certificate.length == 0) { + return null; + } + try { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + return (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(certificate)); + } catch (CertificateException e) { + Log.d(Config.LOGTAG,"certificate exception "+e.getMessage()); + return null; + } + } + } + public void storeIdentityKey(Account account, String name, IdentityKey identityKey) { storeIdentityKey(account, name, false, identityKey.getFingerprint().replaceAll("\\s", ""), Base64.encodeToString(identityKey.serialize(), Base64.DEFAULT)); } diff --git a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java index 10bdaab1..5143190f 100644 --- a/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java @@ -14,6 +14,7 @@ import android.provider.ContactsContract; import android.provider.ContactsContract.CommonDataKinds; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Intents; +import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; @@ -30,12 +31,14 @@ import android.widget.TextView; import org.openintents.openpgp.util.OpenPgpUtils; +import java.security.cert.X509Certificate; import java.util.List; import eu.siacs.conversations.Config; import eu.siacs.conversations.R; import eu.siacs.conversations.crypto.PgpEngine; import eu.siacs.conversations.crypto.axolotl.AxolotlService; +import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession; import eu.siacs.conversations.entities.Account; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.ListItem; @@ -394,7 +397,12 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd } for (final String fingerprint : contact.getAccount().getAxolotlService().getFingerprintsForContact(contact)) { boolean highlight = fingerprint.equals(messageFingerprint); - hasKeys |= addFingerprintRow(keys, contact.getAccount(), fingerprint, highlight); + hasKeys |= addFingerprintRow(keys, contact.getAccount(), fingerprint, highlight, new OnClickListener() { + @Override + public void onClick(View v) { + onOmemoKeyClicked(contact.getAccount(), fingerprint); + } + }); } if (contact.getPgpKeyId() != 0) { hasKeys = true; @@ -446,6 +454,40 @@ public class ContactDetailsActivity extends XmppActivity implements OnAccountUpd } } + private void onOmemoKeyClicked(Account account, String fingerprint) { + Log.d(Config.LOGTAG,"on omemo key clicked"); + final XmppAxolotlSession.Trust trust = account.getAxolotlService().getFingerprintTrust(fingerprint); + if (trust != null) { + X509Certificate x509Certificate = account.getAxolotlService().getFingerprintCertificate(fingerprint); + if (x509Certificate != null) { + Log.d(Config.LOGTAG, "certificate for fingerprint " + fingerprint + " was not null"); + showCertificateInformationDialog(CryptoHelper.extractCertificateInformation(x509Certificate)); + } + } + } + + private void showCertificateInformationDialog(Bundle bundle) { + View view = getLayoutInflater().inflate(R.layout.certificate_information, null); + final String not_available = getString(R.string.certicate_info_not_available); + TextView subject_cn = (TextView) view.findViewById(R.id.subject_cn); + TextView subject_o = (TextView) view.findViewById(R.id.subject_o); + TextView issuer_cn = (TextView) view.findViewById(R.id.issuer_cn); + TextView issuer_o = (TextView) view.findViewById(R.id.issuer_o); + TextView sha1 = (TextView) view.findViewById(R.id.sha1); + + subject_cn.setText(bundle.getString("subject_cn", not_available)); + subject_o.setText(bundle.getString("subject_o", not_available)); + issuer_cn.setText(bundle.getString("issuer_cn", not_available)); + issuer_o.setText(bundle.getString("issuer_o", not_available)); + sha1.setText(bundle.getString("sha1", not_available)); + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.certificate_information); + builder.setView(view); + builder.setPositiveButton(R.string.ok, null); + builder.create().show(); + } + protected void confirmToDeleteFingerprint(final String fingerprint) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.delete_fingerprint); diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java index a6c45996..31d26b23 100644 --- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java @@ -710,7 +710,7 @@ public class EditAccountActivity extends XmppActivity implements OnAccountUpdate continue; } boolean highlight = fingerprint.equals(messageFingerprint); - hasKeys |= addFingerprintRow(keys, mAccount, fingerprint, highlight); + hasKeys |= addFingerprintRow(keys, mAccount, fingerprint, highlight, null); } if (hasKeys) { keysCard.setVisibility(View.VISIBLE); diff --git a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java index 4bd0d42d..2e6d3246 100644 --- a/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java @@ -126,6 +126,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate // own fingerprints have no impact on locked status. } }, + null, null ); } @@ -140,6 +141,7 @@ public class TrustKeysActivity extends XmppActivity implements OnKeyStatusUpdate lockOrUnlockAsNeeded(); } }, + null, null ); } diff --git a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java index 4cb93fde..b372692d 100644 --- a/src/main/java/eu/siacs/conversations/ui/XmppActivity.java +++ b/src/main/java/eu/siacs/conversations/ui/XmppActivity.java @@ -648,7 +648,7 @@ public abstract class XmppActivity extends Activity { builder.create().show(); } - protected boolean addFingerprintRow(LinearLayout keys, final Account account, final String fingerprint, boolean highlight) { + protected boolean addFingerprintRow(LinearLayout keys, final Account account, final String fingerprint, boolean highlight, View.OnClickListener onKeyClickedListener) { final XmppAxolotlSession.Trust trust = account.getAxolotlService() .getFingerprintTrust(fingerprint); if (trust == null) { @@ -670,7 +670,8 @@ public abstract class XmppActivity extends Activity { XmppAxolotlSession.Trust.UNTRUSTED); v.setEnabled(true); } - } + }, + onKeyClickedListener ); } @@ -682,13 +683,16 @@ public abstract class XmppActivity extends Activity { boolean showTag, CompoundButton.OnCheckedChangeListener onCheckedChangeListener, - View.OnClickListener onClickListener) { + View.OnClickListener onClickListener, + View.OnClickListener onKeyClickedListener) { if (trust == XmppAxolotlSession.Trust.COMPROMISED) { return false; } View view = getLayoutInflater().inflate(R.layout.contact_key, keys, false); TextView key = (TextView) view.findViewById(R.id.key); + key.setOnClickListener(onKeyClickedListener); TextView keyType = (TextView) view.findViewById(R.id.key_type); + keyType.setOnClickListener(onKeyClickedListener); Switch trustToggle = (Switch) view.findViewById(R.id.tgl_trust); trustToggle.setVisibility(View.VISIBLE); trustToggle.setOnCheckedChangeListener(onCheckedChangeListener); diff --git a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java index ab407249..d1b90ab6 100644 --- a/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/CryptoHelper.java @@ -1,5 +1,6 @@ package eu.siacs.conversations.utils; +import android.os.Bundle; import android.util.Log; import android.util.Pair; @@ -9,6 +10,7 @@ import org.bouncycastle.asn1.x500.style.IETFUtils; import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; import org.bouncycastle.jce.PrincipalUtil; +import java.security.MessageDigest; import java.security.SecureRandom; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateParsingException; @@ -121,6 +123,14 @@ public final class CryptoHelper { return builder.toString(); } + public static String prettifyFingerprintCert(String fingerprint) { + StringBuilder builder = new StringBuilder(fingerprint); + for(int i=2;i < builder.length(); i+=3) { + builder.insert(i,':'); + } + return builder.toString(); + } + public static String[] getOrderedCipherSuites(final String[] platformSupportedCipherSuites) { final Collection cipherSuites = new LinkedHashSet<>(Arrays.asList(Config.ENABLED_CIPHERS)); final List platformCiphers = Arrays.asList(platformSupportedCipherSuites); @@ -167,6 +177,46 @@ public final class CryptoHelper { } } + public static Bundle extractCertificateInformation(X509Certificate certificate) { + Bundle information = new Bundle(); + try { + JcaX509CertificateHolder holder = new JcaX509CertificateHolder(certificate); + X500Name subject = holder.getSubject(); + try { + information.putString("subject_cn", subject.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString()); + } catch (Exception e) { + //ignored + } + try { + information.putString("subject_o",subject.getRDNs(BCStyle.O)[0].getFirst().getValue().toString()); + } catch (Exception e) { + //ignored + } + + X500Name issuer = holder.getIssuer(); + try { + information.putString("issuer_cn", issuer.getRDNs(BCStyle.CN)[0].getFirst().getValue().toString()); + } catch (Exception e) { + //ignored + } + try { + information.putString("issuer_o", issuer.getRDNs(BCStyle.O)[0].getFirst().getValue().toString()); + } catch (Exception e) { + //ignored + } + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] fingerprint = md.digest(certificate.getEncoded()); + information.putString("sha1", prettifyFingerprintCert(bytesToHex(fingerprint))); + } catch (Exception e) { + + } + return information; + } catch (CertificateEncodingException e) { + return information; + } + } + public static int encryptionTypeToText(int encryption) { switch (encryption) { case Message.ENCRYPTION_OTR: -- cgit v1.2.3