mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-23 11:27:57 +01:00
Add documentation to signature related classes
This commit is contained in:
parent
ec85f53bb6
commit
431a65517e
11 changed files with 587 additions and 44 deletions
|
@ -118,7 +118,7 @@ public class SignatureVerifyingInputStream extends FilterInputStream {
|
|||
signatureStructureIsAcceptable(signingKey, policy).verify(signature);
|
||||
signatureIsEffective(new Date()).verify(signature);
|
||||
|
||||
SignatureChainValidator.validateSigningKey(signature, onePassSignature.getVerificationKeys(), PGPainless.getPolicy(), signature.getCreationTime());
|
||||
SignatureChainValidator.validateSigningKey(signature, onePassSignature.getVerificationKeys(), PGPainless.getPolicy());
|
||||
|
||||
} catch (SignatureValidationException e) {
|
||||
throw new SignatureException("Signature key is not valid.", e);
|
||||
|
|
|
@ -20,38 +20,79 @@ import org.bouncycastle.openpgp.PGPSignature;
|
|||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
import org.pgpainless.key.SubkeyIdentifier;
|
||||
|
||||
/**
|
||||
* Tuple-class which bundles together a signature, the signing key that created the signature,
|
||||
* an identifier of the signing key and a record of whether or not the signature was verified.
|
||||
*/
|
||||
public class DetachedSignature {
|
||||
private final PGPSignature signature;
|
||||
private final PGPKeyRing signingKeyRing;
|
||||
private final SubkeyIdentifier signingKeyIdentifier;
|
||||
private boolean verified;
|
||||
|
||||
/**
|
||||
* Create a new {@link DetachedSignature} object.
|
||||
*
|
||||
* @param signature signature
|
||||
* @param signingKeyRing signing key that created the signature
|
||||
* @param signingKeyIdentifier identifier of the used signing key
|
||||
*/
|
||||
public DetachedSignature(PGPSignature signature, PGPKeyRing signingKeyRing, SubkeyIdentifier signingKeyIdentifier) {
|
||||
this.signature = signature;
|
||||
this.signingKeyRing = signingKeyRing;
|
||||
this.signingKeyIdentifier = signingKeyIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark this {@link DetachedSignature} as verified.
|
||||
*
|
||||
* @param verified verified
|
||||
*/
|
||||
public void setVerified(boolean verified) {
|
||||
this.verified = verified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true iff the signature is verified.
|
||||
*
|
||||
* @return verified
|
||||
*/
|
||||
public boolean isVerified() {
|
||||
return verified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the OpenPGP signature.
|
||||
*
|
||||
* @return signature
|
||||
*/
|
||||
public PGPSignature getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an identifier pointing to the exact signing key which was used to create this signature.
|
||||
*
|
||||
* @return signing key identifier
|
||||
*/
|
||||
public SubkeyIdentifier getSigningKeyIdentifier() {
|
||||
return signingKeyIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key ring that contains the signing key that created this signature.
|
||||
*
|
||||
* @return key ring
|
||||
*/
|
||||
public PGPKeyRing getSigningKeyRing() {
|
||||
return signingKeyRing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link OpenPgpV4Fingerprint} of the key that created the signature.
|
||||
*
|
||||
* @return fingerprint of the signing key
|
||||
*/
|
||||
@Deprecated
|
||||
public OpenPgpV4Fingerprint getFingerprint() {
|
||||
return signingKeyIdentifier.getSubkeyFingerprint();
|
||||
|
|
|
@ -21,29 +21,65 @@ import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
|||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
|
||||
/**
|
||||
* Tuple-class that bundles together a {@link PGPOnePassSignature} object, a {@link PGPPublicKeyRing}
|
||||
* destined to verify the signature, the {@link PGPSignature} itself and a record of whether or not the signature
|
||||
* was verified.
|
||||
*/
|
||||
public class OnePassSignature {
|
||||
private final PGPOnePassSignature onePassSignature;
|
||||
private final PGPPublicKeyRing verificationKeys;
|
||||
private PGPSignature signature;
|
||||
private boolean verified;
|
||||
|
||||
/**
|
||||
* Create a new {@link OnePassSignature}.
|
||||
*
|
||||
* @param onePassSignature one-pass signature packet used to initialize the signature verifier.
|
||||
* @param verificationKeys verification keys
|
||||
*/
|
||||
public OnePassSignature(PGPOnePassSignature onePassSignature, PGPPublicKeyRing verificationKeys) {
|
||||
this.onePassSignature = onePassSignature;
|
||||
this.verificationKeys = verificationKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the signature is verified.
|
||||
*
|
||||
* @return verified
|
||||
*/
|
||||
public boolean isVerified() {
|
||||
return verified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link PGPOnePassSignature} object.
|
||||
*
|
||||
* @return onePassSignature
|
||||
*/
|
||||
public PGPOnePassSignature getOnePassSignature() {
|
||||
return onePassSignature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link OpenPgpV4Fingerprint} of the signing key.
|
||||
*
|
||||
* @return signing key fingerprint
|
||||
*/
|
||||
public OpenPgpV4Fingerprint getFingerprint() {
|
||||
return new OpenPgpV4Fingerprint(verificationKeys.getPublicKey(onePassSignature.getKeyID()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify the one-pass signature.
|
||||
* Note: This method only checks if the signature itself is correct.
|
||||
* It does not check if the signing key was eligible to create the signature, or if the signature is expired etc.
|
||||
* Those checks are being done by {@link org.pgpainless.decryption_verification.SignatureVerifyingInputStream}.
|
||||
*
|
||||
* @param signature parsed-out signature
|
||||
* @return true if the signature was verified, false otherwise
|
||||
* @throws PGPException if signature verification fails with an exception.
|
||||
*/
|
||||
public boolean verify(PGPSignature signature) throws PGPException {
|
||||
this.verified = getOnePassSignature().verify(signature);
|
||||
if (verified) {
|
||||
|
@ -52,10 +88,20 @@ public class OnePassSignature {
|
|||
return verified;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the signature.
|
||||
*
|
||||
* @return signature
|
||||
*/
|
||||
public PGPSignature getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the key ring used to verify the signature.
|
||||
*
|
||||
* @return verification keys
|
||||
*/
|
||||
public PGPPublicKeyRing getVerificationKeys() {
|
||||
return verificationKeys;
|
||||
}
|
||||
|
|
|
@ -31,20 +31,18 @@ import org.pgpainless.algorithm.SignatureType;
|
|||
import org.pgpainless.implementation.ImplementationFactory;
|
||||
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
|
||||
|
||||
/**
|
||||
* Utility class to select signatures from keys based on certain criteria.
|
||||
* This abstract class provides a method {@link #accept(PGPSignature, PGPPublicKey, PGPKeyRing)} which shall only
|
||||
* return true if the provided signature is acceptable regarding the implementations selection criteria.
|
||||
*
|
||||
* The idea is to create an implementation of the class for each criterion, so that those criteria can be
|
||||
* composed to create complex validity checks.
|
||||
*/
|
||||
public abstract class SelectSignatureFromKey {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(SelectSignatureFromKey.class.getName());
|
||||
|
||||
public static SelectSignatureFromKey isValidAt(Date validationDate) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
|
||||
Date expirationDate = SignatureUtils.getSignatureExpirationDate(signature);
|
||||
return !signature.getCreationTime().after(validationDate) && (expirationDate == null || expirationDate.after(validationDate));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public abstract boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing);
|
||||
|
||||
public List<PGPSignature> select(List<PGPSignature> signatures, PGPPublicKey key, PGPKeyRing keyRing) {
|
||||
|
@ -57,6 +55,36 @@ public abstract class SelectSignatureFromKey {
|
|||
return selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that checks if the signature is valid at the validation date.
|
||||
* A signature is not valid if it was created after the validation date, or if it is expired at the validation date.
|
||||
*
|
||||
* creationTime <= validationDate < expirationDate.
|
||||
*
|
||||
* @param validationDate validation date
|
||||
* @return criterion implementation
|
||||
*/
|
||||
public static SelectSignatureFromKey isValidAt(Date validationDate) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
public boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing) {
|
||||
Date expirationDate = SignatureUtils.getSignatureExpirationDate(signature);
|
||||
return !signature.getCreationTime().after(validationDate) && (expirationDate == null || expirationDate.after(validationDate));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that checks if the provided signature is a valid subkey binding signature.
|
||||
*
|
||||
* A signature is only a valid subkey binding signature if it is of type {@link SignatureType#SUBKEY_BINDING},
|
||||
* if it was created by the primary key, and - if the subkey is capable of signing - it contains a valid
|
||||
* primary key binding signature.
|
||||
*
|
||||
* @param primaryKey primary key
|
||||
* @param subkey subkey
|
||||
* @return criterion to validate binding signatures
|
||||
*/
|
||||
public static SelectSignatureFromKey isValidSubkeyBindingSignature(PGPPublicKey primaryKey, PGPPublicKey subkey) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -70,16 +98,20 @@ public abstract class SelectSignatureFromKey {
|
|||
return false;
|
||||
}
|
||||
|
||||
boolean subkeyBindingSigValid;
|
||||
try {
|
||||
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), primaryKey);
|
||||
subkeyBindingSigValid = signature.verifyCertification(primaryKey, subkey);
|
||||
} catch (PGPException e) {
|
||||
LOGGER.log(Level.INFO, "Verification of subkey binding signature failed.", e);
|
||||
if (!isSigNotExpired().accept(signature, subkey, keyRing)) {
|
||||
LOGGER.log(Level.INFO, "Subkey binding signature expired.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!subkeyBindingSigValid) {
|
||||
// Check signature correctness
|
||||
try {
|
||||
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), primaryKey);
|
||||
boolean subkeyBindingSigValid = signature.verifyCertification(primaryKey, subkey);
|
||||
if (!subkeyBindingSigValid) {
|
||||
return false;
|
||||
}
|
||||
} catch (PGPException e) {
|
||||
LOGGER.log(Level.INFO, "Verification of subkey binding signature failed.", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -96,6 +128,13 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that checks if a primary key binding signature is valid.
|
||||
*
|
||||
* @param subkey subkey
|
||||
* @param primaryKey primary key
|
||||
* @return criterion to validate primary key binding signatures
|
||||
*/
|
||||
public static SelectSignatureFromKey isValidPrimaryKeyBindingSignature(PGPPublicKey subkey, PGPPublicKey primaryKey) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -118,6 +157,7 @@ public abstract class SelectSignatureFromKey {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Check signature correctness
|
||||
try {
|
||||
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), subkey);
|
||||
return signature.verifyCertification(primaryKey, subkey);
|
||||
|
@ -128,6 +168,12 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that checks if a signature has an embedded valid primary key binding signature.
|
||||
* @param subkey subkey
|
||||
* @param primaryKey primary key
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey hasValidPrimaryKeyBindingSignatureSubpacket(PGPPublicKey subkey, PGPPublicKey primaryKey) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -149,6 +195,14 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that checks if a signature is a valid v4 direct-key signature.
|
||||
* Note: This method does not check expiration.
|
||||
*
|
||||
* @param signer signing key
|
||||
* @param signee signed key
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isValidDirectKeySignature(PGPPublicKey signer, PGPPublicKey signee) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -161,6 +215,7 @@ public abstract class SelectSignatureFromKey {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Check signature correctness
|
||||
try {
|
||||
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signer);
|
||||
return signature.verifyCertification(signee);
|
||||
|
@ -171,6 +226,12 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that checks if a signature is a valid key revocation signature.
|
||||
*
|
||||
* @param key primary key
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isValidKeyRevocationSignature(PGPPublicKey key) {
|
||||
return and(
|
||||
isVersion4Signature(),
|
||||
|
@ -182,6 +243,11 @@ public abstract class SelectSignatureFromKey {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts valid subkey revocation signatures.
|
||||
*
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isValidSubkeyRevocationSignature() {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -192,6 +258,13 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts valid subkey revocation signatures.
|
||||
*
|
||||
* @param subkey subkey
|
||||
* @param primaryKey primary key
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isValidSubkeyRevocationSignature(PGPPublicKey subkey, PGPPublicKey primaryKey) {
|
||||
return SelectSignatureFromKey.and(
|
||||
isVersion4Signature(),
|
||||
|
@ -201,6 +274,13 @@ public abstract class SelectSignatureFromKey {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts signatures which are valid user-id revocations.
|
||||
*
|
||||
* @param revoker signing key
|
||||
* @param userId user id
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isValidCertificationRevocationSignature(PGPPublicKey revoker, String userId) {
|
||||
return and(
|
||||
isVersion4Signature(),
|
||||
|
@ -210,6 +290,14 @@ public abstract class SelectSignatureFromKey {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts signatures which are valid signatures over a user-id.
|
||||
* This method only checks signature correctness, not expiry etc.
|
||||
*
|
||||
* @param userId user-id
|
||||
* @param signingKey signing key
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isValidSignatureOnUserId(String userId, PGPPublicKey signingKey) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -225,6 +313,14 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts signatures which are valid signatures over a key.
|
||||
* This method only checks signature correctness, not expiry etc.
|
||||
*
|
||||
* @param target signed key
|
||||
* @param signer signing key
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isVerifyingSignatureOnKey(PGPPublicKey target, PGPPublicKey signer) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -241,6 +337,15 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts signatures which are correct binding signatures.
|
||||
* This method only checks signature correctness, not expiry etc.
|
||||
*
|
||||
* @param primaryKey primary key
|
||||
* @param subkey subkey
|
||||
* @param signingKey signing key (either primary, or subkey)
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isVerifyingSignatureOnKeys(PGPPublicKey primaryKey, PGPPublicKey subkey, PGPPublicKey signingKey) {
|
||||
if (signingKey.getKeyID() != primaryKey.getKeyID() && signingKey.getKeyID() != subkey.getKeyID()) {
|
||||
throw new IllegalArgumentException("Signing key MUST be either the primary or subkey.");
|
||||
|
@ -259,6 +364,17 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts certification signatures.
|
||||
*
|
||||
* Those are signature of the following types:
|
||||
* - {@link SignatureType#NO_CERTIFICATION},
|
||||
* - {@link SignatureType#CASUAL_CERTIFICATION},
|
||||
* - {@link SignatureType#GENERIC_CERTIFICATION},
|
||||
* - {@link SignatureType#POSITIVE_CERTIFICATION}.
|
||||
*
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isCertification() {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -268,6 +384,13 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts "well formed" signatures.
|
||||
* A signature is "well formed", iff it has a creation time subpacket and if it does not predate
|
||||
* its creating keys creation time.
|
||||
*
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isWellFormed() {
|
||||
return and(
|
||||
hasCreationTimeSubpacket(),
|
||||
|
@ -275,10 +398,21 @@ public abstract class SelectSignatureFromKey {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts v4 signatures.
|
||||
*
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isVersion4Signature() {
|
||||
return isVersion(4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts signatures which carry a creation time subpacket.
|
||||
* According to the RFC, all signatures are required to have such a subpacket.
|
||||
*
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey hasCreationTimeSubpacket() {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -288,10 +422,22 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts signatures that were created by the provided key.
|
||||
*
|
||||
* @param publicKey public key of the creation key pair
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isCreatedBy(PGPPublicKey publicKey) {
|
||||
return isCreatedBy(publicKey.getKeyID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts signatures which were created by the public key with the provided key id.
|
||||
*
|
||||
* @param keyId key id
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isCreatedBy(long keyId) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -301,10 +447,21 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts signatures which are not expired RIGHT NOW.
|
||||
*
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isSigNotExpired() {
|
||||
return isSigNotExpired(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts signatures which are not expired at comparisonDate.
|
||||
*
|
||||
* @param comparisonDate comparison date
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isSigNotExpired(Date comparisonDate) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -314,6 +471,11 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts signatures which do not predate their signing key's creation date.
|
||||
*
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey doesNotPredateKeyCreationDate() {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -327,6 +489,12 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts signatures which do not predate the creation date of the provided key.
|
||||
*
|
||||
* @param creator key
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey doesNotPredateKeyCreationDate(PGPPublicKey creator) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -336,6 +504,12 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts signatures of the provided signature version.
|
||||
*
|
||||
* @param version signature version
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isVersion(int version) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -345,6 +519,12 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Criterion that only accepts signatures that are of the provided {@link SignatureType}.
|
||||
*
|
||||
* @param signatureType signature type that shall be accepted
|
||||
* @return criterion
|
||||
*/
|
||||
public static SelectSignatureFromKey isOfType(SignatureType signatureType) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -354,6 +534,13 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose different {@link SelectSignatureFromKey} by combining them with a logic AND.
|
||||
* A signature will only be accepted, iff it satisfies every selector from selectors.
|
||||
*
|
||||
* @param selectors one or more selectors
|
||||
* @return combined selector using AND operator
|
||||
*/
|
||||
public static SelectSignatureFromKey and(SelectSignatureFromKey... selectors) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -368,6 +555,13 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Compose different {@link SelectSignatureFromKey} by combining them with a logic OR.
|
||||
* A signature will only be accepted, iff it satisfies at least one selector from selectors.
|
||||
*
|
||||
* @param selectors one or more selectors
|
||||
* @return combined selector using OR operator
|
||||
*/
|
||||
public static SelectSignatureFromKey or(SelectSignatureFromKey... selectors) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
@ -381,6 +575,14 @@ public abstract class SelectSignatureFromKey {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Negate the result of a {@link SelectSignatureFromKey} implementations {@link #accept(PGPSignature, PGPPublicKey, PGPKeyRing)}.
|
||||
* The resulting {@link SelectSignatureFromKey} will only accept signatures that are rejected by the provided selector
|
||||
* and reject those that are accepted by it.
|
||||
*
|
||||
* @param selector selector whose logic operation will be negated
|
||||
* @return negated selector
|
||||
*/
|
||||
public static SelectSignatureFromKey not(SelectSignatureFromKey selector) {
|
||||
return new SelectSignatureFromKey() {
|
||||
@Override
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bouncycastle.bcpg.sig.SignerUserID;
|
||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
|
@ -35,11 +36,32 @@ import org.pgpainless.exception.SignatureValidationException;
|
|||
import org.pgpainless.policy.Policy;
|
||||
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
|
||||
|
||||
/**
|
||||
* This class implements validity checks on OpenPGP signatures.
|
||||
* Its responsibilities are checking if a signing key was eligible to create a certain signature
|
||||
* and if the signature is valid at the time of validation.
|
||||
*/
|
||||
public class SignatureChainValidator {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(SignatureChainValidator.class.getName());
|
||||
|
||||
public static boolean validateSigningKey(PGPSignature signature, PGPPublicKeyRing signingKeyRing, Policy policy, Date validationDate) throws SignatureValidationException {
|
||||
/**
|
||||
* Check if the signing key was eligible to create the provided signature.
|
||||
*
|
||||
* That entails:
|
||||
* - Check, if the primary key is being revoked via key-revocation signatures.
|
||||
* - Check, if the keys user-ids are revoked or not bound.
|
||||
* - Check, if the signing subkey is revoked or expired.
|
||||
* - Check, if the signing key is not capable of signing
|
||||
*
|
||||
* @param signature signature
|
||||
* @param signingKeyRing signing key ring
|
||||
* @param policy validation policy
|
||||
* @return true if the signing key was eligible to create the signature
|
||||
* @throws SignatureValidationException in case of a validation constraint violation
|
||||
*/
|
||||
public static boolean validateSigningKey(PGPSignature signature, PGPPublicKeyRing signingKeyRing, Policy policy)
|
||||
throws SignatureValidationException {
|
||||
|
||||
Map<PGPSignature, Exception> rejections = new ConcurrentHashMap<>();
|
||||
|
||||
|
@ -50,6 +72,7 @@ public class SignatureChainValidator {
|
|||
|
||||
PGPPublicKey primaryKey = signingKeyRing.getPublicKey();
|
||||
|
||||
// Key-Revocation Signatures
|
||||
List<PGPSignature> directKeySignatures = new ArrayList<>();
|
||||
Iterator<PGPSignature> primaryKeyRevocationIterator = primaryKey.getSignaturesOfType(SignatureType.KEY_REVOCATION.getCode());
|
||||
while (primaryKeyRevocationIterator.hasNext()) {
|
||||
|
@ -64,6 +87,7 @@ public class SignatureChainValidator {
|
|||
}
|
||||
}
|
||||
|
||||
// Direct-Key Signatures
|
||||
Iterator<PGPSignature> keySignatures = primaryKey.getSignaturesOfType(SignatureType.DIRECT_KEY.getCode());
|
||||
while (keySignatures.hasNext()) {
|
||||
PGPSignature keySignature = keySignatures.next();
|
||||
|
@ -78,14 +102,13 @@ public class SignatureChainValidator {
|
|||
}
|
||||
|
||||
Collections.sort(directKeySignatures, new SignatureValidityComparator(SignatureCreationDateComparator.Order.NEW_TO_OLD));
|
||||
if (directKeySignatures.isEmpty()) {
|
||||
|
||||
} else {
|
||||
if (!directKeySignatures.isEmpty()) {
|
||||
if (directKeySignatures.get(0).getSignatureType() == SignatureType.KEY_REVOCATION.getCode()) {
|
||||
throw new SignatureValidationException("Primary key has been revoked.");
|
||||
}
|
||||
}
|
||||
|
||||
// User-ID signatures (certifications, revocations)
|
||||
Iterator<String> userIds = primaryKey.getUserIDs();
|
||||
Map<String, List<PGPSignature>> userIdSignatures = new ConcurrentHashMap<>();
|
||||
while (userIds.hasNext()) {
|
||||
|
@ -107,23 +130,39 @@ public class SignatureChainValidator {
|
|||
userIdSignatures.put(userId, signaturesOnUserId);
|
||||
}
|
||||
|
||||
boolean userIdValid = false;
|
||||
boolean anyUserIdValid = false;
|
||||
for (String userId : userIdSignatures.keySet()) {
|
||||
if (!userIdSignatures.get(userId).isEmpty()) {
|
||||
PGPSignature current = userIdSignatures.get(userId).get(0);
|
||||
if (current.getSignatureType() == SignatureType.CERTIFICATION_REVOCATION.getCode()) {
|
||||
LOGGER.log(Level.FINE, "User-ID '" + userId + "' is revoked.");
|
||||
} else {
|
||||
userIdValid = true;
|
||||
anyUserIdValid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!userIdValid) {
|
||||
throw new SignatureValidationException("Key is not valid at this point.", rejections);
|
||||
if (!anyUserIdValid) {
|
||||
throw new SignatureValidationException("No valid user-id found.", rejections);
|
||||
}
|
||||
|
||||
if (signingSubkey != primaryKey) {
|
||||
// Specific signer user-id
|
||||
SignerUserID signerUserID = SignatureSubpacketsUtil.getSignerUserID(signature);
|
||||
if (signerUserID != null) {
|
||||
PGPSignature userIdSig = userIdSignatures.get(signerUserID.getID()).get(0);
|
||||
if (userIdSig.getSignatureType() == SignatureType.CERTIFICATION_REVOCATION.getCode()) {
|
||||
throw new SignatureValidationException("Signature was made with user-id '" + signerUserID.getID() + "' which is revoked.");
|
||||
}
|
||||
}
|
||||
|
||||
if (signingSubkey == primaryKey) {
|
||||
if (!directKeySignatures.isEmpty()) {
|
||||
if (KeyFlag.hasKeyFlag(SignatureSubpacketsUtil.getKeyFlags(directKeySignatures.get(0)).getFlags(), KeyFlag.SIGN_DATA)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} // Subkey Binding Signatures / Subkey Revocation Signatures
|
||||
else {
|
||||
List<PGPSignature> subkeySigs = new ArrayList<>();
|
||||
Iterator<PGPSignature> bindingRevocations = signingSubkey.getSignaturesOfType(SignatureType.SUBKEY_REVOCATION.getCode());
|
||||
while (bindingRevocations.hasNext()) {
|
||||
|
@ -168,14 +207,41 @@ public class SignatureChainValidator {
|
|||
return true;
|
||||
}
|
||||
|
||||
public static boolean validateSignatureChain(PGPSignature signature, InputStream signedData, PGPPublicKeyRing signingKeyRing, Policy policy, Date validationDate)
|
||||
/**
|
||||
* Validate the given signing key and then verify the given signature while parsing out the signed data.
|
||||
* Uninitialized means that no signed data has been read and the hash generators state has not yet been updated.
|
||||
*
|
||||
* @param signature uninitialized signature
|
||||
* @param signedData input stream containing signed data
|
||||
* @param signingKeyRing key ring containing signing key
|
||||
* @param policy validation policy
|
||||
* @param validationDate date of validation
|
||||
* @return true if the signature is valid, false otherwise
|
||||
* @throws SignatureValidationException for validation constraint violations
|
||||
*/
|
||||
public static boolean validateSignatureChain(PGPSignature signature,
|
||||
InputStream signedData,
|
||||
PGPPublicKeyRing signingKeyRing,
|
||||
Policy policy,
|
||||
Date validationDate)
|
||||
throws SignatureValidationException {
|
||||
validateSigningKey(signature, signingKeyRing, policy, validationDate);
|
||||
validateSigningKey(signature, signingKeyRing, policy);
|
||||
return SignatureValidator.verifyUninitializedSignature(signature, signedData, signingKeyRing.getPublicKey(signature.getKeyID()), policy, validationDate);
|
||||
}
|
||||
|
||||
public static boolean validateSignature(PGPSignature signature, PGPPublicKeyRing verificationKeys, Policy policy) throws SignatureValidationException {
|
||||
validateSigningKey(signature, verificationKeys, policy, signature.getCreationTime());
|
||||
/**
|
||||
* Validate the signing key and the given initialized signature.
|
||||
* Initialized means that the signatures hash generator has already been updated by reading the signed data completely.
|
||||
*
|
||||
* @param signature initialized signature
|
||||
* @param verificationKeys key ring containing the verification key
|
||||
* @param policy validation policy
|
||||
* @return true if the signature is valid, false otherwise
|
||||
* @throws SignatureValidationException in case of a validation constraint violation
|
||||
*/
|
||||
public static boolean validateSignature(PGPSignature signature, PGPPublicKeyRing verificationKeys, Policy policy)
|
||||
throws SignatureValidationException {
|
||||
validateSigningKey(signature, verificationKeys, policy);
|
||||
PGPPublicKey signingKey = verificationKeys.getPublicKey(signature.getKeyID());
|
||||
SignatureValidator.verifyInitializedSignature(signature, signingKey, policy, signature.getCreationTime());
|
||||
return true;
|
||||
|
|
|
@ -19,21 +19,38 @@ import java.util.Comparator;
|
|||
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
|
||||
/**
|
||||
* Comparator which can be used to sort signatures with regard to their creation time.
|
||||
*/
|
||||
public class SignatureCreationDateComparator implements Comparator<PGPSignature> {
|
||||
|
||||
public static final Order DEFAULT_ORDER = Order.OLD_TO_NEW;
|
||||
|
||||
public enum Order {
|
||||
/**
|
||||
* Oldest signatures first.
|
||||
*/
|
||||
OLD_TO_NEW,
|
||||
|
||||
/**
|
||||
* Newest signatures first.
|
||||
*/
|
||||
NEW_TO_OLD
|
||||
}
|
||||
|
||||
private final Order order;
|
||||
|
||||
/**
|
||||
* Create a new comparator which sorts signatures old to new.
|
||||
*/
|
||||
public SignatureCreationDateComparator() {
|
||||
this(DEFAULT_ORDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new comparator which sorts signatures according to the passed ordering.
|
||||
* @param order ordering
|
||||
*/
|
||||
public SignatureCreationDateComparator(Order order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ import org.bouncycastle.openpgp.PGPPublicKey;
|
|||
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
import org.bouncycastle.openpgp.PGPSignatureGenerator;
|
||||
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
|
||||
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
|
||||
import org.pgpainless.PGPainless;
|
||||
import org.pgpainless.algorithm.HashAlgorithm;
|
||||
import org.pgpainless.algorithm.SignatureType;
|
||||
|
@ -39,38 +39,89 @@ import org.pgpainless.implementation.ImplementationFactory;
|
|||
import org.pgpainless.key.util.KeyRingUtils;
|
||||
import org.pgpainless.key.util.OpenPgpKeyAttributeUtil;
|
||||
import org.pgpainless.key.util.RevocationAttributes;
|
||||
import org.pgpainless.policy.Policy;
|
||||
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
|
||||
|
||||
/**
|
||||
* Utility methods related to signatures.
|
||||
*/
|
||||
public class SignatureUtils {
|
||||
|
||||
/**
|
||||
* Return a signature generator for the provided signing key.
|
||||
* The signature generator will follow the hash algorithm preferences of the signing key and pick the best algorithm.
|
||||
*
|
||||
* @param singingKey signing key
|
||||
* @return signature generator
|
||||
*/
|
||||
public static PGPSignatureGenerator getSignatureGeneratorFor(PGPSecretKey singingKey) {
|
||||
return getSignatureGeneratorFor(singingKey.getPublicKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a signature generator for the provided signing key.
|
||||
* The signature generator will follow the hash algorithm preferences of the signing key and pick the best algorithm.
|
||||
*
|
||||
* @param signingPubKey signing key
|
||||
* @return signature generator
|
||||
*/
|
||||
public static PGPSignatureGenerator getSignatureGeneratorFor(PGPPublicKey signingPubKey) {
|
||||
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
|
||||
getPgpContentSignerBuilderForKey(signingPubKey));
|
||||
return signatureGenerator;
|
||||
}
|
||||
|
||||
private static BcPGPContentSignerBuilder getPgpContentSignerBuilderForKey(PGPPublicKey publicKey) {
|
||||
/**
|
||||
* Return a content signer builder fot the passed public key.
|
||||
*
|
||||
* The content signer will use a hash algorithm derived from the keys algorithm preferences.
|
||||
* If no preferences can be derived, the key will fall back to the default hash algorithm as set in
|
||||
* the {@link org.pgpainless.policy.Policy}.
|
||||
*
|
||||
* @param publicKey public key
|
||||
* @return content signer builder
|
||||
*/
|
||||
private static PGPContentSignerBuilder getPgpContentSignerBuilderForKey(PGPPublicKey publicKey) {
|
||||
List<HashAlgorithm> preferredHashAlgorithms = OpenPgpKeyAttributeUtil.getPreferredHashAlgorithms(publicKey);
|
||||
if (preferredHashAlgorithms.isEmpty()) {
|
||||
preferredHashAlgorithms = OpenPgpKeyAttributeUtil.guessPreferredHashAlgorithms(publicKey);
|
||||
}
|
||||
HashAlgorithm hashAlgorithm = negotiateHashAlgorithm(preferredHashAlgorithms);
|
||||
|
||||
return new BcPGPContentSignerBuilder(publicKey.getAlgorithm(), hashAlgorithm.getAlgorithmId());
|
||||
return ImplementationFactory.getInstance().getPGPContentSignerBuilder(publicKey.getAlgorithm(), hashAlgorithm.getAlgorithmId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Negotiate an acceptable hash algorithm from the provided list of options.
|
||||
* Acceptance of hash algorithms can be changed by setting a custom {@link Policy}.
|
||||
*
|
||||
* @param preferredHashAlgorithms list of preferred hash algorithms of a key
|
||||
* @return first acceptable algorithm, or policies default hash algorithm
|
||||
*/
|
||||
private static HashAlgorithm negotiateHashAlgorithm(List<HashAlgorithm> preferredHashAlgorithms) {
|
||||
if (preferredHashAlgorithms.isEmpty()) {
|
||||
return PGPainless.getPolicy().getSignatureHashAlgorithmPolicy().defaultHashAlgorithm();
|
||||
Policy policy = PGPainless.getPolicy();
|
||||
for (HashAlgorithm option : preferredHashAlgorithms) {
|
||||
if (policy.getSignatureHashAlgorithmPolicy().isAcceptable(option)) {
|
||||
return option;
|
||||
}
|
||||
}
|
||||
return preferredHashAlgorithms.get(0);
|
||||
|
||||
return PGPainless.getPolicy().getSignatureHashAlgorithmPolicy().defaultHashAlgorithm();
|
||||
}
|
||||
|
||||
public static PGPSignature getLatestValidSignature(PGPPublicKey publicKey, List<PGPSignature> signatures, PGPKeyRing keyRing) throws PGPException {
|
||||
/**
|
||||
* Return the latest valid signature on the provided public key.
|
||||
*
|
||||
* @param publicKey signed key
|
||||
* @param signatures signatures
|
||||
* @param keyRing key ring containing signature creator key
|
||||
* @return latest valid signature
|
||||
* @throws PGPException in case of a validation error
|
||||
*/
|
||||
public static PGPSignature getLatestValidSignature(PGPPublicKey publicKey,
|
||||
List<PGPSignature> signatures,
|
||||
PGPKeyRing keyRing)
|
||||
throws PGPException {
|
||||
List<PGPSignature> valid = new ArrayList<>();
|
||||
for (PGPSignature signature : signatures) {
|
||||
long issuerID = signature.getKeyID();
|
||||
|
@ -93,6 +144,16 @@ public class SignatureUtils {
|
|||
return valid.isEmpty() ? null : valid.get(valid.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true, iff a signature is valid.
|
||||
*
|
||||
* TODO: There is code duplication here ({@link SelectSignatureFromKey}, {@link SignatureChainValidator}, {@link SignatureValidator}).
|
||||
* @param signature signature to validate
|
||||
* @param issuer signing key
|
||||
* @param target signed key
|
||||
* @return true if signature is valid
|
||||
* @throws PGPException if a validation error occurs.
|
||||
*/
|
||||
public static boolean isSignatureValid(PGPSignature signature, PGPPublicKey issuer, PGPPublicKey target) throws PGPException {
|
||||
SignatureType signatureType = SignatureType.valueOf(signature.getSignatureType());
|
||||
switch (signatureType) {
|
||||
|
@ -276,6 +337,15 @@ public class SignatureUtils {
|
|||
return latestSelfSig.getSignatureType() != SignatureType.CERTIFICATION_REVOCATION.getCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the provided signature is a hard revocation.
|
||||
* Hard revocations are revocation signatures which either carry a revocation reason of
|
||||
* {@link RevocationAttributes.Reason#KEY_COMPROMISED} or {@link RevocationAttributes.Reason#NO_REASON},
|
||||
* or no reason at all.
|
||||
*
|
||||
* @param signature signature
|
||||
* @return true if signature is a hard revocation
|
||||
*/
|
||||
public static boolean isHardRevocation(PGPSignature signature) {
|
||||
|
||||
SignatureType type = SignatureType.valueOf(signature.getSignatureType());
|
||||
|
|
|
@ -19,15 +19,30 @@ import java.util.Comparator;
|
|||
|
||||
import org.bouncycastle.openpgp.PGPSignature;
|
||||
|
||||
/**
|
||||
* Comparator which sorts signatures based on an ordering and on revocation hardness.
|
||||
*
|
||||
* If a list of signatures gets ordered using this comparator, hard revocations will always
|
||||
* come first.
|
||||
* Further, signatures are ordered by date according to the {@link org.pgpainless.signature.SignatureCreationDateComparator.Order}.
|
||||
*/
|
||||
public class SignatureValidityComparator implements Comparator<PGPSignature> {
|
||||
|
||||
private final SignatureCreationDateComparator.Order order;
|
||||
private final SignatureCreationDateComparator creationDateComparator;
|
||||
|
||||
/**
|
||||
* Create a new {@link SignatureValidityComparator} which orders signatures oldest first.
|
||||
* Still, hard revocations will come first.
|
||||
*/
|
||||
public SignatureValidityComparator() {
|
||||
this(SignatureCreationDateComparator.DEFAULT_ORDER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SignatureValidityComparator} which orders signatures following the passed ordering.
|
||||
* Still, hard revocations will come first.
|
||||
*/
|
||||
public SignatureValidityComparator(SignatureCreationDateComparator.Order order) {
|
||||
this.order = order;
|
||||
this.creationDateComparator = new SignatureCreationDateComparator(order);
|
||||
|
|
|
@ -31,6 +31,15 @@ import org.pgpainless.algorithm.KeyFlag;
|
|||
*/
|
||||
public class SignatureSubpacketGeneratorUtil {
|
||||
|
||||
/**
|
||||
* Return a list of {@link SignatureSubpacket SignatureSubpackets} from the subpacket generator, which correspond
|
||||
* to the given {@link org.pgpainless.algorithm.SignatureSubpacket} type.
|
||||
*
|
||||
* @param type subpacket type
|
||||
* @param generator subpacket generator
|
||||
* @param <P> generic subpacket type
|
||||
* @return possibly empty list of subpackets
|
||||
*/
|
||||
public static <P extends SignatureSubpacket> List<P> getSubpacketsOfType(org.pgpainless.algorithm.SignatureSubpacket type,
|
||||
PGPSignatureSubpacketGenerator generator) {
|
||||
SignatureSubpacket[] subpackets = generator.getSubpackets(type.getCode());
|
||||
|
@ -41,11 +50,25 @@ public class SignatureSubpacketGeneratorUtil {
|
|||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all packets of the given type from the {@link PGPSignatureSubpacketGenerator PGPSignatureSubpacketGenerators}
|
||||
* internal set.
|
||||
*
|
||||
* @param subpacketType type of subpacket to remove
|
||||
* @param subpacketGenerator subpacket generator
|
||||
*/
|
||||
public static void removeAllPacketsOfType(org.pgpainless.algorithm.SignatureSubpacket subpacketType,
|
||||
PGPSignatureSubpacketGenerator subpacketGenerator) {
|
||||
removeAllPacketsOfType(subpacketType.getCode(), subpacketGenerator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all packets of the given type from the {@link PGPSignatureSubpacketGenerator PGPSignatureSubpacketGenerators}
|
||||
* internal set.
|
||||
*
|
||||
* @param type type of subpacket to remove
|
||||
* @param subpacketGenerator subpacket generator
|
||||
*/
|
||||
public static void removeAllPacketsOfType(int type, PGPSignatureSubpacketGenerator subpacketGenerator) {
|
||||
for (SignatureSubpacket subpacket : subpacketGenerator.getSubpackets(type)) {
|
||||
subpacketGenerator.removePacket(subpacket);
|
||||
|
@ -98,11 +121,22 @@ public class SignatureSubpacketGeneratorUtil {
|
|||
return secondsToExpire;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true, if the subpacket generator has a {@link KeyFlags} subpacket which carries the given key flag.
|
||||
* Returns false, if no {@link KeyFlags} subpacket is present.
|
||||
* If there are more than one instance of a {@link KeyFlags} packet present, only the last occurrence will
|
||||
* be tested.
|
||||
*
|
||||
* @param keyFlag flag to test for
|
||||
* @param generator subpackets generator
|
||||
* @return true if the generator has the given key flag set
|
||||
*/
|
||||
public static boolean hasKeyFlag(KeyFlag keyFlag, PGPSignatureSubpacketGenerator generator) {
|
||||
List<KeyFlags> keyFlagPackets = getSubpacketsOfType(org.pgpainless.algorithm.SignatureSubpacket.keyFlags, generator);
|
||||
if (keyFlagPackets.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return KeyFlag.hasKeyFlag(keyFlagPackets.get(0).getFlags(), keyFlag);
|
||||
KeyFlags last = keyFlagPackets.get(keyFlagPackets.size() - 1);
|
||||
return KeyFlag.hasKeyFlag(last.getFlags(), keyFlag);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ import org.bouncycastle.openpgp.PGPSignatureSubpacketVector;
|
|||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.pgpainless.algorithm.SignatureSubpacket;
|
||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||
import org.pgpainless.signature.SignatureUtils;
|
||||
|
||||
/**
|
||||
* Utility class to access signature subpackets from signatures.
|
||||
|
@ -67,6 +68,13 @@ public class SignatureSubpacketsUtil {
|
|||
return hashedOrUnhashed(signature, SignatureSubpacket.issuerFingerprint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link IssuerFingerprint} subpacket of the signature into a {@link OpenPgpV4Fingerprint}.
|
||||
* If no issuer fingerprint is present in the signature, return null.
|
||||
*
|
||||
* @param signature signature
|
||||
* @return v4 fingerprint of the issuer, or null
|
||||
*/
|
||||
public static OpenPgpV4Fingerprint getIssuerFingerprintAsOpenPgpV4Fingerprint(PGPSignature signature) {
|
||||
IssuerFingerprint subpacket = getIssuerFingerprint(signature);
|
||||
if (subpacket == null) {
|
||||
|
@ -123,12 +131,20 @@ public class SignatureSubpacketsUtil {
|
|||
return hashed(signature, SignatureSubpacket.signatureExpirationTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the signatures expiration time as a date.
|
||||
* The expiration date is computed by adding the expiration time to the signature creation date.
|
||||
* If the signature has no expiration time subpacket, or the expiration time is set to '0', this message returns null.
|
||||
*
|
||||
* @param signature signature
|
||||
* @return expiration time as date
|
||||
*/
|
||||
public static Date getSignatureExpirationTimeAsDate(PGPSignature signature) {
|
||||
SignatureExpirationTime subpacket = getSignatureExpirationTime(signature);
|
||||
if (subpacket == null || subpacket.getTime() == 0) {
|
||||
if (subpacket == null) {
|
||||
return null;
|
||||
}
|
||||
return new Date(signature.getCreationTime().getTime() + 1000 * subpacket.getTime());
|
||||
return SignatureUtils.datePlusSeconds(signature.getCreationTime(), subpacket.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,15 +158,25 @@ public class SignatureSubpacketsUtil {
|
|||
return hashed(signature, SignatureSubpacket.keyExpirationTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the signatures key-expiration time as a date.
|
||||
* The expiration date is computed by adding the signatures' key-expiration time to the signing keys
|
||||
* creation date.
|
||||
* If the signature does not have a key-expiration time subpacket, or its value is '0', this method returns null.
|
||||
*
|
||||
* @param signature self-signature carrying the key-expiration time subpacket
|
||||
* @param signingKey signature creation key
|
||||
* @return key expiration time as date
|
||||
*/
|
||||
public static Date getKeyExpirationTimeAsDate(PGPSignature signature, PGPPublicKey signingKey) {
|
||||
KeyExpirationTime subpacket = getKeyExpirationTime(signature);
|
||||
if (subpacket == null || subpacket.getTime() == 0) {
|
||||
if (subpacket == null) {
|
||||
return null;
|
||||
}
|
||||
if (signature.getKeyID() != signingKey.getKeyID()) {
|
||||
throw new IllegalArgumentException("Provided key (" + Long.toHexString(signingKey.getKeyID()) + ") did not create the signature (" + Long.toHexString(signature.getKeyID()) + ")");
|
||||
}
|
||||
return new Date(signingKey.getCreationTime().getTime() + 1000 * subpacket.getTime());
|
||||
return SignatureUtils.datePlusSeconds(signingKey.getCreationTime(), subpacket.getTime());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -328,14 +354,41 @@ public class SignatureSubpacketsUtil {
|
|||
return hashed(signature, SignatureSubpacket.trustSignature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a list of all signature subpackets of the given type, which are present in the hashed area of
|
||||
* the given signature.
|
||||
*
|
||||
* @param signature signature
|
||||
* @param type subpacket type
|
||||
* @param <P> generic subpacket type
|
||||
* @return list of subpackets from the hashed area
|
||||
*/
|
||||
private static <P extends org.bouncycastle.bcpg.SignatureSubpacket> P hashed(PGPSignature signature, SignatureSubpacket type) {
|
||||
return getSignatureSubpacket(signature.getHashedSubPackets(), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a list of all signature subpackets of the given type, which are present in the unhashed area of
|
||||
* the given signature.
|
||||
*
|
||||
* @param signature signature
|
||||
* @param type subpacket type
|
||||
* @param <P> generic subpacket type
|
||||
* @return list of subpackets from the unhashed area
|
||||
*/
|
||||
private static <P extends org.bouncycastle.bcpg.SignatureSubpacket> P unhashed(PGPSignature signature, SignatureSubpacket type) {
|
||||
return getSignatureSubpacket(signature.getUnhashedSubPackets(), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a list of all signature subpackets of the given type, which are present in either the hashed
|
||||
* or the unhashed area of the given signature.
|
||||
*
|
||||
* @param signature signature
|
||||
* @param type subpacket type
|
||||
* @param <P> generic subpacket type
|
||||
* @return list of subpackets from the hashed/unhashed area
|
||||
*/
|
||||
private static <P extends org.bouncycastle.bcpg.SignatureSubpacket> P hashedOrUnhashed(PGPSignature signature, SignatureSubpacket type) {
|
||||
P hashedSubpacket = hashed(signature, type);
|
||||
return hashedSubpacket != null ? hashedSubpacket : unhashed(signature, type);
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
*/
|
||||
package org.pgpainless.util.selection.signature;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
|
Loading…
Reference in a new issue