mirror of
https://github.com/moparisthebest/open-keychain
synced 2025-03-05 11:49:48 -05:00
new savekeyring operation (mostly stub)
This commit is contained in:
parent
6415290b2d
commit
a53da491c0
@ -21,8 +21,6 @@ import org.spongycastle.openpgp.PGPKeyRing;
|
|||||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||||
import org.spongycastle.openpgp.PGPSecretKey;
|
import org.spongycastle.openpgp.PGPSecretKey;
|
||||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.spongycastle.openpgp.PGPSignature;
|
|
||||||
import org.spongycastle.openpgp.PGPSignatureList;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
@ -122,26 +120,4 @@ public class PgpConversionHelper {
|
|||||||
return new UncachedSecretKey(secKey);
|
return new UncachedSecretKey(secKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert from byte[] to PGPSignature
|
|
||||||
*
|
|
||||||
* @param sigBytes
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static PGPSignature BytesToPGPSignature(byte[] sigBytes) {
|
|
||||||
PGPObjectFactory factory = new PGPObjectFactory(sigBytes);
|
|
||||||
PGPSignatureList signatures = null;
|
|
||||||
try {
|
|
||||||
if ((signatures = (PGPSignatureList) factory.nextObject()) == null || signatures.isEmpty()) {
|
|
||||||
Log.e(Constants.TAG, "No signatures given!");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(Constants.TAG, "Error while converting to PGPSignature!", e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return signatures.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -257,7 +257,8 @@ public class PgpImportExport {
|
|||||||
updateProgress(progress * 100 / masterKeyIdsSize, 100);
|
updateProgress(progress * 100 / masterKeyIdsSize, 100);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
PGPSecretKeyRing secretKeyRing = mProviderHelper.getPGPSecretKeyRing(secretKeyMasterId);
|
WrappedSecretKeyRing secretKeyRing =
|
||||||
|
mProviderHelper.getWrappedSecretKeyRing(secretKeyMasterId);
|
||||||
secretKeyRing.encode(arOutStream);
|
secretKeyRing.encode(arOutStream);
|
||||||
} catch (ProviderHelper.NotFoundException e) {
|
} catch (ProviderHelper.NotFoundException e) {
|
||||||
Log.e(Constants.TAG, "key not found!", e);
|
Log.e(Constants.TAG, "key not found!", e);
|
||||||
@ -287,18 +288,13 @@ public class PgpImportExport {
|
|||||||
public int storeKeyRingInCache(UncachedKeyRing ring, UncachedKeyRing secretRing) {
|
public int storeKeyRingInCache(UncachedKeyRing ring, UncachedKeyRing secretRing) {
|
||||||
int status;
|
int status;
|
||||||
try {
|
try {
|
||||||
// TODO make sure these are correctly typed!
|
UncachedSecretKeyRing secretKeyRing = null;
|
||||||
PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) ring.getRing();
|
|
||||||
PGPSecretKeyRing secretKeyRing = null;
|
|
||||||
if(secretRing != null) {
|
|
||||||
secretKeyRing = (PGPSecretKeyRing) secretRing.getRing();
|
|
||||||
}
|
|
||||||
// see what type we have. we can either have a secret + public keyring, or just public
|
// see what type we have. we can either have a secret + public keyring, or just public
|
||||||
if (secretKeyRing != null) {
|
if (secretKeyRing != null) {
|
||||||
mProviderHelper.saveKeyRing(ring, secretRing);
|
mProviderHelper.saveKeyRing(ring, secretRing);
|
||||||
status = RETURN_OK;
|
status = RETURN_OK;
|
||||||
} else {
|
} else {
|
||||||
mProviderHelper.saveKeyRing(publicKeyRing);
|
mProviderHelper.saveKeyRing(ring);
|
||||||
status = RETURN_OK;
|
status = RETURN_OK;
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -35,6 +35,7 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
|
|||||||
import org.spongycastle.openpgp.PGPSignature;
|
import org.spongycastle.openpgp.PGPSignature;
|
||||||
import org.spongycastle.openpgp.PGPSignatureGenerator;
|
import org.spongycastle.openpgp.PGPSignatureGenerator;
|
||||||
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
|
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
|
||||||
|
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
||||||
import org.spongycastle.openpgp.PGPUtil;
|
import org.spongycastle.openpgp.PGPUtil;
|
||||||
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||||
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
|
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
|
||||||
@ -49,6 +50,8 @@ import org.sufficientlysecure.keychain.Constants;
|
|||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
|
||||||
import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel;
|
import org.sufficientlysecure.keychain.service.OldSaveKeyringParcel;
|
||||||
|
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
|
||||||
|
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||||
import org.sufficientlysecure.keychain.util.Primes;
|
import org.sufficientlysecure.keychain.util.Primes;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -60,9 +63,11 @@ import java.security.NoSuchProviderException;
|
|||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -644,7 +649,7 @@ public class PgpKeyOperation {
|
|||||||
|
|
||||||
for(String uid : new IterableIterator<String>(secretKeyRing.getPublicKey().getUserIDs())) {
|
for(String uid : new IterableIterator<String>(secretKeyRing.getPublicKey().getUserIDs())) {
|
||||||
for(PGPSignature sig : new IterableIterator<PGPSignature>(
|
for(PGPSignature sig : new IterableIterator<PGPSignature>(
|
||||||
secretKeyRing.getPublicKey().getSignaturesForID(uid))) {
|
secretKeyRing.getPublicKey().getSignaturesForId(uid))) {
|
||||||
Log.d(Constants.TAG, "sig: " +
|
Log.d(Constants.TAG, "sig: " +
|
||||||
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
|
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
|
||||||
}
|
}
|
||||||
@ -655,7 +660,7 @@ public class PgpKeyOperation {
|
|||||||
|
|
||||||
for(String uid : new IterableIterator<String>(publicKeyRing.getPublicKey().getUserIDs())) {
|
for(String uid : new IterableIterator<String>(publicKeyRing.getPublicKey().getUserIDs())) {
|
||||||
for(PGPSignature sig : new IterableIterator<PGPSignature>(
|
for(PGPSignature sig : new IterableIterator<PGPSignature>(
|
||||||
publicKeyRing.getPublicKey().getSignaturesForID(uid))) {
|
publicKeyRing.getPublicKey().getSignaturesForId(uid))) {
|
||||||
Log.d(Constants.TAG, "sig: " +
|
Log.d(Constants.TAG, "sig: " +
|
||||||
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
|
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
|
||||||
}
|
}
|
||||||
@ -668,6 +673,335 @@ public class PgpKeyOperation {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Pair<PGPSecretKeyRing, PGPPublicKeyRing> buildSecretKey(PGPSecretKeyRing sKR,
|
||||||
|
PGPPublicKeyRing pKR,
|
||||||
|
SaveKeyringParcel saveParcel,
|
||||||
|
String passphrase)
|
||||||
|
throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
|
||||||
|
|
||||||
|
updateProgress(R.string.progress_building_key, 0, 100);
|
||||||
|
|
||||||
|
// sort these, so we can use binarySearch later on
|
||||||
|
Arrays.sort(saveParcel.revokeSubKeys);
|
||||||
|
Arrays.sort(saveParcel.revokeUserIds);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* What's gonna happen here:
|
||||||
|
*
|
||||||
|
* 1. Unlock private key
|
||||||
|
*
|
||||||
|
* 2. Create new secret key ring
|
||||||
|
*
|
||||||
|
* 3. Copy subkeys
|
||||||
|
* - Generate revocation if requested
|
||||||
|
* - Copy old cert, or generate new if change requested
|
||||||
|
*
|
||||||
|
* 4. Generate and add new subkeys
|
||||||
|
*
|
||||||
|
* 5. Copy user ids
|
||||||
|
* - Generate revocation if requested
|
||||||
|
* - Copy old cert, or generate new if primary user id status changed
|
||||||
|
*
|
||||||
|
* 6. Add new user ids
|
||||||
|
*
|
||||||
|
* 7. Generate PublicKeyRing from SecretKeyRing
|
||||||
|
*
|
||||||
|
* 8. Return pair (PublicKeyRing,SecretKeyRing)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 1. Unlock private key
|
||||||
|
updateProgress(R.string.progress_building_key, 0, 100);
|
||||||
|
|
||||||
|
PGPPublicKey masterPublicKey = sKR.getPublicKey();
|
||||||
|
PGPPrivateKey masterPrivateKey; {
|
||||||
|
PGPSecretKey masterKey = sKR.getSecretKey();
|
||||||
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||||
|
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||||
|
masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Create new secret key ring
|
||||||
|
updateProgress(R.string.progress_certifying_master_key, 20, 100);
|
||||||
|
|
||||||
|
// Note we do NOT use PGPKeyRingGeneraor, it's just one level too high and does stuff
|
||||||
|
// we want to do manually. Instead, we simply use a list of secret keys.
|
||||||
|
ArrayList<PGPSecretKey> secretKeys = new ArrayList<PGPSecretKey>();
|
||||||
|
ArrayList<PGPPublicKey> publicKeys = new ArrayList<PGPPublicKey>();
|
||||||
|
|
||||||
|
// 3. Copy subkeys
|
||||||
|
// - Generate revocation if requested
|
||||||
|
// - Copy old cert, or generate new if change requested
|
||||||
|
for (PGPSecretKey sKey : new IterableIterator<PGPSecretKey>(sKR.getSecretKeys())) {
|
||||||
|
PGPPublicKey pKey = sKey.getPublicKey();
|
||||||
|
if (Arrays.binarySearch(saveParcel.revokeSubKeys, sKey.getKeyID()) >= 0) {
|
||||||
|
// add revocation signature to key, if there is none yet
|
||||||
|
if (!pKey.getSignaturesOfType(PGPSignature.SUBKEY_REVOCATION).hasNext()) {
|
||||||
|
// generate revocation signature
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (saveParcel.changeSubKeys.containsKey(sKey.getKeyID())) {
|
||||||
|
// change subkey flags?
|
||||||
|
SaveKeyringParcel.SubkeyChange change = saveParcel.changeSubKeys.get(sKey.getKeyID());
|
||||||
|
// remove old subkey binding signature(s?)
|
||||||
|
for (PGPSignature sig : new IterableIterator<PGPSignature>(
|
||||||
|
pKey.getSignaturesOfType(PGPSignature.SUBKEY_BINDING))) {
|
||||||
|
pKey = PGPPublicKey.removeCertification(pKey, sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate and add new signature
|
||||||
|
PGPSignature sig = generateSubkeyBindingSignature(masterPublicKey, masterPrivateKey,
|
||||||
|
sKey, pKey, change.mFlags, change.mExpiry, passphrase);
|
||||||
|
pKey = PGPPublicKey.addCertification(pKey, sig);
|
||||||
|
}
|
||||||
|
secretKeys.add(PGPSecretKey.replacePublicKey(sKey, pKey));
|
||||||
|
publicKeys.add(pKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. Generate and add new subkeys
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
// 5. Copy user ids
|
||||||
|
for (String userId : new IterableIterator<String>(masterPublicKey.getUserIDs())) {
|
||||||
|
// - Copy old cert, or generate new if primary user id status changed
|
||||||
|
boolean certified = false, revoked = false;
|
||||||
|
for (PGPSignature sig : new IterableIterator<PGPSignature>(
|
||||||
|
masterPublicKey.getSignaturesForID(userId))) {
|
||||||
|
// We know there are only revocation and certification types in here.
|
||||||
|
switch(sig.getSignatureType()) {
|
||||||
|
case PGPSignature.CERTIFICATION_REVOCATION:
|
||||||
|
revoked = true;
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case PGPSignature.DEFAULT_CERTIFICATION:
|
||||||
|
case PGPSignature.NO_CERTIFICATION:
|
||||||
|
case PGPSignature.CASUAL_CERTIFICATION:
|
||||||
|
case PGPSignature.POSITIVE_CERTIFICATION:
|
||||||
|
// Already got one? Remove this one, then.
|
||||||
|
if (certified) {
|
||||||
|
masterPublicKey = PGPPublicKey.removeCertification(
|
||||||
|
masterPublicKey, userId, sig);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
boolean primary = userId.equals(saveParcel.changePrimaryUserId);
|
||||||
|
// Generate a new one under certain circumstances
|
||||||
|
if (saveParcel.changePrimaryUserId != null &&
|
||||||
|
sig.getHashedSubPackets().isPrimaryUserID() != primary) {
|
||||||
|
PGPSignature cert = generateUserIdSignature(
|
||||||
|
masterPrivateKey, masterPublicKey, userId, primary);
|
||||||
|
PGPPublicKey.addCertification(masterPublicKey, userId, cert);
|
||||||
|
}
|
||||||
|
certified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// - Generate revocation if requested
|
||||||
|
if (!revoked && Arrays.binarySearch(saveParcel.revokeUserIds, userId) >= 0) {
|
||||||
|
PGPSignature cert = generateRevocationSignature(masterPrivateKey,
|
||||||
|
masterPublicKey, userId);
|
||||||
|
masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. Add new user ids
|
||||||
|
for(String userId : saveParcel.addUserIds) {
|
||||||
|
PGPSignature cert = generateUserIdSignature(masterPrivateKey,
|
||||||
|
masterPublicKey, userId, userId.equals(saveParcel.changePrimaryUserId));
|
||||||
|
masterPublicKey = PGPPublicKey.addCertification(masterPublicKey, userId, cert);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Generate PublicKeyRing from SecretKeyRing
|
||||||
|
updateProgress(R.string.progress_building_master_key, 30, 100);
|
||||||
|
PGPSecretKeyRing ring = new PGPSecretKeyRing(secretKeys);
|
||||||
|
|
||||||
|
// Copy all non-self uid certificates
|
||||||
|
for (String userId : new IterableIterator<String>(masterPublicKey.getUserIDs())) {
|
||||||
|
// - Copy old cert, or generate new if primary user id status changed
|
||||||
|
boolean certified = false, revoked = false;
|
||||||
|
for (PGPSignature sig : new IterableIterator<PGPSignature>(
|
||||||
|
masterPublicKey.getSignaturesForID(userId))) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PGPPublicKey newKey : publicKeys) {
|
||||||
|
PGPPublicKey oldKey = pKR.getPublicKey(newKey.getKeyID());
|
||||||
|
for (PGPSignature sig : new IterableIterator<PGPSignature>(
|
||||||
|
oldKey.getSignatures())) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If requested, set new passphrase
|
||||||
|
if (saveParcel.newPassphrase != null) {
|
||||||
|
PGPDigestCalculator sha1Calc = new JcaPGPDigestCalculatorProviderBuilder().build()
|
||||||
|
.get(HashAlgorithmTags.SHA1);
|
||||||
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
||||||
|
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
||||||
|
// Build key encryptor based on new passphrase
|
||||||
|
PBESecretKeyEncryptor keyEncryptorNew = new JcePBESecretKeyEncryptorBuilder(
|
||||||
|
PGPEncryptedData.CAST5, sha1Calc)
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||||
|
saveParcel.newPassphrase.toCharArray());
|
||||||
|
|
||||||
|
sKR = PGPSecretKeyRing.copyWithNewPassword(sKR, keyDecryptor, keyEncryptorNew);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. Return pair (PublicKeyRing,SecretKeyRing)
|
||||||
|
|
||||||
|
return new Pair<PGPSecretKeyRing, PGPPublicKeyRing>(sKR, pKR);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PGPSignature generateUserIdSignature(
|
||||||
|
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId, boolean primary)
|
||||||
|
throws IOException, PGPException, SignatureException {
|
||||||
|
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||||
|
pKey.getAlgorithm(), PGPUtil.SHA1)
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||||
|
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||||
|
subHashedPacketsGen.setSignatureCreationTime(false, new Date());
|
||||||
|
subHashedPacketsGen.setPreferredSymmetricAlgorithms(true, PREFERRED_SYMMETRIC_ALGORITHMS);
|
||||||
|
subHashedPacketsGen.setPreferredHashAlgorithms(true, PREFERRED_HASH_ALGORITHMS);
|
||||||
|
subHashedPacketsGen.setPreferredCompressionAlgorithms(true, PREFERRED_COMPRESSION_ALGORITHMS);
|
||||||
|
subHashedPacketsGen.setPrimaryUserID(false, primary);
|
||||||
|
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
|
||||||
|
sGen.init(PGPSignature.POSITIVE_CERTIFICATION, masterPrivateKey);
|
||||||
|
return sGen.generateCertification(userId, pKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PGPSignature generateRevocationSignature(
|
||||||
|
PGPPrivateKey masterPrivateKey, PGPPublicKey pKey, String userId)
|
||||||
|
throws IOException, PGPException, SignatureException {
|
||||||
|
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||||
|
pKey.getAlgorithm(), PGPUtil.SHA1)
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||||
|
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||||
|
subHashedPacketsGen.setSignatureCreationTime(false, new Date());
|
||||||
|
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
|
||||||
|
sGen.init(PGPSignature.CERTIFICATION_REVOCATION, masterPrivateKey);
|
||||||
|
return sGen.generateCertification(userId, pKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static PGPSignature generateSubkeyBindingSignature(
|
||||||
|
PGPPublicKey masterPublicKey, PGPPrivateKey masterPrivateKey,
|
||||||
|
PGPSecretKey sKey, PGPPublicKey pKey,
|
||||||
|
int flags, Long expiry, String passphrase)
|
||||||
|
throws PgpGeneralMsgIdException, IOException, PGPException, SignatureException {
|
||||||
|
|
||||||
|
// date for signing
|
||||||
|
Date todayDate = new Date();
|
||||||
|
PGPSignatureSubpacketGenerator unhashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||||
|
|
||||||
|
// If this key can sign, we need a primary key binding signature
|
||||||
|
if ((flags & KeyFlags.SIGN_DATA) != 0) {
|
||||||
|
|
||||||
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(
|
||||||
|
passphrase.toCharArray());
|
||||||
|
PGPPrivateKey subPrivateKey = sKey.extractPrivateKey(keyDecryptor);
|
||||||
|
|
||||||
|
// cross-certify signing keys
|
||||||
|
PGPSignatureSubpacketGenerator subHashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||||
|
subHashedPacketsGen.setSignatureCreationTime(false, todayDate);
|
||||||
|
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||||
|
pKey.getAlgorithm(), PGPUtil.SHA1)
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||||
|
sGen.init(PGPSignature.PRIMARYKEY_BINDING, subPrivateKey);
|
||||||
|
sGen.setHashedSubpackets(subHashedPacketsGen.generate());
|
||||||
|
PGPSignature certification = sGen.generateCertification(masterPublicKey, pKey);
|
||||||
|
unhashedPacketsGen.setEmbeddedSignature(false, certification);
|
||||||
|
}
|
||||||
|
|
||||||
|
PGPSignatureSubpacketGenerator hashedPacketsGen;
|
||||||
|
{
|
||||||
|
hashedPacketsGen = new PGPSignatureSubpacketGenerator();
|
||||||
|
hashedPacketsGen.setSignatureCreationTime(false, todayDate);
|
||||||
|
hashedPacketsGen.setKeyFlags(false, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expiry != null) {
|
||||||
|
Calendar creationDate = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
|
||||||
|
creationDate.setTime(pKey.getCreationTime());
|
||||||
|
// note that the below, (a/c) - (b/c) is *not* the same as (a - b) /c
|
||||||
|
// here we purposefully ignore partial days in each date - long type has
|
||||||
|
// no fractional part!
|
||||||
|
long numDays = (expiry / 86400000) -
|
||||||
|
(creationDate.getTimeInMillis() / 86400000);
|
||||||
|
if (numDays <= 0) {
|
||||||
|
throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
|
||||||
|
}
|
||||||
|
hashedPacketsGen.setKeyExpirationTime(false, expiry - creationDate.getTimeInMillis());
|
||||||
|
} else {
|
||||||
|
hashedPacketsGen.setKeyExpirationTime(false, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
PGPContentSignerBuilder signerBuilder = new JcaPGPContentSignerBuilder(
|
||||||
|
pKey.getAlgorithm(), PGPUtil.SHA1)
|
||||||
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
|
PGPSignatureGenerator sGen = new PGPSignatureGenerator(signerBuilder);
|
||||||
|
sGen.init(PGPSignature.SUBKEY_BINDING, masterPrivateKey);
|
||||||
|
sGen.setHashedSubpackets(hashedPacketsGen.generate());
|
||||||
|
sGen.setUnhashedSubpackets(unhashedPacketsGen.generate());
|
||||||
|
|
||||||
|
return sGen.generateCertification(masterPublicKey, pKey);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
* Simple static subclass that stores two values.
|
||||||
* <p/>
|
* <p/>
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
package org.sufficientlysecure.keychain.pgp;
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
|
import org.spongycastle.bcpg.ArmoredOutputStream;
|
||||||
import org.spongycastle.openpgp.PGPKeyRing;
|
import org.spongycastle.openpgp.PGPKeyRing;
|
||||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
import org.spongycastle.openpgp.PGPPublicKey;
|
||||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.spongycastle.openpgp.PGPUtil;
|
import org.spongycastle.openpgp.PGPUtil;
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
@ -13,6 +14,8 @@ import java.io.BufferedInputStream;
|
|||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
@ -26,6 +29,10 @@ public class UncachedKeyRing {
|
|||||||
mIsSecret = ring instanceof PGPSecretKeyRing;
|
mIsSecret = ring instanceof PGPSecretKeyRing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getMasterKeyId() {
|
||||||
|
return mRing.getPublicKey().getKeyID();
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO don't use this */
|
/* TODO don't use this */
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public PGPKeyRing getRing() {
|
public PGPKeyRing getRing() {
|
||||||
@ -36,6 +43,21 @@ public class UncachedKeyRing {
|
|||||||
return new UncachedPublicKey(mRing.getPublicKey());
|
return new UncachedPublicKey(mRing.getPublicKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Iterator<UncachedPublicKey> getPublicKeys() {
|
||||||
|
final Iterator<PGPPublicKey> it = mRing.getPublicKeys();
|
||||||
|
return new Iterator<UncachedPublicKey>() {
|
||||||
|
public void remove() {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
public UncachedPublicKey next() {
|
||||||
|
return new UncachedPublicKey(it.next());
|
||||||
|
}
|
||||||
|
public boolean hasNext() {
|
||||||
|
return it.hasNext();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isSecret() {
|
public boolean isSecret() {
|
||||||
return mIsSecret;
|
return mIsSecret;
|
||||||
}
|
}
|
||||||
@ -50,6 +72,15 @@ public class UncachedKeyRing {
|
|||||||
|
|
||||||
public static UncachedKeyRing decodePubkeyFromData(byte[] data)
|
public static UncachedKeyRing decodePubkeyFromData(byte[] data)
|
||||||
throws PgpGeneralException, IOException {
|
throws PgpGeneralException, IOException {
|
||||||
|
UncachedKeyRing ring = decodeFromData(data);
|
||||||
|
if(ring.isSecret()) {
|
||||||
|
throw new PgpGeneralException("Object not recognized as PGPPublicKeyRing!");
|
||||||
|
}
|
||||||
|
return ring;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UncachedKeyRing decodeFromData(byte[] data)
|
||||||
|
throws PgpGeneralException, IOException {
|
||||||
BufferedInputStream bufferedInput =
|
BufferedInputStream bufferedInput =
|
||||||
new BufferedInputStream(new ByteArrayInputStream(data));
|
new BufferedInputStream(new ByteArrayInputStream(data));
|
||||||
if (bufferedInput.available() > 0) {
|
if (bufferedInput.available() > 0) {
|
||||||
@ -58,13 +89,14 @@ public class UncachedKeyRing {
|
|||||||
|
|
||||||
// get first object in block
|
// get first object in block
|
||||||
Object obj;
|
Object obj;
|
||||||
if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPPublicKeyRing) {
|
if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPKeyRing) {
|
||||||
return new UncachedKeyRing((PGPPublicKeyRing) obj);
|
// the constructor will take care of the public/secret part
|
||||||
|
return new UncachedKeyRing((PGPKeyRing) obj);
|
||||||
} else {
|
} else {
|
||||||
throw new PgpGeneralException("Object not recognized as PGPPublicKeyRing!");
|
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new PgpGeneralException("Object not recognized as PGPPublicKeyRing!");
|
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,4 +121,11 @@ public class UncachedKeyRing {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void encodeArmored(OutputStream out, String version) throws IOException {
|
||||||
|
ArmoredOutputStream aos = new ArmoredOutputStream(out);
|
||||||
|
aos.setHeader("Version", version);
|
||||||
|
aos.write(mRing.getEncoded());
|
||||||
|
aos.close();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class UncachedPublicKey {
|
public class UncachedPublicKey {
|
||||||
@ -173,8 +174,24 @@ public class UncachedPublicKey {
|
|||||||
return mPublicKey.getFingerprint();
|
return mPublicKey.getFingerprint();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note that this method has package visibility - no access outside the pgp package!
|
// TODO This method should have package visibility - no access outside the pgp package!
|
||||||
PGPPublicKey getPublicKey() {
|
// (It's still used in ProviderHelper at this point)
|
||||||
|
public PGPPublicKey getPublicKey() {
|
||||||
return mPublicKey;
|
return mPublicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Iterator<WrappedSignature> getSignaturesForId(String userId) {
|
||||||
|
final Iterator<PGPSignature> it = mPublicKey.getSignaturesForID(userId);
|
||||||
|
return new Iterator<WrappedSignature>() {
|
||||||
|
public void remove() {
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
public WrappedSignature next() {
|
||||||
|
return new WrappedSignature(it.next());
|
||||||
|
}
|
||||||
|
public boolean hasNext() {
|
||||||
|
return it.hasNext();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,19 +1,33 @@
|
|||||||
package org.sufficientlysecure.keychain.pgp;
|
package org.sufficientlysecure.keychain.pgp;
|
||||||
|
|
||||||
|
import org.spongycastle.bcpg.S2K;
|
||||||
|
import org.spongycastle.openpgp.PGPSecretKey;
|
||||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
|
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||||
|
|
||||||
public class UncachedSecretKeyRing {
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
final PGPSecretKeyRing mSecretRing;
|
public class UncachedSecretKeyRing extends UncachedKeyRing {
|
||||||
|
|
||||||
UncachedSecretKeyRing(PGPSecretKeyRing secretRing) {
|
UncachedSecretKeyRing(PGPSecretKeyRing secretRing) {
|
||||||
mSecretRing = secretRing;
|
super(secretRing);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Breaking the pattern here, for key import!
|
public ArrayList<Long> getAvailableSubkeys() {
|
||||||
// TODO reduce this from public to default visibility!
|
ArrayList<Long> result = new ArrayList<Long>();
|
||||||
public PGPSecretKeyRing getSecretKeyRing() {
|
// then, mark exactly the keys we have available
|
||||||
return mSecretRing;
|
for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(
|
||||||
|
((PGPSecretKeyRing) mRing).getSecretKeys())) {
|
||||||
|
S2K s2k = sub.getS2K();
|
||||||
|
// Set to 1, except if the encryption type is GNU_DUMMY_S2K
|
||||||
|
if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) {
|
||||||
|
result.add(sub.getKeyID());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,9 @@ import org.spongycastle.openpgp.PGPPublicKey;
|
|||||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
import org.sufficientlysecure.keychain.util.IterableIterator;
|
import org.sufficientlysecure.keychain.util.IterableIterator;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
public abstract class WrappedKeyRing extends KeyRing {
|
public abstract class WrappedKeyRing extends KeyRing {
|
||||||
|
|
||||||
private final boolean mHasAnySecret;
|
private final boolean mHasAnySecret;
|
||||||
@ -76,6 +79,10 @@ public abstract class WrappedKeyRing extends KeyRing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void encode(OutputStream stream) throws IOException {
|
||||||
|
getRing().encode(stream);
|
||||||
|
}
|
||||||
|
|
||||||
abstract PGPKeyRing getRing();
|
abstract PGPKeyRing getRing();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -120,4 +120,8 @@ public class WrappedSecretKeyRing extends WrappedKeyRing {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UncachedSecretKeyRing getUncached() {
|
||||||
|
return new UncachedSecretKeyRing(mRing);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import org.spongycastle.bcpg.SignatureSubpacketTags;
|
|||||||
import org.spongycastle.bcpg.sig.RevocationReason;
|
import org.spongycastle.bcpg.sig.RevocationReason;
|
||||||
import org.spongycastle.openpgp.PGPException;
|
import org.spongycastle.openpgp.PGPException;
|
||||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||||
|
import org.spongycastle.openpgp.PGPPublicKey;
|
||||||
import org.spongycastle.openpgp.PGPSignature;
|
import org.spongycastle.openpgp.PGPSignature;
|
||||||
import org.spongycastle.openpgp.PGPSignatureList;
|
import org.spongycastle.openpgp.PGPSignatureList;
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
||||||
@ -14,6 +15,7 @@ import org.sufficientlysecure.keychain.util.Log;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.SignatureException;
|
import java.security.SignatureException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
public class WrappedSignature {
|
public class WrappedSignature {
|
||||||
|
|
||||||
@ -33,16 +35,57 @@ public class WrappedSignature {
|
|||||||
return mSig.getKeyID();
|
return mSig.getKeyID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getSignatureType() {
|
||||||
|
return mSig.getSignatureType();
|
||||||
|
}
|
||||||
|
|
||||||
public int getKeyAlgorithm() {
|
public int getKeyAlgorithm() {
|
||||||
return mSig.getKeyAlgorithm();
|
return mSig.getKeyAlgorithm();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Date getCreationTime() {
|
||||||
|
return mSig.getCreationTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getEncoded() throws IOException {
|
||||||
|
return mSig.getEncoded();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRevocation() {
|
||||||
|
return mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPrimaryUserId() {
|
||||||
|
return mSig.getHashedSubPackets().isPrimaryUserID();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRevocationReason() throws PgpGeneralException {
|
||||||
|
if(!isRevocation()) {
|
||||||
|
throw new PgpGeneralException("Not a revocation signature.");
|
||||||
|
}
|
||||||
|
SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(
|
||||||
|
SignatureSubpacketTags.REVOCATION_REASON);
|
||||||
|
// For some reason, this is missing in SignatureSubpacketInputStream:146
|
||||||
|
if (!(p instanceof RevocationReason)) {
|
||||||
|
p = new RevocationReason(false, p.getData());
|
||||||
|
}
|
||||||
|
return ((RevocationReason) p).getRevocationDescription();
|
||||||
|
}
|
||||||
|
|
||||||
public void init(WrappedPublicKey key) throws PgpGeneralException {
|
public void init(WrappedPublicKey key) throws PgpGeneralException {
|
||||||
|
init(key.getPublicKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init(UncachedPublicKey key) throws PgpGeneralException {
|
||||||
|
init(key.getPublicKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void init(PGPPublicKey key) throws PgpGeneralException {
|
||||||
try {
|
try {
|
||||||
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
||||||
new JcaPGPContentVerifierBuilderProvider()
|
new JcaPGPContentVerifierBuilderProvider()
|
||||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||||
mSig.init(contentVerifierBuilderProvider, key.getPublicKey());
|
mSig.init(contentVerifierBuilderProvider, key);
|
||||||
} catch(PGPException e) {
|
} catch(PGPException e) {
|
||||||
throw new PgpGeneralException(e);
|
throw new PgpGeneralException(e);
|
||||||
}
|
}
|
||||||
@ -74,30 +117,9 @@ public class WrappedSignature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRevocation() {
|
protected boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException {
|
||||||
return mSig.getHashedSubPackets().hasSubpacket(SignatureSubpacketTags.REVOCATION_REASON);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getRevocationReason() throws PgpGeneralException {
|
|
||||||
if(!isRevocation()) {
|
|
||||||
throw new PgpGeneralException("Not a revocation signature.");
|
|
||||||
}
|
|
||||||
SignatureSubpacket p = mSig.getHashedSubPackets().getSubpacket(
|
|
||||||
SignatureSubpacketTags.REVOCATION_REASON);
|
|
||||||
// For some reason, this is missing in SignatureSubpacketInputStream:146
|
|
||||||
if (!(p instanceof RevocationReason)) {
|
|
||||||
p = new RevocationReason(false, p.getData());
|
|
||||||
}
|
|
||||||
return ((RevocationReason) p).getRevocationDescription();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Verify a signature for this pubkey, after it has been initialized by the signer using
|
|
||||||
* initSignature(). This method should probably move into a wrapped PGPSignature class
|
|
||||||
* at some point.
|
|
||||||
*/
|
|
||||||
public boolean verifySignature(WrappedPublicKey key, String uid) throws PgpGeneralException {
|
|
||||||
try {
|
try {
|
||||||
return mSig.verifyCertification(uid, key.getPublicKey());
|
return mSig.verifyCertification(uid, key);
|
||||||
} catch (SignatureException e) {
|
} catch (SignatureException e) {
|
||||||
throw new PgpGeneralException("Error!", e);
|
throw new PgpGeneralException("Error!", e);
|
||||||
} catch (PGPException e) {
|
} catch (PGPException e) {
|
||||||
@ -105,6 +127,13 @@ public class WrappedSignature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean verifySignature(UncachedPublicKey key, String uid) throws PgpGeneralException {
|
||||||
|
return verifySignature(key.getPublicKey(), uid);
|
||||||
|
}
|
||||||
|
public boolean verifySignature(WrappedPublicKey key, String uid) throws PgpGeneralException {
|
||||||
|
return verifySignature(key.getPublicKey(), uid);
|
||||||
|
}
|
||||||
|
|
||||||
public static WrappedSignature fromBytes(byte[] data) {
|
public static WrappedSignature fromBytes(byte[] data) {
|
||||||
PGPObjectFactory factory = new PGPObjectFactory(data);
|
PGPObjectFactory factory = new PGPObjectFactory(data);
|
||||||
PGPSignatureList signatures = null;
|
PGPSignatureList signatures = null;
|
||||||
|
@ -23,11 +23,9 @@ import android.database.sqlite.SQLiteDatabase;
|
|||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
import android.provider.BaseColumns;
|
import android.provider.BaseColumns;
|
||||||
|
|
||||||
import org.spongycastle.openpgp.PGPKeyRing;
|
|
||||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
|
||||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsAccountsColumns;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiAppsColumns;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns;
|
||||||
@ -256,6 +254,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
|||||||
}.getReadableDatabase();
|
}.getReadableDatabase();
|
||||||
|
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
ProviderHelper providerHelper = new ProviderHelper(context);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// we insert in two steps: first, all public keys that have secret keys
|
// we insert in two steps: first, all public keys that have secret keys
|
||||||
cursor = db.rawQuery("SELECT key_ring_data FROM key_rings WHERE type = 1 OR EXISTS ("
|
cursor = db.rawQuery("SELECT key_ring_data FROM key_rings WHERE type = 1 OR EXISTS ("
|
||||||
@ -266,14 +266,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
|||||||
for (int i = 0; i < cursor.getCount(); i++) {
|
for (int i = 0; i < cursor.getCount(); i++) {
|
||||||
cursor.moveToPosition(i);
|
cursor.moveToPosition(i);
|
||||||
byte[] data = cursor.getBlob(0);
|
byte[] data = cursor.getBlob(0);
|
||||||
PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
|
try {
|
||||||
ProviderHelper providerHelper = new ProviderHelper(context);
|
UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data);
|
||||||
if (ring instanceof PGPPublicKeyRing)
|
providerHelper.saveKeyRing(ring);
|
||||||
providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
|
} catch(PgpGeneralException e) {
|
||||||
else if (ring instanceof PGPSecretKeyRing)
|
Log.e(Constants.TAG, "Error decoding keyring blob!");
|
||||||
providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
|
|
||||||
else {
|
|
||||||
Log.e(Constants.TAG, "Unknown blob data type!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -293,14 +290,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
|||||||
for (int i = 0; i < cursor.getCount(); i++) {
|
for (int i = 0; i < cursor.getCount(); i++) {
|
||||||
cursor.moveToPosition(i);
|
cursor.moveToPosition(i);
|
||||||
byte[] data = cursor.getBlob(0);
|
byte[] data = cursor.getBlob(0);
|
||||||
PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
|
try {
|
||||||
ProviderHelper providerHelper = new ProviderHelper(context);
|
UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data);
|
||||||
if (ring instanceof PGPPublicKeyRing) {
|
providerHelper.saveKeyRing(ring);
|
||||||
providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
|
} catch(PgpGeneralException e) {
|
||||||
} else if (ring instanceof PGPSecretKeyRing) {
|
Log.e(Constants.TAG, "Error decoding keyring blob!");
|
||||||
providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
|
|
||||||
} else {
|
|
||||||
Log.e(Constants.TAG, "Unknown blob data type!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,30 +23,21 @@ import android.content.ContentValues;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.OperationApplicationException;
|
import android.content.OperationApplicationException;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.DatabaseUtils;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.support.v4.util.LongSparseArray;
|
import android.support.v4.util.LongSparseArray;
|
||||||
|
|
||||||
import org.spongycastle.bcpg.ArmoredOutputStream;
|
|
||||||
import org.spongycastle.bcpg.S2K;
|
|
||||||
import org.spongycastle.openpgp.PGPException;
|
|
||||||
import org.spongycastle.openpgp.PGPKeyRing;
|
|
||||||
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.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
import org.sufficientlysecure.keychain.pgp.KeyRing;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
|
||||||
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
|
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
|
import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.UncachedSecretKeyRing;
|
import org.sufficientlysecure.keychain.pgp.UncachedSecretKeyRing;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.WrappedSignature;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
|
||||||
@ -60,7 +51,6 @@ import org.sufficientlysecure.keychain.util.Log;
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -147,19 +137,26 @@ public class ProviderHelper {
|
|||||||
return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types);
|
return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
private LongSparseArray<UncachedPublicKey> getUncachedMasterKeys(Uri queryUri) {
|
||||||
public LongSparseArray<PGPKeyRing> getPGPKeyRings(Uri queryUri) {
|
|
||||||
Cursor cursor = mContentResolver.query(queryUri,
|
Cursor cursor = mContentResolver.query(queryUri,
|
||||||
new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA},
|
new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA},
|
||||||
null, null, null);
|
null, null, null);
|
||||||
|
|
||||||
LongSparseArray<PGPKeyRing> result = new LongSparseArray<PGPKeyRing>(cursor.getCount());
|
LongSparseArray<UncachedPublicKey> result =
|
||||||
|
new LongSparseArray<UncachedPublicKey>(cursor.getCount());
|
||||||
try {
|
try {
|
||||||
if (cursor != null && cursor.moveToFirst()) do {
|
if (cursor != null && cursor.moveToFirst()) do {
|
||||||
long masterKeyId = cursor.getLong(0);
|
long masterKeyId = cursor.getLong(0);
|
||||||
byte[] data = cursor.getBlob(1);
|
byte[] data = cursor.getBlob(1);
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
result.put(masterKeyId, PgpConversionHelper.BytesToPGPKeyRing(data));
|
try {
|
||||||
|
result.put(masterKeyId,
|
||||||
|
UncachedKeyRing.decodePubkeyFromData(data).getPublicKey());
|
||||||
|
} catch(PgpGeneralException e) {
|
||||||
|
Log.e(Constants.TAG, "Error parsing keyring, skipping.");
|
||||||
|
} catch(IOException e) {
|
||||||
|
Log.e(Constants.TAG, "IO error, skipping keyring");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} while (cursor.moveToNext());
|
} while (cursor.moveToNext());
|
||||||
} finally {
|
} finally {
|
||||||
@ -194,12 +191,13 @@ public class ProviderHelper {
|
|||||||
|
|
||||||
private KeyRing getWrappedKeyRing(Uri queryUri, boolean secret) throws NotFoundException {
|
private KeyRing getWrappedKeyRing(Uri queryUri, boolean secret) throws NotFoundException {
|
||||||
Cursor cursor = mContentResolver.query(queryUri,
|
Cursor cursor = mContentResolver.query(queryUri,
|
||||||
new String[] {
|
new String[]{
|
||||||
// we pick from cache only information that is not easily available from keyrings
|
// we pick from cache only information that is not easily available from keyrings
|
||||||
KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED,
|
KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED,
|
||||||
// and of course, ring data
|
// and of course, ring data
|
||||||
secret ? KeyRings.PRIVKEY_DATA : KeyRings.PUBKEY_DATA
|
secret ? KeyRings.PRIVKEY_DATA : KeyRings.PUBKEY_DATA
|
||||||
}, null, null, null);
|
}, null, null, null
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
|
||||||
@ -219,37 +217,18 @@ public class ProviderHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public PGPKeyRing getPGPKeyRing(Uri queryUri) throws NotFoundException {
|
|
||||||
LongSparseArray<PGPKeyRing> result = getPGPKeyRings(queryUri);
|
|
||||||
if (result.size() == 0) {
|
|
||||||
throw new NotFoundException("PGPKeyRing object not found!");
|
|
||||||
} else {
|
|
||||||
return result.valueAt(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves PGPPublicKeyRing with its keys and userIds in DB
|
* Saves PGPPublicKeyRing with its keys and userIds in DB
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void saveKeyRing(PGPPublicKeyRing keyRing) throws IOException {
|
public void saveKeyRing(UncachedKeyRing keyRing) throws IOException {
|
||||||
PGPPublicKey masterKey = keyRing.getPublicKey();
|
UncachedPublicKey masterKey = keyRing.getPublicKey();
|
||||||
long masterKeyId = masterKey.getKeyID();
|
long masterKeyId = masterKey.getKeyId();
|
||||||
|
|
||||||
// IF there is a secret key, preserve it!
|
// IF there is a secret key, preserve it!
|
||||||
PGPSecretKeyRing secretRing = null;
|
UncachedSecretKeyRing secretRing = null;
|
||||||
try {
|
try {
|
||||||
secretRing = getPGPSecretKeyRing(masterKeyId);
|
secretRing = getWrappedSecretKeyRing(masterKeyId).getUncached();
|
||||||
} catch (NotFoundException e) {
|
} catch (NotFoundException e) {
|
||||||
Log.e(Constants.TAG, "key not found!");
|
Log.e(Constants.TAG, "key not found!");
|
||||||
}
|
}
|
||||||
@ -272,36 +251,38 @@ public class ProviderHelper {
|
|||||||
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
|
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
|
||||||
|
|
||||||
int rank = 0;
|
int rank = 0;
|
||||||
for (PGPPublicKey key : new IterableIterator<PGPPublicKey>(keyRing.getPublicKeys())) {
|
for (UncachedPublicKey key : new IterableIterator<UncachedPublicKey>(keyRing.getPublicKeys())) {
|
||||||
operations.add(buildPublicKeyOperations(masterKeyId, key, rank));
|
operations.add(buildPublicKeyOperations(masterKeyId, key, rank));
|
||||||
++rank;
|
++rank;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get a list of owned secret keys, for verification filtering
|
// get a list of owned secret keys, for verification filtering
|
||||||
LongSparseArray<PGPKeyRing> allKeyRings = getPGPKeyRings(KeyRingData.buildSecretKeyRingUri());
|
LongSparseArray<UncachedPublicKey> allKeyRings =
|
||||||
|
getUncachedMasterKeys(KeyRingData.buildSecretKeyRingUri());
|
||||||
// special case: available secret keys verify themselves!
|
// special case: available secret keys verify themselves!
|
||||||
if (secretRing != null)
|
if (secretRing != null) {
|
||||||
allKeyRings.put(secretRing.getSecretKey().getKeyID(), secretRing);
|
allKeyRings.put(secretRing.getMasterKeyId(), secretRing.getPublicKey());
|
||||||
|
}
|
||||||
|
|
||||||
// classify and order user ids. primary are moved to the front, revoked to the back,
|
// classify and order user ids. primary are moved to the front, revoked to the back,
|
||||||
// otherwise the order in the keyfile is preserved.
|
// otherwise the order in the keyfile is preserved.
|
||||||
List<UserIdItem> uids = new ArrayList<UserIdItem>();
|
List<UserIdItem> uids = new ArrayList<UserIdItem>();
|
||||||
|
|
||||||
for (String userId : new IterableIterator<String>(masterKey.getUserIDs())) {
|
for (String userId : new IterableIterator<String>(
|
||||||
|
masterKey.getUnorderedUserIds().iterator())) {
|
||||||
UserIdItem item = new UserIdItem();
|
UserIdItem item = new UserIdItem();
|
||||||
uids.add(item);
|
uids.add(item);
|
||||||
item.userId = userId;
|
item.userId = userId;
|
||||||
|
|
||||||
// look through signatures for this specific key
|
// look through signatures for this specific key
|
||||||
for (PGPSignature cert : new IterableIterator<PGPSignature>(
|
for (WrappedSignature cert : new IterableIterator<WrappedSignature>(
|
||||||
masterKey.getSignaturesForID(userId))) {
|
masterKey.getSignaturesForId(userId))) {
|
||||||
long certId = cert.getKeyID();
|
long certId = cert.getKeyId();
|
||||||
try {
|
try {
|
||||||
// self signature
|
// self signature
|
||||||
if (certId == masterKeyId) {
|
if (certId == masterKeyId) {
|
||||||
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
|
cert.init(masterKey);
|
||||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME), masterKey);
|
if (!cert.verifySignature(masterKey, userId)) {
|
||||||
if (!cert.verifyCertification(userId, masterKey)) {
|
|
||||||
// not verified?! dang! TODO notify user? this is kinda serious...
|
// not verified?! dang! TODO notify user? this is kinda serious...
|
||||||
Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!");
|
Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!");
|
||||||
continue;
|
continue;
|
||||||
@ -310,31 +291,22 @@ public class ProviderHelper {
|
|||||||
if (item.selfCert == null ||
|
if (item.selfCert == null ||
|
||||||
item.selfCert.getCreationTime().before(cert.getCreationTime())) {
|
item.selfCert.getCreationTime().before(cert.getCreationTime())) {
|
||||||
item.selfCert = cert;
|
item.selfCert = cert;
|
||||||
item.isPrimary = cert.getHashedSubPackets().isPrimaryUserID();
|
item.isPrimary = cert.isPrimaryUserId();
|
||||||
item.isRevoked =
|
item.isRevoked = cert.isRevocation();
|
||||||
cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// verify signatures from known private keys
|
// verify signatures from known private keys
|
||||||
if (allKeyRings.indexOfKey(certId) >= 0) {
|
if (allKeyRings.indexOfKey(certId) >= 0) {
|
||||||
// mark them as verified
|
cert.init(allKeyRings.get(certId));
|
||||||
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
|
if (cert.verifySignature(masterKey, userId)) {
|
||||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME),
|
|
||||||
allKeyRings.get(certId).getPublicKey());
|
|
||||||
if (cert.verifyCertification(userId, masterKey)) {
|
|
||||||
item.trustedCerts.add(cert);
|
item.trustedCerts.add(cert);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (SignatureException e) {
|
} catch (PgpGeneralException e) {
|
||||||
Log.e(Constants.TAG, "Signature verification failed! "
|
Log.e(Constants.TAG, "Signature verification failed! "
|
||||||
+ PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID())
|
+ PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyId())
|
||||||
+ " from "
|
+ " from "
|
||||||
+ PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e);
|
+ PgpKeyHelper.convertKeyIdToHex(cert.getKeyId()), e);
|
||||||
} catch (PGPException e) {
|
|
||||||
Log.e(Constants.TAG, "Signature verification failed! "
|
|
||||||
+ PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID())
|
|
||||||
+ " from "
|
|
||||||
+ PgpKeyHelper.convertKeyIdToHex(cert.getKeyID()), e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -380,8 +352,8 @@ public class ProviderHelper {
|
|||||||
String userId;
|
String userId;
|
||||||
boolean isPrimary = false;
|
boolean isPrimary = false;
|
||||||
boolean isRevoked = false;
|
boolean isRevoked = false;
|
||||||
PGPSignature selfCert;
|
WrappedSignature selfCert;
|
||||||
List<PGPSignature> trustedCerts = new ArrayList<PGPSignature>();
|
List<WrappedSignature> trustedCerts = new ArrayList<WrappedSignature>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(UserIdItem o) {
|
public int compareTo(UserIdItem o) {
|
||||||
@ -401,18 +373,8 @@ public class ProviderHelper {
|
|||||||
* Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring
|
* Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring
|
||||||
* is already in the database!
|
* is already in the database!
|
||||||
*/
|
*/
|
||||||
public void saveKeyRing(UncachedSecretKeyRing wrappedRing) throws IOException {
|
public void saveKeyRing(UncachedSecretKeyRing keyRing) throws IOException {
|
||||||
// TODO split up getters
|
long masterKeyId = keyRing.getMasterKeyId();
|
||||||
PGPSecretKeyRing keyRing = wrappedRing.getSecretKeyRing();
|
|
||||||
saveKeyRing(keyRing);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves a PGPSecretKeyRing in the DB. This will only work if a corresponding public keyring
|
|
||||||
* is already in the database!
|
|
||||||
*/
|
|
||||||
public void saveKeyRing(PGPSecretKeyRing keyRing) throws IOException {
|
|
||||||
long masterKeyId = keyRing.getSecretKey().getKeyID();
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
|
Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
|
||||||
@ -424,14 +386,10 @@ public class ProviderHelper {
|
|||||||
|
|
||||||
values.put(Keys.HAS_SECRET, 1);
|
values.put(Keys.HAS_SECRET, 1);
|
||||||
// then, mark exactly the keys we have available
|
// then, mark exactly the keys we have available
|
||||||
for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(keyRing.getSecretKeys())) {
|
for (Long sub : new IterableIterator<Long>(keyRing.getAvailableSubkeys().iterator())) {
|
||||||
S2K s2k = sub.getS2K();
|
mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[] {
|
||||||
// Set to 1, except if the encryption type is GNU_DUMMY_S2K
|
Long.toString(sub)
|
||||||
if(s2k == null || s2k.getType() != S2K.GNU_DUMMY_S2K) {
|
});
|
||||||
mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[]{
|
|
||||||
Long.toString(sub.getKeyID())
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// this implicitly leaves all keys which were not in the secret key ring
|
// this implicitly leaves all keys which were not in the secret key ring
|
||||||
// with has_secret = 0
|
// with has_secret = 0
|
||||||
@ -449,11 +407,6 @@ public class ProviderHelper {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveKeyRing(UncachedKeyRing ring) throws IOException {
|
|
||||||
PGPPublicKeyRing pubRing = (PGPPublicKeyRing) ring.getRing();
|
|
||||||
saveKeyRing(pubRing);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves (or updates) a pair of public and secret KeyRings in the database
|
* Saves (or updates) a pair of public and secret KeyRings in the database
|
||||||
*/
|
*/
|
||||||
@ -464,32 +417,32 @@ public class ProviderHelper {
|
|||||||
mContentResolver.delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null);
|
mContentResolver.delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null);
|
||||||
|
|
||||||
// save public keyring
|
// save public keyring
|
||||||
saveKeyRing((PGPPublicKeyRing) pubRing.getRing());
|
saveKeyRing(pubRing);
|
||||||
saveKeyRing((PGPSecretKeyRing) secRing.getRing());
|
saveKeyRing(secRing);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
|
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
|
||||||
*/
|
*/
|
||||||
private ContentProviderOperation
|
private ContentProviderOperation
|
||||||
buildPublicKeyOperations(long masterKeyId, PGPPublicKey key, int rank) throws IOException {
|
buildPublicKeyOperations(long masterKeyId, UncachedPublicKey key, int rank) throws IOException {
|
||||||
|
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(Keys.MASTER_KEY_ID, masterKeyId);
|
values.put(Keys.MASTER_KEY_ID, masterKeyId);
|
||||||
values.put(Keys.RANK, rank);
|
values.put(Keys.RANK, rank);
|
||||||
|
|
||||||
values.put(Keys.KEY_ID, key.getKeyID());
|
values.put(Keys.KEY_ID, key.getKeyId());
|
||||||
values.put(Keys.KEY_SIZE, key.getBitStrength());
|
values.put(Keys.KEY_SIZE, key.getBitStrength());
|
||||||
values.put(Keys.ALGORITHM, key.getAlgorithm());
|
values.put(Keys.ALGORITHM, key.getAlgorithm());
|
||||||
values.put(Keys.FINGERPRINT, key.getFingerprint());
|
values.put(Keys.FINGERPRINT, key.getFingerprint());
|
||||||
|
|
||||||
values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key)));
|
values.put(Keys.CAN_CERTIFY, key.canCertify());
|
||||||
values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key)));
|
values.put(Keys.CAN_SIGN, key.canSign());
|
||||||
values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key));
|
values.put(Keys.CAN_ENCRYPT, key.canEncrypt());
|
||||||
values.put(Keys.IS_REVOKED, key.isRevoked());
|
values.put(Keys.IS_REVOKED, key.maybeRevoked());
|
||||||
|
|
||||||
values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000);
|
values.put(Keys.CREATION, key.getCreationTime().getTime() / 1000);
|
||||||
Date expiryDate = PgpKeyHelper.getExpiryDate(key);
|
Date expiryDate = key.getExpiryTime();
|
||||||
if (expiryDate != null) {
|
if (expiryDate != null) {
|
||||||
values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
|
values.put(Keys.EXPIRY, expiryDate.getTime() / 1000);
|
||||||
}
|
}
|
||||||
@ -503,11 +456,12 @@ public class ProviderHelper {
|
|||||||
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
|
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
|
||||||
*/
|
*/
|
||||||
private ContentProviderOperation
|
private ContentProviderOperation
|
||||||
buildCertOperations(long masterKeyId, int rank, PGPSignature cert, int verified) throws IOException {
|
buildCertOperations(long masterKeyId, int rank, WrappedSignature cert, int verified)
|
||||||
|
throws IOException {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(Certs.MASTER_KEY_ID, masterKeyId);
|
values.put(Certs.MASTER_KEY_ID, masterKeyId);
|
||||||
values.put(Certs.RANK, rank);
|
values.put(Certs.RANK, rank);
|
||||||
values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyID());
|
values.put(Certs.KEY_ID_CERTIFIER, cert.getKeyId());
|
||||||
values.put(Certs.TYPE, cert.getSignatureType());
|
values.put(Certs.TYPE, cert.getSignatureType());
|
||||||
values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000);
|
values.put(Certs.CREATION, cert.getCreationTime().getTime() / 1000);
|
||||||
values.put(Certs.VERIFIED, verified);
|
values.put(Certs.VERIFIED, verified);
|
||||||
@ -535,23 +489,11 @@ public class ProviderHelper {
|
|||||||
return ContentProviderOperation.newInsert(uri).withValues(values).build();
|
return ContentProviderOperation.newInsert(uri).withValues(values).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getKeyRingAsArmoredString(byte[] data) throws IOException {
|
private String getKeyRingAsArmoredString(byte[] data) throws IOException, PgpGeneralException {
|
||||||
Object keyRing = null;
|
UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(data);
|
||||||
if (data != null) {
|
|
||||||
keyRing = PgpConversionHelper.BytesToPGPKeyRing(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
ArmoredOutputStream aos = new ArmoredOutputStream(bos);
|
keyRing.encodeArmored(bos, PgpHelper.getFullVersion(mContext));
|
||||||
aos.setHeader("Version", PgpHelper.getFullVersion(mContext));
|
|
||||||
|
|
||||||
if (keyRing instanceof PGPSecretKeyRing) {
|
|
||||||
aos.write(((PGPSecretKeyRing) keyRing).getEncoded());
|
|
||||||
} else if (keyRing instanceof PGPPublicKeyRing) {
|
|
||||||
aos.write(((PGPPublicKeyRing) keyRing).getEncoded());
|
|
||||||
}
|
|
||||||
aos.close();
|
|
||||||
|
|
||||||
String armoredKey = bos.toString("UTF-8");
|
String armoredKey = bos.toString("UTF-8");
|
||||||
|
|
||||||
Log.d(Constants.TAG, "armoredKey:" + armoredKey);
|
Log.d(Constants.TAG, "armoredKey:" + armoredKey);
|
||||||
@ -560,77 +502,12 @@ public class ProviderHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getKeyRingAsArmoredString(Uri uri)
|
public String getKeyRingAsArmoredString(Uri uri)
|
||||||
throws NotFoundException, IOException {
|
throws NotFoundException, IOException, PgpGeneralException {
|
||||||
byte[] data = (byte[]) getGenericData(
|
byte[] data = (byte[]) getGenericData(
|
||||||
uri, KeyRingData.KEY_RING_DATA, ProviderHelper.FIELD_TYPE_BLOB);
|
uri, KeyRingData.KEY_RING_DATA, ProviderHelper.FIELD_TYPE_BLOB);
|
||||||
return getKeyRingAsArmoredString(data);
|
return getKeyRingAsArmoredString(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: currently not used, but will be needed to upload many keys at once!
|
|
||||||
*
|
|
||||||
* @param masterKeyIds
|
|
||||||
* @return
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
public ArrayList<String> getKeyRingsAsArmoredString(long[] masterKeyIds)
|
|
||||||
throws IOException {
|
|
||||||
ArrayList<String> output = new ArrayList<String>();
|
|
||||||
|
|
||||||
if (masterKeyIds == null || masterKeyIds.length == 0) {
|
|
||||||
Log.e(Constants.TAG, "No master keys given!");
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build a cursor for the selected masterKeyIds
|
|
||||||
Cursor cursor;
|
|
||||||
{
|
|
||||||
String inMasterKeyList = KeyRingData.MASTER_KEY_ID + " IN (";
|
|
||||||
for (int i = 0; i < masterKeyIds.length; ++i) {
|
|
||||||
if (i != 0) {
|
|
||||||
inMasterKeyList += ", ";
|
|
||||||
}
|
|
||||||
inMasterKeyList += DatabaseUtils.sqlEscapeString("" + masterKeyIds[i]);
|
|
||||||
}
|
|
||||||
inMasterKeyList += ")";
|
|
||||||
|
|
||||||
cursor = mContentResolver.query(KeyRingData.buildPublicKeyRingUri(), new String[]{
|
|
||||||
KeyRingData._ID, KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA
|
|
||||||
}, inMasterKeyList, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (cursor != null) {
|
|
||||||
int masterIdCol = cursor.getColumnIndex(KeyRingData.MASTER_KEY_ID);
|
|
||||||
int dataCol = cursor.getColumnIndex(KeyRingData.KEY_RING_DATA);
|
|
||||||
if (cursor.moveToFirst()) {
|
|
||||||
do {
|
|
||||||
Log.d(Constants.TAG, "masterKeyId: " + cursor.getLong(masterIdCol));
|
|
||||||
|
|
||||||
byte[] data = cursor.getBlob(dataCol);
|
|
||||||
|
|
||||||
// get actual keyring data blob and write it to ByteArrayOutputStream
|
|
||||||
try {
|
|
||||||
output.add(getKeyRingAsArmoredString(data));
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.e(Constants.TAG, "IOException", e);
|
|
||||||
}
|
|
||||||
} while (cursor.moveToNext());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (cursor != null) {
|
|
||||||
cursor.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (output.size() > 0) {
|
|
||||||
return output;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<String> getRegisteredApiApps() {
|
public ArrayList<String> getRegisteredApiApps() {
|
||||||
Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null);
|
Cursor cursor = mContentResolver.query(ApiApps.CONTENT_URI, null, null, null, null);
|
||||||
|
|
||||||
|
@ -26,16 +26,13 @@ import android.os.Message;
|
|||||||
import android.os.Messenger;
|
import android.os.Messenger;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
|
|
||||||
import org.spongycastle.bcpg.sig.KeyFlags;
|
|
||||||
import org.spongycastle.openpgp.PGPKeyRing;
|
|
||||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
|
||||||
import org.spongycastle.openpgp.PGPUtil;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.helper.FileHelper;
|
import org.sufficientlysecure.keychain.helper.FileHelper;
|
||||||
import org.sufficientlysecure.keychain.helper.OtherHelper;
|
import org.sufficientlysecure.keychain.helper.OtherHelper;
|
||||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
import org.sufficientlysecure.keychain.helper.Preferences;
|
||||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
|
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
|
||||||
import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
|
import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
|
||||||
import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
|
import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
|
||||||
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
|
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
|
||||||
@ -594,21 +591,21 @@ public class KeychainIntentService extends IntentService
|
|||||||
buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
|
buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
|
||||||
4096, passphrase, true);
|
4096, passphrase, true);
|
||||||
os.write(buf);
|
os.write(buf);
|
||||||
keyUsageList.add(KeyFlags.CERTIFY_OTHER);
|
keyUsageList.add(UncachedSecretKey.CERTIFY_OTHER);
|
||||||
keysCreated++;
|
keysCreated++;
|
||||||
setProgress(keysCreated, keysTotal);
|
setProgress(keysCreated, keysTotal);
|
||||||
|
|
||||||
buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
|
buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
|
||||||
4096, passphrase, false);
|
4096, passphrase, false);
|
||||||
os.write(buf);
|
os.write(buf);
|
||||||
keyUsageList.add(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE);
|
keyUsageList.add(UncachedSecretKey.ENCRYPT_COMMS | UncachedSecretKey.ENCRYPT_STORAGE);
|
||||||
keysCreated++;
|
keysCreated++;
|
||||||
setProgress(keysCreated, keysTotal);
|
setProgress(keysCreated, keysTotal);
|
||||||
|
|
||||||
buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
|
buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
|
||||||
4096, passphrase, false);
|
4096, passphrase, false);
|
||||||
os.write(buf);
|
os.write(buf);
|
||||||
keyUsageList.add(KeyFlags.SIGN_DATA);
|
keyUsageList.add(UncachedSecretKey.SIGN_DATA);
|
||||||
keysCreated++;
|
keysCreated++;
|
||||||
setProgress(keysCreated, keysTotal);
|
setProgress(keysCreated, keysTotal);
|
||||||
|
|
||||||
@ -749,23 +746,15 @@ public class KeychainIntentService extends IntentService
|
|||||||
byte[] downloadedKeyBytes = server.get(keybaseId).getBytes();
|
byte[] downloadedKeyBytes = server.get(keybaseId).getBytes();
|
||||||
|
|
||||||
// create PGPKeyRing object based on downloaded armored key
|
// create PGPKeyRing object based on downloaded armored key
|
||||||
PGPKeyRing downloadedKey = null;
|
UncachedKeyRing downloadedKey = null;
|
||||||
BufferedInputStream bufferedInput =
|
BufferedInputStream bufferedInput =
|
||||||
new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
|
new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
|
||||||
if (bufferedInput.available() > 0) {
|
if (bufferedInput.available() > 0) {
|
||||||
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
|
List<UncachedKeyRing> rings = UncachedKeyRing.fromStream(bufferedInput);
|
||||||
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
|
if(rings.isEmpty()) {
|
||||||
|
throw new PgpGeneralException("No keys in result!");
|
||||||
// get first object in block
|
|
||||||
Object obj;
|
|
||||||
if ((obj = objectFactory.nextObject()) != null) {
|
|
||||||
|
|
||||||
if (obj instanceof PGPKeyRing) {
|
|
||||||
downloadedKey = (PGPKeyRing) obj;
|
|
||||||
} else {
|
|
||||||
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
downloadedKey = rings.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// save key bytes in entry object for doing the
|
// save key bytes in entry object for doing the
|
||||||
|
@ -34,12 +34,6 @@ import android.os.Messenger;
|
|||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.support.v4.util.LongSparseArray;
|
import android.support.v4.util.LongSparseArray;
|
||||||
|
|
||||||
import org.spongycastle.openpgp.PGPException;
|
|
||||||
import org.spongycastle.openpgp.PGPPrivateKey;
|
|
||||||
import org.spongycastle.openpgp.PGPSecretKey;
|
|
||||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
|
||||||
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
|
||||||
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
|
|
||||||
import org.sufficientlysecure.keychain.Constants;
|
import org.sufficientlysecure.keychain.Constants;
|
||||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
import org.sufficientlysecure.keychain.helper.Preferences;
|
||||||
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
|
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
|
||||||
@ -48,7 +42,6 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
|||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Iterator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This service runs in its own process, but is available to all other processes as the main
|
* This service runs in its own process, but is available to all other processes as the main
|
||||||
@ -191,7 +184,8 @@ public class PassphraseCacheService extends Service {
|
|||||||
// get cached passphrase
|
// get cached passphrase
|
||||||
String cachedPassphrase = mPassphraseCache.get(keyId);
|
String cachedPassphrase = mPassphraseCache.get(keyId);
|
||||||
if (cachedPassphrase == null) {
|
if (cachedPassphrase == null) {
|
||||||
// this is an error
|
Log.d(TAG, "Passphrase not (yet) cached, returning null");
|
||||||
|
// not really an error, just means the passphrase is not cached but not empty either
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,44 +200,6 @@ public class PassphraseCacheService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public static boolean hasPassphrase(PGPSecretKeyRing secretKeyRing) {
|
|
||||||
PGPSecretKey secretKey = null;
|
|
||||||
boolean foundValidKey = false;
|
|
||||||
for (Iterator keys = secretKeyRing.getSecretKeys(); keys.hasNext(); ) {
|
|
||||||
secretKey = (PGPSecretKey) keys.next();
|
|
||||||
if (!secretKey.isPrivateKeyEmpty()) {
|
|
||||||
foundValidKey = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!foundValidKey) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder()
|
|
||||||
.setProvider("SC").build("".toCharArray());
|
|
||||||
PGPPrivateKey testKey = secretKey.extractPrivateKey(keyDecryptor);
|
|
||||||
return testKey == null;
|
|
||||||
} catch(PGPException e) {
|
|
||||||
// this means the crc check failed -> passphrase required
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if key has a passphrase.
|
|
||||||
*
|
|
||||||
* @param secretKeyId
|
|
||||||
* @return true if it has a passphrase
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static boolean hasPassphrase(Context context, long secretKeyId)
|
|
||||||
throws ProviderHelper.NotFoundException {
|
|
||||||
return new ProviderHelper(context).getWrappedSecretKeyRing(secretKeyId).hasPassphrase();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register BroadcastReceiver that is unregistered when service is destroyed. This
|
* Register BroadcastReceiver that is unregistered when service is destroyed. This
|
||||||
* BroadcastReceiver hears on intents with ACTION_PASSPHRASE_CACHE_SERVICE to then timeout
|
* BroadcastReceiver hears on intents with ACTION_PASSPHRASE_CACHE_SERVICE to then timeout
|
||||||
|
@ -13,7 +13,8 @@ import java.util.HashMap;
|
|||||||
*
|
*
|
||||||
* All changes are done in a differential manner. Besides the two key
|
* All changes are done in a differential manner. Besides the two key
|
||||||
* identification attributes, all attributes may be null, which indicates no
|
* identification attributes, all attributes may be null, which indicates no
|
||||||
* change to the keyring.
|
* change to the keyring. This is also the reason why boxed values are used
|
||||||
|
* instead of primitives in the subclasses.
|
||||||
*
|
*
|
||||||
* Application of operations in the backend should be fail-fast, which means an
|
* Application of operations in the backend should be fail-fast, which means an
|
||||||
* error in any included operation (for example revocation of a non-existent
|
* error in any included operation (for example revocation of a non-existent
|
||||||
@ -45,10 +46,12 @@ public class SaveKeyringParcel implements Parcelable {
|
|||||||
// performance gain for using Parcelable here would probably be negligible,
|
// performance gain for using Parcelable here would probably be negligible,
|
||||||
// use Serializable instead.
|
// use Serializable instead.
|
||||||
public static class SubkeyAdd implements Serializable {
|
public static class SubkeyAdd implements Serializable {
|
||||||
|
public final int mAlgorithm;
|
||||||
public final int mKeysize;
|
public final int mKeysize;
|
||||||
public final int mFlags;
|
public final int mFlags;
|
||||||
public final Long mExpiry;
|
public final Long mExpiry;
|
||||||
public SubkeyAdd(int keysize, int flags, long expiry) {
|
public SubkeyAdd(int algorithm, int keysize, int flags, Long expiry) {
|
||||||
|
mAlgorithm = algorithm;
|
||||||
mKeysize = keysize;
|
mKeysize = keysize;
|
||||||
mFlags = flags;
|
mFlags = flags;
|
||||||
mExpiry = expiry;
|
mExpiry = expiry;
|
||||||
@ -59,7 +62,7 @@ public class SaveKeyringParcel implements Parcelable {
|
|||||||
public final long mKeyId;
|
public final long mKeyId;
|
||||||
public final Integer mFlags;
|
public final Integer mFlags;
|
||||||
public final Long mExpiry;
|
public final Long mExpiry;
|
||||||
public SubkeyChange(long keyId, int flags, long expiry) {
|
public SubkeyChange(long keyId, Integer flags, Long expiry) {
|
||||||
mKeyId = keyId;
|
mKeyId = keyId;
|
||||||
mFlags = flags;
|
mFlags = flags;
|
||||||
mExpiry = expiry;
|
mExpiry = expiry;
|
||||||
|
@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.Constants;
|
|||||||
import org.sufficientlysecure.keychain.R;
|
import org.sufficientlysecure.keychain.R;
|
||||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
||||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||||
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
|
||||||
@ -190,6 +191,9 @@ public class ViewKeyShareFragment extends LoaderFragment implements
|
|||||||
}
|
}
|
||||||
startActivity(Intent.createChooser(sendIntent, title));
|
startActivity(Intent.createChooser(sendIntent, title));
|
||||||
}
|
}
|
||||||
|
} catch (PgpGeneralException e) {
|
||||||
|
Log.e(Constants.TAG, "error processing key!", e);
|
||||||
|
AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(Constants.TAG, "error processing key!", e);
|
Log.e(Constants.TAG, "error processing key!", e);
|
||||||
AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();
|
AppMsg.makeText(getActivity(), R.string.error_key_processing, AppMsg.STYLE_ALERT).show();
|
||||||
|
@ -102,7 +102,7 @@ public class PassphraseDialogFragment extends DialogFragment implements OnEditor
|
|||||||
// check if secret key has a passphrase
|
// check if secret key has a passphrase
|
||||||
if (!(secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none)) {
|
if (!(secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none)) {
|
||||||
try {
|
try {
|
||||||
if (new ProviderHelper(context).getWrappedSecretKeyRing(secretKeyId).hasPassphrase()) {
|
if (!new ProviderHelper(context).getWrappedSecretKeyRing(secretKeyId).hasPassphrase()) {
|
||||||
throw new PgpGeneralException("No passphrase! No passphrase dialog needed!");
|
throw new PgpGeneralException("No passphrase! No passphrase dialog needed!");
|
||||||
}
|
}
|
||||||
} catch(ProviderHelper.NotFoundException e) {
|
} catch(ProviderHelper.NotFoundException e) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user