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.PGPSecretKey;
|
||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.PGPSignatureList;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
@ -122,26 +120,4 @@ public class PgpConversionHelper {
|
||||
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);
|
||||
|
||||
try {
|
||||
PGPSecretKeyRing secretKeyRing = mProviderHelper.getPGPSecretKeyRing(secretKeyMasterId);
|
||||
WrappedSecretKeyRing secretKeyRing =
|
||||
mProviderHelper.getWrappedSecretKeyRing(secretKeyMasterId);
|
||||
secretKeyRing.encode(arOutStream);
|
||||
} catch (ProviderHelper.NotFoundException e) {
|
||||
Log.e(Constants.TAG, "key not found!", e);
|
||||
@ -287,18 +288,13 @@ public class PgpImportExport {
|
||||
public int storeKeyRingInCache(UncachedKeyRing ring, UncachedKeyRing secretRing) {
|
||||
int status;
|
||||
try {
|
||||
// TODO make sure these are correctly typed!
|
||||
PGPPublicKeyRing publicKeyRing = (PGPPublicKeyRing) ring.getRing();
|
||||
PGPSecretKeyRing secretKeyRing = null;
|
||||
if(secretRing != null) {
|
||||
secretKeyRing = (PGPSecretKeyRing) secretRing.getRing();
|
||||
}
|
||||
UncachedSecretKeyRing secretKeyRing = null;
|
||||
// see what type we have. we can either have a secret + public keyring, or just public
|
||||
if (secretKeyRing != null) {
|
||||
mProviderHelper.saveKeyRing(ring, secretRing);
|
||||
status = RETURN_OK;
|
||||
} else {
|
||||
mProviderHelper.saveKeyRing(publicKeyRing);
|
||||
mProviderHelper.saveKeyRing(ring);
|
||||
status = RETURN_OK;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
|
@ -35,6 +35,7 @@ import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.PGPSignatureGenerator;
|
||||
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
|
||||
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
||||
import org.spongycastle.openpgp.PGPUtil;
|
||||
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
||||
import org.spongycastle.openpgp.operator.PBESecretKeyEncryptor;
|
||||
@ -49,6 +50,8 @@ import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
|
||||
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 java.io.IOException;
|
||||
@ -60,9 +63,11 @@ import java.security.NoSuchProviderException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.SignatureException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
@ -644,7 +649,7 @@ public class PgpKeyOperation {
|
||||
|
||||
for(String uid : new IterableIterator<String>(secretKeyRing.getPublicKey().getUserIDs())) {
|
||||
for(PGPSignature sig : new IterableIterator<PGPSignature>(
|
||||
secretKeyRing.getPublicKey().getSignaturesForID(uid))) {
|
||||
secretKeyRing.getPublicKey().getSignaturesForId(uid))) {
|
||||
Log.d(Constants.TAG, "sig: " +
|
||||
PgpKeyHelper.convertKeyIdToHex(sig.getKeyID()) + " for " + uid);
|
||||
}
|
||||
@ -655,7 +660,7 @@ public class PgpKeyOperation {
|
||||
|
||||
for(String uid : new IterableIterator<String>(publicKeyRing.getPublicKey().getUserIDs())) {
|
||||
for(PGPSignature sig : new IterableIterator<PGPSignature>(
|
||||
publicKeyRing.getPublicKey().getSignaturesForID(uid))) {
|
||||
publicKeyRing.getPublicKey().getSignaturesForId(uid))) {
|
||||
Log.d(Constants.TAG, "sig: " +
|
||||
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.
|
||||
* <p/>
|
||||
|
@ -1,8 +1,9 @@
|
||||
package org.sufficientlysecure.keychain.pgp;
|
||||
|
||||
import org.spongycastle.bcpg.ArmoredOutputStream;
|
||||
import org.spongycastle.openpgp.PGPKeyRing;
|
||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.spongycastle.openpgp.PGPPublicKey;
|
||||
import org.spongycastle.openpgp.PGPSecretKeyRing;
|
||||
import org.spongycastle.openpgp.PGPUtil;
|
||||
import org.sufficientlysecure.keychain.Constants;
|
||||
@ -13,6 +14,8 @@ import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
@ -26,6 +29,10 @@ public class UncachedKeyRing {
|
||||
mIsSecret = ring instanceof PGPSecretKeyRing;
|
||||
}
|
||||
|
||||
public long getMasterKeyId() {
|
||||
return mRing.getPublicKey().getKeyID();
|
||||
}
|
||||
|
||||
/* TODO don't use this */
|
||||
@Deprecated
|
||||
public PGPKeyRing getRing() {
|
||||
@ -36,6 +43,21 @@ public class UncachedKeyRing {
|
||||
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() {
|
||||
return mIsSecret;
|
||||
}
|
||||
@ -50,6 +72,15 @@ public class UncachedKeyRing {
|
||||
|
||||
public static UncachedKeyRing decodePubkeyFromData(byte[] data)
|
||||
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 =
|
||||
new BufferedInputStream(new ByteArrayInputStream(data));
|
||||
if (bufferedInput.available() > 0) {
|
||||
@ -58,13 +89,14 @@ public class UncachedKeyRing {
|
||||
|
||||
// get first object in block
|
||||
Object obj;
|
||||
if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPPublicKeyRing) {
|
||||
return new UncachedKeyRing((PGPPublicKeyRing) obj);
|
||||
if ((obj = objectFactory.nextObject()) != null && obj instanceof PGPKeyRing) {
|
||||
// the constructor will take care of the public/secret part
|
||||
return new UncachedKeyRing((PGPKeyRing) obj);
|
||||
} else {
|
||||
throw new PgpGeneralException("Object not recognized as PGPPublicKeyRing!");
|
||||
throw new PgpGeneralException("Object not recognized as PGPKeyRing!");
|
||||
}
|
||||
} 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;
|
||||
}
|
||||
|
||||
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.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class UncachedPublicKey {
|
||||
@ -173,8 +174,24 @@ public class UncachedPublicKey {
|
||||
return mPublicKey.getFingerprint();
|
||||
}
|
||||
|
||||
// Note that this method has package visibility - no access outside the pgp package!
|
||||
PGPPublicKey getPublicKey() {
|
||||
// TODO This method should have package visibility - no access outside the pgp package!
|
||||
// (It's still used in ProviderHelper at this point)
|
||||
public PGPPublicKey getPublicKey() {
|
||||
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;
|
||||
|
||||
import org.spongycastle.bcpg.S2K;
|
||||
import org.spongycastle.openpgp.PGPSecretKey;
|
||||
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) {
|
||||
mSecretRing = secretRing;
|
||||
super(secretRing);
|
||||
}
|
||||
|
||||
// Breaking the pattern here, for key import!
|
||||
// TODO reduce this from public to default visibility!
|
||||
public PGPSecretKeyRing getSecretKeyRing() {
|
||||
return mSecretRing;
|
||||
public ArrayList<Long> getAvailableSubkeys() {
|
||||
ArrayList<Long> result = new ArrayList<Long>();
|
||||
// then, mark exactly the keys we have available
|
||||
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.util.IterableIterator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public abstract class WrappedKeyRing extends KeyRing {
|
||||
|
||||
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();
|
||||
|
||||
}
|
||||
|
@ -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.openpgp.PGPException;
|
||||
import org.spongycastle.openpgp.PGPObjectFactory;
|
||||
import org.spongycastle.openpgp.PGPPublicKey;
|
||||
import org.spongycastle.openpgp.PGPSignature;
|
||||
import org.spongycastle.openpgp.PGPSignatureList;
|
||||
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider;
|
||||
@ -14,6 +15,7 @@ import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.Date;
|
||||
|
||||
public class WrappedSignature {
|
||||
|
||||
@ -33,16 +35,57 @@ public class WrappedSignature {
|
||||
return mSig.getKeyID();
|
||||
}
|
||||
|
||||
public int getSignatureType() {
|
||||
return mSig.getSignatureType();
|
||||
}
|
||||
|
||||
public int 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 {
|
||||
init(key.getPublicKey());
|
||||
}
|
||||
|
||||
public void init(UncachedPublicKey key) throws PgpGeneralException {
|
||||
init(key.getPublicKey());
|
||||
}
|
||||
|
||||
protected void init(PGPPublicKey key) throws PgpGeneralException {
|
||||
try {
|
||||
JcaPGPContentVerifierBuilderProvider contentVerifierBuilderProvider =
|
||||
new JcaPGPContentVerifierBuilderProvider()
|
||||
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
||||
mSig.init(contentVerifierBuilderProvider, key.getPublicKey());
|
||||
mSig.init(contentVerifierBuilderProvider, key);
|
||||
} catch(PGPException e) {
|
||||
throw new PgpGeneralException(e);
|
||||
}
|
||||
@ -74,30 +117,9 @@ public class WrappedSignature {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRevocation() {
|
||||
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 {
|
||||
protected boolean verifySignature(PGPPublicKey key, String uid) throws PgpGeneralException {
|
||||
try {
|
||||
return mSig.verifyCertification(uid, key.getPublicKey());
|
||||
return mSig.verifyCertification(uid, key);
|
||||
} catch (SignatureException e) {
|
||||
throw new PgpGeneralException("Error!", 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) {
|
||||
PGPObjectFactory factory = new PGPObjectFactory(data);
|
||||
PGPSignatureList signatures = null;
|
||||
|
@ -23,11 +23,9 @@ import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
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.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.ApiAppsColumns;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.CertsColumns;
|
||||
@ -256,6 +254,8 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
}.getReadableDatabase();
|
||||
|
||||
Cursor cursor = null;
|
||||
ProviderHelper providerHelper = new ProviderHelper(context);
|
||||
|
||||
try {
|
||||
// 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 ("
|
||||
@ -266,14 +266,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
for (int i = 0; i < cursor.getCount(); i++) {
|
||||
cursor.moveToPosition(i);
|
||||
byte[] data = cursor.getBlob(0);
|
||||
PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
|
||||
ProviderHelper providerHelper = new ProviderHelper(context);
|
||||
if (ring instanceof PGPPublicKeyRing)
|
||||
providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
|
||||
else if (ring instanceof PGPSecretKeyRing)
|
||||
providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
|
||||
else {
|
||||
Log.e(Constants.TAG, "Unknown blob data type!");
|
||||
try {
|
||||
UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data);
|
||||
providerHelper.saveKeyRing(ring);
|
||||
} catch(PgpGeneralException e) {
|
||||
Log.e(Constants.TAG, "Error decoding keyring blob!");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -293,14 +290,11 @@ public class KeychainDatabase extends SQLiteOpenHelper {
|
||||
for (int i = 0; i < cursor.getCount(); i++) {
|
||||
cursor.moveToPosition(i);
|
||||
byte[] data = cursor.getBlob(0);
|
||||
PGPKeyRing ring = PgpConversionHelper.BytesToPGPKeyRing(data);
|
||||
ProviderHelper providerHelper = new ProviderHelper(context);
|
||||
if (ring instanceof PGPPublicKeyRing) {
|
||||
providerHelper.saveKeyRing((PGPPublicKeyRing) ring);
|
||||
} else if (ring instanceof PGPSecretKeyRing) {
|
||||
providerHelper.saveKeyRing((PGPSecretKeyRing) ring);
|
||||
} else {
|
||||
Log.e(Constants.TAG, "Unknown blob data type!");
|
||||
try {
|
||||
UncachedKeyRing ring = UncachedKeyRing.decodeFromData(data);
|
||||
providerHelper.saveKeyRing(ring);
|
||||
} catch(PgpGeneralException e) {
|
||||
Log.e(Constants.TAG, "Error decoding keyring blob!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,30 +23,21 @@ import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.OperationApplicationException;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.net.Uri;
|
||||
import android.os.RemoteException;
|
||||
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.pgp.KeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.UncachedPublicKey;
|
||||
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpConversionHelper;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpHelper;
|
||||
import org.sufficientlysecure.keychain.pgp.PgpKeyHelper;
|
||||
import org.sufficientlysecure.keychain.pgp.UncachedKeyRing;
|
||||
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.Certs;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRingData;
|
||||
@ -60,7 +51,6 @@ import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@ -147,19 +137,26 @@ public class ProviderHelper {
|
||||
return getGenericData(KeyRings.buildUnifiedKeyRingUri(masterKeyId), proj, types);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public LongSparseArray<PGPKeyRing> getPGPKeyRings(Uri queryUri) {
|
||||
private LongSparseArray<UncachedPublicKey> getUncachedMasterKeys(Uri queryUri) {
|
||||
Cursor cursor = mContentResolver.query(queryUri,
|
||||
new String[]{KeyRingData.MASTER_KEY_ID, KeyRingData.KEY_RING_DATA},
|
||||
null, null, null);
|
||||
|
||||
LongSparseArray<PGPKeyRing> result = new LongSparseArray<PGPKeyRing>(cursor.getCount());
|
||||
LongSparseArray<UncachedPublicKey> result =
|
||||
new LongSparseArray<UncachedPublicKey>(cursor.getCount());
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) do {
|
||||
long masterKeyId = cursor.getLong(0);
|
||||
byte[] data = cursor.getBlob(1);
|
||||
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());
|
||||
} finally {
|
||||
@ -194,12 +191,13 @@ public class ProviderHelper {
|
||||
|
||||
private KeyRing getWrappedKeyRing(Uri queryUri, boolean secret) throws NotFoundException {
|
||||
Cursor cursor = mContentResolver.query(queryUri,
|
||||
new String[] {
|
||||
// we pick from cache only information that is not easily available from keyrings
|
||||
KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED,
|
||||
// and of course, ring data
|
||||
secret ? KeyRings.PRIVKEY_DATA : KeyRings.PUBKEY_DATA
|
||||
}, null, null, null);
|
||||
new String[]{
|
||||
// we pick from cache only information that is not easily available from keyrings
|
||||
KeyRings.HAS_ANY_SECRET, KeyRings.VERIFIED,
|
||||
// and of course, ring data
|
||||
secret ? KeyRings.PRIVKEY_DATA : KeyRings.PUBKEY_DATA
|
||||
}, null, null, null
|
||||
);
|
||||
try {
|
||||
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
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void saveKeyRing(PGPPublicKeyRing keyRing) throws IOException {
|
||||
PGPPublicKey masterKey = keyRing.getPublicKey();
|
||||
long masterKeyId = masterKey.getKeyID();
|
||||
public void saveKeyRing(UncachedKeyRing keyRing) throws IOException {
|
||||
UncachedPublicKey masterKey = keyRing.getPublicKey();
|
||||
long masterKeyId = masterKey.getKeyId();
|
||||
|
||||
// IF there is a secret key, preserve it!
|
||||
PGPSecretKeyRing secretRing = null;
|
||||
UncachedSecretKeyRing secretRing = null;
|
||||
try {
|
||||
secretRing = getPGPSecretKeyRing(masterKeyId);
|
||||
secretRing = getWrappedSecretKeyRing(masterKeyId).getUncached();
|
||||
} catch (NotFoundException e) {
|
||||
Log.e(Constants.TAG, "key not found!");
|
||||
}
|
||||
@ -272,36 +251,38 @@ public class ProviderHelper {
|
||||
ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
|
||||
|
||||
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));
|
||||
++rank;
|
||||
}
|
||||
|
||||
// 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!
|
||||
if (secretRing != null)
|
||||
allKeyRings.put(secretRing.getSecretKey().getKeyID(), secretRing);
|
||||
if (secretRing != null) {
|
||||
allKeyRings.put(secretRing.getMasterKeyId(), secretRing.getPublicKey());
|
||||
}
|
||||
|
||||
// classify and order user ids. primary are moved to the front, revoked to the back,
|
||||
// otherwise the order in the keyfile is preserved.
|
||||
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();
|
||||
uids.add(item);
|
||||
item.userId = userId;
|
||||
|
||||
// look through signatures for this specific key
|
||||
for (PGPSignature cert : new IterableIterator<PGPSignature>(
|
||||
masterKey.getSignaturesForID(userId))) {
|
||||
long certId = cert.getKeyID();
|
||||
for (WrappedSignature cert : new IterableIterator<WrappedSignature>(
|
||||
masterKey.getSignaturesForId(userId))) {
|
||||
long certId = cert.getKeyId();
|
||||
try {
|
||||
// self signature
|
||||
if (certId == masterKeyId) {
|
||||
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME), masterKey);
|
||||
if (!cert.verifyCertification(userId, masterKey)) {
|
||||
cert.init(masterKey);
|
||||
if (!cert.verifySignature(masterKey, userId)) {
|
||||
// not verified?! dang! TODO notify user? this is kinda serious...
|
||||
Log.e(Constants.TAG, "Could not verify self signature for " + userId + "!");
|
||||
continue;
|
||||
@ -310,31 +291,22 @@ public class ProviderHelper {
|
||||
if (item.selfCert == null ||
|
||||
item.selfCert.getCreationTime().before(cert.getCreationTime())) {
|
||||
item.selfCert = cert;
|
||||
item.isPrimary = cert.getHashedSubPackets().isPrimaryUserID();
|
||||
item.isRevoked =
|
||||
cert.getSignatureType() == PGPSignature.CERTIFICATION_REVOCATION;
|
||||
item.isPrimary = cert.isPrimaryUserId();
|
||||
item.isRevoked = cert.isRevocation();
|
||||
}
|
||||
}
|
||||
// verify signatures from known private keys
|
||||
if (allKeyRings.indexOfKey(certId) >= 0) {
|
||||
// mark them as verified
|
||||
cert.init(new JcaPGPContentVerifierBuilderProvider().setProvider(
|
||||
Constants.BOUNCY_CASTLE_PROVIDER_NAME),
|
||||
allKeyRings.get(certId).getPublicKey());
|
||||
if (cert.verifyCertification(userId, masterKey)) {
|
||||
cert.init(allKeyRings.get(certId));
|
||||
if (cert.verifySignature(masterKey, userId)) {
|
||||
item.trustedCerts.add(cert);
|
||||
}
|
||||
}
|
||||
} catch (SignatureException e) {
|
||||
} catch (PgpGeneralException e) {
|
||||
Log.e(Constants.TAG, "Signature verification failed! "
|
||||
+ PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyID())
|
||||
+ PgpKeyHelper.convertKeyIdToHex(masterKey.getKeyId())
|
||||
+ " from "
|
||||
+ 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);
|
||||
+ PgpKeyHelper.convertKeyIdToHex(cert.getKeyId()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -380,8 +352,8 @@ public class ProviderHelper {
|
||||
String userId;
|
||||
boolean isPrimary = false;
|
||||
boolean isRevoked = false;
|
||||
PGPSignature selfCert;
|
||||
List<PGPSignature> trustedCerts = new ArrayList<PGPSignature>();
|
||||
WrappedSignature selfCert;
|
||||
List<WrappedSignature> trustedCerts = new ArrayList<WrappedSignature>();
|
||||
|
||||
@Override
|
||||
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
|
||||
* is already in the database!
|
||||
*/
|
||||
public void saveKeyRing(UncachedSecretKeyRing wrappedRing) throws IOException {
|
||||
// TODO split up getters
|
||||
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();
|
||||
public void saveKeyRing(UncachedSecretKeyRing keyRing) throws IOException {
|
||||
long masterKeyId = keyRing.getMasterKeyId();
|
||||
|
||||
{
|
||||
Uri uri = Keys.buildKeysUri(Long.toString(masterKeyId));
|
||||
@ -424,14 +386,10 @@ public class ProviderHelper {
|
||||
|
||||
values.put(Keys.HAS_SECRET, 1);
|
||||
// then, mark exactly the keys we have available
|
||||
for (PGPSecretKey sub : new IterableIterator<PGPSecretKey>(keyRing.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) {
|
||||
mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[]{
|
||||
Long.toString(sub.getKeyID())
|
||||
});
|
||||
}
|
||||
for (Long sub : new IterableIterator<Long>(keyRing.getAvailableSubkeys().iterator())) {
|
||||
mContentResolver.update(uri, values, Keys.KEY_ID + " = ?", new String[] {
|
||||
Long.toString(sub)
|
||||
});
|
||||
}
|
||||
// this implicitly leaves all keys which were not in the secret key ring
|
||||
// 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
|
||||
*/
|
||||
@ -464,32 +417,32 @@ public class ProviderHelper {
|
||||
mContentResolver.delete(KeyRingData.buildSecretKeyRingUri(Long.toString(masterKeyId)), null, null);
|
||||
|
||||
// save public keyring
|
||||
saveKeyRing((PGPPublicKeyRing) pubRing.getRing());
|
||||
saveKeyRing((PGPSecretKeyRing) secRing.getRing());
|
||||
saveKeyRing(pubRing);
|
||||
saveKeyRing(secRing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build ContentProviderOperation to add PGPPublicKey to database corresponding to a keyRing
|
||||
*/
|
||||
private ContentProviderOperation
|
||||
buildPublicKeyOperations(long masterKeyId, PGPPublicKey key, int rank) throws IOException {
|
||||
buildPublicKeyOperations(long masterKeyId, UncachedPublicKey key, int rank) throws IOException {
|
||||
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Keys.MASTER_KEY_ID, masterKeyId);
|
||||
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.ALGORITHM, key.getAlgorithm());
|
||||
values.put(Keys.FINGERPRINT, key.getFingerprint());
|
||||
|
||||
values.put(Keys.CAN_CERTIFY, (PgpKeyHelper.isCertificationKey(key)));
|
||||
values.put(Keys.CAN_SIGN, (PgpKeyHelper.isSigningKey(key)));
|
||||
values.put(Keys.CAN_ENCRYPT, PgpKeyHelper.isEncryptionKey(key));
|
||||
values.put(Keys.IS_REVOKED, key.isRevoked());
|
||||
values.put(Keys.CAN_CERTIFY, key.canCertify());
|
||||
values.put(Keys.CAN_SIGN, key.canSign());
|
||||
values.put(Keys.CAN_ENCRYPT, key.canEncrypt());
|
||||
values.put(Keys.IS_REVOKED, key.maybeRevoked());
|
||||
|
||||
values.put(Keys.CREATION, PgpKeyHelper.getCreationDate(key).getTime() / 1000);
|
||||
Date expiryDate = PgpKeyHelper.getExpiryDate(key);
|
||||
values.put(Keys.CREATION, key.getCreationTime().getTime() / 1000);
|
||||
Date expiryDate = key.getExpiryTime();
|
||||
if (expiryDate != null) {
|
||||
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
|
||||
*/
|
||||
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();
|
||||
values.put(Certs.MASTER_KEY_ID, masterKeyId);
|
||||
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.CREATION, cert.getCreationTime().getTime() / 1000);
|
||||
values.put(Certs.VERIFIED, verified);
|
||||
@ -535,23 +489,11 @@ public class ProviderHelper {
|
||||
return ContentProviderOperation.newInsert(uri).withValues(values).build();
|
||||
}
|
||||
|
||||
private String getKeyRingAsArmoredString(byte[] data) throws IOException {
|
||||
Object keyRing = null;
|
||||
if (data != null) {
|
||||
keyRing = PgpConversionHelper.BytesToPGPKeyRing(data);
|
||||
}
|
||||
private String getKeyRingAsArmoredString(byte[] data) throws IOException, PgpGeneralException {
|
||||
UncachedKeyRing keyRing = UncachedKeyRing.decodeFromData(data);
|
||||
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
ArmoredOutputStream aos = new ArmoredOutputStream(bos);
|
||||
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();
|
||||
|
||||
keyRing.encodeArmored(bos, PgpHelper.getFullVersion(mContext));
|
||||
String armoredKey = bos.toString("UTF-8");
|
||||
|
||||
Log.d(Constants.TAG, "armoredKey:" + armoredKey);
|
||||
@ -560,77 +502,12 @@ public class ProviderHelper {
|
||||
}
|
||||
|
||||
public String getKeyRingAsArmoredString(Uri uri)
|
||||
throws NotFoundException, IOException {
|
||||
throws NotFoundException, IOException, PgpGeneralException {
|
||||
byte[] data = (byte[]) getGenericData(
|
||||
uri, KeyRingData.KEY_RING_DATA, ProviderHelper.FIELD_TYPE_BLOB);
|
||||
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() {
|
||||
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.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.R;
|
||||
import org.sufficientlysecure.keychain.helper.FileHelper;
|
||||
import org.sufficientlysecure.keychain.helper.OtherHelper;
|
||||
import org.sufficientlysecure.keychain.helper.Preferences;
|
||||
import org.sufficientlysecure.keychain.keyimport.HkpKeyserver;
|
||||
import org.sufficientlysecure.keychain.pgp.UncachedSecretKey;
|
||||
import org.sufficientlysecure.keychain.pgp.WrappedPublicKeyRing;
|
||||
import org.sufficientlysecure.keychain.pgp.WrappedSecretKey;
|
||||
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
|
||||
@ -594,21 +591,21 @@ public class KeychainIntentService extends IntentService
|
||||
buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
|
||||
4096, passphrase, true);
|
||||
os.write(buf);
|
||||
keyUsageList.add(KeyFlags.CERTIFY_OTHER);
|
||||
keyUsageList.add(UncachedSecretKey.CERTIFY_OTHER);
|
||||
keysCreated++;
|
||||
setProgress(keysCreated, keysTotal);
|
||||
|
||||
buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
|
||||
4096, passphrase, false);
|
||||
os.write(buf);
|
||||
keyUsageList.add(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE);
|
||||
keyUsageList.add(UncachedSecretKey.ENCRYPT_COMMS | UncachedSecretKey.ENCRYPT_STORAGE);
|
||||
keysCreated++;
|
||||
setProgress(keysCreated, keysTotal);
|
||||
|
||||
buf = keyOperations.createKey(Constants.choice.algorithm.rsa,
|
||||
4096, passphrase, false);
|
||||
os.write(buf);
|
||||
keyUsageList.add(KeyFlags.SIGN_DATA);
|
||||
keyUsageList.add(UncachedSecretKey.SIGN_DATA);
|
||||
keysCreated++;
|
||||
setProgress(keysCreated, keysTotal);
|
||||
|
||||
@ -749,23 +746,15 @@ public class KeychainIntentService extends IntentService
|
||||
byte[] downloadedKeyBytes = server.get(keybaseId).getBytes();
|
||||
|
||||
// create PGPKeyRing object based on downloaded armored key
|
||||
PGPKeyRing downloadedKey = null;
|
||||
UncachedKeyRing downloadedKey = null;
|
||||
BufferedInputStream bufferedInput =
|
||||
new BufferedInputStream(new ByteArrayInputStream(downloadedKeyBytes));
|
||||
if (bufferedInput.available() > 0) {
|
||||
InputStream in = PGPUtil.getDecoderStream(bufferedInput);
|
||||
PGPObjectFactory objectFactory = new PGPObjectFactory(in);
|
||||
|
||||
// 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!");
|
||||
}
|
||||
List<UncachedKeyRing> rings = UncachedKeyRing.fromStream(bufferedInput);
|
||||
if(rings.isEmpty()) {
|
||||
throw new PgpGeneralException("No keys in result!");
|
||||
}
|
||||
downloadedKey = rings.get(0);
|
||||
}
|
||||
|
||||
// save key bytes in entry object for doing the
|
||||
|
@ -34,12 +34,6 @@ import android.os.Messenger;
|
||||
import android.os.RemoteException;
|
||||
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.helper.Preferences;
|
||||
import org.sufficientlysecure.keychain.pgp.WrappedSecretKeyRing;
|
||||
@ -48,7 +42,6 @@ import org.sufficientlysecure.keychain.provider.ProviderHelper;
|
||||
import org.sufficientlysecure.keychain.util.Log;
|
||||
|
||||
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
|
||||
@ -191,7 +184,8 @@ public class PassphraseCacheService extends Service {
|
||||
// get cached passphrase
|
||||
String cachedPassphrase = mPassphraseCache.get(keyId);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
* 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
|
||||
* 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
|
||||
* 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,
|
||||
// use Serializable instead.
|
||||
public static class SubkeyAdd implements Serializable {
|
||||
public final int mAlgorithm;
|
||||
public final int mKeysize;
|
||||
public final int mFlags;
|
||||
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;
|
||||
mFlags = flags;
|
||||
mExpiry = expiry;
|
||||
@ -59,7 +62,7 @@ public class SaveKeyringParcel implements Parcelable {
|
||||
public final long mKeyId;
|
||||
public final Integer mFlags;
|
||||
public final Long mExpiry;
|
||||
public SubkeyChange(long keyId, int flags, long expiry) {
|
||||
public SubkeyChange(long keyId, Integer flags, Long expiry) {
|
||||
mKeyId = keyId;
|
||||
mFlags = flags;
|
||||
mExpiry = expiry;
|
||||
|
@ -41,6 +41,7 @@ import org.sufficientlysecure.keychain.Constants;
|
||||
import org.sufficientlysecure.keychain.R;
|
||||
import org.sufficientlysecure.keychain.compatibility.ClipboardReflection;
|
||||
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.KeyRings;
|
||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Keys;
|
||||
@ -190,6 +191,9 @@ public class ViewKeyShareFragment extends LoaderFragment implements
|
||||
}
|
||||
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) {
|
||||
Log.e(Constants.TAG, "error processing key!", e);
|
||||
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
|
||||
if (!(secretKeyId == Constants.key.symmetric || secretKeyId == Constants.key.none)) {
|
||||
try {
|
||||
if (new ProviderHelper(context).getWrappedSecretKeyRing(secretKeyId).hasPassphrase()) {
|
||||
if (!new ProviderHelper(context).getWrappedSecretKeyRing(secretKeyId).hasPassphrase()) {
|
||||
throw new PgpGeneralException("No passphrase! No passphrase dialog needed!");
|
||||
}
|
||||
} catch(ProviderHelper.NotFoundException e) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user