mirror of
https://github.com/moparisthebest/open-keychain
synced 2024-12-25 08:28:50 -05:00
wrapped-key-ring: move certification logic into secretkey
This commit is contained in:
parent
c2c6a90991
commit
1f8210f743
@ -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/>
|
||||
|
@ -190,6 +190,10 @@ public class ProviderHelper {
|
||||
return result;
|
||||
}
|
||||
|
||||
public CachedPublicKeyRing getCachedPublicKeyRing(long id) throws NotFoundException {
|
||||
return getCachedPublicKeyRing(KeyRings.buildUnifiedKeyRingUri(Long.toString(id)));
|
||||
}
|
||||
|
||||
public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws NotFoundException {
|
||||
Cursor cursor = mContentResolver.query(queryUri,
|
||||
new String[] {
|
||||
|
@ -29,7 +29,6 @@ import android.os.RemoteException;
|
||||
import org.spongycastle.bcpg.sig.KeyFlags;
|
||||
import org.spongycastle.openpgp.PGPKeyRing;
|
||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||
import org.spongycastle.openpgp.PGPPublicKey;
|
||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSecretKey;
|
||||
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.OtherHelper;
|
||||
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.PgpDecryptVerify;
|
||||
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.PgpSignEncrypt;
|
||||
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.PgpGeneralMsgIdException;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||
@ -648,10 +651,8 @@ public class KeychainIntentService extends IntentService
|
||||
try {
|
||||
List<ImportKeysListEntry> entries = data.getParcelableArrayList(IMPORT_KEY_LIST);
|
||||
|
||||
Bundle resultData = new Bundle();
|
||||
|
||||
PgpImportExport pgpImportExport = new PgpImportExport(this, this);
|
||||
resultData = pgpImportExport.importKeyRings(entries);
|
||||
Bundle resultData = pgpImportExport.importKeyRings(entries);
|
||||
|
||||
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY, resultData);
|
||||
} catch (Exception e) {
|
||||
@ -724,15 +725,12 @@ public class KeychainIntentService extends IntentService
|
||||
HkpKeyServer server = new HkpKeyServer(keyServer);
|
||||
|
||||
ProviderHelper providerHelper = new ProviderHelper(this);
|
||||
PGPPublicKeyRing keyring = (PGPPublicKeyRing) providerHelper.getPGPKeyRing(dataUri);
|
||||
if (keyring != null) {
|
||||
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
|
||||
CachedPublicKeyRing keyring = providerHelper.getCachedPublicKeyRing(dataUri);
|
||||
PgpImportExport pgpImportExport = new PgpImportExport(this, null);
|
||||
|
||||
boolean uploaded = pgpImportExport.uploadKeyRingToServer(server,
|
||||
(PGPPublicKeyRing) keyring);
|
||||
if (!uploaded) {
|
||||
throw new PgpGeneralException("Unable to export key to selected server");
|
||||
}
|
||||
boolean uploaded = pgpImportExport.uploadKeyRingToServer(server, keyring);
|
||||
if (!uploaded) {
|
||||
throw new PgpGeneralException("Unable to export key to selected server");
|
||||
}
|
||||
|
||||
sendMessageToHandler(KeychainIntentServiceHandler.MESSAGE_OKAY);
|
||||
@ -795,7 +793,6 @@ public class KeychainIntentService extends IntentService
|
||||
entry.setBytes(downloadedKey.getEncoded());
|
||||
}
|
||||
|
||||
|
||||
Intent importIntent = new Intent(this, KeychainIntentService.class);
|
||||
importIntent.setAction(ACTION_IMPORT_KEYRING);
|
||||
Bundle importData = new Bundle();
|
||||
@ -826,24 +823,15 @@ public class KeychainIntentService extends IntentService
|
||||
}
|
||||
|
||||
ProviderHelper providerHelper = new ProviderHelper(this);
|
||||
PgpKeyOperation keyOperation = new PgpKeyOperation(new ProgressScaler(this, 0, 100, 100));
|
||||
PGPPublicKeyRing publicRing = providerHelper.getPGPPublicKeyRing(pubKeyId);
|
||||
PGPPublicKey publicKey = publicRing.getPublicKey(pubKeyId);
|
||||
PGPSecretKeyRing secretKeyRing = null;
|
||||
try {
|
||||
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);
|
||||
CachedPublicKeyRing publicRing = providerHelper.getCachedPublicKeyRing(pubKeyId);
|
||||
CachedSecretKeyRing secretKeyRing = providerHelper.getCachedSecretKeyRing(masterKeyId);
|
||||
CachedSecretKey certificationKey = secretKeyRing.getSubKey();
|
||||
certificationKey.unlock(signaturePassphrase);
|
||||
UncachedKeyRing newRing = certificationKey.certifyUserIds(publicRing, userIds);
|
||||
|
||||
// store the signed key in our local cache
|
||||
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) {
|
||||
throw new PgpGeneralException("Failed to store signed key in local cache");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user