2014-08-04 10:46:23 -04:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2014 Dominik Schürmann <dominik@dominikschuermann.de>
|
2014-08-20 18:02:27 -04:00
|
|
|
* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
|
2014-08-04 10:46:23 -04:00
|
|
|
*
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2014-05-03 09:55:10 -04:00
|
|
|
package org.sufficientlysecure.keychain.pgp;
|
|
|
|
|
2014-07-21 09:10:29 -04:00
|
|
|
import org.spongycastle.bcpg.HashAlgorithmTags;
|
|
|
|
import org.spongycastle.bcpg.S2K;
|
2014-05-03 09:55:10 -04:00
|
|
|
import org.spongycastle.openpgp.PGPException;
|
|
|
|
import org.spongycastle.openpgp.PGPPrivateKey;
|
2014-05-03 12:32:20 -04:00
|
|
|
import org.spongycastle.openpgp.PGPPublicKey;
|
|
|
|
import org.spongycastle.openpgp.PGPPublicKeyRing;
|
2014-05-03 09:55:10 -04:00
|
|
|
import org.spongycastle.openpgp.PGPSecretKey;
|
|
|
|
import org.spongycastle.openpgp.PGPSignature;
|
|
|
|
import org.spongycastle.openpgp.PGPSignatureGenerator;
|
|
|
|
import org.spongycastle.openpgp.PGPSignatureSubpacketGenerator;
|
2014-05-03 12:32:20 -04:00
|
|
|
import org.spongycastle.openpgp.PGPSignatureSubpacketVector;
|
|
|
|
import org.spongycastle.openpgp.PGPUtil;
|
2014-05-03 09:55:10 -04:00
|
|
|
import org.spongycastle.openpgp.operator.PBESecretKeyDecryptor;
|
2014-07-21 09:10:29 -04:00
|
|
|
import org.spongycastle.openpgp.operator.PGPContentSignerBuilder;
|
2014-05-03 09:55:10 -04:00
|
|
|
import org.spongycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
|
|
|
|
import org.spongycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder;
|
|
|
|
import org.spongycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder;
|
|
|
|
import org.spongycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder;
|
2014-09-08 08:04:46 -04:00
|
|
|
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPublicKeyDataDecryptorFactoryBuilder;
|
2014-07-21 09:10:29 -04:00
|
|
|
import org.spongycastle.openpgp.operator.jcajce.NfcSyncPGPContentSignerBuilder;
|
2014-05-03 09:55:10 -04:00
|
|
|
import org.sufficientlysecure.keychain.Constants;
|
|
|
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
|
2014-05-03 12:32:20 -04:00
|
|
|
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralMsgIdException;
|
2014-10-08 19:37:44 -04:00
|
|
|
import org.sufficientlysecure.keychain.pgp.exception.PgpKeyNotFoundException;
|
2014-05-03 12:32:20 -04:00
|
|
|
import org.sufficientlysecure.keychain.util.IterableIterator;
|
2014-07-21 09:10:29 -04:00
|
|
|
import org.sufficientlysecure.keychain.util.Log;
|
2014-05-03 12:32:20 -04:00
|
|
|
|
|
|
|
import java.security.NoSuchAlgorithmException;
|
|
|
|
import java.security.NoSuchProviderException;
|
|
|
|
import java.security.SignatureException;
|
2014-07-22 12:09:12 -04:00
|
|
|
import java.util.Date;
|
2014-07-21 09:10:29 -04:00
|
|
|
import java.util.LinkedList;
|
2014-05-03 12:32:20 -04:00
|
|
|
import java.util.List;
|
2014-05-03 09:55:10 -04:00
|
|
|
|
2014-08-14 08:50:13 -04:00
|
|
|
/**
|
|
|
|
* Wrapper for a PGPSecretKey.
|
|
|
|
* <p/>
|
2014-05-31 07:10:41 -04:00
|
|
|
* This object can only be obtained from a WrappedSecretKeyRing, and stores a
|
|
|
|
* back reference to its parent.
|
2014-08-14 08:50:13 -04:00
|
|
|
* <p/>
|
2014-05-31 07:10:41 -04:00
|
|
|
* This class represents known secret keys which are stored in the database.
|
|
|
|
* All "crypto operations using a known secret key" should be implemented in
|
|
|
|
* this class, to ensure on type level that these operations are performed on
|
|
|
|
* properly imported secret keys only.
|
|
|
|
*/
|
2014-07-31 11:08:33 -04:00
|
|
|
public class CanonicalizedSecretKey extends CanonicalizedPublicKey {
|
2014-05-03 09:55:10 -04:00
|
|
|
|
2014-05-03 13:04:23 -04:00
|
|
|
private final PGPSecretKey mSecretKey;
|
2014-05-03 09:55:10 -04:00
|
|
|
private PGPPrivateKey mPrivateKey = null;
|
|
|
|
|
2014-07-21 09:10:29 -04:00
|
|
|
private int mPrivateKeyState = PRIVATE_KEY_STATE_LOCKED;
|
|
|
|
private static int PRIVATE_KEY_STATE_LOCKED = 0;
|
|
|
|
private static int PRIVATE_KEY_STATE_UNLOCKED = 1;
|
|
|
|
private static int PRIVATE_KEY_STATE_DIVERT_TO_CARD = 2;
|
|
|
|
|
2014-07-31 11:08:33 -04:00
|
|
|
CanonicalizedSecretKey(CanonicalizedSecretKeyRing ring, PGPSecretKey key) {
|
2014-05-03 13:04:23 -04:00
|
|
|
super(ring, key.getPublicKey());
|
|
|
|
mSecretKey = key;
|
2014-05-03 09:55:10 -04:00
|
|
|
}
|
|
|
|
|
2014-07-31 11:08:33 -04:00
|
|
|
public CanonicalizedSecretKeyRing getRing() {
|
|
|
|
return (CanonicalizedSecretKeyRing) mRing;
|
2014-05-04 10:59:55 -04:00
|
|
|
}
|
|
|
|
|
2014-09-01 19:24:16 -04:00
|
|
|
public enum SecretKeyType {
|
2014-09-08 08:04:46 -04:00
|
|
|
UNAVAILABLE(0), GNU_DUMMY(1), PASSPHRASE(2), PASSPHRASE_EMPTY(3), DIVERT_TO_CARD(4);
|
2014-09-01 19:24:16 -04:00
|
|
|
|
|
|
|
final int mNum;
|
2014-09-08 08:04:46 -04:00
|
|
|
|
2014-09-01 19:24:16 -04:00
|
|
|
SecretKeyType(int num) {
|
|
|
|
mNum = num;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static SecretKeyType fromNum(int num) {
|
|
|
|
switch (num) {
|
2014-09-08 08:04:46 -04:00
|
|
|
case 1:
|
|
|
|
return GNU_DUMMY;
|
|
|
|
case 2:
|
|
|
|
return PASSPHRASE;
|
|
|
|
case 3:
|
|
|
|
return PASSPHRASE_EMPTY;
|
|
|
|
case 4:
|
|
|
|
return DIVERT_TO_CARD;
|
2014-09-01 19:24:16 -04:00
|
|
|
// if this case happens, it's probably a check from a database value
|
2014-09-08 08:04:46 -04:00
|
|
|
default:
|
|
|
|
return UNAVAILABLE;
|
2014-09-01 19:24:16 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public int getNum() {
|
|
|
|
return mNum;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isUsable() {
|
|
|
|
return this != UNAVAILABLE && this != GNU_DUMMY;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
public SecretKeyType getSecretKeyType() {
|
2014-09-21 12:27:03 -04:00
|
|
|
if (mSecretKey.getS2K() != null && mSecretKey.getS2K().getType() == S2K.GNU_DUMMY_S2K) {
|
2014-09-01 19:24:16 -04:00
|
|
|
// divert to card is special
|
|
|
|
if (mSecretKey.getS2K().getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD) {
|
|
|
|
return SecretKeyType.DIVERT_TO_CARD;
|
|
|
|
}
|
|
|
|
// no matter the exact protection mode, it's some kind of dummy key
|
|
|
|
return SecretKeyType.GNU_DUMMY;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
|
|
|
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build("".toCharArray());
|
|
|
|
// If this doesn't throw
|
|
|
|
mSecretKey.extractPrivateKey(keyDecryptor);
|
|
|
|
// It means the passphrase is empty
|
|
|
|
return SecretKeyType.PASSPHRASE_EMPTY;
|
|
|
|
} catch (PGPException e) {
|
|
|
|
// Otherwise, it's just a regular ol' passphrase
|
|
|
|
return SecretKeyType.PASSPHRASE;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-07-21 09:10:29 -04:00
|
|
|
/**
|
|
|
|
* Returns true on right passphrase
|
|
|
|
*/
|
2014-05-03 21:38:58 -04:00
|
|
|
public boolean unlock(String passphrase) throws PgpGeneralException {
|
2014-07-21 09:10:29 -04:00
|
|
|
// handle keys on OpenPGP cards like they were unlocked
|
2014-10-02 06:33:56 -04:00
|
|
|
if (mSecretKey.getS2K() != null
|
|
|
|
&& mSecretKey.getS2K().getType() == S2K.GNU_DUMMY_S2K
|
2014-07-21 09:10:29 -04:00
|
|
|
&& mSecretKey.getS2K().getProtectionMode() == S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD) {
|
|
|
|
mPrivateKeyState = PRIVATE_KEY_STATE_DIVERT_TO_CARD;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// try to extract keys using the passphrase
|
2014-05-03 09:55:10 -04:00
|
|
|
try {
|
|
|
|
PBESecretKeyDecryptor keyDecryptor = new JcePBESecretKeyDecryptorBuilder().setProvider(
|
|
|
|
Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(passphrase.toCharArray());
|
2014-05-03 13:04:23 -04:00
|
|
|
mPrivateKey = mSecretKey.extractPrivateKey(keyDecryptor);
|
2014-07-21 09:10:29 -04:00
|
|
|
mPrivateKeyState = PRIVATE_KEY_STATE_UNLOCKED;
|
2014-05-03 09:55:10 -04:00
|
|
|
} catch (PGPException e) {
|
2014-05-03 21:38:58 -04:00
|
|
|
return false;
|
2014-05-03 09:55:10 -04:00
|
|
|
}
|
2014-08-14 08:50:13 -04:00
|
|
|
if (mPrivateKey == null) {
|
2014-05-03 21:38:58 -04:00
|
|
|
throw new PgpGeneralException("error extracting key");
|
2014-05-03 09:55:10 -04:00
|
|
|
}
|
2014-05-03 21:38:58 -04:00
|
|
|
return true;
|
2014-05-03 09:55:10 -04:00
|
|
|
}
|
|
|
|
|
2014-08-14 08:50:13 -04:00
|
|
|
/**
|
|
|
|
* Returns a list of all supported hash algorithms. This list is currently hardcoded to return
|
|
|
|
* a limited set of algorithms supported by Yubikeys.
|
|
|
|
*
|
|
|
|
* @return
|
|
|
|
*/
|
2014-07-21 09:10:29 -04:00
|
|
|
public LinkedList<Integer> getSupportedHashAlgorithms() {
|
|
|
|
LinkedList<Integer> supported = new LinkedList<Integer>();
|
|
|
|
|
|
|
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
|
2014-08-31 16:12:52 -04:00
|
|
|
// No support for MD5
|
2014-08-14 08:50:13 -04:00
|
|
|
supported.add(HashAlgorithmTags.RIPEMD160);
|
|
|
|
supported.add(HashAlgorithmTags.SHA1);
|
|
|
|
supported.add(HashAlgorithmTags.SHA224);
|
2014-07-21 09:10:29 -04:00
|
|
|
supported.add(HashAlgorithmTags.SHA256);
|
2014-08-14 08:50:13 -04:00
|
|
|
supported.add(HashAlgorithmTags.SHA384);
|
|
|
|
supported.add(HashAlgorithmTags.SHA512); // preferred is latest
|
2014-07-21 09:10:29 -04:00
|
|
|
} else {
|
|
|
|
supported.add(HashAlgorithmTags.MD5);
|
|
|
|
supported.add(HashAlgorithmTags.RIPEMD160);
|
|
|
|
supported.add(HashAlgorithmTags.SHA1);
|
|
|
|
supported.add(HashAlgorithmTags.SHA224);
|
|
|
|
supported.add(HashAlgorithmTags.SHA256);
|
|
|
|
supported.add(HashAlgorithmTags.SHA384);
|
|
|
|
supported.add(HashAlgorithmTags.SHA512); // preferred is latest
|
|
|
|
}
|
|
|
|
|
|
|
|
return supported;
|
|
|
|
}
|
|
|
|
|
2014-09-07 18:01:29 -04:00
|
|
|
private PGPContentSignerBuilder getContentSignerBuilder(int hashAlgo, byte[] nfcSignedHash,
|
|
|
|
Date nfcCreationTimestamp) {
|
2014-07-21 09:10:29 -04:00
|
|
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
|
|
|
|
// use synchronous "NFC based" SignerBuilder
|
2014-09-07 18:01:29 -04:00
|
|
|
return new NfcSyncPGPContentSignerBuilder(
|
2014-07-21 09:10:29 -04:00
|
|
|
mSecretKey.getPublicKey().getAlgorithm(), hashAlgo,
|
2014-07-22 12:09:12 -04:00
|
|
|
mSecretKey.getKeyID(), nfcSignedHash, nfcCreationTimestamp)
|
2014-07-21 09:10:29 -04:00
|
|
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
|
|
|
} else {
|
|
|
|
// content signer based on signing key algorithm and chosen hash algorithm
|
2014-09-07 18:01:29 -04:00
|
|
|
return new JcaPGPContentSignerBuilder(
|
2014-07-21 09:10:29 -04:00
|
|
|
mSecretKey.getPublicKey().getAlgorithm(), hashAlgo)
|
|
|
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME);
|
|
|
|
}
|
2014-09-07 18:01:29 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
public PGPSignatureGenerator getSignatureGenerator(int hashAlgo, boolean cleartext,
|
|
|
|
byte[] nfcSignedHash, Date nfcCreationTimestamp)
|
|
|
|
throws PgpGeneralException {
|
|
|
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
|
|
|
throw new PrivateKeyNotUnlockedException();
|
|
|
|
}
|
2014-09-23 19:37:28 -04:00
|
|
|
if (nfcSignedHash != null && nfcCreationTimestamp == null) {
|
|
|
|
throw new PgpGeneralException("Got nfc hash without timestamp!!");
|
|
|
|
}
|
|
|
|
|
|
|
|
// We explicitly create a signature creation timestamp in this place.
|
|
|
|
// That way, we can inject an artificial one from outside, ie the one
|
|
|
|
// used in previous runs of this function.
|
|
|
|
if (nfcCreationTimestamp == null) {
|
|
|
|
// to sign using nfc PgpSignEncrypt is executed two times.
|
|
|
|
// the first time it stops to return the PendingIntent for nfc connection and signing the hash
|
|
|
|
// the second time the signed hash is used.
|
|
|
|
// to get the same hash we cache the timestamp for the second round!
|
|
|
|
nfcCreationTimestamp = new Date();
|
|
|
|
}
|
2014-09-07 18:01:29 -04:00
|
|
|
|
|
|
|
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(hashAlgo,
|
|
|
|
nfcSignedHash, nfcCreationTimestamp);
|
2014-05-03 09:55:10 -04:00
|
|
|
|
|
|
|
int signatureType;
|
|
|
|
if (cleartext) {
|
|
|
|
// for sign-only ascii text
|
|
|
|
signatureType = PGPSignature.CANONICAL_TEXT_DOCUMENT;
|
|
|
|
} else {
|
|
|
|
signatureType = PGPSignature.BINARY_DOCUMENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
|
|
|
signatureGenerator.init(signatureType, mPrivateKey);
|
|
|
|
|
|
|
|
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
2014-07-15 18:22:45 -04:00
|
|
|
spGen.setSignerUserID(false, mRing.getPrimaryUserIdWithFallback());
|
2014-09-23 19:37:28 -04:00
|
|
|
spGen.setSignatureCreationTime(false, nfcCreationTimestamp);
|
2014-05-03 09:55:10 -04:00
|
|
|
signatureGenerator.setHashedSubpackets(spGen.generate());
|
|
|
|
return signatureGenerator;
|
2014-10-08 19:37:44 -04:00
|
|
|
} catch (PgpKeyNotFoundException e) {
|
2014-10-08 18:59:45 -04:00
|
|
|
// TODO: simply throw PGPException!
|
2014-10-08 18:58:07 -04:00
|
|
|
throw new PgpGeneralException("Error initializing signature!", e);
|
2014-10-08 19:37:44 -04:00
|
|
|
} catch (PGPException e) {
|
|
|
|
throw new PgpGeneralException("Error initializing signature!", e);
|
2014-05-03 09:55:10 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-08 08:04:46 -04:00
|
|
|
public PublicKeyDataDecryptorFactory getDecryptorFactory(byte[] nfcDecryptedSessionKey) {
|
2014-09-07 18:01:29 -04:00
|
|
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
2014-05-03 09:55:10 -04:00
|
|
|
throw new PrivateKeyNotUnlockedException();
|
|
|
|
}
|
2014-09-07 18:01:29 -04:00
|
|
|
|
|
|
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_DIVERT_TO_CARD) {
|
2014-09-08 08:04:46 -04:00
|
|
|
return new NfcSyncPublicKeyDataDecryptorFactoryBuilder()
|
|
|
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(nfcDecryptedSessionKey);
|
2014-09-07 18:01:29 -04:00
|
|
|
} else {
|
|
|
|
return new JcePublicKeyDataDecryptorFactoryBuilder()
|
|
|
|
.setProvider(Constants.BOUNCY_CASTLE_PROVIDER_NAME).build(mPrivateKey);
|
|
|
|
}
|
2014-05-03 09:55:10 -04:00
|
|
|
}
|
|
|
|
|
2014-05-03 12:32:20 -04:00
|
|
|
/**
|
|
|
|
* Certify the given pubkeyid with the given masterkeyid.
|
|
|
|
*
|
2014-08-14 08:50:13 -04:00
|
|
|
* @param publicKeyRing Keyring to add certification to.
|
2014-10-04 08:11:51 -04:00
|
|
|
* @param userIds User IDs to certify, or all if null
|
2014-05-03 12:32:20 -04:00
|
|
|
* @return A keyring with added certifications
|
|
|
|
*/
|
2014-09-07 18:01:29 -04:00
|
|
|
public UncachedKeyRing certifyUserIds(CanonicalizedPublicKeyRing publicKeyRing, List<String> userIds,
|
2014-10-12 13:22:34 -04:00
|
|
|
byte[] nfcSignedHash, Date nfcCreationTimestamp) {
|
2014-09-07 18:01:29 -04:00
|
|
|
if (mPrivateKeyState == PRIVATE_KEY_STATE_LOCKED) {
|
2014-05-03 12:32:20 -04:00
|
|
|
throw new PrivateKeyNotUnlockedException();
|
|
|
|
}
|
|
|
|
|
|
|
|
// create a signatureGenerator from the supplied masterKeyId and passphrase
|
|
|
|
PGPSignatureGenerator signatureGenerator;
|
|
|
|
{
|
|
|
|
// TODO: SHA256 fixed?
|
2014-09-07 18:01:29 -04:00
|
|
|
PGPContentSignerBuilder contentSignerBuilder = getContentSignerBuilder(PGPUtil.SHA256,
|
|
|
|
nfcSignedHash, nfcCreationTimestamp);
|
2014-05-03 12:32:20 -04:00
|
|
|
|
|
|
|
signatureGenerator = new PGPSignatureGenerator(contentSignerBuilder);
|
2014-10-12 13:22:34 -04:00
|
|
|
try {
|
|
|
|
signatureGenerator.init(PGPSignature.DEFAULT_CERTIFICATION, mPrivateKey);
|
|
|
|
} catch (PGPException e) {
|
|
|
|
Log.e(Constants.TAG, "signing error", e);
|
|
|
|
return null;
|
|
|
|
}
|
2014-05-03 12:32:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
{ // supply signatureGenerator with a SubpacketVector
|
|
|
|
PGPSignatureSubpacketGenerator spGen = new PGPSignatureSubpacketGenerator();
|
2014-09-07 18:01:29 -04:00
|
|
|
if (nfcCreationTimestamp != null) {
|
|
|
|
spGen.setSignatureCreationTime(false, nfcCreationTimestamp);
|
|
|
|
Log.d(Constants.TAG, "For NFC: set sig creation time to " + nfcCreationTimestamp);
|
|
|
|
}
|
2014-05-03 12:32:20 -04:00
|
|
|
PGPSignatureSubpacketVector packetVector = spGen.generate();
|
|
|
|
signatureGenerator.setHashedSubpackets(packetVector);
|
|
|
|
}
|
|
|
|
|
|
|
|
// get the master subkey (which we certify for)
|
2014-07-15 13:47:40 -04:00
|
|
|
PGPPublicKey publicKey = publicKeyRing.getPublicKey().getPublicKey();
|
2014-05-03 12:32:20 -04:00
|
|
|
|
|
|
|
// fetch public key ring, add the certification and return it
|
2014-10-04 08:11:51 -04:00
|
|
|
Iterable<String> it = userIds != null ? userIds
|
|
|
|
: new IterableIterator<String>(publicKey.getUserIDs());
|
2014-10-12 13:22:34 -04:00
|
|
|
try {
|
|
|
|
for (String userId : it) {
|
|
|
|
PGPSignature sig = signatureGenerator.generateCertification(userId, publicKey);
|
|
|
|
publicKey = PGPPublicKey.addCertification(publicKey, userId, sig);
|
|
|
|
}
|
|
|
|
} catch (PGPException e) {
|
|
|
|
Log.e(Constants.TAG, "signing error", e);
|
|
|
|
return null;
|
2014-05-03 12:32:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
PGPPublicKeyRing ring = PGPPublicKeyRing.insertPublicKey(publicKeyRing.getRing(), publicKey);
|
|
|
|
|
|
|
|
return new UncachedKeyRing(ring);
|
|
|
|
}
|
|
|
|
|
2014-05-03 09:55:10 -04:00
|
|
|
static class PrivateKeyNotUnlockedException extends RuntimeException {
|
|
|
|
// this exception is a programming error which happens when an operation which requires
|
|
|
|
// the private key is called without a previous call to unlock()
|
|
|
|
}
|
|
|
|
|
2014-05-04 10:56:44 -04:00
|
|
|
public UncachedSecretKey getUncached() {
|
|
|
|
return new UncachedSecretKey(mSecretKey);
|
|
|
|
}
|
|
|
|
|
2014-09-22 19:32:36 -04:00
|
|
|
// HACK, for TESTING ONLY!!
|
|
|
|
PGPPrivateKey getPrivateKey () {
|
|
|
|
return mPrivateKey;
|
2014-07-17 14:29:07 -04:00
|
|
|
}
|
|
|
|
|
2014-05-03 09:55:10 -04:00
|
|
|
}
|