open-keychain/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/WrappedSecretKey.java
2014-06-02 13:49:11 +02:00

201 lines
8.2 KiB
Java

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;
/** 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;
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<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(
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<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()
}
public UncachedSecretKey getUncached() {
return new UncachedSecretKey(mSecretKey);
}
}