wrapped-key-ring: move certification logic into secretkey
This commit is contained in:
@@ -36,7 +36,7 @@ public class CachedPublicKeyRing extends CachedKeyRing {
|
||||
mPubKey = pubkey;
|
||||
}
|
||||
|
||||
private PGPPublicKeyRing getRing() {
|
||||
PGPPublicKeyRing getRing() {
|
||||
if(mRing == null) {
|
||||
mRing = (PGPPublicKeyRing) PgpConversionHelper.BytesToPGPKeyRing(mPubKey);
|
||||
}
|
||||
@@ -47,6 +47,10 @@ public class CachedPublicKeyRing extends CachedKeyRing {
|
||||
getRing().encode(stream);
|
||||
}
|
||||
|
||||
public CachedPublicKey getSubkey() {
|
||||
return new CachedPublicKey(this, getRing().getPublicKey());
|
||||
}
|
||||
|
||||
public CachedPublicKey getSubkey(long id) {
|
||||
return new CachedPublicKey(this, getRing().getPublicKey(id));
|
||||
}
|
||||
@@ -128,7 +132,6 @@ public class CachedPublicKeyRing extends CachedKeyRing {
|
||||
|
||||
}
|
||||
|
||||
|
||||
static boolean isEncryptionKey(PGPPublicKey key) {
|
||||
if (!key.isEncryptionKey()) {
|
||||
return false;
|
||||
|
||||
@@ -2,10 +2,14 @@ package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.spongycastle.openpgp.PGPException;
|
||||
import org.spongycastle.openpgp.PGPPrivateKey;
|
||||
import org.spongycastle.openpgp.PGPPublicKey;
|
||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSecretKey;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.PGPSignatureGenerator;
|
||||
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
|
||||
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
||||
import org.spongycastle.openpgp.PGPUtil;
|
||||
import org.spongycastle.openpgp.PGPV3SignatureGenerator;
|
||||
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||
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.sufficientlysecure.keychain.Constants;
|
||||
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 {
|
||||
|
||||
@@ -113,6 +124,53 @@ public class CachedSecretKey {
|
||||
.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 {
|
||||
// this exception is a programming error which happens when an operation which requires
|
||||
// the private key is called without a previous call to unlock()
|
||||
|
||||
@@ -23,7 +23,11 @@ public class CachedSecretKeyRing extends CachedKeyRing {
|
||||
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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
ArmoredOutputStream aos = null;
|
||||
try {
|
||||
aos = new ArmoredOutputStream(bos);
|
||||
aos.write(keyring.getEncoded());
|
||||
keyring.encode(aos);
|
||||
aos.close();
|
||||
|
||||
String armoredKey = bos.toString("UTF-8");
|
||||
@@ -147,8 +147,25 @@ public class PgpImportExport {
|
||||
|
||||
if (obj instanceof PGPKeyRing) {
|
||||
PGPKeyRing keyring = (PGPKeyRing) obj;
|
||||
|
||||
int status = storeKeyRingInCache(keyring);
|
||||
int status;
|
||||
// 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) {
|
||||
throw new PgpGeneralException(
|
||||
@@ -259,44 +276,16 @@ public class PgpImportExport {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public int storeKeyRingInCache(PGPKeyRing keyring) {
|
||||
public int storeKeyRingInCache(UncachedKeyRing keyring) {
|
||||
int status = RETURN_ERROR;
|
||||
try {
|
||||
if (keyring instanceof PGPSecretKeyRing) {
|
||||
PGPSecretKeyRing secretKeyRing = (PGPSecretKeyRing) keyring;
|
||||
boolean save = true;
|
||||
|
||||
for (PGPSecretKey testSecretKey : new IterableIterator<PGPSecretKey>(
|
||||
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;
|
||||
}
|
||||
} else if (keyring instanceof PGPPublicKeyRing) {
|
||||
PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) keyring;
|
||||
PGPSecretKeyRing secretKeyRing = keyring.getSecretRing();
|
||||
PGPPublicKeyRing publicKeyRing = keyring.getPublicRing();
|
||||
// see what type we have. we can either have a secret + public keyring, or just public
|
||||
if (secretKeyRing != null) {
|
||||
mProviderHelper.saveKeyRing(publicKeyRing, secretKeyRing);
|
||||
status = RETURN_OK;
|
||||
} else {
|
||||
mProviderHelper.saveKeyRing(publicKeyRing);
|
||||
status = RETURN_OK;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
* <p/>
|
||||
|
||||
Reference in New Issue
Block a user