From 761d87b661ef14023870ad7be107d33d69ab03e7 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Wed, 21 May 2014 21:07:32 +0200 Subject: wrapped-key-ring: split up CachedKeyRing and WrappedKeyRing --- .../keychain/pgp/WrappedSecretKey.java | 189 +++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java new file mode 100644 index 000000000..a03e10098 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java @@ -0,0 +1,189 @@ +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; +import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +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 WrappedSecretKey extends WrappedPublicKey { + + private final PGPSecretKey mSecretKey; + private PGPPrivateKey mPrivateKey = null; + + WrappedSecretKey(WrappedSecretKeyRing ring, PGPSecretKey key) { + super(ring, key.getPublicKey()); + mSecretKey = key; + } + + public WrappedSecretKeyRing getRing() { + return (WrappedSecretKeyRing) mRing; + } + + /** Returns the wrapped PGPSecretKeyRing. + * This function is for compatibility only, should not be used anymore and will be removed + */ + @Deprecated + public PGPSecretKey getKeyExternal() { + return mSecretKey; + } + + public boolean unlock(String passphrase) throws PgpGeneralException { + try { + PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider( + Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray()); + mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor); + } catch (PGPException e) { + return false; + } + if(mPrivateKey == null) { + throw new PgpGeneralException("error extracting key"); + } + return true; + } + + public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext) + throws PgpGeneralException { + if(mPrivateKey == null) { + throw new PrivateKeyNotUnlockedException(); + } + + // content signer based on signing key algorithm and chosen hash algorithm + JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( + mSecretKey.getPublicKey().getAlgorithm(), hashAlgo) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + + int signatureType; + if (cleartext) { + // for sign-only ascii text + signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT; + } else { + signatureType = PGPSignature.BINARY_DOCUMENT; + } + + try { + PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder); + signatureGenerator.init(signatureType, mPrivateKey); + + PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator(); + spGen.setSignerUserID(false, mRing.getPrimaryUserId()); + signatureGenerator.setHashedSubpackets(spGen.generate()); + return signatureGenerator; + } catch(PGPException e) { + throw new PgpGeneralException("Error initializing signature!", e); + } + } + + public PGPV3SignatureGenerator getV3SignatureGenerator(int hashAlgo, boolean cleartext) + throws PgpGeneralException { + if(mPrivateKey == null) { + throw new PrivateKeyNotUnlockedException(); + } + + // content signer based on signing key algorithm and chosen hash algorithm + JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder( + mSecretKey.getPublicKey().getAlgorithm(), hashAlgo) + .setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME); + + int signatureType; + if (cleartext) { + // for sign-only ascii text + signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT; + } else { + signatureType = PGPSignature.BINARY_DOCUMENT; + } + + try { + PGPV3SignatureGenerator signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder); + signatureV3Generator.init(signatureType, mPrivateKey); + return signatureV3Generator; + } catch(PGPException e) { + throw new PgpGeneralException("Error initializing signature!", e); + } + } + + public PublicKeyDataDecryptorFactory getDecryptorFactory() { + if(mPrivateKey == null) { + throw new PrivateKeyNotUnlockedException(); + } + return new JcePublicKeyDataDecryptorFactoryBuilder() + .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(WrappedPublicKeyRing publicKeyRing, List 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( + mSecretKey.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().getPublicKey(); + + // fetch public key ring, add the certification and return it + for (String userId : new IterableIterator(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() + } + + public UncachedSecretKey getUncached() { + return new UncachedSecretKey(mSecretKey); + } + +} -- cgit v1.2.3 From 58edc0af674f273578a76febd9dfaad95e3380f9 Mon Sep 17 00:00:00 2001 From: Vincent Breitmoser Date: Sat, 31 May 2014 13:10:41 +0200 Subject: wrapped-key-ring: add documentation --- .../org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java') diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java index a03e10098..ef8044a9b 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java @@ -26,6 +26,17 @@ import java.security.NoSuchProviderException; import java.security.SignatureException; import java.util.List; +/** Wrapper for a PGPSecretKey. + * + * This object can only be obtained from a WrappedSecretKeyRing, and stores a + * back reference to its parent. + * + * This class represents known secret keys which are stored in the database. + * All "crypto operations using a known secret key" should be implemented in + * this class, to ensure on type level that these operations are performed on + * properly imported secret keys only. + * + */ public class WrappedSecretKey extends WrappedPublicKey { private final PGPSecretKey mSecretKey; -- cgit v1.2.3