mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-01-09 19:57:57 +01:00
Work on signaturePicker
This commit is contained in:
parent
a30767eb91
commit
6cb9091b2a
9 changed files with 614 additions and 337 deletions
|
@ -16,17 +16,132 @@
|
||||||
package org.pgpainless.key;
|
package org.pgpainless.key;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.bouncycastle.bcpg.UserAttributePacket;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.pgpainless.algorithm.KeyFlag;
|
import org.pgpainless.algorithm.KeyFlag;
|
||||||
|
import org.pgpainless.exception.SignatureValidationException;
|
||||||
|
import org.pgpainless.signature.SignatureUtils;
|
||||||
|
import org.pgpainless.util.NonEmptyList;
|
||||||
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
|
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
|
||||||
|
|
||||||
public interface EvaluatedKeyRing {
|
public interface EvaluatedKeyRing {
|
||||||
|
|
||||||
PGPSignature getUserIdCertification(String userId);
|
class EvaluatedSignature {
|
||||||
|
private final PGPSignature signature;
|
||||||
|
private final SignatureValidationException exception;
|
||||||
|
|
||||||
PGPSignature getUserIdRevocation(String userId);
|
PGPSignature getValidSignature() throws SignatureValidationException {
|
||||||
|
if (getException() != null) {
|
||||||
|
throw new SignatureValidationException("Signature is not valid.", getException());
|
||||||
|
}
|
||||||
|
return signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignatureValidationException getException() {
|
||||||
|
return exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EvaluatedSignature(PGPSignature signature, SignatureValidationException exception) {
|
||||||
|
this.signature = signature;
|
||||||
|
this.exception = exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a {@link Map} of user-ids and associated user-id certification signatures.
|
||||||
|
* Each map entry consists of a user-id and a {@link NonEmptyList} of associated certification signatures which
|
||||||
|
* contains the latest non-revoking certification signature as its first element.
|
||||||
|
*
|
||||||
|
* @return map of user-ids and certifications
|
||||||
|
*/
|
||||||
|
Map<String, NonEmptyList<PGPSignature>> getUserIdCertifications();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the latest user-id certification signature associated to the provided user-id.
|
||||||
|
*
|
||||||
|
* @param userId user-id
|
||||||
|
* @return latest user-id certification signature
|
||||||
|
* @throws IllegalArgumentException if the key doesn't have at least one certification signature for the
|
||||||
|
* provided user-id.
|
||||||
|
*/
|
||||||
|
default PGPSignature getUserIdCertification(String userId) {
|
||||||
|
NonEmptyList<PGPSignature> userIdCerts = getUserIdCertifications().get(userId);
|
||||||
|
if (userIdCerts == null) {
|
||||||
|
throw new IllegalArgumentException("No user-id '" + userId + "' found on the key.");
|
||||||
|
}
|
||||||
|
return userIdCerts.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a {@link Map} of user-ids and associated user-id revocation signatures.
|
||||||
|
*
|
||||||
|
* @return map of user-ids and revocations
|
||||||
|
*/
|
||||||
|
Map<String, List<PGPSignature>> getUserIdRevocations();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the latest, hardest revocation signature for the passed in user-id.
|
||||||
|
*
|
||||||
|
* @param userId user-id
|
||||||
|
* @return latest hardest revocation signature
|
||||||
|
* @throws IllegalArgumentException if the key doesn't have at least one certification signature for the given user-id.
|
||||||
|
*/
|
||||||
|
default PGPSignature getUserIdRevocation(String userId) {
|
||||||
|
List<PGPSignature> userIdRevs = getUserIdRevocations().get(userId);
|
||||||
|
if (userIdRevs == null) {
|
||||||
|
throw new IllegalArgumentException("No user-id '" + userId + "' found on the key.");
|
||||||
|
}
|
||||||
|
return userIdRevs.isEmpty() ? null : userIdRevs.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
default boolean isRevoked(String userId) {
|
||||||
|
PGPSignature latestCertification = getUserIdCertification(userId);
|
||||||
|
PGPSignature latestRevocation = getUserIdRevocation(userId);
|
||||||
|
|
||||||
|
if (latestRevocation == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return latestRevocation.getCreationTime().after(latestCertification.getCreationTime())
|
||||||
|
|| SignatureUtils.isHardRevocation(latestRevocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a {@link Map} of {@link UserAttributePacket UserAttributePackets} and associated certification signatures.
|
||||||
|
* Each map entry consists of a {@link UserAttributePacket} and a {@link NonEmptyList} of associated certification
|
||||||
|
* signatures which contains the latest non-revoking certification signtaure as its first element.
|
||||||
|
*
|
||||||
|
* @return map of user-attributes and certifications
|
||||||
|
*/
|
||||||
|
Map<UserAttributePacket, NonEmptyList<PGPSignature>> getUserAttributeCertifications();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the latest certification signature for the provided {@link UserAttributePacket}.
|
||||||
|
*
|
||||||
|
* @param userAttribute user attribute
|
||||||
|
* @return latest certification signature
|
||||||
|
* @throws IllegalArgumentException if the key doesn't carry such user-attribute
|
||||||
|
*/
|
||||||
|
default PGPSignature getUserAttributeCertification(UserAttributePacket userAttribute) {
|
||||||
|
NonEmptyList<PGPSignature> userAttrCerts = getUserAttributeCertifications().get(userAttribute);
|
||||||
|
if (userAttrCerts == null) {
|
||||||
|
throw new IllegalArgumentException("No such user-attribute found on the key.");
|
||||||
|
}
|
||||||
|
return userAttrCerts.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<UserAttributePacket, List<PGPSignature>> getUserAttributeRevocations();
|
||||||
|
|
||||||
|
default PGPSignature getUserAttributeRevocation(UserAttributePacket userAttribute) {
|
||||||
|
List<PGPSignature> userAttrRevs = getUserAttributeRevocations().get(userAttribute);
|
||||||
|
if (userAttrRevs == null) {
|
||||||
|
throw new IllegalArgumentException("No such user-attribute found on the key.");
|
||||||
|
}
|
||||||
|
return userAttrRevs.isEmpty() ? null : userAttrRevs.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
PGPSignature getSubkeyBinding(long subkeyId);
|
PGPSignature getSubkeyBinding(long subkeyId);
|
||||||
|
|
||||||
|
|
|
@ -26,17 +26,13 @@ import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPKeyRing;
|
import org.bouncycastle.openpgp.PGPKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector;
|
import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector;
|
||||||
import org.pgpainless.algorithm.SignatureType;
|
import org.pgpainless.algorithm.SignatureType;
|
||||||
import org.pgpainless.implementation.ImplementationFactory;
|
|
||||||
import org.pgpainless.key.info.KeyRingInfo;
|
|
||||||
import org.pgpainless.key.util.KeyRingUtils;
|
|
||||||
import org.pgpainless.policy.Policy;
|
|
||||||
import org.pgpainless.signature.SelectSignatureFromKey;
|
|
||||||
import org.pgpainless.signature.SignatureCreationDateComparator;
|
|
||||||
import org.pgpainless.exception.SignatureValidationException;
|
import org.pgpainless.exception.SignatureValidationException;
|
||||||
|
import org.pgpainless.implementation.ImplementationFactory;
|
||||||
|
import org.pgpainless.policy.Policy;
|
||||||
|
import org.pgpainless.signature.SignatureCreationDateComparator;
|
||||||
import org.pgpainless.signature.SignatureValidator;
|
import org.pgpainless.signature.SignatureValidator;
|
||||||
import org.pgpainless.util.CollectionUtils;
|
import org.pgpainless.util.CollectionUtils;
|
||||||
|
|
||||||
|
@ -146,115 +142,4 @@ public class KeyRingValidator {
|
||||||
return blank;
|
return blank;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <R extends PGPKeyRing> R getKeyRingAtDate(R keyRing, KeyRingInfo info) {
|
|
||||||
Iterator<PGPPublicKey> iterator = keyRing.getPublicKeys();
|
|
||||||
while (iterator.hasNext()) {
|
|
||||||
PGPPublicKey publicKey = iterator.next();
|
|
||||||
if (publicKey.isMasterKey()) {
|
|
||||||
keyRing = assessPrimaryKeyAtDate(publicKey, keyRing, info);
|
|
||||||
} else {
|
|
||||||
keyRing = assessSubkeyAtDate(publicKey, keyRing, info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return keyRing;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <R extends PGPKeyRing> R assessPrimaryKeyAtDate(PGPPublicKey primaryKey, PGPKeyRing keyRing, KeyRingInfo info) {
|
|
||||||
if (!primaryKey.isMasterKey()) {
|
|
||||||
throw new IllegalArgumentException("Passed in key is not a primary key");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Direct Key Signatures
|
|
||||||
PGPSignature latestSelfSig = info.getCurrentDirectKeySelfSignature();
|
|
||||||
PGPSignature latestSelfRevocation = info.getRevocationSelfSignature();
|
|
||||||
|
|
||||||
|
|
||||||
// User-ID certifications
|
|
||||||
Iterator<String> userIdIterator = primaryKey.getUserIDs();
|
|
||||||
while (userIdIterator.hasNext()) {
|
|
||||||
String userId = userIdIterator.next();
|
|
||||||
boolean isUserIdBound = false;
|
|
||||||
Iterator<PGPSignature> userIdSigIterator = primaryKey.getSignaturesForID(userId);
|
|
||||||
while (userIdSigIterator.hasNext()) {
|
|
||||||
PGPSignature userIdSig = userIdSigIterator.next();
|
|
||||||
if (!SelectSignatureFromKey.isValidSignatureOnUserId(userId, primaryKey)
|
|
||||||
.accept(userIdSig, primaryKey, keyRing)) {
|
|
||||||
primaryKey = PGPPublicKey.removeCertification(primaryKey, userId, userIdSig);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
isUserIdBound = true;
|
|
||||||
}
|
|
||||||
if (!isUserIdBound) {
|
|
||||||
primaryKey = PGPPublicKey.removeCertification(primaryKey, userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revocations
|
|
||||||
Iterator<PGPSignature> revocationSignatures = primaryKey.getSignaturesOfType(SignatureType.KEY_REVOCATION.getCode());
|
|
||||||
while (revocationSignatures.hasNext()) {
|
|
||||||
PGPSignature revocationSig = revocationSignatures.next();
|
|
||||||
if (!SelectSignatureFromKey.isValidKeyRevocationSignature(primaryKey)
|
|
||||||
.accept(revocationSig, primaryKey, keyRing)) {
|
|
||||||
primaryKey = PGPPublicKey.removeCertification(primaryKey, revocationSig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (R) replacePublicKey(keyRing, primaryKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <R extends PGPKeyRing> R assessSubkeyAtDate(PGPPublicKey subkey, PGPKeyRing keyRing, KeyRingInfo info) {
|
|
||||||
if (subkey.isMasterKey()) {
|
|
||||||
throw new IllegalArgumentException("Passed in key is not a subkey");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subkey binding sigs
|
|
||||||
Iterator<PGPSignature> subkeyBindingSigIterator = subkey.getSignaturesOfType(SignatureType.SUBKEY_BINDING.getCode());
|
|
||||||
while (subkeyBindingSigIterator.hasNext()) {
|
|
||||||
PGPSignature signature = subkeyBindingSigIterator.next();
|
|
||||||
if (!SelectSignatureFromKey.isValidSubkeyBindingSignature(keyRing.getPublicKey(), subkey)
|
|
||||||
.accept(signature, subkey, keyRing)) {
|
|
||||||
subkey = PGPPublicKey.removeCertification(subkey, signature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subkey revocation sigs
|
|
||||||
Iterator<PGPSignature> revocationSigIterator = subkey.getSignaturesOfType(SignatureType.SUBKEY_REVOCATION.getCode());
|
|
||||||
while (revocationSigIterator.hasNext()) {
|
|
||||||
PGPSignature signature = revocationSigIterator.next();
|
|
||||||
if (!SelectSignatureFromKey.isValidSubkeyRevocationSignature().accept(signature, subkey, keyRing)) {
|
|
||||||
subkey = PGPPublicKey.removeCertification(subkey, signature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterator<PGPSignature> directKeySigIterator = subkey.getSignaturesOfType(SignatureType.DIRECT_KEY.getCode());
|
|
||||||
while (directKeySigIterator.hasNext()) {
|
|
||||||
PGPSignature signature = directKeySigIterator.next();
|
|
||||||
PGPPublicKey creator = keyRing.getPublicKey(signature.getKeyID());
|
|
||||||
if (creator == null) {
|
|
||||||
// remove external signature
|
|
||||||
subkey = PGPPublicKey.removeCertification(subkey, signature);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SelectSignatureFromKey.isValidDirectKeySignature(creator, subkey)
|
|
||||||
.accept(signature, subkey, keyRing)) {
|
|
||||||
subkey = PGPPublicKey.removeCertification(subkey, signature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (R) replacePublicKey(keyRing, subkey);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static PGPKeyRing replacePublicKey(PGPKeyRing keyRing, PGPPublicKey publicKey) {
|
|
||||||
if (keyRing instanceof PGPPublicKeyRing) {
|
|
||||||
keyRing = PGPPublicKeyRing.insertPublicKey((PGPPublicKeyRing) keyRing, publicKey);
|
|
||||||
} else if (keyRing instanceof PGPSecretKeyRing) {
|
|
||||||
PGPSecretKeyRing secretKeys = (PGPSecretKeyRing) keyRing;
|
|
||||||
PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys);
|
|
||||||
publicKeys = PGPPublicKeyRing.insertPublicKey(publicKeys, publicKey);
|
|
||||||
secretKeys = PGPSecretKeyRing.replacePublicKeys(secretKeys, publicKeys);
|
|
||||||
keyRing = secretKeys;
|
|
||||||
}
|
|
||||||
return keyRing;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -629,8 +629,8 @@ public class KeyRingInfo {
|
||||||
private final Map<Long, PGPSignature> subkeyBindings;
|
private final Map<Long, PGPSignature> subkeyBindings;
|
||||||
|
|
||||||
public Signatures(PGPKeyRing keyRing, Date evaluationDate, Policy policy) {
|
public Signatures(PGPKeyRing keyRing, Date evaluationDate, Policy policy) {
|
||||||
primaryKeyRevocation = SignaturePicker.pickCurrentRevocationSelfSignature(keyRing, evaluationDate);
|
primaryKeyRevocation = SignaturePicker.pickCurrentRevocationSelfSignature(keyRing, policy, evaluationDate);
|
||||||
primaryKeySelfSignature = SignaturePicker.pickCurrentDirectKeySelfSignature(keyRing, evaluationDate);
|
primaryKeySelfSignature = SignaturePicker.pickLatestDirectKeySignature(keyRing, policy, evaluationDate);
|
||||||
userIdRevocations = new HashMap<>();
|
userIdRevocations = new HashMap<>();
|
||||||
userIdCertifications = new HashMap<>();
|
userIdCertifications = new HashMap<>();
|
||||||
subkeyRevocations = new HashMap<>();
|
subkeyRevocations = new HashMap<>();
|
||||||
|
@ -638,11 +638,11 @@ public class KeyRingInfo {
|
||||||
|
|
||||||
for (Iterator<String> it = keyRing.getPublicKey().getUserIDs(); it.hasNext(); ) {
|
for (Iterator<String> it = keyRing.getPublicKey().getUserIDs(); it.hasNext(); ) {
|
||||||
String userId = it.next();
|
String userId = it.next();
|
||||||
PGPSignature revocation = SignaturePicker.pickCurrentUserIdRevocationSignature(keyRing, userId, evaluationDate);
|
PGPSignature revocation = SignaturePicker.pickCurrentUserIdRevocationSignature(keyRing, userId, policy, evaluationDate);
|
||||||
if (revocation != null) {
|
if (revocation != null) {
|
||||||
userIdRevocations.put(userId, revocation);
|
userIdRevocations.put(userId, revocation);
|
||||||
}
|
}
|
||||||
PGPSignature certification = SignaturePicker.pickCurrentUserIdCertificationSignature(keyRing, userId, evaluationDate);
|
PGPSignature certification = SignaturePicker.pickLatestUserIdCertificationSignature(keyRing, userId, policy, evaluationDate);
|
||||||
if (certification != null) {
|
if (certification != null) {
|
||||||
userIdCertifications.put(userId, certification);
|
userIdCertifications.put(userId, certification);
|
||||||
}
|
}
|
||||||
|
@ -652,11 +652,11 @@ public class KeyRingInfo {
|
||||||
keys.next(); // Skip primary key
|
keys.next(); // Skip primary key
|
||||||
while (keys.hasNext()) {
|
while (keys.hasNext()) {
|
||||||
PGPPublicKey subkey = keys.next();
|
PGPPublicKey subkey = keys.next();
|
||||||
PGPSignature subkeyRevocation = SignaturePicker.pickCurrentSubkeyBindingRevocationSignature(keyRing, subkey, evaluationDate);
|
PGPSignature subkeyRevocation = SignaturePicker.pickCurrentSubkeyBindingRevocationSignature(keyRing, subkey, policy, evaluationDate);
|
||||||
if (subkeyRevocation != null) {
|
if (subkeyRevocation != null) {
|
||||||
subkeyRevocations.put(subkey.getKeyID(), subkeyRevocation);
|
subkeyRevocations.put(subkey.getKeyID(), subkeyRevocation);
|
||||||
}
|
}
|
||||||
PGPSignature subkeyBinding = SignaturePicker.pickCurrentSubkeyBindingSignature(keyRing, subkey, evaluationDate);
|
PGPSignature subkeyBinding = SignaturePicker.pickLatestSubkeyBindingSignature(keyRing, subkey, policy, evaluationDate);
|
||||||
if (subkeyBinding != null) {
|
if (subkeyBinding != null) {
|
||||||
subkeyBindings.put(subkey.getKeyID(), subkeyBinding);
|
subkeyBindings.put(subkey.getKeyID(), subkeyBinding);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,17 +20,12 @@ import java.util.Date;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.bouncycastle.bcpg.sig.RevocationReason;
|
|
||||||
import org.bouncycastle.bcpg.sig.SignatureCreationTime;
|
|
||||||
import org.bouncycastle.openpgp.PGPKeyRing;
|
import org.bouncycastle.openpgp.PGPKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.pgpainless.PGPainless;
|
|
||||||
import org.pgpainless.algorithm.SignatureType;
|
import org.pgpainless.algorithm.SignatureType;
|
||||||
import org.pgpainless.exception.SignatureValidationException;
|
import org.pgpainless.exception.SignatureValidationException;
|
||||||
import org.pgpainless.key.util.RevocationAttributes;
|
|
||||||
import org.pgpainless.policy.Policy;
|
import org.pgpainless.policy.Policy;
|
||||||
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
|
|
||||||
import org.pgpainless.util.CollectionUtils;
|
import org.pgpainless.util.CollectionUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,14 +44,14 @@ import org.pgpainless.util.CollectionUtils;
|
||||||
public class SignaturePicker {
|
public class SignaturePicker {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pick the most current (at the time of evaluation) key revocation signature.
|
* Pick the, at validation date most recent valid key revocation signature.
|
||||||
* If there is a hard revocation signature, it is picked, regardless of expiration or creation time.
|
* If there are hard revocation signatures, the latest hard revocation sig is picked, even if it was created after
|
||||||
|
* validationDate or if it is already expired.
|
||||||
*
|
*
|
||||||
* @param keyRing key ring
|
* @param keyRing key ring
|
||||||
* @return most recent, valid key revocation signature
|
* @return most recent, valid key revocation signature
|
||||||
*/
|
*/
|
||||||
public static PGPSignature pickCurrentRevocationSelfSignature(PGPKeyRing keyRing, Date validationDate) {
|
public static PGPSignature pickCurrentRevocationSelfSignature(PGPKeyRing keyRing, Policy policy, Date validationDate) {
|
||||||
Policy policy = PGPainless.getPolicy();
|
|
||||||
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
||||||
|
|
||||||
List<PGPSignature> signatures = getSortedSignaturesOfType(primaryKey, SignatureType.KEY_REVOCATION);
|
List<PGPSignature> signatures = getSortedSignaturesOfType(primaryKey, SignatureType.KEY_REVOCATION);
|
||||||
|
@ -76,42 +71,36 @@ public class SignaturePicker {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pick the current direct key self-signature on the primary key.
|
* Pick the, at validationDate most recent, valid direct key signature.
|
||||||
|
* This method might return null, if there is no direct key self-signature which is valid at validationDate.
|
||||||
|
*
|
||||||
* @param keyRing key ring
|
* @param keyRing key ring
|
||||||
* @param validationDate validation date
|
* @param validationDate validation date
|
||||||
* @return direct-key self-signature
|
* @return direct-key self-signature
|
||||||
*/
|
*/
|
||||||
public static PGPSignature pickCurrentDirectKeySelfSignature(PGPKeyRing keyRing, Date validationDate) {
|
public static PGPSignature pickCurrentDirectKeySelfSignature(PGPKeyRing keyRing, Policy policy, Date validationDate) {
|
||||||
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
||||||
return pickCurrentDirectKeySignature(primaryKey, primaryKey, keyRing, validationDate);
|
return pickCurrentDirectKeySignature(primaryKey, primaryKey, policy, validationDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pick the current direct-key signature made by the signing key on the signed key.
|
* Pick the, at validationDate, latest, valid direct key signature made by signingKey on signedKey.
|
||||||
|
* This method might return null, if there is no direct key self signature which is valid at validationDate.
|
||||||
*
|
*
|
||||||
* @param signingKey key that created the signature
|
* @param signingKey key that created the signature
|
||||||
* @param signedKey key that carries the signature
|
* @param signedKey key that carries the signature
|
||||||
* @param keyRing key ring
|
|
||||||
* @param validationDate validation date
|
* @param validationDate validation date
|
||||||
* @return direct key sig
|
* @return direct key sig
|
||||||
*/
|
*/
|
||||||
public static PGPSignature pickCurrentDirectKeySignature(PGPPublicKey signingKey, PGPPublicKey signedKey, PGPKeyRing keyRing, Date validationDate) {
|
public static PGPSignature pickCurrentDirectKeySignature(PGPPublicKey signingKey, PGPPublicKey signedKey, Policy policy, Date validationDate) {
|
||||||
List<PGPSignature> directKeySignatures = getSortedSignaturesOfType(signedKey, SignatureType.DIRECT_KEY);
|
List<PGPSignature> directKeySignatures = getSortedSignaturesOfType(signedKey, SignatureType.DIRECT_KEY);
|
||||||
|
|
||||||
PGPSignature mostRecentDirectKeySigBySigningKey = null;
|
PGPSignature mostRecentDirectKeySigBySigningKey = null;
|
||||||
for (PGPSignature signature : directKeySignatures) {
|
for (PGPSignature signature : directKeySignatures) {
|
||||||
if (!SelectSignatureFromKey.isWellFormed().accept(signature, signingKey, keyRing)) {
|
try {
|
||||||
// signature is not well formed
|
SignatureValidator.verifyDirectKeySignature(signature, signingKey, signedKey, policy, validationDate);
|
||||||
continue;
|
} catch (SignatureValidationException e) {
|
||||||
}
|
// Direct key sig is not valid
|
||||||
|
|
||||||
if (!SelectSignatureFromKey.isValidAt(validationDate).accept(signature, signedKey, keyRing)) {
|
|
||||||
// Signature is either expired or not yet valid
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SelectSignatureFromKey.isValidDirectKeySignature(signingKey, signedKey).accept(signature, signedKey, keyRing)) {
|
|
||||||
// signature does not check out.
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
mostRecentDirectKeySigBySigningKey = signature;
|
mostRecentDirectKeySigBySigningKey = signature;
|
||||||
|
@ -121,57 +110,94 @@ public class SignaturePicker {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pick the most recent user-id revocation signature.
|
* Pick the, at validationDate, latest direct key signature.
|
||||||
|
* This method might return an expired signature.
|
||||||
|
* If there are more than one direct-key signature, and some of those are not expired, the latest non-expired
|
||||||
|
* yet already effective direct-key signature will be returned.
|
||||||
|
*
|
||||||
|
* @param keyRing key ring
|
||||||
|
* @param validationDate validation date
|
||||||
|
* @return latest direct key signature
|
||||||
|
*/
|
||||||
|
public static PGPSignature pickLatestDirectKeySignature(PGPKeyRing keyRing, Policy policy, Date validationDate) {
|
||||||
|
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
||||||
|
return pickLatestDirectKeySignature(primaryKey, primaryKey, policy, validationDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick the, at validationDate, latest direct key signature made by signingKey on signedKey.
|
||||||
|
* This method might return an expired signature.
|
||||||
|
* If a non-expired direct-key signature exists, the latest non-expired yet already effective direct-key
|
||||||
|
* signature will be returned.
|
||||||
|
*
|
||||||
|
* @param signingKey signing key (key that made the sig)
|
||||||
|
* @param signedKey signed key (key that carries the sig)
|
||||||
|
* @param validationDate date of validation
|
||||||
|
* @return latest direct key sig
|
||||||
|
*/
|
||||||
|
public static PGPSignature pickLatestDirectKeySignature(PGPPublicKey signingKey, PGPPublicKey signedKey, Policy policy, Date validationDate) {
|
||||||
|
List<PGPSignature> signatures = getSortedSignaturesOfType(signedKey, SignatureType.DIRECT_KEY);
|
||||||
|
|
||||||
|
PGPSignature latestDirectKeySignature = null;
|
||||||
|
for (PGPSignature signature : signatures) {
|
||||||
|
try {
|
||||||
|
SignatureValidator.signatureIsOfType(SignatureType.DIRECT_KEY).verify(signature);
|
||||||
|
SignatureValidator.signatureStructureIsAcceptable(signingKey, policy).verify(signature);
|
||||||
|
SignatureValidator.signatureIsAlreadyEffective(validationDate).verify(signature);
|
||||||
|
// if the currently latest signature is not yet expired, check if the next candidate is not yet expired
|
||||||
|
if (latestDirectKeySignature != null && !SignatureUtils.isSignatureExpired(latestDirectKeySignature, validationDate)) {
|
||||||
|
SignatureValidator.signatureIsNotYetExpired(validationDate).verify(signature);
|
||||||
|
}
|
||||||
|
SignatureValidator.correctSignatureOverKey(signingKey, signedKey).verify(signature);
|
||||||
|
} catch (SignatureValidationException e) {
|
||||||
|
// Direct key signature is not valid
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
latestDirectKeySignature = signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
return latestDirectKeySignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick the, at validationDate most recent, valid user-id revocation signature.
|
||||||
|
* If there are hard revocation signatures, the latest hard revocation sig is picked, even if it was created after
|
||||||
|
* validationDate or if it is already expired.
|
||||||
*
|
*
|
||||||
* @param keyRing key ring
|
* @param keyRing key ring
|
||||||
* @param userId user-Id that gets revoked
|
* @param userId user-Id that gets revoked
|
||||||
* @param validationDate validation date
|
* @param validationDate validation date
|
||||||
* @return revocation signature
|
* @return revocation signature
|
||||||
*/
|
*/
|
||||||
public static PGPSignature pickCurrentUserIdRevocationSignature(PGPKeyRing keyRing, String userId, Date validationDate) {
|
public static PGPSignature pickCurrentUserIdRevocationSignature(PGPKeyRing keyRing, String userId, Policy policy, Date validationDate) {
|
||||||
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
||||||
|
List<PGPSignature> signatures = getSortedSignaturesOfType(primaryKey, SignatureType.CERTIFICATION_REVOCATION);
|
||||||
|
|
||||||
Iterator<PGPSignature> certificationRevocations = primaryKey.getSignaturesOfType(SignatureType.CERTIFICATION_REVOCATION.getCode());
|
PGPSignature latestUserIdRevocation = null;
|
||||||
List<PGPSignature> signatures = CollectionUtils.iteratorToList(certificationRevocations);
|
|
||||||
Collections.sort(signatures, new SignatureCreationDateComparator());
|
|
||||||
|
|
||||||
PGPSignature mostRecentUserIdRevocation = null;
|
|
||||||
for (PGPSignature signature : signatures) {
|
for (PGPSignature signature : signatures) {
|
||||||
if (!SelectSignatureFromKey.isWellFormed().accept(signature, primaryKey, keyRing)) {
|
try {
|
||||||
// Sig is not well formed.
|
SignatureValidator.verifyUserIdRevocation(userId, signature, primaryKey, policy, validationDate);
|
||||||
continue;
|
} catch (SignatureValidationException e) {
|
||||||
}
|
// User-id revocation is not valid
|
||||||
|
|
||||||
RevocationReason reason = SignatureSubpacketsUtil.getRevocationReason(signature);
|
|
||||||
if (reason != null && !RevocationAttributes.Reason.isHardRevocation(reason.getRevocationReason())) {
|
|
||||||
// reason code states soft revocation
|
|
||||||
if (!SelectSignatureFromKey.isValidAt(validationDate).accept(signature, primaryKey, keyRing)) {
|
|
||||||
// Soft revocation is either expired or not yet valid
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
latestUserIdRevocation = signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SelectSignatureFromKey.isValidCertificationRevocationSignature(primaryKey, userId)
|
return latestUserIdRevocation;
|
||||||
.accept(signature, primaryKey, keyRing)) {
|
|
||||||
// sig does not check out for userid
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
mostRecentUserIdRevocation = signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mostRecentUserIdRevocation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pick the most current certification self-signature for the given user-id.
|
* Pick the, at validationDate latest, valid certification self-signature for the given user-id.
|
||||||
|
* This method might return null, if there is no certification self signature for that user-id which is valid
|
||||||
|
* at validationDate.
|
||||||
*
|
*
|
||||||
* @param keyRing keyring
|
* @param keyRing keyring
|
||||||
* @param userId userid
|
* @param userId userid
|
||||||
* @param validationDate validation date
|
* @param validationDate validation date
|
||||||
* @return user-id certification
|
* @return user-id certification
|
||||||
*/
|
*/
|
||||||
public static PGPSignature pickCurrentUserIdCertificationSignature(PGPKeyRing keyRing, String userId, Date validationDate) {
|
public static PGPSignature pickCurrentUserIdCertificationSignature(PGPKeyRing keyRing, String userId, Policy policy, Date validationDate) {
|
||||||
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
||||||
|
|
||||||
Iterator<PGPSignature> userIdSigIterator = primaryKey.getSignaturesForID(userId);
|
Iterator<PGPSignature> userIdSigIterator = primaryKey.getSignaturesForID(userId);
|
||||||
|
@ -180,21 +206,12 @@ public class SignaturePicker {
|
||||||
|
|
||||||
PGPSignature mostRecentUserIdCertification = null;
|
PGPSignature mostRecentUserIdCertification = null;
|
||||||
for (PGPSignature signature : signatures) {
|
for (PGPSignature signature : signatures) {
|
||||||
if (!SelectSignatureFromKey.isWellFormed().accept(signature, primaryKey, keyRing)) {
|
try {
|
||||||
// Sig not well formed
|
SignatureValidator.verifyUserIdCertification(userId, signature, primaryKey, policy, validationDate);
|
||||||
|
} catch (SignatureValidationException e) {
|
||||||
|
// User-id certification is not valid
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SelectSignatureFromKey.isValidAt(validationDate).accept(signature, primaryKey, keyRing)) {
|
|
||||||
// Sig is either expired or not valid yet
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SelectSignatureFromKey.isValidSignatureOnUserId(userId, primaryKey).accept(signature, primaryKey, keyRing)) {
|
|
||||||
// Sig does not check out
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
mostRecentUserIdCertification = signature;
|
mostRecentUserIdCertification = signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,57 +219,88 @@ public class SignaturePicker {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current subkey binding revocation signature for the given subkey.
|
* Pick the, at validationDate latest certification self-signature for the given user-id.
|
||||||
|
* This method might return an expired signature.
|
||||||
|
* If a non-expired user-id certification signature exists, the latest non-expired yet already effective
|
||||||
|
* user-id certification signature for the given user-id will be returned.
|
||||||
|
*
|
||||||
|
* @param keyRing keyring
|
||||||
|
* @param userId userid
|
||||||
|
* @param validationDate validation date
|
||||||
|
* @return user-id certification
|
||||||
|
*/
|
||||||
|
public static PGPSignature pickLatestUserIdCertificationSignature(PGPKeyRing keyRing, String userId, Policy policy, Date validationDate) {
|
||||||
|
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
||||||
|
|
||||||
|
Iterator<PGPSignature> userIdSigIterator = primaryKey.getSignaturesForID(userId);
|
||||||
|
List<PGPSignature> signatures = CollectionUtils.iteratorToList(userIdSigIterator);
|
||||||
|
Collections.sort(signatures, new SignatureCreationDateComparator());
|
||||||
|
|
||||||
|
PGPSignature latestUserIdCert = null;
|
||||||
|
for (PGPSignature signature : signatures) {
|
||||||
|
try {
|
||||||
|
SignatureValidator.signatureIsCertification().verify(signature);
|
||||||
|
SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(signature);
|
||||||
|
SignatureValidator.signatureIsAlreadyEffective(validationDate).verify(signature);
|
||||||
|
// if the currently latest signature is not yet expired, check if the next candidate is not yet expired
|
||||||
|
if (latestUserIdCert != null && !SignatureUtils.isSignatureExpired(latestUserIdCert, validationDate)) {
|
||||||
|
SignatureValidator.signatureIsNotYetExpired(validationDate).verify(signature);
|
||||||
|
}
|
||||||
|
SignatureValidator.correctSignatureOverUserId(userId, primaryKey, primaryKey).verify(signature);
|
||||||
|
} catch (SignatureValidationException e) {
|
||||||
|
// User-id certification is not valid
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
latestUserIdCert = signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
return latestUserIdCert;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick the, at validationDate most recent, valid subkey revocation signature.
|
||||||
|
* If there are hard revocation signatures, the latest hard revocation sig is picked, even if it was created after
|
||||||
|
* validationDate or if it is already expired.
|
||||||
*
|
*
|
||||||
* @param keyRing keyring
|
* @param keyRing keyring
|
||||||
* @param subkey subkey
|
* @param subkey subkey
|
||||||
* @param validationDate validation date
|
* @param validationDate validation date
|
||||||
* @return subkey revocation signature
|
* @return subkey revocation signature
|
||||||
*/
|
*/
|
||||||
public static PGPSignature pickCurrentSubkeyBindingRevocationSignature(PGPKeyRing keyRing, PGPPublicKey subkey, Date validationDate) {
|
public static PGPSignature pickCurrentSubkeyBindingRevocationSignature(PGPKeyRing keyRing, PGPPublicKey subkey, Policy policy, Date validationDate) {
|
||||||
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
||||||
if (primaryKey.getKeyID() == subkey.getKeyID()) {
|
if (primaryKey.getKeyID() == subkey.getKeyID()) {
|
||||||
throw new IllegalArgumentException("Primary key cannot have subkey binding revocations.");
|
throw new IllegalArgumentException("Primary key cannot have subkey binding revocations.");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<PGPSignature> subkeyRevocationSigs = getSortedSignaturesOfType(subkey, SignatureType.SUBKEY_BINDING);
|
List<PGPSignature> signatures = getSortedSignaturesOfType(subkey, SignatureType.SUBKEY_BINDING);
|
||||||
PGPSignature mostRecentSubkeyRevocation = null;
|
PGPSignature latestSubkeyRevocation = null;
|
||||||
|
|
||||||
for (PGPSignature signature : subkeyRevocationSigs) {
|
for (PGPSignature signature : signatures) {
|
||||||
if (!SelectSignatureFromKey.isWellFormed().accept(signature, primaryKey, keyRing)) {
|
try {
|
||||||
// Signature is not well formed
|
SignatureValidator.verifySubkeyBindingRevocation(signature, primaryKey, subkey, policy, validationDate);
|
||||||
continue;
|
} catch (SignatureValidationException e) {
|
||||||
}
|
// subkey binding revocation is not valid
|
||||||
|
|
||||||
RevocationReason reason = SignatureSubpacketsUtil.getRevocationReason(signature);
|
|
||||||
if (reason != null && !RevocationAttributes.Reason.isHardRevocation(reason.getRevocationReason())) {
|
|
||||||
// reason code states soft revocation
|
|
||||||
if (!SelectSignatureFromKey.isValidAt(validationDate).accept(signature, primaryKey, keyRing)) {
|
|
||||||
// Soft revocation is either expired or not yet valid
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
latestSubkeyRevocation = signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SelectSignatureFromKey.isValidSubkeyRevocationSignature().accept(signature, subkey, keyRing)) {
|
return latestSubkeyRevocation;
|
||||||
// Signature does not check out
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mostRecentSubkeyRevocation = signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mostRecentSubkeyRevocation;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the (at the time of validation) most recent, valid subkey binding signature
|
* Pick the, at validationDate latest, valid subkey binding signature for the given subkey.
|
||||||
* made by the primary key of the key ring on the subkey.
|
* This method might return null, if there is no subkey binding signature which is valid
|
||||||
|
* at validationDate.
|
||||||
*
|
*
|
||||||
* @param keyRing key ring
|
* @param keyRing key ring
|
||||||
* @param subkey subkey
|
* @param subkey subkey
|
||||||
* @param validationDate date of validation
|
* @param validationDate date of validation
|
||||||
* @return most recent valid subkey binding signature
|
* @return most recent valid subkey binding signature
|
||||||
*/
|
*/
|
||||||
public static PGPSignature pickCurrentSubkeyBindingSignature(PGPKeyRing keyRing, PGPPublicKey subkey, Date validationDate) {
|
public static PGPSignature pickCurrentSubkeyBindingSignature(PGPKeyRing keyRing, PGPPublicKey subkey, Policy policy, Date validationDate) {
|
||||||
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
||||||
if (primaryKey.getKeyID() == subkey.getKeyID()) {
|
if (primaryKey.getKeyID() == subkey.getKeyID()) {
|
||||||
throw new IllegalArgumentException("Primary key cannot have subkey binding signature.");
|
throw new IllegalArgumentException("Primary key cannot have subkey binding signature.");
|
||||||
|
@ -262,35 +310,68 @@ public class SignaturePicker {
|
||||||
PGPSignature mostCurrentValidSig = null;
|
PGPSignature mostCurrentValidSig = null;
|
||||||
|
|
||||||
for (PGPSignature signature : subkeyBindingSigs) {
|
for (PGPSignature signature : subkeyBindingSigs) {
|
||||||
// has hashed creation time, does not predate signing key creation date
|
try {
|
||||||
if (!SelectSignatureFromKey.isWellFormed().accept(signature, primaryKey, keyRing)) {
|
SignatureValidator.verifySubkeyBindingSignature(signature, primaryKey, subkey, policy, validationDate);
|
||||||
// Signature is not well-formed. Reject.
|
} catch (SignatureValidationException validationException) {
|
||||||
|
// Subkey binding sig is not valid
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SignatureCreationTime creationTime = SignatureSubpacketsUtil.getSignatureCreationTime(signature);
|
|
||||||
if (creationTime.getTime().after(validationDate)) {
|
|
||||||
// signature is not yet valid
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SignatureUtils.isSignatureExpired(signature, validationDate)) {
|
|
||||||
// Signature is expired
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!SelectSignatureFromKey.isValidSubkeyBindingSignature(primaryKey, subkey)
|
|
||||||
.accept(signature, subkey, keyRing)) {
|
|
||||||
// Invalid subkey binding signature
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
mostCurrentValidSig = signature;
|
mostCurrentValidSig = signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
return mostCurrentValidSig;
|
return mostCurrentValidSig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pick the, at validationDate latest subkey binding signature for the given subkey.
|
||||||
|
* This method might return an expired signature.
|
||||||
|
* If a non-expired subkey binding signature exists, the latest non-expired yet already effective
|
||||||
|
* subkey binding signature for the given subkey will be returned.
|
||||||
|
*
|
||||||
|
* @param keyRing key ring
|
||||||
|
* @param subkey subkey
|
||||||
|
* @param validationDate validationDate
|
||||||
|
* @return subkey binding signature
|
||||||
|
*/
|
||||||
|
public static PGPSignature pickLatestSubkeyBindingSignature(PGPKeyRing keyRing, PGPPublicKey subkey, Policy policy, Date validationDate) {
|
||||||
|
PGPPublicKey primaryKey = keyRing.getPublicKey();
|
||||||
|
if (primaryKey.getKeyID() == subkey.getKeyID()) {
|
||||||
|
throw new IllegalArgumentException("Primary key cannot have subkey binding signature.");
|
||||||
|
}
|
||||||
|
|
||||||
|
List<PGPSignature> signatures = getSortedSignaturesOfType(subkey, SignatureType.SUBKEY_BINDING);
|
||||||
|
PGPSignature latestSubkeyBinding = null;
|
||||||
|
|
||||||
|
for (PGPSignature signature : signatures) {
|
||||||
|
try {
|
||||||
|
SignatureValidator.signatureIsOfType(SignatureType.SUBKEY_BINDING).verify(signature);
|
||||||
|
SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(signature);
|
||||||
|
SignatureValidator.signatureIsAlreadyEffective(validationDate).verify(signature);
|
||||||
|
// if the currently latest signature is not yet expired, check if the next candidate is not yet expired
|
||||||
|
if (latestSubkeyBinding != null && !SignatureUtils.isSignatureExpired(latestSubkeyBinding, validationDate)) {
|
||||||
|
SignatureValidator.signatureIsNotYetExpired(validationDate).verify(signature);
|
||||||
|
}
|
||||||
|
SignatureValidator.correctSubkeyBindingSignature(primaryKey, subkey).verify(signature);
|
||||||
|
} catch (SignatureValidationException e) {
|
||||||
|
// Subkey binding sig is not valid
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
latestSubkeyBinding = signature;
|
||||||
|
}
|
||||||
|
|
||||||
|
return latestSubkeyBinding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of all signatures of the given {@link SignatureType} on the given key, sorted using a
|
||||||
|
* {@link SignatureCreationDateComparator}.
|
||||||
|
*
|
||||||
|
* The returned list will be sorted first by ascending signature creation time.
|
||||||
|
*
|
||||||
|
* @param key key
|
||||||
|
* @param type type of signatures which shall be collected and sorted
|
||||||
|
* @return sorted list of signatures
|
||||||
|
*/
|
||||||
private static List<PGPSignature> getSortedSignaturesOfType(PGPPublicKey key, SignatureType type) {
|
private static List<PGPSignature> getSortedSignaturesOfType(PGPPublicKey key, SignatureType type) {
|
||||||
Iterator<PGPSignature> signaturesOfType = key.getSignaturesOfType(type.getCode());
|
Iterator<PGPSignature> signaturesOfType = key.getSignaturesOfType(type.getCode());
|
||||||
List<PGPSignature> signatureList = CollectionUtils.iteratorToList(signaturesOfType);
|
List<PGPSignature> signatureList = CollectionUtils.iteratorToList(signaturesOfType);
|
||||||
|
|
|
@ -178,7 +178,7 @@ public abstract class SignatureValidator {
|
||||||
signatureStructureIsAcceptable(primaryKey, policy).verify(signature);
|
signatureStructureIsAcceptable(primaryKey, policy).verify(signature);
|
||||||
signatureIsEffective(validationDate).verify(signature);
|
signatureIsEffective(validationDate).verify(signature);
|
||||||
hasValidPrimaryKeyBindingSignatureIfRequired(primaryKey, subkey, policy, validationDate).verify(signature);
|
hasValidPrimaryKeyBindingSignatureIfRequired(primaryKey, subkey, policy, validationDate).verify(signature);
|
||||||
correctSignatureOverKey(primaryKey, subkey).verify(signature);
|
correctSubkeyBindingSignature(primaryKey, subkey).verify(signature);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -274,7 +274,24 @@ public abstract class SignatureValidator {
|
||||||
@Override
|
@Override
|
||||||
public void verify(PGPSignature signature) throws SignatureValidationException {
|
public void verify(PGPSignature signature) throws SignatureValidationException {
|
||||||
HashAlgorithm hashAlgorithm = HashAlgorithm.fromId(signature.getHashAlgorithm());
|
HashAlgorithm hashAlgorithm = HashAlgorithm.fromId(signature.getHashAlgorithm());
|
||||||
|
Policy.HashAlgorithmPolicy hashAlgorithmPolicy = getHashAlgorithmPolicyForSignature(signature, policy);
|
||||||
|
|
||||||
|
if (!hashAlgorithmPolicy.isAcceptable(signature.getHashAlgorithm())) {
|
||||||
|
throw new SignatureValidationException("Signature uses unacceptable hash algorithm " + hashAlgorithm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the applicable {@link Policy.HashAlgorithmPolicy} for the given {@link PGPSignature}.
|
||||||
|
* Revocation signatures are being policed using a different policy than non-revocation signatures.
|
||||||
|
*
|
||||||
|
* @param signature signature
|
||||||
|
* @param policy revocation policy for revocation sigs, normal policy for non-rev sigs
|
||||||
|
* @return policy
|
||||||
|
*/
|
||||||
|
private static Policy.HashAlgorithmPolicy getHashAlgorithmPolicyForSignature(PGPSignature signature, Policy policy) {
|
||||||
Policy.HashAlgorithmPolicy hashAlgorithmPolicy = null;
|
Policy.HashAlgorithmPolicy hashAlgorithmPolicy = null;
|
||||||
SignatureType type = SignatureType.valueOf(signature.getSignatureType());
|
SignatureType type = SignatureType.valueOf(signature.getSignatureType());
|
||||||
if (type == SignatureType.CERTIFICATION_REVOCATION || type == SignatureType.KEY_REVOCATION || type == SignatureType.SUBKEY_REVOCATION) {
|
if (type == SignatureType.CERTIFICATION_REVOCATION || type == SignatureType.KEY_REVOCATION || type == SignatureType.SUBKEY_REVOCATION) {
|
||||||
|
@ -282,12 +299,7 @@ public abstract class SignatureValidator {
|
||||||
} else {
|
} else {
|
||||||
hashAlgorithmPolicy = policy.getSignatureHashAlgorithmPolicy();
|
hashAlgorithmPolicy = policy.getSignatureHashAlgorithmPolicy();
|
||||||
}
|
}
|
||||||
|
return hashAlgorithmPolicy;
|
||||||
if (!hashAlgorithmPolicy.isAcceptable(signature.getHashAlgorithm())) {
|
|
||||||
throw new SignatureValidationException("Signature uses inacceptable hash algorithm " + hashAlgorithm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SignatureValidator signatureDoesNotHaveCriticalUnknownNotations(NotationRegistry registry) {
|
public static SignatureValidator signatureDoesNotHaveCriticalUnknownNotations(NotationRegistry registry) {
|
||||||
|
@ -324,16 +336,40 @@ public abstract class SignatureValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SignatureValidator signatureIsEffective(Date validationDate) {
|
public static SignatureValidator signatureIsEffective(Date validationDate) {
|
||||||
|
return new SignatureValidator() {
|
||||||
|
@Override
|
||||||
|
public void verify(PGPSignature signature) throws SignatureValidationException {
|
||||||
|
signatureIsAlreadyEffective(validationDate).verify(signature);
|
||||||
|
signatureIsNotYetExpired(validationDate).verify(signature);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SignatureValidator signatureIsAlreadyEffective(Date validationDate) {
|
||||||
return new SignatureValidator() {
|
return new SignatureValidator() {
|
||||||
@Override
|
@Override
|
||||||
public void verify(PGPSignature signature) throws SignatureValidationException {
|
public void verify(PGPSignature signature) throws SignatureValidationException {
|
||||||
Date signatureCreationTime = SignatureSubpacketsUtil.getSignatureCreationTime(signature).getTime();
|
Date signatureCreationTime = SignatureSubpacketsUtil.getSignatureCreationTime(signature).getTime();
|
||||||
// For hard revocations, skip the creation time check
|
// Hard revocations are always effective
|
||||||
if (!SignatureUtils.isHardRevocation(signature)) {
|
if (SignatureUtils.isHardRevocation(signature)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (signatureCreationTime.after(validationDate)) {
|
if (signatureCreationTime.after(validationDate)) {
|
||||||
throw new SignatureValidationException("Signature was created at " + signatureCreationTime + " and is therefore not yet valid at " + validationDate);
|
throw new SignatureValidationException("Signature was created at " + signatureCreationTime + " and is therefore not yet valid at " + validationDate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SignatureValidator signatureIsNotYetExpired(Date validationDate) {
|
||||||
|
return new SignatureValidator() {
|
||||||
|
@Override
|
||||||
|
public void verify(PGPSignature signature) throws SignatureValidationException {
|
||||||
|
// Hard revocations do not expire
|
||||||
|
if (SignatureUtils.isHardRevocation(signature)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Date signatureExpirationTime = SignatureSubpacketsUtil.getSignatureExpirationTimeAsDate(signature);
|
Date signatureExpirationTime = SignatureSubpacketsUtil.getSignatureExpirationTimeAsDate(signature);
|
||||||
if (signatureExpirationTime != null && signatureExpirationTime.before(validationDate)) {
|
if (signatureExpirationTime != null && signatureExpirationTime.before(validationDate)) {
|
||||||
|
@ -420,6 +456,26 @@ public abstract class SignatureValidator {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SignatureValidator correctSubkeyBindingSignature(PGPPublicKey primaryKey, PGPPublicKey subkey) {
|
||||||
|
return new SignatureValidator() {
|
||||||
|
@Override
|
||||||
|
public void verify(PGPSignature signature) throws SignatureValidationException {
|
||||||
|
if (primaryKey.getKeyID() == subkey.getKeyID()) {
|
||||||
|
throw new SignatureValidationException("Primary key cannot be its own subkey.");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), primaryKey);
|
||||||
|
boolean valid = signature.verifyCertification(primaryKey, subkey);
|
||||||
|
if (!valid) {
|
||||||
|
throw new SignatureValidationException("Signature is not correct.");
|
||||||
|
}
|
||||||
|
} catch (PGPException e) {
|
||||||
|
throw new SignatureValidationException("Cannot verify subkey binding signature correctness", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static SignatureValidator correctPrimaryKeyBindingSignature(PGPPublicKey primaryKey, PGPPublicKey subkey) {
|
public static SignatureValidator correctPrimaryKeyBindingSignature(PGPPublicKey primaryKey, PGPPublicKey subkey) {
|
||||||
return new SignatureValidator() {
|
return new SignatureValidator() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pgpainless.util;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class of an immutable list which cannot be empty.
|
||||||
|
* The first element can be accessed via {@link #get()} which is guaranteed to return a non-null value.
|
||||||
|
* The rest of the list can be accessed via {@link #getOthers()}, which is guaranteed to return a non-null list which is possibly empty.
|
||||||
|
* Lastly, the whole list can be accessed via {@link #getAll()}, which is guaranteed to return a non-empty list.
|
||||||
|
*
|
||||||
|
* @param <E> element type
|
||||||
|
*/
|
||||||
|
public class NonEmptyList<E> {
|
||||||
|
|
||||||
|
private final List<E> elements;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a singleton list from the given element.
|
||||||
|
*
|
||||||
|
* @param element element
|
||||||
|
*/
|
||||||
|
public NonEmptyList(E element) {
|
||||||
|
if (element == null) {
|
||||||
|
throw new IllegalArgumentException("Singleton element cannot be null.");
|
||||||
|
}
|
||||||
|
this.elements = Collections.singletonList(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a non-empty list from the given list of elements.
|
||||||
|
*
|
||||||
|
* @param elements elements
|
||||||
|
* @throws IllegalArgumentException if the provided list of elements is empty.
|
||||||
|
*/
|
||||||
|
public NonEmptyList(List<E> elements) {
|
||||||
|
if (elements.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("Underlying list cannot be empty.");
|
||||||
|
}
|
||||||
|
this.elements = Collections.unmodifiableList(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the first element of the list.
|
||||||
|
*
|
||||||
|
* @return first
|
||||||
|
*/
|
||||||
|
public @Nonnull E get() {
|
||||||
|
return elements.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of all elements of the list except the first.
|
||||||
|
*
|
||||||
|
* @return list of all but the first element
|
||||||
|
*/
|
||||||
|
public @Nonnull List<E> getOthers() {
|
||||||
|
List<E> others = new LinkedList<>(elements);
|
||||||
|
others.remove(0);
|
||||||
|
return Collections.unmodifiableList(others);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a non-empty list of all elements of this list.
|
||||||
|
*
|
||||||
|
* @return all elements
|
||||||
|
*/
|
||||||
|
public List<E> getAll() {
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pgpainless.util;
|
||||||
|
|
||||||
|
public class SignatureTree {
|
||||||
|
|
||||||
|
public interface Node {
|
||||||
|
long getKeyId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface PrimaryKeyNode extends Node {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface SubkeyNode extends Node {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface SignatureNode extends Node {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 Paul Schaub.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.pgpainless.util;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
public class NonEmptyListTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEmptyListThrows() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new NonEmptyList<>(Collections.emptyList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleElementList() {
|
||||||
|
List<String> singleElement = Collections.singletonList("Hello");
|
||||||
|
NonEmptyList<String> nonEmpty = new NonEmptyList<>(singleElement);
|
||||||
|
assertEquals("Hello", nonEmpty.get());
|
||||||
|
assertTrue(nonEmpty.getOthers().isEmpty());
|
||||||
|
assertEquals(1, nonEmpty.getAll().size());
|
||||||
|
assertTrue(nonEmpty.getAll().contains("Hello"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingletonElement() {
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> new NonEmptyList<>((String) null));
|
||||||
|
NonEmptyList<String> nonEmpty = new NonEmptyList<>("Foo");
|
||||||
|
assertEquals("Foo", nonEmpty.get());
|
||||||
|
assertTrue(nonEmpty.getOthers().isEmpty());
|
||||||
|
assertEquals(Collections.singletonList("Foo"), nonEmpty.getAll());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleElements() {
|
||||||
|
List<String> multipleElements = Arrays.asList("Foo", "Bar", "Baz");
|
||||||
|
NonEmptyList<String> nonEmpty = new NonEmptyList<>(multipleElements);
|
||||||
|
assertEquals("Foo", nonEmpty.get());
|
||||||
|
assertEquals(Arrays.asList("Bar", "Baz"), nonEmpty.getOthers());
|
||||||
|
assertEquals(multipleElements, nonEmpty.getAll());
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,28 +22,22 @@ import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
|
||||||
import org.bouncycastle.util.io.Streams;
|
import org.bouncycastle.util.io.Streams;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.algorithm.DocumentSignatureType;
|
import org.pgpainless.algorithm.DocumentSignatureType;
|
||||||
import org.pgpainless.algorithm.KeyFlag;
|
import org.pgpainless.encryption_signing.EncryptionOptions;
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
|
||||||
import org.pgpainless.encryption_signing.EncryptionBuilderInterface;
|
|
||||||
import org.pgpainless.encryption_signing.EncryptionStream;
|
import org.pgpainless.encryption_signing.EncryptionStream;
|
||||||
|
import org.pgpainless.encryption_signing.ProducerOptions;
|
||||||
|
import org.pgpainless.encryption_signing.SigningOptions;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import org.pgpainless.key.protection.KeyRingProtectionSettings;
|
|
||||||
import org.pgpainless.key.protection.CachingSecretKeyRingProtector;
|
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
|
import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider;
|
||||||
import org.pgpainless.util.Passphrase;
|
import org.pgpainless.util.Passphrase;
|
||||||
import picocli.CommandLine;
|
import picocli.CommandLine;
|
||||||
|
|
||||||
|
@ -90,96 +84,57 @@ public class Encrypt implements Runnable {
|
||||||
System.exit(19);
|
System.exit(19);
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPPublicKeyRing[] pubKeysArray = new PGPPublicKeyRing[certs.length];
|
EncryptionOptions encOpt = new EncryptionOptions();
|
||||||
|
SigningOptions signOpt = new SigningOptions();
|
||||||
|
|
||||||
for (int i = 0 ; i < certs.length; i++) {
|
for (int i = 0 ; i < certs.length; i++) {
|
||||||
try (InputStream fileIn = new FileInputStream(certs[i])) {
|
try (InputStream fileIn = new FileInputStream(certs[i])) {
|
||||||
PGPPublicKeyRing publicKey = PGPainless.readKeyRing().publicKeyRing(fileIn);
|
PGPPublicKeyRing publicKey = PGPainless.readKeyRing().publicKeyRing(fileIn);
|
||||||
pubKeysArray[i] = publicKey;
|
encOpt.addRecipient(publicKey);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
err_ln("Cannot read certificate " + certs[i].getName());
|
err_ln("Cannot read certificate " + certs[i].getName());
|
||||||
err_ln(e.getMessage());
|
err_ln(e.getMessage());
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PGPPublicKeyRingCollection publicKeys;
|
|
||||||
try {
|
for (int i = 0; i < withPassword.length; i++) {
|
||||||
publicKeys = new PGPPublicKeyRingCollection(Arrays.asList(pubKeysArray));
|
Passphrase passphrase = Passphrase.fromPassword(withPassword[i]);
|
||||||
} catch (IOException | PGPException e) {
|
encOpt.addPassphrase(passphrase);
|
||||||
err_ln("Cannot construct public key collection.");
|
|
||||||
err_ln(e.getMessage());
|
|
||||||
System.exit(1);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
PGPSecretKeyRing[] secretKeys = new PGPSecretKeyRing[signWith.length];
|
|
||||||
|
final Scanner scanner = new Scanner(System.in);
|
||||||
for (int i = 0; i < signWith.length; i++) {
|
for (int i = 0; i < signWith.length; i++) {
|
||||||
try (FileInputStream fileIn = new FileInputStream(signWith[i])) {
|
try (FileInputStream fileIn = new FileInputStream(signWith[i])) {
|
||||||
PGPSecretKeyRing secretKey = PGPainless.readKeyRing().secretKeyRing(fileIn);
|
PGPSecretKeyRing secretKey = PGPainless.readKeyRing().secretKeyRing(fileIn);
|
||||||
secretKeys[i] = secretKey;
|
SecretKeyRingProtector protector = SecretKeyRingProtector.defaultSecretKeyRingProtector(
|
||||||
|
new SecretKeyPassphraseProvider() {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Passphrase getPassphraseFor(Long keyId) {
|
||||||
|
print_ln("Please provide the passphrase for key " + new OpenPgpV4Fingerprint(secretKey));
|
||||||
|
String password = scanner.nextLine();
|
||||||
|
Passphrase passphrase = Passphrase.fromPassword(password.trim());
|
||||||
|
return passphrase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
signOpt.addInlineSignature(protector, secretKey,
|
||||||
|
type == Type.text || type == Type.mime ?
|
||||||
|
DocumentSignatureType.CANONICAL_TEXT_DOCUMENT : DocumentSignatureType.BINARY_DOCUMENT);
|
||||||
} catch (IOException | PGPException e) {
|
} catch (IOException | PGPException e) {
|
||||||
err_ln("Cannot read secret key from file " + signWith[i].getName());
|
err_ln("Cannot read secret key from file " + signWith[i].getName());
|
||||||
err_ln(e.getMessage());
|
err_ln(e.getMessage());
|
||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Passphrase[] passphraseArray = new Passphrase[withPassword.length];
|
|
||||||
for (int i = 0; i < withPassword.length; i++) {
|
|
||||||
String password = withPassword[i];
|
|
||||||
passphraseArray[i] = Passphrase.fromPassword(password);
|
|
||||||
}
|
|
||||||
|
|
||||||
Map<Long, Passphrase> passphraseMap = new HashMap<>();
|
|
||||||
Scanner scanner = null;
|
|
||||||
for (PGPSecretKeyRing ring : secretKeys) {
|
|
||||||
for (PGPSecretKey key : ring) {
|
|
||||||
// Skip non-signing keys
|
|
||||||
PGPSignature signature = (PGPSignature) key.getPublicKey().getSignatures().next();
|
|
||||||
int flags = signature.getHashedSubPackets().getKeyFlags();
|
|
||||||
if (!key.isSigningKey() || !KeyFlag.hasKeyFlag(flags, KeyFlag.SIGN_DATA)) {
|
|
||||||
// Key cannot sign
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key.getKeyEncryptionAlgorithm() == SymmetricKeyAlgorithm.NULL.getAlgorithmId()) {
|
|
||||||
passphraseMap.put(key.getKeyID(), Passphrase.emptyPassphrase());
|
|
||||||
} else {
|
|
||||||
print_ln("Please provide the passphrase for key " + new OpenPgpV4Fingerprint(key));
|
|
||||||
if (scanner == null) {
|
|
||||||
scanner = new Scanner(System.in);
|
|
||||||
}
|
|
||||||
String password = scanner.nextLine();
|
|
||||||
Passphrase passphrase = Passphrase.fromPassword(password.trim());
|
|
||||||
passphraseMap.put(key.getKeyID(), passphrase);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EncryptionBuilderInterface.ToRecipientsOrSign builder = PGPainless.encryptAndOrSign()
|
|
||||||
.onOutputStream(System.out)
|
|
||||||
.toRecipients(publicKeys)
|
|
||||||
.and();
|
|
||||||
for (Passphrase passphrase : passphraseArray) {
|
|
||||||
builder = builder.forPassphrase(passphrase).and();
|
|
||||||
}
|
|
||||||
EncryptionBuilderInterface.Armor builder_armor = null;
|
|
||||||
EncryptionBuilderInterface.SignWith builder1 = builder;
|
|
||||||
try {
|
try {
|
||||||
if (signWith.length != 0) {
|
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign()
|
||||||
for (int i = 0; i < signWith.length; i++) {
|
.onOutputStream(System.out)
|
||||||
PGPSecretKeyRing secretKeyRing = secretKeys[i];
|
.withOptions(ProducerOptions
|
||||||
EncryptionBuilderInterface.AdditionalSignWith additionalSignWith = builder1.signInlineWith(
|
.signAndEncrypt(encOpt, signOpt)
|
||||||
SecretKeyRingProtector.unlockAllKeysWith(
|
.setAsciiArmor(armor));
|
||||||
passphraseMap.get(secretKeyRing.getPublicKey().getKeyID()),
|
|
||||||
secretKeyRing),
|
|
||||||
secretKeyRing, null,
|
|
||||||
type == Type.text || type == Type.mime ?
|
|
||||||
DocumentSignatureType.CANONICAL_TEXT_DOCUMENT : DocumentSignatureType.BINARY_DOCUMENT);
|
|
||||||
builder_armor = additionalSignWith;
|
|
||||||
builder1 = additionalSignWith.and();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
builder_armor = builder.doNotSign();
|
|
||||||
}
|
|
||||||
EncryptionStream encryptionStream = !armor ? builder_armor.noArmor() : builder_armor.asciiArmor();
|
|
||||||
|
|
||||||
Streams.pipeAll(System.in, encryptionStream);
|
Streams.pipeAll(System.in, encryptionStream);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue