201 lines
8.2 KiB
Java
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);
|
|
}
|
|
|
|
}
|