mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-27 06:42:05 +01:00
Work on KeyRingInfo
This commit is contained in:
parent
6cb9091b2a
commit
909f0e7be3
4 changed files with 224 additions and 72 deletions
|
@ -15,10 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package org.pgpainless.encryption_signing;
|
package org.pgpainless.encryption_signing;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
@ -32,15 +34,46 @@ import org.pgpainless.key.SubkeyIdentifier;
|
||||||
import org.pgpainless.key.info.KeyRingInfo;
|
import org.pgpainless.key.info.KeyRingInfo;
|
||||||
import org.pgpainless.util.Passphrase;
|
import org.pgpainless.util.Passphrase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Options for the encryption process.
|
||||||
|
* This class can be used to set encryption parameters, like encryption keys and passphrases, algorithms etc.
|
||||||
|
*
|
||||||
|
* A typical use might look like follows:
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* EncryptionOptions opt = new EncryptionOptions();
|
||||||
|
* opt.addRecipient(aliceKey, "Alice <alice@wonderland.lit>");
|
||||||
|
* opt.addPassphrase(Passphrase.fromPassword("AdditionalDecryptionPassphrase123"));
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* To use a custom symmetric encryption algorithm, use {@link #overrideEncryptionAlgorithm(SymmetricKeyAlgorithm)}.
|
||||||
|
* This will cause PGPainless to use the provided algorithm for message encryption, instead of negotiating an algorithm
|
||||||
|
* by inspecting the provided recipient keys.
|
||||||
|
*
|
||||||
|
* By default, PGPainless will only encrypt to a single encryption capable subkey per recipient key.
|
||||||
|
* This behavior can be changed, eg. by calling
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* opt.addRecipient(aliceKey, EncryptionOptions.encryptToAllCapableSubkeys());
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* when adding the recipient key.
|
||||||
|
*/
|
||||||
public class EncryptionOptions {
|
public class EncryptionOptions {
|
||||||
|
|
||||||
private final EncryptionStream.Purpose purpose;
|
private final EncryptionStream.Purpose purpose;
|
||||||
private final Set<PGPKeyEncryptionMethodGenerator> encryptionMethods = new LinkedHashSet<>();
|
private final Set<PGPKeyEncryptionMethodGenerator> encryptionMethods = new LinkedHashSet<>();
|
||||||
private final Set<SubkeyIdentifier> encryptionKeys = new LinkedHashSet<>();
|
private final Set<SubkeyIdentifier> encryptionKeys = new LinkedHashSet<>();
|
||||||
private final Map<SubkeyIdentifier, KeyRingInfo> keyRingInfo = new HashMap<>();
|
private final Map<SubkeyIdentifier, KeyRingInfo> keyRingInfo = new HashMap<>();
|
||||||
|
private final EncryptionKeySelector encryptionKeySelector = encryptToFirstSubkey();
|
||||||
|
|
||||||
private SymmetricKeyAlgorithm encryptionAlgorithmOverride = null;
|
private SymmetricKeyAlgorithm encryptionAlgorithmOverride = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt to keys both carrying the key flag {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_COMMS}
|
||||||
|
* or {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_STORAGE}.
|
||||||
|
*/
|
||||||
public EncryptionOptions() {
|
public EncryptionOptions() {
|
||||||
this(EncryptionStream.Purpose.STORAGE_AND_COMMUNICATIONS);
|
this(EncryptionStream.Purpose.STORAGE_AND_COMMUNICATIONS);
|
||||||
}
|
}
|
||||||
|
@ -49,10 +82,22 @@ public class EncryptionOptions {
|
||||||
this.purpose = purpose;
|
this.purpose = purpose;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory method to create an {@link EncryptionOptions} object which will encrypt for keys
|
||||||
|
* which carry the flag {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_COMMS}.
|
||||||
|
*
|
||||||
|
* @return encryption options
|
||||||
|
*/
|
||||||
public static EncryptionOptions encryptCommunications() {
|
public static EncryptionOptions encryptCommunications() {
|
||||||
return new EncryptionOptions(EncryptionStream.Purpose.COMMUNICATIONS);
|
return new EncryptionOptions(EncryptionStream.Purpose.COMMUNICATIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factory method to create an {@link EncryptionOptions} object which will encrypt for keys
|
||||||
|
* which carry the flag {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_STORAGE}.
|
||||||
|
*
|
||||||
|
* @return encryption options
|
||||||
|
*/
|
||||||
public static EncryptionOptions encryptDataAtRest() {
|
public static EncryptionOptions encryptDataAtRest() {
|
||||||
return new EncryptionOptions(EncryptionStream.Purpose.STORAGE);
|
return new EncryptionOptions(EncryptionStream.Purpose.STORAGE);
|
||||||
}
|
}
|
||||||
|
@ -64,30 +109,65 @@ public class EncryptionOptions {
|
||||||
* @param key key ring
|
* @param key key ring
|
||||||
* @param userId user id
|
* @param userId user id
|
||||||
*/
|
*/
|
||||||
public void addRecipient(PGPPublicKeyRing key, String userId) {
|
public EncryptionOptions addRecipient(PGPPublicKeyRing key, String userId) {
|
||||||
|
return addRecipient(key, userId, encryptionKeySelector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a recipient by providing a key and recipient user-id, as well as a strategy for selecting one or multiple
|
||||||
|
* encryption capable subkeys from the key.
|
||||||
|
*
|
||||||
|
* @param key key
|
||||||
|
* @param userId user-id
|
||||||
|
* @param encryptionKeySelectionStrategy strategy to select one or more encryption subkeys to encrypt to
|
||||||
|
*/
|
||||||
|
public EncryptionOptions addRecipient(PGPPublicKeyRing key, String userId, EncryptionKeySelector encryptionKeySelectionStrategy) {
|
||||||
KeyRingInfo info = new KeyRingInfo(key, new Date());
|
KeyRingInfo info = new KeyRingInfo(key, new Date());
|
||||||
|
|
||||||
PGPPublicKey encryptionSubkey = info.getEncryptionSubkey(userId, purpose);
|
List<PGPPublicKey> encryptionSubkeys = encryptionKeySelectionStrategy
|
||||||
if (encryptionSubkey == null) {
|
.selectEncryptionSubkeys(info.getEncryptionSubkeys(userId, purpose));
|
||||||
throw new AssertionError("Key has no encryption subkey.");
|
if (encryptionSubkeys.isEmpty()) {
|
||||||
|
throw new AssertionError("Key has no suitable encryption subkeys.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (PGPPublicKey encryptionSubkey : encryptionSubkeys) {
|
||||||
addRecipientKey(key, encryptionSubkey);
|
addRecipientKey(key, encryptionSubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a recipient by providing a key.
|
* Add a recipient by providing a key.
|
||||||
*
|
*
|
||||||
* @param key key ring
|
* @param key key ring
|
||||||
*/
|
*/
|
||||||
public void addRecipient(PGPPublicKeyRing key) {
|
public EncryptionOptions addRecipient(PGPPublicKeyRing key) {
|
||||||
KeyRingInfo info = new KeyRingInfo(key, new Date());
|
return addRecipient(key, encryptionKeySelector);
|
||||||
PGPPublicKey encryptionSubkey = info.getEncryptionSubkey(purpose);
|
|
||||||
if (encryptionSubkey == null) {
|
|
||||||
throw new IllegalArgumentException("Key has no encryption subkey.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a recipient by providing a key and an encryption key selection strategy.
|
||||||
|
*
|
||||||
|
* @param key key ring
|
||||||
|
* @param encryptionKeySelectionStrategy strategy used to select one or multiple encryption subkeys.
|
||||||
|
*/
|
||||||
|
public EncryptionOptions addRecipient(PGPPublicKeyRing key, EncryptionKeySelector encryptionKeySelectionStrategy) {
|
||||||
|
KeyRingInfo info = new KeyRingInfo(key, new Date());
|
||||||
|
|
||||||
|
List<PGPPublicKey> encryptionSubkeys = encryptionKeySelectionStrategy
|
||||||
|
.selectEncryptionSubkeys(info.getEncryptionSubkeys(purpose));
|
||||||
|
if (encryptionSubkeys.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Key has no suitable encryption subkeys.");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (PGPPublicKey encryptionSubkey : encryptionSubkeys) {
|
||||||
addRecipientKey(key, encryptionSubkey);
|
addRecipientKey(key, encryptionSubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private void addRecipientKey(PGPPublicKeyRing keyRing, PGPPublicKey key) {
|
private void addRecipientKey(PGPPublicKeyRing keyRing, PGPPublicKey key) {
|
||||||
encryptionKeys.add(new SubkeyIdentifier(keyRing, key.getKeyID()));
|
encryptionKeys.add(new SubkeyIdentifier(keyRing, key.getKeyID()));
|
||||||
PGPKeyEncryptionMethodGenerator encryptionMethod = ImplementationFactory
|
PGPKeyEncryptionMethodGenerator encryptionMethod = ImplementationFactory
|
||||||
|
@ -100,13 +180,13 @@ public class EncryptionOptions {
|
||||||
*
|
*
|
||||||
* @param passphrase passphrase
|
* @param passphrase passphrase
|
||||||
*/
|
*/
|
||||||
public void addPassphrase(Passphrase passphrase) {
|
public EncryptionOptions addPassphrase(Passphrase passphrase) {
|
||||||
if (passphrase.isEmpty()) {
|
if (passphrase.isEmpty()) {
|
||||||
throw new IllegalArgumentException("Passphrase must not be empty.");
|
throw new IllegalArgumentException("Passphrase must not be empty.");
|
||||||
}
|
}
|
||||||
PBEKeyEncryptionMethodGenerator encryptionMethod = ImplementationFactory
|
PBEKeyEncryptionMethodGenerator encryptionMethod = ImplementationFactory
|
||||||
.getInstance().getPBEKeyEncryptionMethodGenerator(passphrase);
|
.getInstance().getPBEKeyEncryptionMethodGenerator(passphrase);
|
||||||
addEncryptionMethod(encryptionMethod);
|
return addEncryptionMethod(encryptionMethod);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -119,8 +199,9 @@ public class EncryptionOptions {
|
||||||
*
|
*
|
||||||
* @param encryptionMethod encryption method
|
* @param encryptionMethod encryption method
|
||||||
*/
|
*/
|
||||||
public void addEncryptionMethod(PGPKeyEncryptionMethodGenerator encryptionMethod) {
|
public EncryptionOptions addEncryptionMethod(PGPKeyEncryptionMethodGenerator encryptionMethod) {
|
||||||
encryptionMethods.add(encryptionMethod);
|
encryptionMethods.add(encryptionMethod);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<PGPKeyEncryptionMethodGenerator> getEncryptionMethods() {
|
public Set<PGPKeyEncryptionMethodGenerator> getEncryptionMethods() {
|
||||||
|
@ -141,4 +222,38 @@ public class EncryptionOptions {
|
||||||
}
|
}
|
||||||
this.encryptionAlgorithmOverride = encryptionAlgorithm;
|
this.encryptionAlgorithmOverride = encryptionAlgorithm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface EncryptionKeySelector {
|
||||||
|
List<PGPPublicKey> selectEncryptionSubkeys(List<PGPPublicKey> encryptionCapableKeys);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only encrypt to the first valid encryption capable subkey we stumble upon.
|
||||||
|
*
|
||||||
|
* @return encryption key selector
|
||||||
|
*/
|
||||||
|
public static EncryptionKeySelector encryptToFirstSubkey() {
|
||||||
|
return new EncryptionKeySelector() {
|
||||||
|
@Override
|
||||||
|
public List<PGPPublicKey> selectEncryptionSubkeys(List<PGPPublicKey> encryptionCapableKeys) {
|
||||||
|
return encryptionCapableKeys.isEmpty() ? Collections.emptyList() : Collections.singletonList(encryptionCapableKeys.get(0));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt to any valid, encryption capable subkey on the key ring.
|
||||||
|
*
|
||||||
|
* @return encryption key selector
|
||||||
|
*/
|
||||||
|
public static EncryptionKeySelector encryptToAllCapableSubkeys() {
|
||||||
|
return new EncryptionKeySelector() {
|
||||||
|
@Override
|
||||||
|
public List<PGPPublicKey> selectEncryptionSubkeys(List<PGPPublicKey> encryptionCapableKeys) {
|
||||||
|
return encryptionCapableKeys;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Create encryptToBestSubkey() method
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,19 +82,22 @@ public final class SigningOptions {
|
||||||
KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date());
|
KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date());
|
||||||
if (userId != null) {
|
if (userId != null) {
|
||||||
if (!keyRingInfo.isUserIdValid(userId)) {
|
if (!keyRingInfo.isUserIdValid(userId)) {
|
||||||
throw new KeyValidationException(userId, keyRingInfo.getCurrentUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId));
|
throw new KeyValidationException(userId, keyRingInfo.getLatestUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPPublicKey signingPubKey = keyRingInfo.getSigningSubkey();
|
List<PGPPublicKey> signingPubKeys = keyRingInfo.getSigningSubkeys();
|
||||||
if (signingPubKey == null) {
|
if (signingPubKeys.isEmpty()) {
|
||||||
throw new AssertionError("Key has no valid signing key.");
|
throw new AssertionError("Key has no valid signing key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (PGPPublicKey signingPubKey : signingPubKeys) {
|
||||||
PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID());
|
PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID());
|
||||||
PGPPrivateKey signingSubkey = signingSecKey.extractPrivateKey(secretKeyDecryptor.getDecryptor(signingPubKey.getKeyID()));
|
PGPPrivateKey signingSubkey = signingSecKey.extractPrivateKey(secretKeyDecryptor.getDecryptor(signingPubKey.getKeyID()));
|
||||||
List<HashAlgorithm> hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(userId, signingPubKey.getKeyID());
|
List<HashAlgorithm> hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(userId, signingPubKey.getKeyID());
|
||||||
addSigningMethod(secretKey, signingSubkey, hashAlgorithms.get(0), signatureType, false);
|
addSigningMethod(secretKey, signingSubkey, hashAlgorithms.get(0), signatureType, false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void addDetachedSignature(SecretKeyRingProtector secretKeyDecryptor,
|
public void addDetachedSignature(SecretKeyRingProtector secretKeyDecryptor,
|
||||||
PGPSecretKeyRing secretKey,
|
PGPSecretKeyRing secretKey,
|
||||||
|
@ -111,19 +114,22 @@ public final class SigningOptions {
|
||||||
KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date());
|
KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date());
|
||||||
if (userId != null) {
|
if (userId != null) {
|
||||||
if (!keyRingInfo.isUserIdValid(userId)) {
|
if (!keyRingInfo.isUserIdValid(userId)) {
|
||||||
throw new KeyValidationException(userId, keyRingInfo.getCurrentUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId));
|
throw new KeyValidationException(userId, keyRingInfo.getLatestUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPPublicKey signingPubKey = keyRingInfo.getSigningSubkey();
|
List<PGPPublicKey> signingPubKeys = keyRingInfo.getSigningSubkeys();
|
||||||
if (signingPubKey == null) {
|
if (signingPubKeys.isEmpty()) {
|
||||||
throw new AssertionError("Key has no valid signing key.");
|
throw new AssertionError("Key has no valid signing key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (PGPPublicKey signingPubKey : signingPubKeys) {
|
||||||
PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID());
|
PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID());
|
||||||
PGPPrivateKey signingSubkey = signingSecKey.extractPrivateKey(secretKeyDecryptor.getDecryptor(signingPubKey.getKeyID()));
|
PGPPrivateKey signingSubkey = signingSecKey.extractPrivateKey(secretKeyDecryptor.getDecryptor(signingPubKey.getKeyID()));
|
||||||
List<HashAlgorithm> hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(userId, signingPubKey.getKeyID());
|
List<HashAlgorithm> hashAlgorithms = keyRingInfo.getPreferredHashAlgorithms(userId, signingPubKey.getKeyID());
|
||||||
addSigningMethod(secretKey, signingSubkey, hashAlgorithms.get(0), signatureType, true);
|
addSigningMethod(secretKey, signingSubkey, hashAlgorithms.get(0), signatureType, true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void addSigningMethod(PGPSecretKeyRing secretKey,
|
private void addSigningMethod(PGPSecretKeyRing secretKey,
|
||||||
PGPPrivateKey signingSubkey,
|
PGPPrivateKey signingSubkey,
|
||||||
|
|
|
@ -29,7 +29,6 @@ import java.util.Set;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import org.bouncycastle.bcpg.sig.KeyFlags;
|
|
||||||
import org.bouncycastle.bcpg.sig.PrimaryUserID;
|
import org.bouncycastle.bcpg.sig.PrimaryUserID;
|
||||||
import org.bouncycastle.openpgp.PGPKeyRing;
|
import org.bouncycastle.openpgp.PGPKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
|
@ -116,7 +115,7 @@ public class KeyRingInfo {
|
||||||
* @return public key or null
|
* @return public key or null
|
||||||
*/
|
*/
|
||||||
public PGPPublicKey getPublicKey(long keyId) {
|
public PGPPublicKey getPublicKey(long keyId) {
|
||||||
return keys.getPublicKey(keyId);
|
return getPublicKey(keys, keyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,12 +142,36 @@ public class KeyRingInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (publicKey == getPublicKey()) {
|
if (publicKey == getPublicKey()) {
|
||||||
|
if (signatures.primaryKeyRevocation != null) {
|
||||||
|
if (SignatureUtils.isHardRevocation(signatures.primaryKeyRevocation)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return signatures.primaryKeyRevocation == null;
|
return signatures.primaryKeyRevocation == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPSignature binding = signatures.subkeyBindings.get(keyId);
|
PGPSignature binding = signatures.subkeyBindings.get(keyId);
|
||||||
PGPSignature revocation = signatures.subkeyRevocations.get(keyId);
|
PGPSignature revocation = signatures.subkeyRevocations.get(keyId);
|
||||||
return binding != null && revocation == null;
|
|
||||||
|
// No valid binding
|
||||||
|
if (binding == null || SignatureUtils.isSignatureExpired(binding)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revocation
|
||||||
|
if (revocation != null) {
|
||||||
|
if (SignatureUtils.isHardRevocation(revocation)) {
|
||||||
|
// Subkey is hard revoked
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (!SignatureUtils.isSignatureExpired(revocation) && revocation.getCreationTime().after(binding.getCreationTime())) {
|
||||||
|
// Key is soft-revoked, not yet re-bound
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -322,11 +345,13 @@ public class KeyRingInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current direct-key self signature.
|
* Return the latest direct-key self signature.
|
||||||
*
|
*
|
||||||
* @return
|
* Note: This signature might be expired (check with {@link SignatureUtils#isSignatureExpired(PGPSignature)}).
|
||||||
|
*
|
||||||
|
* @return latest direct key self-signature
|
||||||
*/
|
*/
|
||||||
public PGPSignature getCurrentDirectKeySelfSignature() {
|
public PGPSignature getLatestDirectKeySelfSignature() {
|
||||||
return signatures.primaryKeySelfSignature;
|
return signatures.primaryKeySelfSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -334,7 +359,7 @@ public class KeyRingInfo {
|
||||||
return signatures.primaryKeyRevocation;
|
return signatures.primaryKeyRevocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PGPSignature getCurrentUserIdCertification(String userId) {
|
public PGPSignature getLatestUserIdCertification(String userId) {
|
||||||
return signatures.userIdCertifications.get(userId);
|
return signatures.userIdCertifications.get(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,27 +378,28 @@ public class KeyRingInfo {
|
||||||
public List<KeyFlag> getKeyFlagsOf(long keyId) {
|
public List<KeyFlag> getKeyFlagsOf(long keyId) {
|
||||||
if (getPublicKey().getKeyID() == keyId) {
|
if (getPublicKey().getKeyID() == keyId) {
|
||||||
|
|
||||||
PGPSignature directKeySignature = getCurrentDirectKeySelfSignature();
|
PGPSignature directKeySignature = getLatestDirectKeySelfSignature();
|
||||||
if (directKeySignature != null) {
|
if (directKeySignature != null) {
|
||||||
KeyFlags flags = SignatureSubpacketsUtil.getKeyFlags(directKeySignature);
|
List<KeyFlag> keyFlags = SignatureSubpacketsUtil.parseKeyFlags(directKeySignature);
|
||||||
if (flags != null) {
|
if (keyFlags != null) {
|
||||||
return KeyFlag.fromBitmask(flags.getFlags());
|
return keyFlags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String primaryUserId = getPrimaryUserId();
|
String primaryUserId = getPrimaryUserId();
|
||||||
if (primaryUserId != null) {
|
if (primaryUserId != null) {
|
||||||
KeyFlags flags = SignatureSubpacketsUtil.getKeyFlags(getCurrentUserIdCertification(primaryUserId));
|
PGPSignature userIdSignature = getLatestUserIdCertification(primaryUserId);
|
||||||
if (flags != null) {
|
List<KeyFlag> keyFlags = SignatureSubpacketsUtil.parseKeyFlags(userIdSignature);
|
||||||
return KeyFlag.fromBitmask(flags.getFlags());
|
if (keyFlags != null) {
|
||||||
|
return keyFlags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
PGPSignature bindingSignature = getCurrentSubkeyBindingSignature(keyId);
|
PGPSignature bindingSignature = getCurrentSubkeyBindingSignature(keyId);
|
||||||
if (bindingSignature != null) {
|
if (bindingSignature != null) {
|
||||||
KeyFlags flags = SignatureSubpacketsUtil.getKeyFlags(bindingSignature);
|
List<KeyFlag> keyFlags = SignatureSubpacketsUtil.parseKeyFlags(bindingSignature);
|
||||||
if (flags != null) {
|
if (keyFlags != null) {
|
||||||
return KeyFlag.fromBitmask(flags.getFlags());
|
return keyFlags;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -385,14 +411,14 @@ public class KeyRingInfo {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPSignature userIdCertification = getCurrentUserIdCertification(userId);
|
PGPSignature userIdCertification = getLatestUserIdCertification(userId);
|
||||||
if (userIdCertification == null) {
|
if (userIdCertification == null) {
|
||||||
return Collections.emptyList();
|
throw new AssertionError("While user-id '" + userId + "' was reported as valid, there appears to be no certification for it.");
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyFlags keyFlags = SignatureSubpacketsUtil.getKeyFlags(userIdCertification);
|
List<KeyFlag> keyFlags = SignatureSubpacketsUtil.parseKeyFlags(userIdCertification);
|
||||||
if (keyFlags != null) {
|
if (keyFlags != null) {
|
||||||
return KeyFlag.fromBitmask(keyFlags.getFlags());
|
return keyFlags;
|
||||||
}
|
}
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
@ -428,7 +454,7 @@ public class KeyRingInfo {
|
||||||
|
|
||||||
private PGPSignature getMostRecentSignature() {
|
private PGPSignature getMostRecentSignature() {
|
||||||
Set<PGPSignature> allSignatures = new HashSet<>();
|
Set<PGPSignature> allSignatures = new HashSet<>();
|
||||||
PGPSignature mostRecentSelfSignature = getCurrentDirectKeySelfSignature();
|
PGPSignature mostRecentSelfSignature = getLatestDirectKeySelfSignature();
|
||||||
PGPSignature revocationSelfSignature = getRevocationSelfSignature();
|
PGPSignature revocationSelfSignature = getRevocationSelfSignature();
|
||||||
if (mostRecentSelfSignature != null) allSignatures.add(mostRecentSelfSignature);
|
if (mostRecentSelfSignature != null) allSignatures.add(mostRecentSelfSignature);
|
||||||
if (revocationSelfSignature != null) allSignatures.add(revocationSelfSignature);
|
if (revocationSelfSignature != null) allSignatures.add(revocationSelfSignature);
|
||||||
|
@ -462,12 +488,12 @@ public class KeyRingInfo {
|
||||||
*/
|
*/
|
||||||
public Date getPrimaryKeyExpirationDate() {
|
public Date getPrimaryKeyExpirationDate() {
|
||||||
Date lastExpiration = null;
|
Date lastExpiration = null;
|
||||||
if (getCurrentDirectKeySelfSignature() != null) {
|
if (getLatestDirectKeySelfSignature() != null) {
|
||||||
lastExpiration = SignatureUtils.getKeyExpirationDate(getCreationDate(), getCurrentDirectKeySelfSignature());
|
lastExpiration = SignatureUtils.getKeyExpirationDate(getCreationDate(), getLatestDirectKeySelfSignature());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String userId : getValidUserIds()) {
|
for (String userId : getValidUserIds()) {
|
||||||
PGPSignature signature = getCurrentUserIdCertification(userId);
|
PGPSignature signature = getLatestUserIdCertification(userId);
|
||||||
Date expiration = SignatureUtils.getKeyExpirationDate(getCreationDate(), signature);
|
Date expiration = SignatureUtils.getKeyExpirationDate(getCreationDate(), signature);
|
||||||
if (expiration != null && (lastExpiration == null || expiration.after(lastExpiration))) {
|
if (expiration != null && (lastExpiration == null || expiration.after(lastExpiration))) {
|
||||||
lastExpiration = expiration;
|
lastExpiration = expiration;
|
||||||
|
@ -543,8 +569,9 @@ public class KeyRingInfo {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PGPPublicKey getEncryptionSubkey(EncryptionStream.Purpose purpose) {
|
public List<PGPPublicKey> getEncryptionSubkeys(EncryptionStream.Purpose purpose) {
|
||||||
Iterator<PGPPublicKey> subkeys = keys.getPublicKeys();
|
Iterator<PGPPublicKey> subkeys = keys.getPublicKeys();
|
||||||
|
List<PGPPublicKey> encryptionKeys = new ArrayList<>();
|
||||||
while (subkeys.hasNext()) {
|
while (subkeys.hasNext()) {
|
||||||
PGPPublicKey subKey = subkeys.next();
|
PGPPublicKey subKey = subkeys.next();
|
||||||
|
|
||||||
|
@ -560,36 +587,37 @@ public class KeyRingInfo {
|
||||||
switch (purpose) {
|
switch (purpose) {
|
||||||
case COMMUNICATIONS:
|
case COMMUNICATIONS:
|
||||||
if (keyFlags.contains(KeyFlag.ENCRYPT_COMMS)) {
|
if (keyFlags.contains(KeyFlag.ENCRYPT_COMMS)) {
|
||||||
return subKey;
|
encryptionKeys.add(subKey);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case STORAGE:
|
case STORAGE:
|
||||||
if (keyFlags.contains(KeyFlag.ENCRYPT_STORAGE)) {
|
if (keyFlags.contains(KeyFlag.ENCRYPT_STORAGE)) {
|
||||||
return subKey;
|
encryptionKeys.add(subKey);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case STORAGE_AND_COMMUNICATIONS:
|
case STORAGE_AND_COMMUNICATIONS:
|
||||||
if (keyFlags.contains(KeyFlag.ENCRYPT_COMMS) || keyFlags.contains(KeyFlag.ENCRYPT_STORAGE)) {
|
if (keyFlags.contains(KeyFlag.ENCRYPT_COMMS) || keyFlags.contains(KeyFlag.ENCRYPT_STORAGE)) {
|
||||||
return subKey;
|
encryptionKeys.add(subKey);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return encryptionKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PGPPublicKey getEncryptionSubkey(String userId, EncryptionStream.Purpose purpose) {
|
public List<PGPPublicKey> getEncryptionSubkeys(String userId, EncryptionStream.Purpose purpose) {
|
||||||
if (userId != null) {
|
if (userId != null) {
|
||||||
if (!isUserIdValid(userId)) {
|
if (!isUserIdValid(userId)) {
|
||||||
throw new KeyValidationException(userId, getCurrentUserIdCertification(userId), getUserIdRevocation(userId));
|
throw new KeyValidationException(userId, getLatestUserIdCertification(userId), getUserIdRevocation(userId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return getEncryptionSubkey(purpose);
|
return getEncryptionSubkeys(purpose);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PGPPublicKey getSigningSubkey() {
|
public List<PGPPublicKey> getSigningSubkeys() {
|
||||||
Iterator<PGPPublicKey> subkeys = keys.getPublicKeys();
|
Iterator<PGPPublicKey> subkeys = keys.getPublicKeys();
|
||||||
|
List<PGPPublicKey> signingKeys = new ArrayList<>();
|
||||||
while (subkeys.hasNext()) {
|
while (subkeys.hasNext()) {
|
||||||
PGPPublicKey subKey = subkeys.next();
|
PGPPublicKey subKey = subkeys.next();
|
||||||
|
|
||||||
|
@ -599,16 +627,16 @@ public class KeyRingInfo {
|
||||||
|
|
||||||
List<KeyFlag> keyFlags = getKeyFlagsOf(subKey.getKeyID());
|
List<KeyFlag> keyFlags = getKeyFlagsOf(subKey.getKeyID());
|
||||||
if (keyFlags.contains(KeyFlag.SIGN_DATA)) {
|
if (keyFlags.contains(KeyFlag.SIGN_DATA)) {
|
||||||
return subKey;
|
signingKeys.add(subKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return signingKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<HashAlgorithm> getPreferredHashAlgorithms(String userId, long keyID) {
|
public List<HashAlgorithm> getPreferredHashAlgorithms(String userId, long keyID) {
|
||||||
PGPSignature signature = getCurrentUserIdCertification(userId == null ? getPrimaryUserId() : userId);
|
PGPSignature signature = getLatestUserIdCertification(userId == null ? getPrimaryUserId() : userId);
|
||||||
if (signature == null) {
|
if (signature == null) {
|
||||||
signature = getCurrentDirectKeySelfSignature();
|
signature = getLatestDirectKeySelfSignature();
|
||||||
}
|
}
|
||||||
if (signature == null) {
|
if (signature == null) {
|
||||||
signature = getCurrentSubkeyBindingSignature(keyID);
|
signature = getCurrentSubkeyBindingSignature(keyID);
|
||||||
|
|
|
@ -26,11 +26,12 @@ import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.util.io.Streams;
|
import org.bouncycastle.util.io.Streams;
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
|
import org.pgpainless.encryption_signing.EncryptionOptions;
|
||||||
import org.pgpainless.encryption_signing.EncryptionResult;
|
import org.pgpainless.encryption_signing.EncryptionResult;
|
||||||
import org.pgpainless.encryption_signing.EncryptionStream;
|
import org.pgpainless.encryption_signing.EncryptionStream;
|
||||||
|
import org.pgpainless.encryption_signing.ProducerOptions;
|
||||||
import org.pgpainless.key.WeirdKeys;
|
import org.pgpainless.key.WeirdKeys;
|
||||||
import org.pgpainless.key.util.KeyRingUtils;
|
import org.pgpainless.key.util.KeyRingUtils;
|
||||||
|
|
||||||
|
@ -46,23 +47,25 @@ public class TestTwoSubkeysEncryption {
|
||||||
* {@link WeirdKeys#TWO_CRYPT_SUBKEYS} is a key that has two subkeys which both carry the key flags
|
* {@link WeirdKeys#TWO_CRYPT_SUBKEYS} is a key that has two subkeys which both carry the key flags
|
||||||
* {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_COMMS} and {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_STORAGE}.
|
* {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_COMMS} and {@link org.pgpainless.algorithm.KeyFlag#ENCRYPT_STORAGE}.
|
||||||
*
|
*
|
||||||
* This test makes sure that both subkeys are used for encryption.
|
* This test verifies that {@link EncryptionOptions#addRecipient(PGPPublicKeyRing, EncryptionOptions.EncryptionKeySelector)}
|
||||||
|
* works properly, if {@link EncryptionOptions#encryptToAllCapableSubkeys()} is provided as argument.
|
||||||
*
|
*
|
||||||
* @throws IOException not expected
|
* @throws IOException not expected
|
||||||
* @throws PGPException not expected
|
* @throws PGPException not expected
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
@Disabled("We may not want to encrypt to all enc capable subkeys.")
|
|
||||||
public void testEncryptsToBothSubkeys() throws IOException, PGPException {
|
public void testEncryptsToBothSubkeys() throws IOException, PGPException {
|
||||||
PGPSecretKeyRing twoSuitableSubkeysKeyRing = WeirdKeys.getTwoCryptSubkeysKey();
|
PGPSecretKeyRing twoSuitableSubkeysKeyRing = WeirdKeys.getTwoCryptSubkeysKey();
|
||||||
PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(twoSuitableSubkeysKeyRing);
|
PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(twoSuitableSubkeysKeyRing);
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign(EncryptionStream.Purpose.STORAGE)
|
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign(EncryptionStream.Purpose.STORAGE)
|
||||||
.onOutputStream(out)
|
.onOutputStream(out)
|
||||||
.toRecipient(publicKeys)
|
.withOptions(
|
||||||
.and()
|
ProducerOptions.encrypt(new EncryptionOptions(EncryptionStream.Purpose.STORAGE_AND_COMMUNICATIONS)
|
||||||
.doNotSign()
|
.addRecipient(publicKeys, EncryptionOptions.encryptToAllCapableSubkeys())
|
||||||
.noArmor();
|
)
|
||||||
|
.setAsciiArmor(false)
|
||||||
|
);
|
||||||
|
|
||||||
Streams.pipeAll(getPlainIn(), encryptionStream);
|
Streams.pipeAll(getPlainIn(), encryptionStream);
|
||||||
encryptionStream.close();
|
encryptionStream.close();
|
||||||
|
|
Loading…
Reference in a new issue