new savekeyring operation (mostly stub)
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user