aboutsummaryrefslogtreecommitdiffstats
path: root/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
diff options
context:
space:
mode:
Diffstat (limited to 'OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java')
-rw-r--r--OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java216
1 files changed, 110 insertions, 106 deletions
diff --git a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
index 592bdec73..520189448 100644
--- a/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
+++ b/OpenPGP-Keychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpKeyOperation.java
@@ -17,20 +17,6 @@
package org.sufficientlysecure.keychain.pgp;
-import java.io.IOException;
-import java.math.BigInteger;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.KeyPairGenerator;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.SecureRandom;
-import java.security.SignatureException;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.Iterator;
-import java.util.TimeZone;
-import android.content.Context;
import org.spongycastle.bcpg.CompressionAlgorithmTags;
import org.spongycastle.bcpg.HashAlgorithmTags;
import org.spongycastle.bcpg.SymmetricKeyAlgorithmTags;
@@ -46,18 +32,11 @@ import org.spongycastle.openpgp.operator.jcajce.*;
import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Id;
import org.sufficientlysecure.keychain.R;
-import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
-import org.sufficientlysecure.keychain.provider.ProviderHelper;
+import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
import org.sufficientlysecure.keychain.service.SaveKeyringParcel;
import org.sufficientlysecure.keychain.util.Primes;
import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
-
-import android.content.Context;
-import android.util.Pair;
import org.sufficientlysecure.keychain.util.IterableIterator;
-import org.sufficientlysecure.keychain.util.Log;
-import org.sufficientlysecure.keychain.util.Primes;
-import org.sufficientlysecure.keychain.util.ProgressDialogUpdater;
import java.io.IOException;
import java.math.BigInteger;
@@ -69,9 +48,17 @@ import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
+/** This class is the single place where ALL operations that actually modify a PGP public or secret
+ * key take place.
+ *
+ * Note that no android specific stuff should be done here, ie no imports from com.android.
+ *
+ * All operations support progress reporting to a ProgressDialogUpdater passed on initialization.
+ * This indicator may be null.
+ *
+ */
public class PgpKeyOperation {
- private final Context mContext;
- private final ProgressDialogUpdater mProgress;
+ private ProgressDialogUpdater mProgress;
private static final int[] PREFERRED_SYMMETRIC_ALGORITHMS = new int[]{
SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192,
@@ -83,9 +70,8 @@ public class PgpKeyOperation {
CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2,
CompressionAlgorithmTags.ZIP};
- public PgpKeyOperation(Context context, ProgressDialogUpdater progress) {
+ public PgpKeyOperation(ProgressDialogUpdater progress) {
super();
- this.mContext = context;
this.mProgress = progress;
}
@@ -101,13 +87,29 @@ public class PgpKeyOperation {
}
}
+ /**
+ * Creates new secret key.
+ *
+ * @param algorithmChoice
+ * @param keySize
+ * @param passphrase
+ * @param isMasterKey
+ * @return A newly created PGPSecretKey
+ * @throws NoSuchAlgorithmException
+ * @throws PGPException
+ * @throws NoSuchProviderException
+ * @throws PgpGeneralMsgIdException
+ * @throws InvalidAlgorithmParameterException
+ */
+
+ // TODO: key flags?
public PGPSecretKey createKey(int algorithmChoice, int keySize, String passphrase,
boolean isMasterKey)
throws NoSuchAlgorithmException, PGPException, NoSuchProviderException,
- PgpGeneralException, InvalidAlgorithmParameterException {
+ PgpGeneralMsgIdException, InvalidAlgorithmParameterException {
if (keySize < 512) {
- throw new PgpGeneralException(mContext.getString(R.string.error_key_size_minimum512bit));
+ throw new PgpGeneralMsgIdException(R.string.error_key_size_minimum512bit);
}
if (passphrase == null) {
@@ -127,8 +129,7 @@ public class PgpKeyOperation {
case Id.choice.algorithm.elgamal: {
if (isMasterKey) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_master_key_must_not_be_el_gamal));
+ throw new PgpGeneralMsgIdException(R.string.error_master_key_must_not_be_el_gamal);
}
keyGen = KeyPairGenerator.getInstance("ElGamal", Constants.BOUNCY_CASTLE_PROVIDER_NAME);
BigInteger p = Primes.getBestPrime(keySize);
@@ -150,8 +151,7 @@ public class PgpKeyOperation {
}
default: {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_unknown_algorithm_choice));
+ throw new PgpGeneralMsgIdException(R.string.error_unknown_algorithm_choice);
}
}
@@ -171,8 +171,9 @@ public class PgpKeyOperation {
sha1Calc, isMasterKey, keyEncryptor);
}
- public void changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
- String newPassPhrase) throws IOException, PGPException {
+ public PGPSecretKeyRing changeSecretKeyPassphrase(PGPSecretKeyRing keyRing, String oldPassPhrase,
+ String newPassPhrase) throws IOException, PGPException,
+ NoSuchProviderException {
updateProgress(R.string.progress_building_key, 0, 100);
if (oldPassPhrase == null) {
@@ -190,16 +191,16 @@ public class PgpKeyOperation {
new JcePBESecretKeyEncryptorBuilder(keyRing.getSecretKey()
.getKeyEncryptionAlgorithm()).build(newPassPhrase.toCharArray()));
- updateProgress(R.string.progress_saving_key_ring, 50, 100);
-
- ProviderHelper.saveKeyRing(mContext, newKeyRing);
-
- updateProgress(R.string.progress_done, 100, 100);
+ return newKeyRing;
}
- private void buildNewSecretKey(ArrayList<String> userIds, ArrayList<PGPSecretKey> keys, ArrayList<GregorianCalendar> keysExpiryDates, ArrayList<Integer> keysUsages, String newPassPhrase, String oldPassPhrase) throws PgpGeneralException,
- PGPException, SignatureException, IOException {
+ private Pair<PGPSecretKeyRing,PGPPublicKeyRing> buildNewSecretKey(
+ ArrayList<String> userIds, ArrayList<PGPSecretKey> keys,
+ ArrayList<GregorianCalendar> keysExpiryDates,
+ ArrayList<Integer> keysUsages,
+ String newPassPhrase, String oldPassPhrase)
+ throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
int usageId = keysUsages.get(0);
boolean canSign;
@@ -210,8 +211,6 @@ public class PgpKeyOperation {
// this removes all userIds and certifications previously attached to the masterPublicKey
PGPPublicKey masterPublicKey = masterKey.getPublicKey();
- PGPSecretKeyRing mKR = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, masterKey.getKeyID());
-
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(oldPassPhrase.toCharArray());
PGPPrivateKey masterPrivateKey = masterKey.extractPrivateKey(keyDecryptor);
@@ -249,7 +248,7 @@ public class PgpKeyOperation {
//here we purposefully ignore partial days in each date - long type has no fractional part!
long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
if (numDays <= 0)
- throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation));
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
} else {
hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding,
@@ -319,9 +318,11 @@ public class PgpKeyOperation {
GregorianCalendar expiryDate = keysExpiryDates.get(i);
//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 = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
- if (numDays <= 0)
- throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation));
+ long numDays =
+ (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
+ if (numDays <= 0) {
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
+ }
hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
} else {
hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding,
@@ -334,23 +335,18 @@ public class PgpKeyOperation {
PGPSecretKeyRing secretKeyRing = keyGen.generateSecretKeyRing();
PGPPublicKeyRing publicKeyRing = keyGen.generatePublicKeyRing();
- updateProgress(R.string.progress_saving_key_ring, 90, 100);
-
- ProviderHelper.saveKeyRing(mContext, secretKeyRing);
- ProviderHelper.saveKeyRing(mContext, publicKeyRing);
+ return new Pair<PGPSecretKeyRing,PGPPublicKeyRing>(secretKeyRing, publicKeyRing);
- updateProgress(R.string.progress_done, 100, 100);
}
- public void buildSecretKey (SaveKeyringParcel saveParcel)throws PgpGeneralException,
- PGPException, SignatureException, IOException {
+ public Pair<PGPSecretKeyRing,PGPPublicKeyRing> buildSecretKey (PGPSecretKeyRing mKR,
+ PGPPublicKeyRing pKR,
+ SaveKeyringParcel saveParcel)
+ throws PgpGeneralMsgIdException, PGPException, SignatureException, IOException {
updateProgress(R.string.progress_building_key, 0, 100);
PGPSecretKey masterKey = saveParcel.keys.get(0);
- PGPSecretKeyRing mKR = ProviderHelper.getPGPSecretKeyRingByKeyId(mContext, masterKey.getKeyID());
- PGPPublicKeyRing pKR = ProviderHelper.getPGPPublicKeyRingByKeyId(mContext, masterKey.getKeyID());
-
if (saveParcel.oldPassPhrase == null) {
saveParcel.oldPassPhrase = "";
}
@@ -359,9 +355,8 @@ public class PgpKeyOperation {
}
if (mKR == null) {
- buildNewSecretKey(saveParcel.userIDs, saveParcel.keys, saveParcel.keysExpiryDates,
+ return buildNewSecretKey(saveParcel.userIDs, saveParcel.keys, saveParcel.keysExpiryDates,
saveParcel.keysUsages, saveParcel.newPassPhrase, saveParcel.oldPassPhrase); //new Keyring
- return;
}
/*
@@ -430,7 +425,7 @@ public class PgpKeyOperation {
//here we purposefully ignore partial days in each date - long type has no fractional part!
long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
if (numDays <= 0)
- throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation));
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
} else {
hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding,
@@ -592,7 +587,7 @@ public class PgpKeyOperation {
//here we purposefully ignore partial days in each date - long type has no fractional part!
long numDays = (expiryDate.getTimeInMillis() / 86400000) - (creationDate.getTimeInMillis() / 86400000);
if (numDays <= 0)
- throw new PgpGeneralException(mContext.getString(R.string.error_expiry_must_come_after_creation));
+ throw new PgpGeneralMsgIdException(R.string.error_expiry_must_come_after_creation);
hashedPacketsGen.setKeyExpirationTime(false, numDays * 86400);
} else {
hashedPacketsGen.setKeyExpirationTime(false, 0); //do this explicitly, although since we're rebuilding,
@@ -637,7 +632,6 @@ public class PgpKeyOperation {
//update the passphrase
mKR = PGPSecretKeyRing.copyWithNewPassword(mKR, keyDecryptor, keyEncryptorNew);
- updateProgress(R.string.progress_saving_key_ring, 90, 100);
/* additional handy debug info
@@ -660,62 +654,72 @@ public class PgpKeyOperation {
*/
+ return new Pair<PGPSecretKeyRing,PGPPublicKeyRing>(mKR, pKR);
- ProviderHelper.saveKeyRing(mContext, mKR);
- ProviderHelper.saveKeyRing(mContext, pKR);
-
- updateProgress(R.string.progress_done, 100, 100);
}
- public PGPPublicKeyRing certifyKey(long masterKeyId, long pubKeyId, List<String> userIds, String passphrase)
- throws PgpGeneralException, NoSuchAlgorithmException, NoSuchProviderException,
+ /**
+ * 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 {
- if (passphrase == null) {
- throw new PgpGeneralException("Unable to obtain passphrase");
- } else {
- // create a signatureGenerator from the supplied masterKeyId and passphrase
- PGPSignatureGenerator signatureGenerator; {
+ // create a signatureGenerator from the supplied masterKeyId and passphrase
+ PGPSignatureGenerator signatureGenerator; {
- PGPSecretKey certificationKey = PgpKeyHelper.getCertificationKey(mContext, masterKeyId);
- if (certificationKey == null) {
- throw new PgpGeneralException(mContext.getString(R.string.error_signature_failed));
- }
+ if (certificationKey == null) {
+ throw new PgpGeneralMsgIdException(R.string.error_signature_failed);
+ }
- PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
- Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
- PGPPrivateKey signaturePrivateKey = certificationKey.extractPrivateKey(keyDecryptor);
- if (signaturePrivateKey == null) {
- throw new PgpGeneralException(
- mContext.getString(R.string.error_could_not_extract_private_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);
+ // 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);
- }
+ 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);
- }
+ { // 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
- PGPPublicKeyRing pubring = ProviderHelper
- .getPGPPublicKeyRingByKeyId(mContext, pubKeyId);
- PGPPublicKey signedKey = pubring.getPublicKey(pubKeyId);
- for(String userId : new IterableIterator<String>(userIds.iterator())) {
- PGPSignature sig = signatureGenerator.generateCertification(userId, signedKey);
- signedKey = PGPPublicKey.addCertification(signedKey, userId, sig);
- }
- pubring = PGPPublicKeyRing.insertPublicKey(pubring, signedKey);
+ // 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;
+ }
- return pubring;
+ /** Simple static subclass that stores two values.
+ *
+ * This is only used to return a pair of values in one function above. We specifically don't use
+ * com.android.Pair to keep this class free from android dependencies.
+ */
+ public static class Pair<K, V> {
+ public final K first;
+ public final V second;
+ public Pair(K first, V second) {
+ this.first = first;
+ this.second = second;
}
}
}