show certificate information
This commit is contained in:
parent
d0bad09f13
commit
f46cbb38a9
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue