diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/SignaturePicker.java b/pgpainless-core/src/main/java/org/pgpainless/signature/SignaturePicker.java index 514cd9fc..dc9b99af 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/SignaturePicker.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/SignaturePicker.java @@ -239,6 +239,7 @@ public class SignaturePicker { PGPSignature latestUserIdCert = null; for (PGPSignature signature : signatures) { try { + SignatureValidator.verifyWasPossiblyMadeByKey(primaryKey, signature); SignatureValidator.signatureIsCertification().verify(signature); SignatureValidator.signatureStructureIsAcceptable(primaryKey, policy).verify(signature); SignatureValidator.signatureIsAlreadyEffective(validationDate).verify(signature); diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/SignatureValidator.java b/pgpainless-core/src/main/java/org/pgpainless/signature/SignatureValidator.java index 2b64b368..60eb36f6 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/SignatureValidator.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/SignatureValidator.java @@ -38,6 +38,7 @@ import org.pgpainless.algorithm.SignatureSubpacket; import org.pgpainless.algorithm.SignatureType; import org.pgpainless.exception.SignatureValidationException; import org.pgpainless.implementation.ImplementationFactory; +import org.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.policy.Policy; import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; import org.pgpainless.util.BCUtil; @@ -103,6 +104,44 @@ public abstract class SignatureValidator { } } + /** + * Check, whether there is the possibility that the given signature was created by the given key. + * This method throws a {@link SignatureValidationException} if we can say with certainty that the signature + * was not created by the given key (e.g. if the sig carries another issuer, issuer fingerprint packet). + * + * If there is no information found in the signature about who created it (no issuer, no fingerprint), + * return true since it is plausible that the given key created the sig. + * + * @param signingKey signing key + * @param signature signature + * @return true only if the signing key either created the signature or the signature doesn't carry signer information + * @throws SignatureValidationException if the sig was not created by the key + */ + public static boolean verifyWasPossiblyMadeByKey(PGPPublicKey signingKey, PGPSignature signature) throws SignatureValidationException { + OpenPgpV4Fingerprint signingKeyFingerprint = new OpenPgpV4Fingerprint(signingKey); + + Long issuer = SignatureSubpacketsUtil.getIssuerKeyIdAsLong(signature); + if (issuer != null) { + if (issuer != signingKey.getKeyID()) { + throw new SignatureValidationException("Signature was not created by " + signingKeyFingerprint + " (signature issuer: " + Long.toHexString(issuer) + ")"); + } else { + return true; + } + } + + OpenPgpV4Fingerprint fingerprint = SignatureSubpacketsUtil.getIssuerFingerprintAsOpenPgpV4Fingerprint(signature); + if (fingerprint != null) { + if (!fingerprint.equals(signingKeyFingerprint)) { + throw new SignatureValidationException("Signature was not created by " + signingKeyFingerprint + " (signature fingerprint: " + fingerprint + ")"); + } else { + return true; + } + } + + // No issuer information found, so we cannot rule out that we did not create the sig + return true; + } + public static boolean verifyUserIdCertification(String userId, PGPSignature signature, PGPPublicKey primaryKey, Policy policy, Date validationDate) throws SignatureValidationException { return verifyUserIdCertification(userId, signature, primaryKey, primaryKey, policy, validationDate); diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SignatureSubpacketsUtil.java b/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SignatureSubpacketsUtil.java index 7f240a3b..367a4ecd 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SignatureSubpacketsUtil.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/subpackets/SignatureSubpacketsUtil.java @@ -104,6 +104,14 @@ public class SignatureSubpacketsUtil { return hashedOrUnhashed(signature, SignatureSubpacket.issuerKeyId); } + public static Long getIssuerKeyIdAsLong(PGPSignature signature) { + IssuerKeyID keyID = getIssuerKeyId(signature); + if (keyID == null) { + return null; + } + return keyID.getKeyID(); + } + /** * Return the revocation reason subpacket of the signature. * Since this packet is rather important for revocations, we only search for it in the