aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Gultsch <daniel@gultsch.de>2015-12-23 19:18:53 +0100
committerDaniel Gultsch <daniel@gultsch.de>2015-12-23 19:18:53 +0100
commitf46cbb38a92ff5281a974ecc0932ba5459c7334e (patch)
treebe812812e040602398584ab65764203326bd19e3
parentd0bad09f13886c7d8ee20c0205293cb0250d9c2f (diff)
show certificate information
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java4
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/SQLiteAxolotlStore.java4
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java39
-rw-r--r--src/main/java/eu/siacs/conversations/ui/ContactDetailsActivity.java44
-rw-r--r--src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/TrustKeysActivity.java2
-rw-r--r--src/main/java/eu/siacs/conversations/ui/XmppActivity.java10
-rw-r--r--src/main/java/eu/siacs/conversations/utils/CryptoHelper.java50
-rw-r--r--src/main/res/layout/certificate_information.xml88
-rw-r--r--src/main/res/values/strings.xml7
10 files changed, 240 insertions, 10 deletions
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 ad9b8be30..31b23e3cb 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 788a391db..3c8cb3c10 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<IdentityKey> 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 4882d72b3..3077c4889 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<Long,String> getLastMessageReceived(Account account) {
+ public Pair<Long, String> 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 10bdaab16..5143190fd 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 a6c459968..31d26b23c 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 4bd0d42da..2e6d32466 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 4cb93fdef..b372692d3 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 ab407249c..d1b90ab64 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<String> cipherSuites = new LinkedHashSet<>(Arrays.asList(Config.ENABLED_CIPHERS));
final List<String> 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:
diff --git a/src/main/res/layout/certificate_information.xml b/src/main/res/layout/certificate_information.xml
new file mode 100644
index 000000000..4c085459b
--- /dev/null
+++ b/src/main/res/layout/certificate_information.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="16dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/certificate_subject"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeHeadline"/>
+ <TextView
+ android:layout_marginTop="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/certificate_cn"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/subject_cn"
+ android:textColor="@color/black54"
+ android:textSize="?attr/TextSizeBody"/>
+ <TextView
+ android:layout_marginTop="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/certificate_o"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/subject_o"
+ android:textColor="@color/black54"
+ android:textSize="?attr/TextSizeBody"/>
+ <TextView
+ android:layout_marginTop="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/certificate_issuer"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeHeadline"/>
+ <TextView
+ android:layout_marginTop="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/certificate_cn"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/issuer_cn"
+ android:textColor="@color/black54"
+ android:textSize="?attr/TextSizeBody"/>
+ <TextView
+ android:layout_marginTop="8dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/certificate_o"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/issuer_o"
+ android:textColor="@color/black54"
+ android:textSize="?attr/TextSizeBody"/>
+ <TextView
+ android:layout_marginTop="16dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/certificate_sha1"
+ android:textColor="@color/black87"
+ android:textSize="?attr/TextSizeBody"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/sha1"
+ android:textColor="@color/black54"
+ android:textSize="?attr/TextSizeBody"
+ android:typeface="monospace"
+ android:fontFamily="monospace"/>
+</LinearLayout> \ No newline at end of file
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index d3cda8668..2666e98d4 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -560,4 +560,11 @@
<string name="no_storage_permission">Conversations need access to external storage</string>
<string name="sync_with_contacts">Synchronize with contacts</string>
<string name="sync_with_contacts_long">Conversations wants to match your XMPP roster with your contacts to show their full names and avatars.\n\nConversations will only read your contacts and match them locally without uploading them to your server.\n\nYou will now be asked to grant permission to access your contacts.</string>
+ <string name="certificate_information">Certificate Information</string>
+ <string name="certificate_subject">Subject</string>
+ <string name="certificate_issuer">Issuer</string>
+ <string name="certificate_cn">Common Name</string>
+ <string name="certificate_o">Organization</string>
+ <string name="certificate_sha1">SHA1</string>
+ <string name="certicate_info_not_available">(Not available)</string>
</resources>