wrapped-key-ring: first steps, get rid of key imports in decryptverify and signencrypt

This commit is contained in:
Vincent Breitmoser 2014-05-03 15:55:10 +02:00
parent 4053e1ebd7
commit 4b3cfd4fa4
12 changed files with 744 additions and 320 deletions

View File

@ -0,0 +1,61 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPSignature;
public abstract class CachedKeyRing {
private final long mMasterKeyId;
private final int mKeySize;
private final boolean mIsRevoked;
private final boolean mCanCertify;
private final long mCreation;
private final long mExpiry;
private final int mAlgorithm;
private final byte[] mFingerprint;
private final String mUserId;
private final int mVerified;
private final boolean mHasSecret;
protected CachedKeyRing(long masterKeyId, int keySize, boolean isRevoked,
boolean canCertify, long creation, long expiry, int algorithm,
byte[] fingerprint, String userId, int verified, boolean hasSecret)
{
mMasterKeyId = masterKeyId;
mKeySize = keySize;
mIsRevoked = isRevoked;
mCanCertify = canCertify;
mCreation = creation;
mExpiry = expiry;
mAlgorithm = algorithm;
mFingerprint = fingerprint;
mUserId = userId;
mVerified = verified;
mHasSecret = hasSecret;
}
public boolean isRevoked() {
return mIsRevoked;
}
public byte[] getFingerprint() {
return mFingerprint;
}
public String getPrimaryUserId() {
return mUserId;
}
public long getMasterKeyId() {
return mMasterKeyId;
}
public int getVerified() {
return mVerified;
}
public void initSignature(PGPSignature sig) {
}
}

View File

@ -0,0 +1,53 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPOnePassSignature;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.sufficientlysecure.keychain.Constants;
public class CachedPublicKey {
// this is the parent key ring
private final CachedPublicKeyRing mRing;
private final PGPPublicKey mKey;
CachedPublicKey(CachedPublicKeyRing ring, PGPPublicKey key) {
mRing = ring;
mKey = key;
}
public CachedPublicKeyRing getKeyRing() {
return mRing;
}
JcePublicKeyKeyEncryptionMethodGenerator getPubKeyEncryptionGenerator() {
return new JcePublicKeyKeyEncryptionMethodGenerator(mKey);
}
public void initSignature(PGPSignature sig) throws PGPException {
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
sig.init(contentVerifierBuilderProvider, mKey);
}
public void initSignature(PGPOnePassSignature sig) throws PGPException {
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
sig.init(contentVerifierBuilderProvider, mKey);
}
public byte[] getFingerprint() {
return mKey.getFingerprint();
}
// Note that this method has package visibility - no access outside the pgp package!
PGPPublicKey getKey() {
return mKey;
}
}

View File

@ -0,0 +1,219 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.ArmoredOutputStream;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.util.IterableIterator;
import java.io.IOException;
import java.security.SignatureException;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
public class CachedPublicKeyRing extends CachedKeyRing {
private PGPPublicKeyRing mRing;
private final byte[] mPubKey;
public CachedPublicKeyRing(long masterKeyId, int keySize, boolean isRevoked,
boolean canCertify, long creation, long expiry, int algorithm,
byte[] fingerprint, String userId, int verified, boolean hasSecret,
byte[] pubkey)
{
super(masterKeyId, keySize, isRevoked, canCertify, creation, expiry,
algorithm, fingerprint, userId, verified, hasSecret);
mPubKey = pubkey;
}
private PGPPublicKeyRing getRing() {
if(mRing == null) {
mRing = (PGPPublicKeyRing) PgpConversionHelper.BytesToPGPKeyRing(mPubKey);
}
return mRing;
}
public void encode(ArmoredOutputStream stream) throws IOException {
getRing().encode(stream);
}
public CachedPublicKey getSubkey(long id) {
return new CachedPublicKey(this, getRing().getPublicKey(id));
}
public CachedPublicKey getFirstEncryptSubkey() throws PgpGeneralException {
// only return master key if no other encryption key is available
PGPPublicKey masterKey = null;
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(getRing().getPublicKeys())) {
if (key.isRevoked() || !isEncryptionKey(key) || isExpired(key)) {
continue;
}
if (key.isMasterKey()) {
masterKey = key;
} else {
return new CachedPublicKey(this, key);
}
}
if(masterKey == null) {
// TODO proper exception
throw new PgpGeneralException("key not found");
}
return new CachedPublicKey(this, masterKey);
}
public boolean verifySubkeyBinding(CachedPublicKey cachedSubkey) {
boolean validSubkeyBinding = false;
boolean validTempSubkeyBinding = false;
boolean validPrimaryKeyBinding = false;
PGPPublicKey masterKey = getRing().getPublicKey();
PGPPublicKey subKey = cachedSubkey.getKey();
// Is this the master key? Match automatically, then.
if(Arrays.equals(masterKey.getFingerprint(), subKey.getFingerprint())) {
return true;
}
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
Iterator<PGPSignature> itr = subKey.getSignatures();
while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
//gpg has an invalid subkey binding error on key import I think, but doesn't shout
//about keys without subkey signing. Can't get it to import a slightly broken one
//either, so we will err on bad subkey binding here.
PGPSignature sig = itr.next();
if (sig.getKeyID() == masterKey.getKeyID() &&
sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
//check and if ok, check primary key binding.
try {
sig.init(contentVerifierBuilderProvider, masterKey);
validTempSubkeyBinding = sig.verifyCertification(masterKey, subKey);
} catch (PGPException e) {
continue;
} catch (SignatureException e) {
continue;
}
if (validTempSubkeyBinding) {
validSubkeyBinding = true;
}
if (validTempSubkeyBinding) {
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
masterKey, subKey);
if (validPrimaryKeyBinding) {
break;
}
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
masterKey, subKey);
if (validPrimaryKeyBinding) {
break;
}
}
}
}
return validSubkeyBinding && validPrimaryKeyBinding;
}
static boolean isEncryptionKey(PGPPublicKey key) {
if (!key.isEncryptionKey()) {
return false;
}
if (key.getVersion() <= 3) {
// this must be true now
return key.isEncryptionKey();
}
// special cases
if (key.getAlgorithm() == PGPPublicKey.ELGAMAL_ENCRYPT) {
return true;
}
if (key.getAlgorithm() == PGPPublicKey.RSA_ENCRYPT) {
return true;
}
for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
continue;
}
PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
if (hashed != null
&& (hashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) {
return true;
}
PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
if (unhashed != null
&& (unhashed.getKeyFlags() & (KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE)) != 0) {
return true;
}
}
return false;
}
static boolean isExpired(PGPPublicKey key) {
Date creationDate = key.getCreationTime();
Date expiryDate = key.getValidSeconds() > 0
? new Date(creationDate.getTime() + key.getValidSeconds() * 1000) : null;
Date now = new Date();
return creationDate.after(now) || (expiryDate != null && expiryDate.before(now));
}
static boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
PGPPublicKey masterPublicKey,
PGPPublicKey signingPublicKey) {
boolean validPrimaryKeyBinding = false;
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureList eSigList;
if (pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
try {
eSigList = pkts.getEmbeddedSignatures();
} catch (IOException e) {
return false;
} catch (PGPException e) {
return false;
}
for (int j = 0; j < eSigList.size(); ++j) {
PGPSignature emSig = eSigList.get(j);
if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
try {
emSig.init(contentVerifierBuilderProvider, signingPublicKey);
validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
if (validPrimaryKeyBinding) {
break;
}
} catch (PGPException e) {
continue;
} catch (SignatureException e) {
continue;
}
}
}
}
return validPrimaryKeyBinding;
}
}

View File

@ -0,0 +1,121 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
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;
public class CachedSecretKey {
// this is the parent key ring
private final CachedSecretKeyRing mRing;
private final PGPSecretKey mKey;
private PGPPrivateKey mPrivateKey = null;
CachedSecretKey(CachedSecretKeyRing ring, PGPSecretKey key) {
mRing = ring;
mKey = key;
}
public CachedSecretKeyRing getRing() {
return mRing;
}
public void unlock(String passphrase) throws PgpGeneralException {
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
mPrivateKey = mKey.extractPrivateKey(keyDecryptor);
} catch (PGPException e) {
throw new PgpGeneralException("error extracting key!", e);
}
if(mPrivateKey == null) {
throw new PgpGeneralException("error extracting key (bad passphrase?)");
}
}
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(
mKey.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(
mKey.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);
}
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()
}
}

View File

@ -0,0 +1,74 @@
package org.sufficientlysecure.keychain.pgp;
import org.spongycastle.bcpg.sig.KeyFlags;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.sufficientlysecure.keychain.util.IterableIterator;
public class CachedSecretKeyRing extends CachedKeyRing {
private PGPSecretKeyRing mRing;
public CachedSecretKeyRing(long masterKeyId, int keySize, boolean isRevoked,
boolean canCertify, long creation, long expiry, int algorithm,
byte[] fingerprint, String userId, int verified, boolean hasSecret,
byte[] blob)
{
super(masterKeyId, keySize, isRevoked, canCertify, creation, expiry,
algorithm, fingerprint, userId, verified, hasSecret);
mRing = (PGPSecretKeyRing) PgpConversionHelper.BytesToPGPKeyRing(blob);
}
CachedSecretKey getSubKey(long id) {
return new CachedSecretKey(this, mRing.getSecretKey(id));
}
/** This returns the subkey that should be used for signing.
* At this point, this is simply the first suitable subkey.
*/
CachedSecretKey getSigningSubKey() {
for (PGPSecretKey key : new IterableIterator<PGPSecretKey>(mRing.getSecretKeys())) {
if (isSigningKey(key.getPublicKey())) {
return new CachedSecretKey(this, key);
}
}
// TODO exception
return null;
}
@SuppressWarnings("unchecked")
public static boolean isSigningKey(PGPPublicKey key) {
if (key.getVersion() <= 3) {
return true;
}
// special case
if (key.getAlgorithm() == PGPPublicKey.RSA_SIGN) {
return true;
}
for (PGPSignature sig : new IterableIterator<PGPSignature>(key.getSignatures())) {
if (key.isMasterKey() && sig.getKeyID() != key.getKeyID()) {
continue;
}
PGPSignatureSubpacketVector hashed = sig.getHashedSubPackets();
if (hashed != null && (hashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) {
return true;
}
PGPSignatureSubpacketVector unhashed = sig.getUnhashedSubPackets();
if (unhashed != null && (unhashed.getKeyFlags() & KeyFlags.SIGN_DATA) != 0) {
return true;
}
}
return false;
}
}

View File

@ -18,10 +18,7 @@
package org.sufficientlysecure.keychain.pgp;
import android.net.Uri;
import org.spongycastle.bcpg.ArmoredInputStream;
import org.spongycastle.bcpg.SignatureSubpacketTags;
import org.spongycastle.openpgp.PGPCompressedData;
import org.spongycastle.openpgp.PGPEncryptedData;
import org.spongycastle.openpgp.PGPEncryptedDataList;
@ -31,29 +28,18 @@ import org.spongycastle.openpgp.PGPObjectFactory;
import org.spongycastle.openpgp.PGPOnePassSignature;
import org.spongycastle.openpgp.PGPOnePassSignatureList;
import org.spongycastle.openpgp.PGPPBEEncryptedData;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyEncryptedData;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureList;
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
import org.spongycastle.openpgp.PGPUtil;
import org.spongycastle.openpgp.operator.PBEDataDecryptorFactory;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.PGPDigestCalculatorProvider;
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.InputData;
@ -67,7 +53,6 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.security.SignatureException;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
/**
@ -248,7 +233,7 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encryptedDataAsymmetric = null;
PGPPBEEncryptedData encryptedDataSymmetric = null;
PGPSecretKey secretEncryptionKey = null;
CachedSecretKey secretEncryptionKey = null;
Iterator<?> it = enc.getEncryptedDataObjects();
boolean asymmetricPacketFound = false;
boolean symmetricPacketFound = false;
@ -260,15 +245,13 @@ public class PgpDecryptVerify {
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) obj;
long masterKeyId;
PGPSecretKeyRing secretKeyRing;
CachedSecretKeyRing secretKeyRing;
try {
// get master key id for this encryption key id
masterKeyId = mProviderHelper.getMasterKeyId(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(encData.getKeyID()))
);
// get actual keyring object based on master key id
secretKeyRing = mProviderHelper.getPGPSecretKeyRing(masterKeyId);
secretKeyRing = mProviderHelper.getCachedSecretKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
Long.toString(encData.getKeyID()))
);
} catch (ProviderHelper.NotFoundException e) {
// continue with the next packet in the while loop
continue;
@ -278,13 +261,14 @@ public class PgpDecryptVerify {
continue;
}
// get subkey which has been used for this encryption packet
secretEncryptionKey = secretKeyRing.getSecretKey(encData.getKeyID());
secretEncryptionKey = secretKeyRing.getSubKey(encData.getKeyID());
if (secretEncryptionKey == null) {
// continue with the next packet in the while loop
continue;
}
/* secret key exists in database! */
long masterKeyId = secretEncryptionKey.getRing().getMasterKeyId();
// allow only specific keys for decryption?
if (mAllowedKeyIds != null) {
@ -359,23 +343,15 @@ public class PgpDecryptVerify {
} else if (asymmetricPacketFound) {
currentProgress += 5;
updateProgress(R.string.progress_extracting_key, currentProgress, 100);
PGPPrivateKey privateKey;
try {
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
mPassphrase.toCharArray());
privateKey = secretEncryptionKey.extractPrivateKey(keyDecryptor);
} catch (PGPException e) {
secretEncryptionKey.unlock(mPassphrase);
} catch (PgpGeneralException e) {
throw new WrongPassphraseException();
}
if (privateKey == null) {
throw new KeyExtractionException();
}
currentProgress += 5;
updateProgress(R.string.progress_preparing_streams, currentProgress, 100);
PublicKeyDataDecryptorFactory decryptorFactory = new JcePublicKeyDataDecryptorFactoryBuilder()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(privateKey);
PublicKeyDataDecryptorFactory decryptorFactory = secretEncryptionKey.getDecryptorFactory();
clear = encryptedDataAsymmetric.getDataStream(decryptorFactory);
@ -388,10 +364,9 @@ public class PgpDecryptVerify {
PGPObjectFactory plainFact = new PGPObjectFactory(clear);
Object dataChunk = plainFact.nextObject();
PGPOnePassSignature signature = null;
OpenPgpSignatureResultBuilder signatureResultBuilder = new OpenPgpSignatureResultBuilder();
PGPPublicKey signatureKey = null;
int signatureIndex = -1;
CachedPublicKey signingKey = null;
if (dataChunk instanceof PGPCompressedData) {
updateProgress(R.string.progress_decompressing_data, currentProgress, 100);
@ -403,6 +378,8 @@ public class PgpDecryptVerify {
currentProgress += 10;
}
PGPOnePassSignature signature = null;
if (dataChunk instanceof PGPOnePassSignatureList) {
updateProgress(R.string.progress_processing_signature, currentProgress, 100);
@ -410,19 +387,15 @@ public class PgpDecryptVerify {
// go through all signatures
// and find out for which signature we have a key in our database
Long masterKeyId = null;
String primaryUserId = null;
for (int i = 0; i < sigList.size(); ++i) {
try {
Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
Long.toString(sigList.get(i).getKeyID()));
Map<String, Object> data = mProviderHelper.getGenericData(uri,
new String[] { KeyRings.MASTER_KEY_ID, KeyRings.USER_ID },
new int[] { ProviderHelper.FIELD_TYPE_INTEGER,
ProviderHelper.FIELD_TYPE_STRING }
long sigKeyId = sigList.get(i).getKeyID();
CachedPublicKeyRing signingRing = mProviderHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
Long.toString(sigKeyId)
)
);
masterKeyId = (Long) data.get(KeyRings.MASTER_KEY_ID);
primaryUserId = (String) data.get(KeyRings.USER_ID);
signingKey = signingRing.getSubkey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@ -430,43 +403,17 @@ public class PgpDecryptVerify {
}
}
if (masterKeyId != null) {
if (signingKey != null) {
// key found in our database!
signature = sigList.get(signatureIndex);
PGPPublicKeyRing publicKeyRing = null;
try {
publicKeyRing = mProviderHelper
.getPGPPublicKeyRing(masterKeyId);
} catch (ProviderHelper.NotFoundException e) {
// can't happen
}
// get the subkey which has been used to generate this signature
signatureKey = publicKeyRing.getPublicKey(signature.getKeyID());
signatureResultBuilder.signatureAvailable(true);
signatureResultBuilder.knownKey(true);
signatureResultBuilder.userId(primaryUserId);
signatureResultBuilder.keyId(masterKeyId);
signatureResultBuilder.keyId(signingKey.getKeyRing().getMasterKeyId());
signatureResultBuilder.userId(signingKey.getKeyRing().getPrimaryUserId());
signatureResultBuilder.signatureKeyCertified(signingKey.getKeyRing().getVerified() > 0);
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
signature.init(contentVerifierBuilderProvider, signatureKey);
// get certification status of this key
boolean isSignatureKeyCertified;
try {
Object data = mProviderHelper.getGenericData(
KeychainContract.KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)),
KeyRings.VERIFIED,
ProviderHelper.FIELD_TYPE_INTEGER);
isSignatureKeyCertified = ((Long) data > 0);
} catch (ProviderHelper.NotFoundException e) {
isSignatureKeyCertified = false;
}
signatureResultBuilder.signatureKeyCertified(isSignatureKeyCertified);
signingKey.initSignature(signature);
} else {
// no key in our database -> return "unknown pub key" status including the first key id
if (!sigList.isEmpty()) {
@ -541,7 +488,7 @@ public class PgpDecryptVerify {
// Verify signature and check binding signatures
boolean validSignature = signature.verify(messageSignature);
boolean validKeyBinding = verifyKeyBinding(messageSignature, signatureKey);
boolean validKeyBinding = signingKey.getKeyRing().verifySubkeyBinding(signingKey);
signatureResultBuilder.validSignature(validSignature);
signatureResultBuilder.validKeyBinding(validKeyBinding);
@ -617,22 +564,20 @@ public class PgpDecryptVerify {
throw new InvalidDataException();
}
CachedPublicKey signingKey = null;
int signatureIndex = -1;
// go through all signatures
// and find out for which signature we have a key in our database
Long masterKeyId = null;
String primaryUserId = null;
int signatureIndex = 0;
for (int i = 0; i < sigList.size(); ++i) {
try {
Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
Long.toString(sigList.get(i).getKeyID()));
Map<String, Object> data = mProviderHelper.getGenericData(uri,
new String[] { KeyRings.MASTER_KEY_ID, KeyRings.USER_ID },
new int[] { ProviderHelper.FIELD_TYPE_INTEGER,
ProviderHelper.FIELD_TYPE_STRING }
long sigKeyId = sigList.get(i).getKeyID();
CachedPublicKeyRing signingRing = mProviderHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(
Long.toString(sigKeyId)
)
);
masterKeyId = (Long) data.get(KeyRings.MASTER_KEY_ID);
primaryUserId = (String) data.get(KeyRings.USER_ID);
signingKey = signingRing.getSubkey(sigKeyId);
signatureIndex = i;
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found!");
@ -641,44 +586,18 @@ public class PgpDecryptVerify {
}
PGPSignature signature = null;
PGPPublicKey signatureKey = null;
if (masterKeyId != null) {
if (signingKey != null) {
// key found in our database!
signature = sigList.get(signatureIndex);
PGPPublicKeyRing publicKeyRing = null;
try {
publicKeyRing = mProviderHelper
.getPGPPublicKeyRing(masterKeyId);
} catch (ProviderHelper.NotFoundException e) {
// can't happen
}
// get the subkey which has been used to generate this signature
signatureKey = publicKeyRing.getPublicKey(signature.getKeyID());
signatureResultBuilder.signatureAvailable(true);
signatureResultBuilder.knownKey(true);
signatureResultBuilder.userId(primaryUserId);
signatureResultBuilder.keyId(masterKeyId);
signatureResultBuilder.keyId(signingKey.getKeyRing().getMasterKeyId());
signatureResultBuilder.userId(signingKey.getKeyRing().getPrimaryUserId());
signatureResultBuilder.signatureKeyCertified(signingKey.getKeyRing().getVerified() > 0);
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
signature.init(contentVerifierBuilderProvider, signatureKey);
// get certification status of this key
boolean isSignatureKeyCertified;
try {
Object data = mProviderHelper.getGenericData(
KeychainContract.KeyRings.buildUnifiedKeyRingUri(Long.toString(masterKeyId)),
KeyRings.VERIFIED,
ProviderHelper.FIELD_TYPE_INTEGER);
isSignatureKeyCertified = ((Long) data > 0);
} catch (ProviderHelper.NotFoundException e) {
isSignatureKeyCertified = false;
}
signatureResultBuilder.signatureKeyCertified(isSignatureKeyCertified);
signingKey.initSignature(signature);
} else {
// no key in our database -> return "unknown pub key" status including the first key id
if (!sigList.isEmpty()) {
@ -710,7 +629,7 @@ public class PgpDecryptVerify {
// Verify signature and check binding signatures
boolean validSignature = signature.verify();
boolean validKeyBinding = verifyKeyBinding(signature, signatureKey);
boolean validKeyBinding = signingKey.getKeyRing().verifySubkeyBinding(signingKey);
signatureResultBuilder.validSignature(validSignature);
signatureResultBuilder.validKeyBinding(validKeyBinding);
@ -722,113 +641,6 @@ public class PgpDecryptVerify {
return result;
}
private boolean verifyKeyBinding(PGPSignature signature, PGPPublicKey signatureKey) {
long signatureKeyId = signature.getKeyID();
boolean validKeyBinding = false;
PGPPublicKey mKey = null;
try {
PGPPublicKeyRing signKeyRing = mProviderHelper.getPGPPublicKeyRingWithKeyId(
signatureKeyId);
mKey = signKeyRing.getPublicKey();
} catch (ProviderHelper.NotFoundException e) {
Log.d(Constants.TAG, "key not found");
}
if (signature.getKeyID() != mKey.getKeyID()) {
validKeyBinding = verifyKeyBinding(mKey, signatureKey);
} else { //if the key used to make the signature was the master key, no need to check binding sigs
validKeyBinding = true;
}
return validKeyBinding;
}
private boolean verifyKeyBinding(PGPPublicKey masterPublicKey, PGPPublicKey signingPublicKey) {
boolean validSubkeyBinding = false;
boolean validTempSubkeyBinding = false;
boolean validPrimaryKeyBinding = false;
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
Iterator<PGPSignature> itr = signingPublicKey.getSignatures();
while (itr.hasNext()) { //what does gpg do if the subkey binding is wrong?
//gpg has an invalid subkey binding error on key import I think, but doesn't shout
//about keys without subkey signing. Can't get it to import a slightly broken one
//either, so we will err on bad subkey binding here.
PGPSignature sig = itr.next();
if (sig.getKeyID() == masterPublicKey.getKeyID() &&
sig.getSignatureType() == PGPSignature.SUBKEY_BINDING) {
//check and if ok, check primary key binding.
try {
sig.init(contentVerifierBuilderProvider, masterPublicKey);
validTempSubkeyBinding = sig.verifyCertification(masterPublicKey, signingPublicKey);
} catch (PGPException e) {
continue;
} catch (SignatureException e) {
continue;
}
if (validTempSubkeyBinding) {
validSubkeyBinding = true;
}
if (validTempSubkeyBinding) {
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getUnhashedSubPackets(),
masterPublicKey, signingPublicKey);
if (validPrimaryKeyBinding) {
break;
}
validPrimaryKeyBinding = verifyPrimaryKeyBinding(sig.getHashedSubPackets(),
masterPublicKey, signingPublicKey);
if (validPrimaryKeyBinding) {
break;
}
}
}
}
return (validSubkeyBinding & validPrimaryKeyBinding);
}
private boolean verifyPrimaryKeyBinding(PGPSignatureSubpacketVector pkts,
PGPPublicKey masterPublicKey,
PGPPublicKey signingPublicKey) {
boolean validPrimaryKeyBinding = false;
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
new JcaPGPContentVerifierBuilderProvider()
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
PGPSignatureList eSigList;
if (pkts.hasSubpacket(SignatureSubpacketTags.EMBEDDED_SIGNATURE)) {
try {
eSigList = pkts.getEmbeddedSignatures();
} catch (IOException e) {
return false;
} catch (PGPException e) {
return false;
}
for (int j = 0; j < eSigList.size(); ++j) {
PGPSignature emSig = eSigList.get(j);
if (emSig.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) {
try {
emSig.init(contentVerifierBuilderProvider, signingPublicKey);
validPrimaryKeyBinding = emSig.verifyCertification(masterPublicKey, signingPublicKey);
if (validPrimaryKeyBinding) {
break;
}
} catch (PGPException e) {
continue;
} catch (SignatureException e) {
continue;
}
}
}
}
return validPrimaryKeyBinding;
}
/**
* Mostly taken from ClearSignedFileProcessor in Bouncy Castle
*

View File

@ -32,8 +32,8 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.service.KeychainIntentService;
import org.sufficientlysecure.keychain.ui.adapter.ImportKeysListEntry;
@ -208,9 +208,11 @@ public class PgpImportExport {
updateProgress(progress * 100 / masterKeyIdsSize, 100);
try {
PGPPublicKeyRing publicKeyRing = mProviderHelper.getPGPPublicKeyRing(pubKeyMasterId);
CachedPublicKeyRing ring = mProviderHelper.getCachedPublicKeyRing(
KeychainContract.KeyRings.buildGenericKeyRingUri(pubKeyMasterId)
);
publicKeyRing.encode(arOutStream);
ring.encode(arOutStream);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
// TODO: inform user?

View File

@ -52,14 +52,17 @@ public class PgpKeyHelper {
private static final Pattern USER_ID_PATTERN = Pattern.compile("^(.*?)(?: \\((.*)\\))?(?: <(.*)>)?$");
@Deprecated
public static Date getCreationDate(PGPPublicKey key) {
return key.getCreationTime();
}
@Deprecated
public static Date getCreationDate(PGPSecretKey key) {
return key.getPublicKey().getCreationTime();
}
@Deprecated
public static Date getExpiryDate(PGPPublicKey key) {
Date creationDate = getCreationDate(key);
if (key.getValidDays() == 0) {
@ -73,10 +76,12 @@ public class PgpKeyHelper {
return calendar.getTime();
}
@Deprecated
public static Date getExpiryDate(PGPSecretKey key) {
return getExpiryDate(key.getPublicKey());
}
@Deprecated
public static boolean isExpired(PGPPublicKey key) {
Date creationDate = getCreationDate(key);
Date expiryDate = getExpiryDate(key);
@ -89,6 +94,7 @@ public class PgpKeyHelper {
}
@SuppressWarnings("unchecked")
@Deprecated
public static PGPSecretKey getKeyNum(PGPSecretKeyRing keyRing, long num) {
long cnt = 0;
if (keyRing == null) {
@ -105,6 +111,7 @@ public class PgpKeyHelper {
}
@SuppressWarnings("unchecked")
@Deprecated
private static Vector<PGPPublicKey> getEncryptKeys(PGPPublicKeyRing keyRing) {
Vector<PGPPublicKey> encryptKeys = new Vector<PGPPublicKey>();
@ -118,6 +125,7 @@ public class PgpKeyHelper {
}
@SuppressWarnings("unchecked")
@Deprecated
private static Vector<PGPSecretKey> getSigningKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = new Vector<PGPSecretKey>();
@ -131,6 +139,7 @@ public class PgpKeyHelper {
}
@SuppressWarnings("unchecked")
@Deprecated
private static Vector<PGPSecretKey> getCertificationKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = new Vector<PGPSecretKey>();
@ -143,26 +152,7 @@ public class PgpKeyHelper {
return signingKeys;
}
private static Vector<PGPPublicKey> getUsableEncryptKeys(PGPPublicKeyRing keyRing) {
Vector<PGPPublicKey> usableKeys = new Vector<PGPPublicKey>();
Vector<PGPPublicKey> encryptKeys = getEncryptKeys(keyRing);
PGPPublicKey masterKey = null;
for (int i = 0; i < encryptKeys.size(); ++i) {
PGPPublicKey key = encryptKeys.get(i);
if (!isExpired(key) && !key.isRevoked()) {
if (key.isMasterKey()) {
masterKey = key;
} else {
usableKeys.add(key);
}
}
}
if (masterKey != null) {
usableKeys.add(masterKey);
}
return usableKeys;
}
@Deprecated
private static Vector<PGPSecretKey> getUsableCertificationKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
Vector<PGPSecretKey> signingKeys = getCertificationKeys(keyRing);
@ -181,6 +171,7 @@ public class PgpKeyHelper {
return usableKeys;
}
@Deprecated
private static Vector<PGPSecretKey> getUsableSigningKeys(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> usableKeys = new Vector<PGPSecretKey>();
Vector<PGPSecretKey> signingKeys = getSigningKeys(keyRing);
@ -199,16 +190,6 @@ public class PgpKeyHelper {
return usableKeys;
}
public static PGPPublicKey getFirstEncryptSubkey(PGPPublicKeyRing keyRing) {
Vector<PGPPublicKey> encryptKeys = getUsableEncryptKeys(keyRing);
if (encryptKeys.size() == 0) {
Log.e(Constants.TAG, "encryptKeys is null!");
return null;
}
return encryptKeys.get(0);
}
public static PGPSecretKey getFirstCertificationSubkey(PGPSecretKeyRing keyRing) {
Vector<PGPSecretKey> signingKeys = getUsableCertificationKeys(keyRing);
if (signingKeys.size() == 0) {
@ -253,6 +234,7 @@ public class PgpKeyHelper {
}
@SuppressWarnings("unchecked")
@Deprecated
public static boolean isEncryptionKey(PGPPublicKey key) {
if (!key.isEncryptionKey()) {
return false;
@ -298,6 +280,7 @@ public class PgpKeyHelper {
}
@SuppressWarnings("unchecked")
@Deprecated
public static boolean isSigningKey(PGPPublicKey key) {
if (key.getVersion() <= 3) {
return true;
@ -328,11 +311,13 @@ public class PgpKeyHelper {
return false;
}
@Deprecated
public static boolean isSigningKey(PGPSecretKey key) {
return isSigningKey(key.getPublicKey());
}
@SuppressWarnings("unchecked")
@Deprecated
public static boolean isCertificationKey(PGPPublicKey key) {
if (key.getVersion() <= 3) {
return true;
@ -358,11 +343,13 @@ public class PgpKeyHelper {
return false;
}
@Deprecated
public static boolean isAuthenticationKey(PGPSecretKey key) {
return isAuthenticationKey(key.getPublicKey());
}
@SuppressWarnings("unchecked")
@Deprecated
public static boolean isAuthenticationKey(PGPPublicKey key) {
if (key.getVersion() <= 3) {
return true;
@ -388,6 +375,7 @@ public class PgpKeyHelper {
return false;
}
@Deprecated
public static boolean isCertificationKey(PGPSecretKey key) {
return isCertificationKey(key.getPublicKey());
}

View File

@ -25,25 +25,14 @@ import org.spongycastle.openpgp.PGPEncryptedDataGenerator;
import org.spongycastle.openpgp.PGPException;
import org.spongycastle.openpgp.PGPLiteralData;
import org.spongycastle.openpgp.PGPLiteralDataGenerator;
import org.spongycastle.openpgp.PGPPrivateKey;
import org.spongycastle.openpgp.PGPPublicKey;
import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.PGPSignatureGenerator;
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.spongycastle.openpgp.PGPV3SignatureGenerator;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator;
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder;
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.Progressable;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
import org.sufficientlysecure.keychain.provider.ProviderHelper;
import org.sufficientlysecure.keychain.util.InputData;
import org.sufficientlysecure.keychain.util.Log;
@ -279,19 +268,15 @@ public class PgpSignEncrypt {
}
/* Get keys for signature generation for later usage */
PGPSecretKey signingKey = null;
PGPSecretKeyRing signingKeyRing = null;
PGPPrivateKey signaturePrivateKey = null;
String signingUserId = null;
CachedSecretKey signingKey = null;
if (enableSignature) {
CachedSecretKeyRing signingKeyRing = null;
try {
signingKeyRing = mProviderHelper.getPGPSecretKeyRing(mSignatureMasterKeyId);
signingUserId = (String) mProviderHelper.getUnifiedData(mSignatureMasterKeyId,
KeychainContract.KeyRings.USER_ID, ProviderHelper.FIELD_TYPE_STRING);
signingKeyRing = mProviderHelper.getCachedSecretKeyRing(mSignatureMasterKeyId);
} catch (ProviderHelper.NotFoundException e) {
throw new NoSigningKeyException();
}
signingKey = PgpKeyHelper.getFirstSigningSubkey(signingKeyRing);
signingKey = signingKeyRing.getSigningSubKey();
if (signingKey == null) {
throw new NoSigningKeyException();
}
@ -302,10 +287,9 @@ public class PgpSignEncrypt {
updateProgress(R.string.progress_extracting_signature_key, 0, 100);
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mSignaturePassphrase.toCharArray());
signaturePrivateKey = signingKey.extractPrivateKey(keyDecryptor);
if (signaturePrivateKey == null) {
try {
signingKey.unlock(mSignaturePassphrase);
} catch (PgpGeneralException e) {
throw new KeyExtractionException();
}
}
@ -333,13 +317,12 @@ public class PgpSignEncrypt {
// Asymmetric encryption
for (long id : mEncryptionMasterKeyIds) {
try {
PGPPublicKeyRing keyRing = mProviderHelper.getPGPPublicKeyRing(id);
PGPPublicKey key = PgpKeyHelper.getFirstEncryptSubkey(keyRing);
if (key != null) {
JcePublicKeyKeyEncryptionMethodGenerator pubKeyEncryptionGenerator =
new JcePublicKeyKeyEncryptionMethodGenerator(key);
cPk.addMethod(pubKeyEncryptionGenerator);
}
CachedPublicKeyRing keyRing = mProviderHelper.getCachedPublicKeyRing(
KeyRings.buildUnifiedKeyRingUri(Long.toString(id)));
CachedPublicKey key = keyRing.getFirstEncryptSubkey();
cPk.addMethod(key.getPubKeyEncryptionGenerator());
} catch (PgpGeneralException e) {
Log.e(Constants.TAG, "key not found!", e);
} catch (ProviderHelper.NotFoundException e) {
Log.e(Constants.TAG, "key not found!", e);
}
@ -353,29 +336,18 @@ public class PgpSignEncrypt {
if (enableSignature) {
updateProgress(R.string.progress_preparing_signature, 10, 100);
// content signer based on signing key algorithm and chosen hash algorithm
JcaPGPContentSignerBuilder contentSignerBuilder = new JcaPGPContentSignerBuilder(
signingKey.getPublicKey().getAlgorithm(), mSignatureHashAlgorithm)
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
int signatureType;
if (mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption) {
// for sign-only ascii text
signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
} else {
signatureType = PGPSignature.BINARY_DOCUMENT;
}
if (mSignatureForceV3) {
signatureV3Generator = new PGPV3SignatureGenerator(contentSignerBuilder);
signatureV3Generator.init(signatureType, signaturePrivateKey);
} else {
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
signatureGenerator.init(signatureType, signaturePrivateKey);
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
spGen.setSignerUserID(false, signingUserId);
signatureGenerator.setHashedSubpackets(spGen.generate());
try {
boolean cleartext = mCleartextInput && mEnableAsciiArmorOutput && !enableEncryption;
if (mSignatureForceV3) {
signatureV3Generator = signingKey.getV3SignatureGenerator(
mSignatureHashAlgorithm,cleartext);
} else {
signatureGenerator = signingKey.getSignatureGenerator(
mSignatureHashAlgorithm, cleartext);
}
} catch (PgpGeneralException e) {
// TODO throw correct type of exception (which shouldn't be PGPException)
throw new KeyExtractionException();
}
}

View File

@ -110,6 +110,8 @@ public class KeychainContract {
public static final String HAS_ANY_SECRET = "has_any_secret";
public static final String HAS_ENCRYPT = "has_encrypt";
public static final String HAS_SIGN = "has_sign";
public static final String PUBKEY_DATA = "pubkey_data";
public static final String PRIVKEY_DATA = "privkey_data";
public static final Uri CONTENT_URI = BASE_CONTENT_URI_INTERNAL.buildUpon()
.appendPath(BASE_KEY_RINGS).build();
@ -123,6 +125,10 @@ public class KeychainContract {
return CONTENT_URI.buildUpon().appendPath(PATH_UNIFIED).build();
}
public static Uri buildGenericKeyRingUri(long masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(Long.toString(masterKeyId)).build();
}
public static Uri buildGenericKeyRingUri(String masterKeyId) {
return CONTENT_URI.buildUpon().appendPath(masterKeyId).build();
}

View File

@ -254,6 +254,12 @@ public class KeychainProvider extends ContentProvider {
projectionMap.put(KeyRings.FINGERPRINT, Keys.FINGERPRINT);
projectionMap.put(KeyRings.USER_ID, UserIds.USER_ID);
projectionMap.put(KeyRings.VERIFIED, KeyRings.VERIFIED);
projectionMap.put(KeyRings.PUBKEY_DATA,
Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.KEY_RING_DATA
+ " AS " + KeyRings.PUBKEY_DATA);
projectionMap.put(KeyRings.PRIVKEY_DATA,
Tables.KEY_RINGS_SECRET + "." + KeyRingData.KEY_RING_DATA
+ " AS " + KeyRings.PRIVKEY_DATA);
projectionMap.put(KeyRings.HAS_SECRET, KeyRings.HAS_SECRET);
projectionMap.put(KeyRings.HAS_ANY_SECRET,
"(EXISTS (SELECT * FROM " + Tables.KEY_RINGS_SECRET
@ -295,6 +301,22 @@ public class KeychainProvider extends ContentProvider {
+ " AND " + Tables.CERTS + "." + Certs.VERIFIED
+ " = " + Certs.VERIFIED_SECRET
+ ")"
// fairly expensive join (due to blob data), only do it when requested
+ (Arrays.asList(projection).contains(KeyRings.PUBKEY_DATA) ?
" INNER JOIN " + Tables.KEY_RINGS_PUBLIC + " ON ("
+ Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " = "
+ Tables.KEY_RINGS_PUBLIC + "." + KeyRingData.MASTER_KEY_ID
+ ")"
: "")
// fairly expensive join (due to blob data), only do it when requested
+ (Arrays.asList(projection).contains(KeyRings.PRIVKEY_DATA) ?
" LEFT JOIN " + Tables.KEY_RINGS_SECRET + " ON ("
+ Tables.KEYS + "." + Keys.MASTER_KEY_ID
+ " = "
+ Tables.KEY_RINGS_SECRET + "." + KeyRingData.MASTER_KEY_ID
+ ")"
: "")
);
qb.appendWhere(Tables.KEYS + "." + Keys.RANK + " = 0");
// in case there are multiple verifying certificates

View File

@ -36,9 +36,10 @@ import org.spongycastle.openpgp.PGPPublicKeyRing;
import org.spongycastle.openpgp.PGPSecretKey;
import org.spongycastle.openpgp.PGPSecretKeyRing;
import org.spongycastle.openpgp.PGPSignature;
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.pgp.CachedSecretKeyRing;
import org.sufficientlysecure.keychain.pgp.CachedPublicKeyRing;
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
@ -167,6 +168,7 @@ public class ProviderHelper {
}
}
@Deprecated
public Map<Long, PGPKeyRing> getPGPKeyRings(Uri queryUri) {
Cursor cursor = mContentResolver.query(queryUri,
new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA},
@ -188,6 +190,94 @@ public class ProviderHelper {
return result;
}
public CachedPublicKeyRing getCachedPublicKeyRing(Uri queryUri) throws NotFoundException {
Cursor cursor = mContentResolver.query(queryUri,
new String[] {
KeyRings.MASTER_KEY_ID, KeyRings.KEY_SIZE,
KeyRings.IS_REVOKED, KeyRings.CAN_CERTIFY,
KeyRings.CREATION, KeyRings.EXPIRY,
KeyRings.ALGORITHM, KeyRings.FINGERPRINT,
KeyRings.USER_ID, KeyRings.VERIFIED,
KeyRings.HAS_SECRET, KeyRings.PUBKEY_DATA
}, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
long masterKeyId = cursor.getLong(0);
int keySize = cursor.getInt(1);
boolean isRevoked = cursor.getInt(2) > 0;
boolean canCertify = cursor.getInt(3) > 0;
long creation = cursor.getLong(4);
long expiry = cursor.getLong(5);
int algorithm = cursor.getInt(6);
byte[] fingerprint = cursor.getBlob(7);
String userId = cursor.getString(8);
int verified = cursor.getInt(9);
boolean hasSecret = cursor.getInt(10) > 0;
byte[] pubkey = cursor.getBlob(11);
return new CachedPublicKeyRing(
masterKeyId, keySize, isRevoked, canCertify,
creation, expiry, algorithm, fingerprint,
userId, verified, hasSecret, pubkey
);
} else {
throw new NotFoundException("Key not found!");
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}
public CachedSecretKeyRing getCachedSecretKeyRing(long id) throws NotFoundException {
return getCachedSecretKeyRing(KeyRings.buildUnifiedKeyRingUri(Long.toString(id)));
}
public CachedSecretKeyRing getCachedSecretKeyRing(Uri queryUri) throws NotFoundException {
Cursor cursor = mContentResolver.query(queryUri,
new String[] {
KeyRings.MASTER_KEY_ID, KeyRings.KEY_SIZE,
KeyRings.IS_REVOKED, KeyRings.CAN_CERTIFY,
KeyRings.CREATION, KeyRings.EXPIRY,
KeyRings.ALGORITHM, KeyRings.FINGERPRINT,
KeyRings.USER_ID, KeyRings.VERIFIED,
KeyRings.HAS_SECRET, KeyRings.PRIVKEY_DATA
}, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
// check if a privkey is actually available
byte[] privkey = cursor.getBlob(11);
if(privkey == null) {
throw new NotFoundException("Key found, but no secret key available!");
}
long masterKeyId = cursor.getLong(0);
int keySize = cursor.getInt(1);
boolean isRevoked = cursor.getInt(2) > 0;
boolean canCertify = cursor.getInt(3) > 0;
long creation = cursor.getLong(4);
long expiry = cursor.getLong(5);
int algorithm = cursor.getInt(6);
byte[] fingerprint = cursor.getBlob(7);
String userId = cursor.getString(8);
int verified = cursor.getInt(9);
boolean hasSecret = cursor.getInt(10) > 0;
return new CachedSecretKeyRing(
masterKeyId, keySize, isRevoked, canCertify,
creation, expiry, algorithm, fingerprint,
userId, verified, hasSecret, privkey
);
} else {
throw new NotFoundException("Key not found!");
}
} finally {
if (cursor != null) {
cursor.close();
}
}
}
@Deprecated
public PGPKeyRing getPGPKeyRing(Uri queryUri) throws NotFoundException {
Map<Long, PGPKeyRing> result = getPGPKeyRings(queryUri);
if (result.isEmpty()) {
@ -197,6 +287,7 @@ public class ProviderHelper {
}
}
@Deprecated
public PGPPublicKeyRing getPGPPublicKeyRingWithKeyId(long keyId)
throws NotFoundException {
Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
@ -204,6 +295,7 @@ public class ProviderHelper {
return getPGPPublicKeyRing(masterKeyId);
}
@Deprecated
public PGPSecretKeyRing getPGPSecretKeyRingWithKeyId(long keyId)
throws NotFoundException {
Uri uri = KeyRings.buildUnifiedKeyRingsFindBySubkeyUri(Long.toString(keyId));
@ -214,6 +306,7 @@ public class ProviderHelper {
/**
* Retrieves the actual PGPPublicKeyRing object from the database blob based on the masterKeyId
*/
@Deprecated
public PGPPublicKeyRing getPGPPublicKeyRing(long masterKeyId) throws NotFoundException {
Uri queryUri = KeyRingData.buildPublicKeyRingUri(Long.toString(masterKeyId));
return (PGPPublicKeyRing) getPGPKeyRing(queryUri);
@ -222,6 +315,7 @@ public class ProviderHelper {
/**
* Retrieves the actual PGPSecretKeyRing object from the database blob based on the maserKeyId
*/
@Deprecated
public PGPSecretKeyRing getPGPSecretKeyRing(long masterKeyId) throws NotFoundException {
Uri queryUri = KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId));
return (PGPSecretKeyRing) getPGPKeyRing(queryUri);