mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-11-11 03:25:05 -05:00
certs: ditch expiry, re-add data blob, improve ViewCertActivity
GnuPG doesn't support expiry of user id certifications. The number of rings with an expiration subpacket in a cert out there is likely negligible. ViewCertActivity now verifies the key and displays a status. For revocation certs, the revocation reason is also shown.
This commit is contained in:
parent
f01a96f56e
commit
456a634149
@ -62,7 +62,7 @@ public class KeychainContract {
|
||||
String TYPE = "type";
|
||||
String VERIFIED = "verified";
|
||||
String CREATION = "creation";
|
||||
String EXPIRY = "expiry";
|
||||
String DATA = "data";
|
||||
}
|
||||
|
||||
interface ApiAppsColumns {
|
||||
|
@ -114,7 +114,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
+ CertsColumns.TYPE + " INTEGER, "
|
||||
+ CertsColumns.VERIFIED + " INTEGER, "
|
||||
+ CertsColumns.CREATION + " INTEGER, "
|
||||
+ CertsColumns.EXPIRY + " INTEGER, "
|
||||
|
||||
+ CertsColumns.DATA + " BLOB, "
|
||||
|
||||
+ "PRIMARY KEY(" + CertsColumns.MASTER_KEY_ID + ", " + CertsColumns.RANK + ", "
|
||||
+ CertsColumns.KEY_ID_CERTIFIER + "), "
|
||||
|
@ -448,8 +448,8 @@ public class KeychainProvider extends ContentProvider {
|
||||
projectionMap.put(Certs.VERIFIED, Tables.CERTS + "." + Certs.VERIFIED);
|
||||
projectionMap.put(Certs.TYPE, Tables.CERTS + "." + Certs.TYPE);
|
||||
projectionMap.put(Certs.CREATION, Tables.CERTS + "." + Certs.CREATION);
|
||||
projectionMap.put(Certs.EXPIRY, Tables.CERTS + "." + Certs.EXPIRY);
|
||||
projectionMap.put(Certs.KEY_ID_CERTIFIER, Tables.CERTS + "." + Certs.KEY_ID_CERTIFIER);
|
||||
projectionMap.put(Certs.DATA, Tables.CERTS + "." + Certs.DATA);
|
||||
projectionMap.put(Certs.USER_ID, Tables.USER_IDS + "." + UserIds.USER_ID);
|
||||
projectionMap.put(Certs.SIGNER_UID, "signer." + UserIds.USER_ID + " AS " + Certs.SIGNER_UID);
|
||||
qb.setProjectionMap(projectionMap);
|
||||
|
@ -246,10 +246,8 @@ public class ProviderHelper {
|
||||
try {
|
||||
// self signature
|
||||
if(certId == masterKeyId) {
|
||||
cert.init(
|
||||
new JcaPGPContentVerifierBuilderProvider().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME),
|
||||
masterKey);
|
||||
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME), masterKey);
|
||||
if(!cert.verifyCertification(userId, masterKey)) {
|
||||
// not verified?! dang! TODO notify user? this is kinda serious...
|
||||
Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!");
|
||||
@ -267,8 +265,7 @@ public class ProviderHelper {
|
||||
// verify signatures from known private keys
|
||||
if(allKeyRings.containsKey(certId)) {
|
||||
// mark them as verified
|
||||
cert.init(
|
||||
new JcaPGPContentVerifierBuilderProvider().setProvider(
|
||||
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME),
|
||||
allKeyRings.get(certId).getPublicKey());
|
||||
if(cert.verifyCertification(userId, masterKey)) {
|
||||
@ -423,12 +420,8 @@ public class ProviderHelper {
|
||||
values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyID());
|
||||
values.put(Certs.TYPE, cert.getSignatureType());
|
||||
values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000);
|
||||
if(cert.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.EXPIRE_TIME)) {
|
||||
long ext = ((SignatureExpirationTime) cert.getHashedSubPackets().getSubpacket(
|
||||
SignatureSubpacketTags.EXPIRE_TIME)).getTime();
|
||||
values.put(Certs.EXPIRY, cert.getCreationTime().getTime() / 1000 + ext);
|
||||
}
|
||||
values.put(Certs.VERIFIED, verified);
|
||||
values.put(Certs.DATA, cert.getEncoded());
|
||||
|
||||
Uri uri = Certs.buildCertsUri(Long.toString(masterKeyId));
|
||||
|
||||
|
@ -30,18 +30,27 @@ import android.support.v7.app.ActionBarActivity;
|
||||
import android.text.format.DateFormat;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.spongycastle.bcpg.SignatureSubpacket;
|
||||
import org.spongycastle.bcpg.SignatureSubpacketTags;
|
||||
import org.spongycastle.bcpg.sig.RevocationReason;
|
||||
import org.spongycastle.openpgp.PGPException;
|
||||
import org.spongycastle.openpgp.PGPKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
|
||||
import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.security.SignatureException;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
@ -56,24 +65,25 @@ public class ViewCertActivity extends ActionBarActivity
|
||||
Certs.USER_ID,
|
||||
Certs.TYPE,
|
||||
Certs.CREATION,
|
||||
Certs.EXPIRY,
|
||||
Certs.KEY_ID_CERTIFIER,
|
||||
Certs.SIGNER_UID,
|
||||
Certs.DATA,
|
||||
};
|
||||
private static final int INDEX_MASTER_KEY_ID = 0;
|
||||
private static final int INDEX_USER_ID = 1;
|
||||
private static final int INDEX_TYPE = 2;
|
||||
private static final int INDEX_CREATION = 3;
|
||||
private static final int INDEX_EXPIRY = 4;
|
||||
private static final int INDEX_KEY_ID_CERTIFIER = 5;
|
||||
private static final int INDEX_SIGNER_UID = 6;
|
||||
private static final int INDEX_KEY_ID_CERTIFIER = 4;
|
||||
private static final int INDEX_SIGNER_UID = 5;
|
||||
private static final int INDEX_DATA = 6;
|
||||
|
||||
private Uri mDataUri;
|
||||
|
||||
private long mSignerKeyId;
|
||||
|
||||
private TextView mSigneeKey, mSigneeUid, mRank, mAlgorithm, mType, mCreation, mExpiry;
|
||||
private TextView mSignerKey, mSignerUid;
|
||||
private TextView mSigneeKey, mSigneeUid, mAlgorithm, mType, mRReason, mCreation;
|
||||
private TextView mSignerKey, mSignerUid, mStatus;
|
||||
private View mRowReason;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@ -84,16 +94,19 @@ public class ViewCertActivity extends ActionBarActivity
|
||||
|
||||
setContentView(R.layout.view_cert_activity);
|
||||
|
||||
mStatus = (TextView) findViewById(R.id.status);
|
||||
mSigneeKey = (TextView) findViewById(R.id.signee_key);
|
||||
mSigneeUid = (TextView) findViewById(R.id.signee_uid);
|
||||
mAlgorithm = (TextView) findViewById(R.id.algorithm);
|
||||
mType = (TextView) findViewById(R.id.signature_type);
|
||||
mRReason = (TextView) findViewById(R.id.reason);
|
||||
mCreation = (TextView) findViewById(R.id.creation);
|
||||
mExpiry = (TextView) findViewById(R.id.expiry);
|
||||
|
||||
mSignerKey = (TextView) findViewById(R.id.signer_key_id);
|
||||
mSignerUid = (TextView) findViewById(R.id.signer_uid);
|
||||
|
||||
mRowReason = findViewById(R.id.row_reason);
|
||||
|
||||
mDataUri = getIntent().getData();
|
||||
if (mDataUri == null) {
|
||||
Log.e(Constants.TAG, "Intent data missing. Should be Uri of key!");
|
||||
@ -134,9 +147,36 @@ public class ViewCertActivity extends ActionBarActivity
|
||||
else
|
||||
mSignerUid.setText(R.string.unknown_uid);
|
||||
|
||||
// String algorithmStr = PgpKeyHelper.getAlgorithmInfo(sig.getKeyAlgorithm(), 0);
|
||||
// mAlgorithm.setText(algorithmStr);
|
||||
PGPSignature sig = PgpConversionHelper.BytesToPGPSignature(data.getBlob(INDEX_DATA));
|
||||
PGPKeyRing signeeRing = ProviderHelper.getPGPKeyRing(this,
|
||||
KeychainContract.KeyRingData.buildPublicKeyRingUri(Long.toString(data.getLong(INDEX_MASTER_KEY_ID))));
|
||||
PGPKeyRing signerRing = ProviderHelper.getPGPKeyRing(this,
|
||||
KeychainContract.KeyRingData.buildPublicKeyRingUri(Long.toString(sig.getKeyID())));
|
||||
if(signerRing != null) try {
|
||||
sig.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME), signeeRing.getPublicKey());
|
||||
if (sig.verifyCertification(signeeUid, signerRing.getPublicKey())) {
|
||||
mStatus.setText("ok");
|
||||
mStatus.setTextColor(getResources().getColor(R.color.bbutton_success));
|
||||
} else {
|
||||
mStatus.setText("failed!");
|
||||
mStatus.setTextColor(getResources().getColor(R.color.alert));
|
||||
}
|
||||
} catch(SignatureException e) {
|
||||
mStatus.setText("error!");
|
||||
mStatus.setTextColor(getResources().getColor(R.color.alert));
|
||||
} catch(PGPException e) {
|
||||
mStatus.setText("error!");
|
||||
mStatus.setTextColor(getResources().getColor(R.color.alert));
|
||||
} else {
|
||||
mStatus.setText("key unavailable");
|
||||
mStatus.setTextColor(getResources().getColor(R.color.black));
|
||||
}
|
||||
|
||||
String algorithmStr = PgpKeyHelper.getAlgorithmInfo(sig.getKeyAlgorithm(), 0);
|
||||
mAlgorithm.setText(algorithmStr);
|
||||
|
||||
mRowReason.setVisibility(View.GONE);
|
||||
switch(data.getInt(INDEX_TYPE)) {
|
||||
case PGPSignature.DEFAULT_CERTIFICATION:
|
||||
mType.setText(R.string.cert_default); break;
|
||||
@ -146,16 +186,21 @@ public class ViewCertActivity extends ActionBarActivity
|
||||
mType.setText(R.string.cert_casual); break;
|
||||
case PGPSignature.POSITIVE_CERTIFICATION:
|
||||
mType.setText(R.string.cert_positive); break;
|
||||
case PGPSignature.CERTIFICATION_REVOCATION:
|
||||
mType.setText(R.string.cert_revoke); break;
|
||||
}
|
||||
|
||||
long expiry = data.getLong(INDEX_EXPIRY);
|
||||
if(expiry == 0) {
|
||||
mExpiry.setText(R.string.never);
|
||||
} else {
|
||||
Date expiryDate = new Date(creationDate.getTime() + expiry * 1000);
|
||||
mExpiry.setText(DateFormat.getDateFormat(getApplicationContext()).format(expiryDate));
|
||||
case PGPSignature.CERTIFICATION_REVOCATION: {
|
||||
mType.setText(R.string.cert_revoke);
|
||||
if(sig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON)) {
|
||||
SignatureSubpacket p = sig.getHashedSubPackets().getSubpacket(
|
||||
SignatureSubpacketTags.REVOCATION_REASON);
|
||||
// For some reason, this is missing in SignatureSubpacketInputStream:146
|
||||
if(!(p instanceof RevocationReason)) {
|
||||
p = new RevocationReason(false, p.getData());
|
||||
}
|
||||
String reason = ((RevocationReason) p).getRevocationDescription();
|
||||
mRReason.setText(reason);
|
||||
mRowReason.setVisibility(View.VISIBLE);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,26 @@
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingRight="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:layout_marginTop="14dp">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Verification Status" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="ok"
|
||||
android:id="@+id/status"
|
||||
android:layout_marginLeft="30dp" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
style="@style/SectionHeader"
|
||||
android:layout_width="wrap_content"
|
||||
@ -96,6 +116,25 @@
|
||||
android:paddingRight="5dip" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:id="@+id/row_reason">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
android:paddingRight="10dip"
|
||||
android:text="Revocation Reason" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/reason"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="5dip" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow>
|
||||
|
||||
<TextView
|
||||
@ -112,24 +151,6 @@
|
||||
android:paddingRight="5dip" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top"
|
||||
android:paddingRight="10dip"
|
||||
android:text="Expires" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/expiry"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="5dip" />
|
||||
</TableRow>
|
||||
|
||||
</TableLayout>
|
||||
|
||||
<TextView
|
||||
|
Loading…
Reference in New Issue
Block a user