wrapped-key-ring: move certification logic into secretkey

This commit is contained in:
Vincent Breitmoser 2014-05-03 18:32:20 +02:00
parent c2c6a90991
commit 1f8210f743
7 changed files with 117 additions and 124 deletions

View File

@ -36,7 +36,7 @@ public class CachedPublicKeyRing extends CachedKeyRing {
mPubKey = pubkey; mPubKey = pubkey;
} }
private PGPPublicKeyRing getRing() { PGPPublicKeyRing getRing() {
if(mRing == null) { if(mRing == null) {
mRing = (PGPPublicKeyRing) PgpConversionHelper.BytesToPGPKeyRing(mPubKey); mRing = (PGPPublicKeyRing) PgpConversionHelper.BytesToPGPKeyRing(mPubKey);
} }
@ -47,6 +47,10 @@ public class CachedPublicKeyRing extends CachedKeyRing {
getRing().encode(stream); getRing().encode(stream);
} }
public CachedPublicKey getSubkey() {
return new CachedPublicKey(this, getRing().getPublicKey());
}
public CachedPublicKey getSubkey(long id) { public CachedPublicKey getSubkey(long id) {
return new CachedPublicKey(this, getRing().getPublicKey(id)); return new CachedPublicKey(this, getRing().getPublicKey(id));
} }
@ -128,7 +132,6 @@ public class CachedPublicKeyRing extends CachedKeyRing {
} }
static boolean isEncryptionKey(PGPPublicKey key) { static boolean isEncryptionKey(PGPPublicKey key) {
if (!key.isEncryptionKey()) { if (!key.isEncryptionKey()) {
return false; return false;

View File

@ -2,10 +2,14 @@ package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPException; import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey; import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSignature; import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator; import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator; import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.PGPV3SignatureGenerator; import org.spongycastle.openpgp.PGPV3SignatureGenerator;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor; import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory; import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
@ -14,6 +18,13 @@ import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.util.IterableIterator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.util.List;
public class CachedSecretKey { public class CachedSecretKey {
@ -113,6 +124,53 @@ public class CachedSecretKey {
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey); .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey);
} }
/**
* Certify the given pubkeyid with the given masterkeyid.
*
* @param publicKeyRing Keyring to add certification to.
* @param userIds User IDs to certify, must not be null or empty
* @return A keyring with added certifications
*/
public UncachedKeyRing certifyUserIds(CachedPublicKeyRing publicKeyRing, List<String> userIds)
throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
PGPException, SignatureException {
if(mPrivateKey == null) {
throw new PrivateKeyNotUnlockedException();
}
// create a signatureGenerator from the supplied masterKeyId and passphrase
PGPSignatureGenerator signatureGenerator;
{
// TODO: SHA256 fixed?
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
mKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey);
}
{ // supply signatureGenerator with a SubpacketVector
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
PGPSignatureSubpacketVector packetVector = spGen.generate();
signatureGenerator.setHashedSubpackets(packetVector);
}
// get the master subkey (which we certify for)
PGPPublicKey publicKey = publicKeyRing.getSubkey().getKey();
// fetch public key ring, add the certification and return it
for (String userId : new IterableIterator<String>(userIds.iterator())) {
PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
}
PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicKeyRing.getRing(), publicKey);
return new UncachedKeyRing(ring);
}
static class PrivateKeyNotUnlockedException extends RuntimeException { static class PrivateKeyNotUnlockedException extends RuntimeException {
// this exception is a programming error which happens when an operation which requires // this exception is a programming error which happens when an operation which requires
// the private key is called without a previous call to unlock() // the private key is called without a previous call to unlock()

View File

@ -23,7 +23,11 @@ public class CachedSecretKeyRing extends CachedKeyRing {
mRing = (PGPSecretKeyRing) PgpConversionHelper.BytesToPGPKeyRing(blob); mRing = (PGPSecretKeyRing) PgpConversionHelper.BytesToPGPKeyRing(blob);
} }
CachedSecretKey getSubKey(long id) { public CachedSecretKey getSubKey() {
return new CachedSecretKey(this, mRing.getSecretKey());
}
public CachedSecretKey getSubKey(long id) {
return new CachedSecretKey(this, mRing.getSecretKey(id)); return new CachedSecretKey(this, mRing.getSecretKey(id));
} }

View File

@ -97,12 +97,12 @@ public class PgpImportExport {
} }
} }
public boolean uploadKeyRingToServer(HkpKeyServer server, PGPPublicKeyRing keyring) { public boolean uploadKeyRingToServer(HkpKeyServer server, CachedPublicKeyRing keyring) {
ByteArrayOutputStream bos = new ByteArrayOutputStream(); ByteArrayOutputStream bos = new ByteArrayOutputStream();
ArmoredOutputStream aos = null; ArmoredOutputStream aos = null;
try { try {
aos = new ArmoredOutputStream(bos); aos = new ArmoredOutputStream(bos);
aos.write(keyring.getEncoded()); keyring.encode(aos);
aos.close(); aos.close();
String armoredKey = bos.toString("UTF-8"); String armoredKey = bos.toString("UTF-8");
@ -147,8 +147,25 @@ public class PgpImportExport {
if (obj instanceof PGPKeyRing) { if (obj instanceof PGPKeyRing) {
PGPKeyRing keyring = (PGPKeyRing) obj; PGPKeyRing keyring = (PGPKeyRing) obj;
int status;
int status = storeKeyRingInCache(keyring); // TODO Better try to get this one from the db first!
if(keyring instanceof PGPSecretKeyRing) {
PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring;
// TODO: preserve certifications
// (http://osdir.com/ml/encryption.bouncy-castle.devel/2007-01/msg00054.html ?)
PGPPublicKeyRing newPubRing = null;
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(
secretKeyRing.getPublicKeys())) {
if (newPubRing == null) {
newPubRing = new PGPPublicKeyRing(key.getEncoded(),
new JcaKeyFingerprintCalculator());
}
newPubRing = PGPPublicKeyRing.insertPublicKey(newPubRing, key);
}
status = storeKeyRingInCache(new UncachedKeyRing(newPubRing ,secretKeyRing));
} else {
status = storeKeyRingInCache(new UncachedKeyRing((PGPPublicKeyRing) keyring));
}
if (status == RETURN_ERROR) { if (status == RETURN_ERROR) {
throw new PgpGeneralException( throw new PgpGeneralException(
@ -259,44 +276,16 @@ public class PgpImportExport {
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public int storeKeyRingInCache(PGPKeyRing keyring) { public int storeKeyRingInCache(UncachedKeyRing keyring) {
int status = RETURN_ERROR; int status = RETURN_ERROR;
try { try {
if (keyring instanceof PGPSecretKeyRing) { PGPSecretKeyRing secretKeyRing = keyring.getSecretRing();
PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring; PGPPublicKeyRing publicKeyRing = keyring.getPublicRing();
boolean save = true; // see what type we have. we can either have a secret + public keyring, or just public
if (secretKeyRing != null) {
for (PGPSecretKey testSecretKey : new IterableIterator<PGPSecretKey>( mProviderHelper.saveKeyRing(publicKeyRing, secretKeyRing);
secretKeyRing.getSecretKeys())) {
if (!testSecretKey.isMasterKey()) {
if (testSecretKey.isPrivateKeyEmpty()) {
// this is bad, something is very wrong...
save = false;
status = RETURN_BAD;
}
}
}
if (save) {
// TODO: preserve certifications
// (http://osdir.com/ml/encryption.bouncy-castle.devel/2007-01/msg00054.html ?)
PGPPublicKeyRing newPubRing = null;
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(
secretKeyRing.getPublicKeys())) {
if (newPubRing == null) {
newPubRing = new PGPPublicKeyRing(key.getEncoded(),
new JcaKeyFingerprintCalculator());
}
newPubRing = PGPPublicKeyRing.insertPublicKey(newPubRing, key);
}
if (newPubRing != null) {
mProviderHelper.saveKeyRing(newPubRing);
}
mProviderHelper.saveKeyRing(secretKeyRing);
status = RETURN_OK; status = RETURN_OK;
} } else {
} else if (keyring instanceof PGPPublicKeyRing) {
PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring;
mProviderHelper.saveKeyRing(publicKeyRing); mProviderHelper.saveKeyRing(publicKeyRing);
status = RETURN_OK; status = RETURN_OK;
} }

View File

@ -690,59 +690,6 @@ public class PgpKeyOperation {
} }
/**
* Certify the given pubkeyid with the given masterkeyid.
*
* @param certificationKey Certifying key
* @param publicKey public key to certify
* @param userIds User IDs to certify, must not be null or empty
* @param passphrase Passphrase of the secret key
* @return A keyring with added certifications
*/
public PGPPublicKey certifyKey(PGPSecretKey certificationKey, PGPPublicKey publicKey,
List<String> userIds, String passphrase)
throws PgpGeneralMsgIdException, NoSuchAlgorithmException, NoSuchProviderException,
PGPException, SignatureException {
// create a signatureGenerator from the supplied masterKeyId and passphrase
PGPSignatureGenerator signatureGenerator;
{
if (certificationKey == null) {
throw new PgpGeneralMsgIdException(R.string.error_no_signature_key);
}
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
if (signaturePrivateKey == null) {
throw new PgpGeneralMsgIdException(R.string.error_could_not_extract_private_key);
}
// TODO: SHA256 fixed?
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
certificationKey.getPublicKey().getAlgorithm(), PGPUtil.SHA256)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, signaturePrivateKey);
}
{ // supply signatureGenerator with a SubpacketVector
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
PGPSignatureSubpacketVector packetVector = spGen.generate();
signatureGenerator.setHashedSubpackets(packetVector);
}
// fetch public key ring, add the certification and return it
for (String userId : new IterableIterator<String>(userIds.iterator())) {
PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
}
return publicKey;
}
/** /**
* Simple static subclass that stores two values. * Simple static subclass that stores two values.
* <p/> * <p/>

View File

@ -190,6 +190,10 @@ public class ProviderHelper {
return result; return result;
} }
public CachedPublicKeyRing getCachedPublicKeyRing(long id) throws NotFoundException {
return getCachedPublicKeyRing(KeyRings.buildUnifiedKeyRingUri(Long.toString(id)));
}
public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws NotFoundException { public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws NotFoundException {
Cursor cursor = mContentResolver.query(queryUri, Cursor cursor = mContentResolver.query(queryUri,
new String[] { new String[] {

View File

@ -29,7 +29,6 @@ import android.os.RemoteException;
import org.spongycastle.bcpg.sig.KeyFlags; import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPKeyRing; import org.spongycastle.openpgp.PGPKeyRing;
import org.spongycastle.openpgp.PGPObjectFactory; import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing; import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey; import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing; import org.spongycastle.openpgp.PGPSecretKeyRing;
@ -39,6 +38,9 @@ import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.helper.FileHelper; import org.sufficientlysecure.keychain.helper.FileHelper;
import org.sufficientlysecure.keychain.helper.OtherHelper; import org.sufficientlysecure.keychain.helper.OtherHelper;
import org.sufficientlysecure.keychain.helper.Preferences; import org.sufficientlysecure.keychain.helper.Preferences;
import org.sufficientlysecure.keychain.pgp.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.CachedSecretKey;
import org.sufficientlysecure.keychain.pgp.CachedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper; import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerify;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyResult;
@ -48,6 +50,7 @@ import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyOperation; import org.sufficientlysecure.keychain.pgp.PgpKeyOperation;
import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt; import org.sufficientlysecure.keychain.pgp.PgpSignEncrypt;
import org.sufficientlysecure.keychain.pgp.Progressable; import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings; import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@ -648,10 +651,8 @@ public class KeychainIntentService extends IntentService
try { try {
List<ImportKeysListEntry> entries = data.getParcelableArrayList(IMPORT_KEY_LIST); List<ImportKeysListEntry> entries = data.getParcelableArrayList(IMPORT_KEY_LIST);
Bundle resultData = new Bundle();
PgpImportExport pgpImportExport = new PgpImportExport(this, this); PgpImportExport pgpImportExport = new PgpImportExport(this, this);
resultData = pgpImportExport.importKeyRings(entries); Bundle resultData = pgpImportExport.importKeyRings(entries);
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData); sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
} catch (Exception e) { } catch (Exception e) {
@ -724,16 +725,13 @@ public class KeychainIntentService extends IntentService
HkpKeyServer server = new HkpKeyServer(keyServer); HkpKeyServer server = new HkpKeyServer(keyServer);
ProviderHelper providerHelper = new ProviderHelper(this); ProviderHelper providerHelper = new ProviderHelper(this);
PGPPublicKeyRing keyring = (PGPPublicKeyRing) providerHelper.getPGPKeyRing(dataUri); CachedPublicKeyRing keyring = providerHelper.getCachedPublicKeyRing(dataUri);
if (keyring != null) {
PgpImportExport pgpImportExport = new PgpImportExport(this, null); PgpImportExport pgpImportExport = new PgpImportExport(this, null);
boolean uploaded = pgpImportExport.uploadKeyRingToServer(server, boolean uploaded = pgpImportExport.uploadKeyRingToServer(server, keyring);
(PGPPublicKeyRing) keyring);
if (!uploaded) { if (!uploaded) {
throw new PgpGeneralException("Unable to export key to selected server"); throw new PgpGeneralException("Unable to export key to selected server");
} }
}
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY); sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
} catch (Exception e) { } catch (Exception e) {
@ -795,7 +793,6 @@ public class KeychainIntentService extends IntentService
entry.setBytes(downloadedKey.getEncoded()); entry.setBytes(downloadedKey.getEncoded());
} }
Intent importIntent = new Intent(this, KeychainIntentService.class); Intent importIntent = new Intent(this, KeychainIntentService.class);
importIntent.setAction(ACTION_IMPORT_KEYRING); importIntent.setAction(ACTION_IMPORT_KEYRING);
Bundle importData = new Bundle(); Bundle importData = new Bundle();
@ -826,24 +823,15 @@ public class KeychainIntentService extends IntentService
} }
ProviderHelper providerHelper = new ProviderHelper(this); ProviderHelper providerHelper = new ProviderHelper(this);
PgpKeyOperation keyOperation = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100)); CachedPublicKeyRing publicRing = providerHelper.getCachedPublicKeyRing(pubKeyId);
PGPPublicKeyRing publicRing = providerHelper.getPGPPublicKeyRing(pubKeyId); CachedSecretKeyRing secretKeyRing = providerHelper.getCachedSecretKeyRing(masterKeyId);
PGPPublicKey publicKey = publicRing.getPublicKey(pubKeyId); CachedSecretKey certificationKey = secretKeyRing.getSubKey();
PGPSecretKeyRing secretKeyRing = null; certificationKey.unlock(signaturePassphrase);
try { UncachedKeyRing newRing = certificationKey.certifyUserIds(publicRing, userIds);
secretKeyRing = providerHelper.getPGPSecretKeyRing(masterKeyId);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
// TODO: throw exception here!
}
PGPSecretKey certificationKey = secretKeyRing.getSecretKey();
publicKey = keyOperation.certifyKey(certificationKey, publicKey,
userIds, signaturePassphrase);
publicRing = PGPPublicKeyRing.insertPublicKey(publicRing, publicKey);
// store the signed key in our local cache // store the signed key in our local cache
PgpImportExport pgpImportExport = new PgpImportExport(this, null); PgpImportExport pgpImportExport = new PgpImportExport(this, null);
int retval = pgpImportExport.storeKeyRingInCache(publicRing); int retval = pgpImportExport.storeKeyRingInCache(newRing);
if (retval != PgpImportExport.RETURN_OK && retval != PgpImportExport.RETURN_UPDATED) { if (retval != PgpImportExport.RETURN_OK && retval != PgpImportExport.RETURN_UPDATED) {
throw new PgpGeneralException("Failed to store signed key in local cache"); throw new PgpGeneralException("Failed to store signed key in local cache");
} }