mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-23 12:52:07 +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);
|
signatureStructureIsAcceptable(signingKey, policy).verify(signature);
|
||||||
signatureIsEffective(new Date()).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) {
|
} catch (SignatureValidationException e) {
|
||||||
throw new SignatureException("Signature key is not valid.", 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.OpenPgpV4Fingerprint;
|
||||||
import org.pgpainless.key.SubkeyIdentifier;
|
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 {
|
public class DetachedSignature {
|
||||||
private final PGPSignature signature;
|
private final PGPSignature signature;
|
||||||
private final PGPKeyRing signingKeyRing;
|
private final PGPKeyRing signingKeyRing;
|
||||||
private final SubkeyIdentifier signingKeyIdentifier;
|
private final SubkeyIdentifier signingKeyIdentifier;
|
||||||
private boolean verified;
|
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) {
|
public DetachedSignature(PGPSignature signature, PGPKeyRing signingKeyRing, SubkeyIdentifier signingKeyIdentifier) {
|
||||||
this.signature = signature;
|
this.signature = signature;
|
||||||
this.signingKeyRing = signingKeyRing;
|
this.signingKeyRing = signingKeyRing;
|
||||||
this.signingKeyIdentifier = signingKeyIdentifier;
|
this.signingKeyIdentifier = signingKeyIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark this {@link DetachedSignature} as verified.
|
||||||
|
*
|
||||||
|
* @param verified verified
|
||||||
|
*/
|
||||||
public void setVerified(boolean verified) {
|
public void setVerified(boolean verified) {
|
||||||
this.verified = verified;
|
this.verified = verified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true iff the signature is verified.
|
||||||
|
*
|
||||||
|
* @return verified
|
||||||
|
*/
|
||||||
public boolean isVerified() {
|
public boolean isVerified() {
|
||||||
return verified;
|
return verified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the OpenPGP signature.
|
||||||
|
*
|
||||||
|
* @return signature
|
||||||
|
*/
|
||||||
public PGPSignature getSignature() {
|
public PGPSignature getSignature() {
|
||||||
return signature;
|
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() {
|
public SubkeyIdentifier getSigningKeyIdentifier() {
|
||||||
return signingKeyIdentifier;
|
return signingKeyIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the key ring that contains the signing key that created this signature.
|
||||||
|
*
|
||||||
|
* @return key ring
|
||||||
|
*/
|
||||||
public PGPKeyRing getSigningKeyRing() {
|
public PGPKeyRing getSigningKeyRing() {
|
||||||
return signingKeyRing;
|
return signingKeyRing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the {@link OpenPgpV4Fingerprint} of the key that created the signature.
|
||||||
|
*
|
||||||
|
* @return fingerprint of the signing key
|
||||||
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public OpenPgpV4Fingerprint getFingerprint() {
|
public OpenPgpV4Fingerprint getFingerprint() {
|
||||||
return signingKeyIdentifier.getSubkeyFingerprint();
|
return signingKeyIdentifier.getSubkeyFingerprint();
|
||||||
|
|
|
@ -21,29 +21,65 @@ import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
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 {
|
public class OnePassSignature {
|
||||||
private final PGPOnePassSignature onePassSignature;
|
private final PGPOnePassSignature onePassSignature;
|
||||||
private final PGPPublicKeyRing verificationKeys;
|
private final PGPPublicKeyRing verificationKeys;
|
||||||
private PGPSignature signature;
|
private PGPSignature signature;
|
||||||
private boolean verified;
|
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) {
|
public OnePassSignature(PGPOnePassSignature onePassSignature, PGPPublicKeyRing verificationKeys) {
|
||||||
this.onePassSignature = onePassSignature;
|
this.onePassSignature = onePassSignature;
|
||||||
this.verificationKeys = verificationKeys;
|
this.verificationKeys = verificationKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the signature is verified.
|
||||||
|
*
|
||||||
|
* @return verified
|
||||||
|
*/
|
||||||
public boolean isVerified() {
|
public boolean isVerified() {
|
||||||
return verified;
|
return verified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the {@link PGPOnePassSignature} object.
|
||||||
|
*
|
||||||
|
* @return onePassSignature
|
||||||
|
*/
|
||||||
public PGPOnePassSignature getOnePassSignature() {
|
public PGPOnePassSignature getOnePassSignature() {
|
||||||
return onePassSignature;
|
return onePassSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the {@link OpenPgpV4Fingerprint} of the signing key.
|
||||||
|
*
|
||||||
|
* @return signing key fingerprint
|
||||||
|
*/
|
||||||
public OpenPgpV4Fingerprint getFingerprint() {
|
public OpenPgpV4Fingerprint getFingerprint() {
|
||||||
return new OpenPgpV4Fingerprint(verificationKeys.getPublicKey(onePassSignature.getKeyID()));
|
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 {
|
public boolean verify(PGPSignature signature) throws PGPException {
|
||||||
this.verified = getOnePassSignature().verify(signature);
|
this.verified = getOnePassSignature().verify(signature);
|
||||||
if (verified) {
|
if (verified) {
|
||||||
|
@ -52,10 +88,20 @@ public class OnePassSignature {
|
||||||
return verified;
|
return verified;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the signature.
|
||||||
|
*
|
||||||
|
* @return signature
|
||||||
|
*/
|
||||||
public PGPSignature getSignature() {
|
public PGPSignature getSignature() {
|
||||||
return signature;
|
return signature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the key ring used to verify the signature.
|
||||||
|
*
|
||||||
|
* @return verification keys
|
||||||
|
*/
|
||||||
public PGPPublicKeyRing getVerificationKeys() {
|
public PGPPublicKeyRing getVerificationKeys() {
|
||||||
return verificationKeys;
|
return verificationKeys;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,20 +31,18 @@ import org.pgpainless.algorithm.SignatureType;
|
||||||
import org.pgpainless.implementation.ImplementationFactory;
|
import org.pgpainless.implementation.ImplementationFactory;
|
||||||
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
|
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 {
|
public abstract class SelectSignatureFromKey {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(SelectSignatureFromKey.class.getName());
|
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 abstract boolean accept(PGPSignature signature, PGPPublicKey key, PGPKeyRing keyRing);
|
||||||
|
|
||||||
public List<PGPSignature> select(List<PGPSignature> signatures, 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;
|
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) {
|
public static SelectSignatureFromKey isValidSubkeyBindingSignature(PGPPublicKey primaryKey, PGPPublicKey subkey) {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -70,18 +98,22 @@ public abstract class SelectSignatureFromKey {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean subkeyBindingSigValid;
|
if (!isSigNotExpired().accept(signature, subkey, keyRing)) {
|
||||||
try {
|
LOGGER.log(Level.INFO, "Subkey binding signature expired.");
|
||||||
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);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check signature correctness
|
||||||
|
try {
|
||||||
|
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), primaryKey);
|
||||||
|
boolean subkeyBindingSigValid = signature.verifyCertification(primaryKey, subkey);
|
||||||
if (!subkeyBindingSigValid) {
|
if (!subkeyBindingSigValid) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} catch (PGPException e) {
|
||||||
|
LOGGER.log(Level.INFO, "Verification of subkey binding signature failed.", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
List<KeyFlag> flags = KeyFlag.fromBitmask(signature.getHashedSubPackets().getKeyFlags());
|
List<KeyFlag> flags = KeyFlag.fromBitmask(signature.getHashedSubPackets().getKeyFlags());
|
||||||
boolean isSigningKey = flags.contains(KeyFlag.SIGN_DATA) || flags.contains(KeyFlag.CERTIFY_OTHER);
|
boolean isSigningKey = flags.contains(KeyFlag.SIGN_DATA) || flags.contains(KeyFlag.CERTIFY_OTHER);
|
||||||
|
@ -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) {
|
public static SelectSignatureFromKey isValidPrimaryKeyBindingSignature(PGPPublicKey subkey, PGPPublicKey primaryKey) {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -118,6 +157,7 @@ public abstract class SelectSignatureFromKey {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check signature correctness
|
||||||
try {
|
try {
|
||||||
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), subkey);
|
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), subkey);
|
||||||
return signature.verifyCertification(primaryKey, 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) {
|
public static SelectSignatureFromKey hasValidPrimaryKeyBindingSignatureSubpacket(PGPPublicKey subkey, PGPPublicKey primaryKey) {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@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) {
|
public static SelectSignatureFromKey isValidDirectKeySignature(PGPPublicKey signer, PGPPublicKey signee) {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -161,6 +215,7 @@ public abstract class SelectSignatureFromKey {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check signature correctness
|
||||||
try {
|
try {
|
||||||
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signer);
|
signature.init(ImplementationFactory.getInstance().getPGPContentVerifierBuilderProvider(), signer);
|
||||||
return signature.verifyCertification(signee);
|
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) {
|
public static SelectSignatureFromKey isValidKeyRevocationSignature(PGPPublicKey key) {
|
||||||
return and(
|
return and(
|
||||||
isVersion4Signature(),
|
isVersion4Signature(),
|
||||||
|
@ -182,6 +243,11 @@ public abstract class SelectSignatureFromKey {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Criterion that only accepts valid subkey revocation signatures.
|
||||||
|
*
|
||||||
|
* @return criterion
|
||||||
|
*/
|
||||||
public static SelectSignatureFromKey isValidSubkeyRevocationSignature() {
|
public static SelectSignatureFromKey isValidSubkeyRevocationSignature() {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@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) {
|
public static SelectSignatureFromKey isValidSubkeyRevocationSignature(PGPPublicKey subkey, PGPPublicKey primaryKey) {
|
||||||
return SelectSignatureFromKey.and(
|
return SelectSignatureFromKey.and(
|
||||||
isVersion4Signature(),
|
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) {
|
public static SelectSignatureFromKey isValidCertificationRevocationSignature(PGPPublicKey revoker, String userId) {
|
||||||
return and(
|
return and(
|
||||||
isVersion4Signature(),
|
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) {
|
public static SelectSignatureFromKey isValidSignatureOnUserId(String userId, PGPPublicKey signingKey) {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@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) {
|
public static SelectSignatureFromKey isVerifyingSignatureOnKey(PGPPublicKey target, PGPPublicKey signer) {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@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) {
|
public static SelectSignatureFromKey isVerifyingSignatureOnKeys(PGPPublicKey primaryKey, PGPPublicKey subkey, PGPPublicKey signingKey) {
|
||||||
if (signingKey.getKeyID() != primaryKey.getKeyID() && signingKey.getKeyID() != subkey.getKeyID()) {
|
if (signingKey.getKeyID() != primaryKey.getKeyID() && signingKey.getKeyID() != subkey.getKeyID()) {
|
||||||
throw new IllegalArgumentException("Signing key MUST be either the primary or subkey.");
|
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() {
|
public static SelectSignatureFromKey isCertification() {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@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() {
|
public static SelectSignatureFromKey isWellFormed() {
|
||||||
return and(
|
return and(
|
||||||
hasCreationTimeSubpacket(),
|
hasCreationTimeSubpacket(),
|
||||||
|
@ -275,10 +398,21 @@ public abstract class SelectSignatureFromKey {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Criterion that only accepts v4 signatures.
|
||||||
|
*
|
||||||
|
* @return criterion
|
||||||
|
*/
|
||||||
public static SelectSignatureFromKey isVersion4Signature() {
|
public static SelectSignatureFromKey isVersion4Signature() {
|
||||||
return isVersion(4);
|
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() {
|
public static SelectSignatureFromKey hasCreationTimeSubpacket() {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@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) {
|
public static SelectSignatureFromKey isCreatedBy(PGPPublicKey publicKey) {
|
||||||
return isCreatedBy(publicKey.getKeyID());
|
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) {
|
public static SelectSignatureFromKey isCreatedBy(long keyId) {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@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() {
|
public static SelectSignatureFromKey isSigNotExpired() {
|
||||||
return isSigNotExpired(new Date());
|
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) {
|
public static SelectSignatureFromKey isSigNotExpired(Date comparisonDate) {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@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() {
|
public static SelectSignatureFromKey doesNotPredateKeyCreationDate() {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@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) {
|
public static SelectSignatureFromKey doesNotPredateKeyCreationDate(PGPPublicKey creator) {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@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) {
|
public static SelectSignatureFromKey isVersion(int version) {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@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) {
|
public static SelectSignatureFromKey isOfType(SignatureType signatureType) {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@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) {
|
public static SelectSignatureFromKey and(SelectSignatureFromKey... selectors) {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@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) {
|
public static SelectSignatureFromKey or(SelectSignatureFromKey... selectors) {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@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) {
|
public static SelectSignatureFromKey not(SelectSignatureFromKey selector) {
|
||||||
return new SelectSignatureFromKey() {
|
return new SelectSignatureFromKey() {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -26,6 +26,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.bouncycastle.bcpg.sig.SignerUserID;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
|
@ -35,11 +36,32 @@ import org.pgpainless.exception.SignatureValidationException;
|
||||||
import org.pgpainless.policy.Policy;
|
import org.pgpainless.policy.Policy;
|
||||||
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
|
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 {
|
public class SignatureChainValidator {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(SignatureChainValidator.class.getName());
|
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<>();
|
Map<PGPSignature, Exception> rejections = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
@ -50,6 +72,7 @@ public class SignatureChainValidator {
|
||||||
|
|
||||||
PGPPublicKey primaryKey = signingKeyRing.getPublicKey();
|
PGPPublicKey primaryKey = signingKeyRing.getPublicKey();
|
||||||
|
|
||||||
|
// Key-Revocation Signatures
|
||||||
List<PGPSignature> directKeySignatures = new ArrayList<>();
|
List<PGPSignature> directKeySignatures = new ArrayList<>();
|
||||||
Iterator<PGPSignature> primaryKeyRevocationIterator = primaryKey.getSignaturesOfType(SignatureType.KEY_REVOCATION.getCode());
|
Iterator<PGPSignature> primaryKeyRevocationIterator = primaryKey.getSignaturesOfType(SignatureType.KEY_REVOCATION.getCode());
|
||||||
while (primaryKeyRevocationIterator.hasNext()) {
|
while (primaryKeyRevocationIterator.hasNext()) {
|
||||||
|
@ -64,6 +87,7 @@ public class SignatureChainValidator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Direct-Key Signatures
|
||||||
Iterator<PGPSignature> keySignatures = primaryKey.getSignaturesOfType(SignatureType.DIRECT_KEY.getCode());
|
Iterator<PGPSignature> keySignatures = primaryKey.getSignaturesOfType(SignatureType.DIRECT_KEY.getCode());
|
||||||
while (keySignatures.hasNext()) {
|
while (keySignatures.hasNext()) {
|
||||||
PGPSignature keySignature = keySignatures.next();
|
PGPSignature keySignature = keySignatures.next();
|
||||||
|
@ -78,14 +102,13 @@ public class SignatureChainValidator {
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.sort(directKeySignatures, new SignatureValidityComparator(SignatureCreationDateComparator.Order.NEW_TO_OLD));
|
Collections.sort(directKeySignatures, new SignatureValidityComparator(SignatureCreationDateComparator.Order.NEW_TO_OLD));
|
||||||
if (directKeySignatures.isEmpty()) {
|
if (!directKeySignatures.isEmpty()) {
|
||||||
|
|
||||||
} else {
|
|
||||||
if (directKeySignatures.get(0).getSignatureType() == SignatureType.KEY_REVOCATION.getCode()) {
|
if (directKeySignatures.get(0).getSignatureType() == SignatureType.KEY_REVOCATION.getCode()) {
|
||||||
throw new SignatureValidationException("Primary key has been revoked.");
|
throw new SignatureValidationException("Primary key has been revoked.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// User-ID signatures (certifications, revocations)
|
||||||
Iterator<String> userIds = primaryKey.getUserIDs();
|
Iterator<String> userIds = primaryKey.getUserIDs();
|
||||||
Map<String, List<PGPSignature>> userIdSignatures = new ConcurrentHashMap<>();
|
Map<String, List<PGPSignature>> userIdSignatures = new ConcurrentHashMap<>();
|
||||||
while (userIds.hasNext()) {
|
while (userIds.hasNext()) {
|
||||||
|
@ -107,23 +130,39 @@ public class SignatureChainValidator {
|
||||||
userIdSignatures.put(userId, signaturesOnUserId);
|
userIdSignatures.put(userId, signaturesOnUserId);
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean userIdValid = false;
|
boolean anyUserIdValid = false;
|
||||||
for (String userId : userIdSignatures.keySet()) {
|
for (String userId : userIdSignatures.keySet()) {
|
||||||
if (!userIdSignatures.get(userId).isEmpty()) {
|
if (!userIdSignatures.get(userId).isEmpty()) {
|
||||||
PGPSignature current = userIdSignatures.get(userId).get(0);
|
PGPSignature current = userIdSignatures.get(userId).get(0);
|
||||||
if (current.getSignatureType() == SignatureType.CERTIFICATION_REVOCATION.getCode()) {
|
if (current.getSignatureType() == SignatureType.CERTIFICATION_REVOCATION.getCode()) {
|
||||||
LOGGER.log(Level.FINE, "User-ID '" + userId + "' is revoked.");
|
LOGGER.log(Level.FINE, "User-ID '" + userId + "' is revoked.");
|
||||||
} else {
|
} else {
|
||||||
userIdValid = true;
|
anyUserIdValid = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!userIdValid) {
|
if (!anyUserIdValid) {
|
||||||
throw new SignatureValidationException("Key is not valid at this point.", rejections);
|
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<>();
|
List<PGPSignature> subkeySigs = new ArrayList<>();
|
||||||
Iterator<PGPSignature> bindingRevocations = signingSubkey.getSignaturesOfType(SignatureType.SUBKEY_REVOCATION.getCode());
|
Iterator<PGPSignature> bindingRevocations = signingSubkey.getSignaturesOfType(SignatureType.SUBKEY_REVOCATION.getCode());
|
||||||
while (bindingRevocations.hasNext()) {
|
while (bindingRevocations.hasNext()) {
|
||||||
|
@ -168,14 +207,41 @@ public class SignatureChainValidator {
|
||||||
return true;
|
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 {
|
throws SignatureValidationException {
|
||||||
validateSigningKey(signature, signingKeyRing, policy, validationDate);
|
validateSigningKey(signature, signingKeyRing, policy);
|
||||||
return SignatureValidator.verifyUninitializedSignature(signature, signedData, signingKeyRing.getPublicKey(signature.getKeyID()), policy, validationDate);
|
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());
|
PGPPublicKey signingKey = verificationKeys.getPublicKey(signature.getKeyID());
|
||||||
SignatureValidator.verifyInitializedSignature(signature, signingKey, policy, signature.getCreationTime());
|
SignatureValidator.verifyInitializedSignature(signature, signingKey, policy, signature.getCreationTime());
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -19,21 +19,38 @@ import java.util.Comparator;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
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 class SignatureCreationDateComparator implements Comparator<PGPSignature> {
|
||||||
|
|
||||||
public static final Order DEFAULT_ORDER = Order.OLD_TO_NEW;
|
public static final Order DEFAULT_ORDER = Order.OLD_TO_NEW;
|
||||||
|
|
||||||
public enum Order {
|
public enum Order {
|
||||||
|
/**
|
||||||
|
* Oldest signatures first.
|
||||||
|
*/
|
||||||
OLD_TO_NEW,
|
OLD_TO_NEW,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Newest signatures first.
|
||||||
|
*/
|
||||||
NEW_TO_OLD
|
NEW_TO_OLD
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Order order;
|
private final Order order;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new comparator which sorts signatures old to new.
|
||||||
|
*/
|
||||||
public SignatureCreationDateComparator() {
|
public SignatureCreationDateComparator() {
|
||||||
this(DEFAULT_ORDER);
|
this(DEFAULT_ORDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new comparator which sorts signatures according to the passed ordering.
|
||||||
|
* @param order ordering
|
||||||
|
*/
|
||||||
public SignatureCreationDateComparator(Order order) {
|
public SignatureCreationDateComparator(Order order) {
|
||||||
this.order = order;
|
this.order = order;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ import org.bouncycastle.openpgp.PGPPublicKey;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
import org.bouncycastle.openpgp.PGPSecretKey;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
import org.bouncycastle.openpgp.PGPSignatureGenerator;
|
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.PGPainless;
|
||||||
import org.pgpainless.algorithm.HashAlgorithm;
|
import org.pgpainless.algorithm.HashAlgorithm;
|
||||||
import org.pgpainless.algorithm.SignatureType;
|
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.KeyRingUtils;
|
||||||
import org.pgpainless.key.util.OpenPgpKeyAttributeUtil;
|
import org.pgpainless.key.util.OpenPgpKeyAttributeUtil;
|
||||||
import org.pgpainless.key.util.RevocationAttributes;
|
import org.pgpainless.key.util.RevocationAttributes;
|
||||||
|
import org.pgpainless.policy.Policy;
|
||||||
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
|
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility methods related to signatures.
|
||||||
|
*/
|
||||||
public class SignatureUtils {
|
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) {
|
public static PGPSignatureGenerator getSignatureGeneratorFor(PGPSecretKey singingKey) {
|
||||||
return getSignatureGeneratorFor(singingKey.getPublicKey());
|
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) {
|
public static PGPSignatureGenerator getSignatureGeneratorFor(PGPPublicKey signingPubKey) {
|
||||||
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
|
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(
|
||||||
getPgpContentSignerBuilderForKey(signingPubKey));
|
getPgpContentSignerBuilderForKey(signingPubKey));
|
||||||
return signatureGenerator;
|
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);
|
List<HashAlgorithm> preferredHashAlgorithms = OpenPgpKeyAttributeUtil.getPreferredHashAlgorithms(publicKey);
|
||||||
if (preferredHashAlgorithms.isEmpty()) {
|
if (preferredHashAlgorithms.isEmpty()) {
|
||||||
preferredHashAlgorithms = OpenPgpKeyAttributeUtil.guessPreferredHashAlgorithms(publicKey);
|
preferredHashAlgorithms = OpenPgpKeyAttributeUtil.guessPreferredHashAlgorithms(publicKey);
|
||||||
}
|
}
|
||||||
HashAlgorithm hashAlgorithm = negotiateHashAlgorithm(preferredHashAlgorithms);
|
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) {
|
private static HashAlgorithm negotiateHashAlgorithm(List<HashAlgorithm> preferredHashAlgorithms) {
|
||||||
if (preferredHashAlgorithms.isEmpty()) {
|
Policy policy = PGPainless.getPolicy();
|
||||||
|
for (HashAlgorithm option : preferredHashAlgorithms) {
|
||||||
|
if (policy.getSignatureHashAlgorithmPolicy().isAcceptable(option)) {
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return PGPainless.getPolicy().getSignatureHashAlgorithmPolicy().defaultHashAlgorithm();
|
return PGPainless.getPolicy().getSignatureHashAlgorithmPolicy().defaultHashAlgorithm();
|
||||||
}
|
}
|
||||||
return preferredHashAlgorithms.get(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
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<>();
|
List<PGPSignature> valid = new ArrayList<>();
|
||||||
for (PGPSignature signature : signatures) {
|
for (PGPSignature signature : signatures) {
|
||||||
long issuerID = signature.getKeyID();
|
long issuerID = signature.getKeyID();
|
||||||
|
@ -93,6 +144,16 @@ public class SignatureUtils {
|
||||||
return valid.isEmpty() ? null : valid.get(valid.size() - 1);
|
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 {
|
public static boolean isSignatureValid(PGPSignature signature, PGPPublicKey issuer, PGPPublicKey target) throws PGPException {
|
||||||
SignatureType signatureType = SignatureType.valueOf(signature.getSignatureType());
|
SignatureType signatureType = SignatureType.valueOf(signature.getSignatureType());
|
||||||
switch (signatureType) {
|
switch (signatureType) {
|
||||||
|
@ -276,6 +337,15 @@ public class SignatureUtils {
|
||||||
return latestSelfSig.getSignatureType() != SignatureType.CERTIFICATION_REVOCATION.getCode();
|
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) {
|
public static boolean isHardRevocation(PGPSignature signature) {
|
||||||
|
|
||||||
SignatureType type = SignatureType.valueOf(signature.getSignatureType());
|
SignatureType type = SignatureType.valueOf(signature.getSignatureType());
|
||||||
|
|
|
@ -19,15 +19,30 @@ import java.util.Comparator;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
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> {
|
public class SignatureValidityComparator implements Comparator<PGPSignature> {
|
||||||
|
|
||||||
private final SignatureCreationDateComparator.Order order;
|
private final SignatureCreationDateComparator.Order order;
|
||||||
private final SignatureCreationDateComparator creationDateComparator;
|
private final SignatureCreationDateComparator creationDateComparator;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link SignatureValidityComparator} which orders signatures oldest first.
|
||||||
|
* Still, hard revocations will come first.
|
||||||
|
*/
|
||||||
public SignatureValidityComparator() {
|
public SignatureValidityComparator() {
|
||||||
this(SignatureCreationDateComparator.DEFAULT_ORDER);
|
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) {
|
public SignatureValidityComparator(SignatureCreationDateComparator.Order order) {
|
||||||
this.order = order;
|
this.order = order;
|
||||||
this.creationDateComparator = new SignatureCreationDateComparator(order);
|
this.creationDateComparator = new SignatureCreationDateComparator(order);
|
||||||
|
|
|
@ -31,6 +31,15 @@ import org.pgpainless.algorithm.KeyFlag;
|
||||||
*/
|
*/
|
||||||
public class SignatureSubpacketGeneratorUtil {
|
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,
|
public static <P extends SignatureSubpacket> List<P> getSubpacketsOfType(org.pgpainless.algorithm.SignatureSubpacket type,
|
||||||
PGPSignatureSubpacketGenerator generator) {
|
PGPSignatureSubpacketGenerator generator) {
|
||||||
SignatureSubpacket[] subpackets = generator.getSubpackets(type.getCode());
|
SignatureSubpacket[] subpackets = generator.getSubpackets(type.getCode());
|
||||||
|
@ -41,11 +50,25 @@ public class SignatureSubpacketGeneratorUtil {
|
||||||
return list;
|
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,
|
public static void removeAllPacketsOfType(org.pgpainless.algorithm.SignatureSubpacket subpacketType,
|
||||||
PGPSignatureSubpacketGenerator subpacketGenerator) {
|
PGPSignatureSubpacketGenerator subpacketGenerator) {
|
||||||
removeAllPacketsOfType(subpacketType.getCode(), 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) {
|
public static void removeAllPacketsOfType(int type, PGPSignatureSubpacketGenerator subpacketGenerator) {
|
||||||
for (SignatureSubpacket subpacket : subpacketGenerator.getSubpackets(type)) {
|
for (SignatureSubpacket subpacket : subpacketGenerator.getSubpackets(type)) {
|
||||||
subpacketGenerator.removePacket(subpacket);
|
subpacketGenerator.removePacket(subpacket);
|
||||||
|
@ -98,11 +121,22 @@ public class SignatureSubpacketGeneratorUtil {
|
||||||
return secondsToExpire;
|
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) {
|
public static boolean hasKeyFlag(KeyFlag keyFlag, PGPSignatureSubpacketGenerator generator) {
|
||||||
List<KeyFlags> keyFlagPackets = getSubpacketsOfType(org.pgpainless.algorithm.SignatureSubpacket.keyFlags, generator);
|
List<KeyFlags> keyFlagPackets = getSubpacketsOfType(org.pgpainless.algorithm.SignatureSubpacket.keyFlags, generator);
|
||||||
if (keyFlagPackets.isEmpty()) {
|
if (keyFlagPackets.isEmpty()) {
|
||||||
return false;
|
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.bouncycastle.util.encoders.Hex;
|
||||||
import org.pgpainless.algorithm.SignatureSubpacket;
|
import org.pgpainless.algorithm.SignatureSubpacket;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
import org.pgpainless.signature.SignatureUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class to access signature subpackets from signatures.
|
* Utility class to access signature subpackets from signatures.
|
||||||
|
@ -67,6 +68,13 @@ public class SignatureSubpacketsUtil {
|
||||||
return hashedOrUnhashed(signature, SignatureSubpacket.issuerFingerprint);
|
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) {
|
public static OpenPgpV4Fingerprint getIssuerFingerprintAsOpenPgpV4Fingerprint(PGPSignature signature) {
|
||||||
IssuerFingerprint subpacket = getIssuerFingerprint(signature);
|
IssuerFingerprint subpacket = getIssuerFingerprint(signature);
|
||||||
if (subpacket == null) {
|
if (subpacket == null) {
|
||||||
|
@ -123,12 +131,20 @@ public class SignatureSubpacketsUtil {
|
||||||
return hashed(signature, SignatureSubpacket.signatureExpirationTime);
|
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) {
|
public static Date getSignatureExpirationTimeAsDate(PGPSignature signature) {
|
||||||
SignatureExpirationTime subpacket = getSignatureExpirationTime(signature);
|
SignatureExpirationTime subpacket = getSignatureExpirationTime(signature);
|
||||||
if (subpacket == null || subpacket.getTime() == 0) {
|
if (subpacket == null) {
|
||||||
return 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 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) {
|
public static Date getKeyExpirationTimeAsDate(PGPSignature signature, PGPPublicKey signingKey) {
|
||||||
KeyExpirationTime subpacket = getKeyExpirationTime(signature);
|
KeyExpirationTime subpacket = getKeyExpirationTime(signature);
|
||||||
if (subpacket == null || subpacket.getTime() == 0) {
|
if (subpacket == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (signature.getKeyID() != signingKey.getKeyID()) {
|
if (signature.getKeyID() != signingKey.getKeyID()) {
|
||||||
throw new IllegalArgumentException("Provided key (" + Long.toHexString(signingKey.getKeyID()) + ") did not create the signature (" + Long.toHexString(signature.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);
|
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) {
|
private static <P extends org.bouncycastle.bcpg.SignatureSubpacket> P hashed(PGPSignature signature, SignatureSubpacket type) {
|
||||||
return getSignatureSubpacket(signature.getHashedSubPackets(), 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) {
|
private static <P extends org.bouncycastle.bcpg.SignatureSubpacket> P unhashed(PGPSignature signature, SignatureSubpacket type) {
|
||||||
return getSignatureSubpacket(signature.getUnhashedSubPackets(), 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) {
|
private static <P extends org.bouncycastle.bcpg.SignatureSubpacket> P hashedOrUnhashed(PGPSignature signature, SignatureSubpacket type) {
|
||||||
P hashedSubpacket = hashed(signature, type);
|
P hashedSubpacket = hashed(signature, type);
|
||||||
return hashedSubpacket != null ? hashedSubpacket : unhashed(signature, type);
|
return hashedSubpacket != null ? hashedSubpacket : unhashed(signature, type);
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package org.pgpainless.util.selection.signature;
|
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.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue