1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-09-09 02:09:38 +02:00

Port signature validation to BC

This commit is contained in:
Paul Schaub 2025-03-21 13:44:24 +01:00
parent 63d1f855de
commit 4c180bbd59
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
8 changed files with 191 additions and 490 deletions

View file

@ -13,6 +13,7 @@ import java.util.zip.InflaterInputStream
import openpgp.openPgpKeyId
import org.bouncycastle.bcpg.BCPGInputStream
import org.bouncycastle.bcpg.CompressionAlgorithmTags
import org.bouncycastle.bcpg.KeyIdentifier
import org.bouncycastle.bcpg.UnsupportedPacketVersionException
import org.bouncycastle.openpgp.PGPCompressedData
import org.bouncycastle.openpgp.PGPEncryptedData
@ -30,6 +31,7 @@ import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey
import org.bouncycastle.openpgp.api.OpenPGPSignature.OpenPGPDocumentSignature
import org.bouncycastle.openpgp.api.exception.MalformedOpenPGPSignatureException
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory
import org.bouncycastle.util.io.TeeInputStream
@ -60,8 +62,6 @@ import org.pgpainless.exception.UnacceptableAlgorithmException
import org.pgpainless.exception.WrongPassphraseException
import org.pgpainless.key.SubkeyIdentifier
import org.pgpainless.key.protection.UnlockSecretKey.Companion.unlockSecretKey
import org.pgpainless.policy.Policy
import org.pgpainless.signature.consumer.CertificateValidator
import org.pgpainless.signature.consumer.OnePassSignatureCheck
import org.pgpainless.signature.consumer.SignatureValidator
import org.pgpainless.util.ArmoredInputStreamFactory
@ -312,8 +312,7 @@ class OpenPgpMessageInputStream(
signatures
.leaveNesting() // TODO: Only leave nesting if all OPSs of the nesting layer are
// dealt with
signatures.addCorrespondingOnePassSignature(
signature, layerMetadata, api.algorithmPolicy)
signatures.addCorrespondingOnePassSignature(signature, layerMetadata)
} else {
LOGGER.debug(
"Prepended Signature Packet by key ${keyId.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.")
@ -618,7 +617,7 @@ class OpenPgpMessageInputStream(
throw RuntimeException(e)
}
}
signatures.finish(layerMetadata, api.algorithmPolicy)
signatures.finish(layerMetadata)
}
return r
}
@ -645,7 +644,7 @@ class OpenPgpMessageInputStream(
throw RuntimeException(e)
}
}
signatures.finish(layerMetadata, api.algorithmPolicy)
signatures.finish(layerMetadata)
}
return r
}
@ -832,15 +831,11 @@ class OpenPgpMessageInputStream(
}
}
fun addCorrespondingOnePassSignature(
signature: PGPSignature,
layer: Layer,
policy: Policy
) {
fun addCorrespondingOnePassSignature(signature: PGPSignature, layer: Layer) {
var found = false
val keyId = signature.issuerKeyId
for ((i, check) in onePassSignatures.withIndex().reversed()) {
if (check.onePassSignature.keyID != keyId) {
if (!KeyIdentifier.matches(
signature.keyIdentifiers, check.onePassSignature.keyIdentifier, true)) {
continue
}
found = true
@ -848,8 +843,11 @@ class OpenPgpMessageInputStream(
if (check.signature != null) {
continue
}
check.signature = signature
val documentSignature =
OpenPGPDocumentSignature(
signature, check.verificationKeys.getSigningKeyFor(signature))
val verification =
SignatureVerification(
signature,
@ -861,11 +859,15 @@ class OpenPgpMessageInputStream(
SignatureValidator.signatureWasCreatedInBounds(
options.getVerifyNotBefore(), options.getVerifyNotAfter())
.verify(signature)
CertificateValidator.validateCertificateAndVerifyOnePassSignature(check, policy)
LOGGER.debug("Acceptable signature by key ${verification.signingKey}")
layer.addVerifiedOnePassSignature(verification)
if (documentSignature.verify(check.onePassSignature) &&
documentSignature.isValid(api.implementation.policy())) {
layer.addVerifiedOnePassSignature(verification)
} else {
throw SignatureValidationException("Incorrect OnePassSignature.")
}
} catch (e: MalformedOpenPGPSignatureException) {
throw SignatureValidationException("Malformed OnePassSignature.", e)
} catch (e: SignatureValidationException) {
LOGGER.debug("Rejected signature by key ${verification.signingKey}", e)
layer.addRejectedOnePassSignature(
SignatureVerification.Failure(verification, e))
}
@ -874,7 +876,7 @@ class OpenPgpMessageInputStream(
if (!found) {
LOGGER.debug(
"No suitable certificate for verification of signature by key ${keyId.openPgpKeyId()} found.")
"No suitable certificate for verification of signature by key ${signature.issuerKeyId.openPgpKeyId()} found.")
inbandSignaturesWithMissingCert.add(
SignatureVerification.Failure(
signature, null, SignatureValidationException("Missing verification key.")))
@ -963,7 +965,7 @@ class OpenPgpMessageInputStream(
}
}
fun finish(layer: Layer, policy: Policy) {
fun finish(layer: Layer) {
for (detached in detachedSignatures) {
val verification =
SignatureVerification(detached.signature, SubkeyIdentifier(detached.issuer))
@ -971,12 +973,14 @@ class OpenPgpMessageInputStream(
SignatureValidator.signatureWasCreatedInBounds(
options.getVerifyNotBefore(), options.getVerifyNotAfter())
.verify(detached.signature)
CertificateValidator.validateCertificateAndVerifyInitializedSignature(
detached.signature, detached.issuerCertificate.pgpPublicKeyRing, policy)
LOGGER.debug("Acceptable signature by key ${verification.signingKey}")
layer.addVerifiedDetachedSignature(verification)
if (detached.verify() && detached.isValid(api.implementation.policy())) {
layer.addVerifiedDetachedSignature(verification)
} else {
throw SignatureValidationException("Incorrect detached signature.")
}
} catch (e: MalformedOpenPGPSignatureException) {
throw SignatureValidationException("Malformed detached signature.", e)
} catch (e: SignatureValidationException) {
LOGGER.debug("Rejected signature by key ${verification.signingKey}", e)
layer.addRejectedDetachedSignature(
SignatureVerification.Failure(verification, e))
}
@ -989,10 +993,13 @@ class OpenPgpMessageInputStream(
SignatureValidator.signatureWasCreatedInBounds(
options.getVerifyNotBefore(), options.getVerifyNotAfter())
.verify(prepended.signature)
CertificateValidator.validateCertificateAndVerifyInitializedSignature(
prepended.signature, prepended.issuerCertificate.pgpPublicKeyRing, policy)
LOGGER.debug("Acceptable signature by key ${verification.signingKey}")
layer.addVerifiedPrependedSignature(verification)
if (prepended.verify() && prepended.isValid(api.implementation.policy())) {
layer.addVerifiedPrependedSignature(verification)
} else {
throw SignatureValidationException("Incorrect prepended signature.")
}
} catch (e: MalformedOpenPGPSignatureException) {
throw SignatureValidationException("Malformed prepended signature.", e)
} catch (e: SignatureValidationException) {
LOGGER.debug("Rejected signature by key ${verification.signingKey}", e)
layer.addRejectedPrependedSignature(

View file

@ -1,318 +0,0 @@
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.signature.consumer
import java.io.InputStream
import java.util.*
import openpgp.openPgpKeyId
import org.bouncycastle.openpgp.PGPPublicKey
import org.bouncycastle.openpgp.PGPPublicKeyRing
import org.bouncycastle.openpgp.PGPSignature
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.KeyFlag
import org.pgpainless.algorithm.SignatureType
import org.pgpainless.bouncycastle.extensions.getPublicKey
import org.pgpainless.bouncycastle.extensions.issuerKeyId
import org.pgpainless.exception.SignatureValidationException
import org.pgpainless.key.util.KeyRingUtils
import org.pgpainless.policy.Policy
import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil
import org.slf4j.LoggerFactory
/**
* A collection of static methods that validate signing certificates (public keys) and verify
* signature correctness.
*/
class CertificateValidator {
companion object {
@JvmStatic private val LOGGER = LoggerFactory.getLogger(CertificateValidator::class.java)
/**
* 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
*/
@JvmStatic
@Throws(SignatureValidationException::class)
fun validateCertificate(
signature: PGPSignature,
signingKeyRing: PGPPublicKeyRing,
policy: Policy = PGPainless.getInstance().algorithmPolicy
): Boolean {
val signingSubkey: PGPPublicKey =
signingKeyRing.getPublicKey(signature.issuerKeyId)
?: throw SignatureValidationException(
"Provided key ring does not contain a subkey with id ${signature.issuerKeyId.openPgpKeyId()}.")
val primaryKey = signingKeyRing.publicKey!!
val directKeyAndRevSigs = mutableListOf<PGPSignature>()
val rejections = mutableMapOf<PGPSignature, Exception>()
// revocations
primaryKey
.getSignaturesOfType(SignatureType.KEY_REVOCATION.code)
.asSequence()
.filter {
it.issuerKeyId == primaryKey.keyID
} // We do not support external rev keys
.forEach {
try {
if (SignatureVerifier.verifyKeyRevocationSignature(
it, primaryKey, policy, signature.creationTime)) {
directKeyAndRevSigs.add(it)
}
} catch (e: SignatureValidationException) {
rejections[it] = e
LOGGER.debug("Rejecting key revocation signature: ${e.message}", e)
}
}
// direct-key sigs
primaryKey
.getSignaturesOfType(SignatureType.DIRECT_KEY.code)
.asSequence()
.filter { it.issuerKeyId == primaryKey.keyID }
.forEach {
try {
if (SignatureVerifier.verifyDirectKeySignature(
it, primaryKey, policy, signature.creationTime)) {
directKeyAndRevSigs.add(it)
}
} catch (e: SignatureValidationException) {
rejections[it] = e
LOGGER.debug("Rejecting key signature: ${e.message}, e")
}
}
directKeyAndRevSigs.sortWith(
SignatureValidityComparator(SignatureCreationDateComparator.Order.NEW_TO_OLD))
if (directKeyAndRevSigs.isNotEmpty()) {
if (directKeyAndRevSigs[0].signatureType == SignatureType.KEY_REVOCATION.code) {
throw SignatureValidationException("Primary key has been revoked.")
}
}
// UserID signatures
val userIdSignatures = mutableMapOf<String, List<PGPSignature>>()
KeyRingUtils.getUserIdsIgnoringInvalidUTF8(primaryKey).forEach { userId ->
buildList<PGPSignature> {
primaryKey
.getSignaturesForID(userId)
.asSequence()
.filter { it.issuerKeyId == primaryKey.keyID }
.forEach { uidSig ->
try {
if (SignatureVerifier.verifySignatureOverUserId(
userId,
uidSig,
primaryKey,
policy,
signature.creationTime)) {
add(uidSig)
}
} catch (e: SignatureValidationException) {
rejections[uidSig] = e
LOGGER.debug("Rejecting user-id signature: ${e.message}", e)
}
}
}
.sortedWith(
SignatureValidityComparator(
SignatureCreationDateComparator.Order.NEW_TO_OLD))
.let { userIdSignatures[userId] = it }
}
val hasAnyUserIds = userIdSignatures.isNotEmpty()
val isAnyUserIdValid =
userIdSignatures.any { entry ->
entry.value.isNotEmpty() &&
entry.value[0].signatureType != SignatureType.CERTIFICATION_REVOCATION.code
}
if (hasAnyUserIds && !isAnyUserIdValid) {
throw SignatureValidationException("No valid user-id found.", rejections)
}
// Specific signer user-id
if (policy.signerUserIdValidationLevel == Policy.SignerUserIdValidationLevel.STRICT) {
SignatureSubpacketsUtil.getSignerUserID(signature)?.let {
if (userIdSignatures[it.id] == null || userIdSignatures[it.id]!!.isEmpty()) {
throw SignatureValidationException(
"Signature was allegedly made by user-id '${it.id}'," +
" but we have no valid signatures for that on the certificate.")
}
if (userIdSignatures[it.id]!![0].signatureType ==
SignatureType.CERTIFICATION_REVOCATION.code) {
throw SignatureValidationException(
"Signature was made with user-id '${it.id}' which is revoked.")
}
}
}
if (signingSubkey.keyID == primaryKey.keyID) { // signing key is primary key
if (directKeyAndRevSigs.isNotEmpty()) {
val directKeySig = directKeyAndRevSigs[0]
val flags = SignatureSubpacketsUtil.getKeyFlags(directKeySig)
if (flags != null && KeyFlag.hasKeyFlag(flags.flags, KeyFlag.SIGN_DATA)) {
return true
}
}
// Reject sigs by non-signing keys
if (userIdSignatures.none { (_, sigs) ->
sigs.any {
SignatureSubpacketsUtil.getKeyFlags(it)?.let { f ->
KeyFlag.hasKeyFlag(f.flags, KeyFlag.SIGN_DATA)
} == true
}
}) {
throw SignatureValidationException(
"Signature was generated by non-signing key.")
}
} else { // signing key is subkey
val subkeySigs = mutableListOf<PGPSignature>()
signingSubkey
.getSignaturesOfType(SignatureType.SUBKEY_REVOCATION.code)
.asSequence()
.filter { it.issuerKeyId == primaryKey.keyID }
.forEach {
try {
if (SignatureVerifier.verifySubkeyBindingRevocation(
it, primaryKey, signingSubkey, policy, signature.creationTime)) {
subkeySigs.add(it)
}
} catch (e: SignatureValidationException) {
rejections[it] = e
LOGGER.debug("Rejecting subkey revocation signature: ${e.message}", e)
}
}
signingSubkey
.getSignaturesOfType(SignatureType.SUBKEY_BINDING.code)
.asSequence()
.forEach {
try {
if (SignatureVerifier.verifySubkeyBindingSignature(
it, primaryKey, signingSubkey, policy, signature.creationTime)) {
subkeySigs.add(it)
}
} catch (e: SignatureValidationException) {
rejections[it] = e
LOGGER.debug("Rejecting subkey binding signature: ${e.message}", e)
}
}
subkeySigs.sortWith(
SignatureValidityComparator(SignatureCreationDateComparator.Order.NEW_TO_OLD))
if (subkeySigs.isEmpty()) {
throw SignatureValidationException("Subkey is not bound.", rejections)
}
if (subkeySigs[0].signatureType == SignatureType.SUBKEY_REVOCATION.code) {
throw SignatureValidationException("Subkey is revoked.")
}
val keyFlags = SignatureSubpacketsUtil.getKeyFlags(subkeySigs[0])
if (keyFlags == null || !KeyFlag.hasKeyFlag(keyFlags.flags, KeyFlag.SIGN_DATA)) {
throw SignatureValidationException(
"Signature was made by key which is not capable of signing (no keyflag).")
}
}
return true
}
/**
* 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
*/
@JvmStatic
@Throws(SignatureValidationException::class)
fun validateCertificateAndVerifyUninitializedSignature(
signature: PGPSignature,
signedData: InputStream,
signingKeyRing: PGPPublicKeyRing,
policy: Policy,
referenceTime: Date = signature.creationTime
): Boolean {
return validateCertificate(signature, signingKeyRing, policy) &&
SignatureVerifier.verifyUninitializedSignature(
signature,
signedData,
signingKeyRing.getPublicKey(signature.issuerKeyId)!!,
policy,
referenceTime)
}
/**
* 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
*/
@JvmStatic
@Throws(SignatureValidationException::class)
fun validateCertificateAndVerifyInitializedSignature(
signature: PGPSignature,
verificationKeys: PGPPublicKeyRing,
policy: Policy
): Boolean {
return validateCertificate(signature, verificationKeys, policy) &&
SignatureVerifier.verifyInitializedSignature(
signature,
verificationKeys.getPublicKey(signature.issuerKeyId),
policy,
signature.creationTime)
}
/**
* Validate the signing key certificate and the given [OnePassSignatureCheck].
*
* @param onePassSignature corresponding one-pass-signature
* @param policy policy
* @return true if the certificate is valid and the signature is correct, false otherwise.
* @throws SignatureValidationException in case of a validation error
*/
@JvmStatic
@Throws(SignatureValidationException::class)
fun validateCertificateAndVerifyOnePassSignature(
onePassSignature: OnePassSignatureCheck,
policy: Policy
): Boolean {
return validateCertificate(
onePassSignature.signature!!,
onePassSignature.verificationKeys.pgpPublicKeyRing,
policy) &&
SignatureVerifier.verifyOnePassSignature(
onePassSignature.signature!!,
onePassSignature.verificationKeys.pgpKeyRing.getPublicKey(
onePassSignature.signature!!.issuerKeyId),
onePassSignature,
policy)
}
}
}

View file

@ -1,25 +0,0 @@
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.signature.consumer
import org.bouncycastle.openpgp.PGPKeyRing
import org.bouncycastle.openpgp.PGPSignature
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 the signature was verified.
*
* @param signature OpenPGP signature
* @param signingKeyIdentifier identifier pointing to the exact signing key which was used to create
* the signature
* @param signingKeyRing certificate or key ring that contains the signing key that created the
* signature
*/
data class SignatureCheck(
val signature: PGPSignature,
val signingKeyRing: PGPKeyRing,
val signingKeyIdentifier: SubkeyIdentifier
) {}

View file

@ -211,7 +211,6 @@ public class ModifiedPublicKeysInvestigation {
@Test
public void assertModifiedDSAKeyThrowsKeyIntegrityException() throws IOException {
System.out.println(DSA);
PGPainless api = PGPainless.newInstance();
Policy policy = api.getAlgorithmPolicy();
policy.setEnableKeyParameterValidation(true);

View file

@ -19,7 +19,6 @@ import java.nio.charset.StandardCharsets;
import java.util.Random;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
@ -36,9 +35,7 @@ import org.pgpainless.encryption_signing.SigningOptions;
import org.pgpainless.exception.WrongConsumingMethodException;
import org.pgpainless.key.TestKeys;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.signature.consumer.CertificateValidator;
import org.pgpainless.signature.SignatureUtils;
import org.pgpainless.signature.consumer.SignatureVerifier;
import org.pgpainless.util.ArmorUtils;
import org.pgpainless.util.TestUtils;
@ -136,19 +133,6 @@ public class CleartextSignatureVerificationTest {
assertArrayEquals(MESSAGE_BODY, bytes.toByteArray());
}
@Test
public void verifySignatureDetached()
throws IOException, PGPException {
PGPPublicKeyRing signingKeys = TestKeys.getEmilPublicKeyRing();
PGPSignature signature = SignatureUtils.readSignatures(SIGNATURE).get(0);
PGPPublicKey signingKey = signingKeys.getPublicKey(signature.getKeyID());
SignatureVerifier.initializeSignatureAndUpdateWithSignedData(signature, new ByteArrayInputStream(MESSAGE_BODY), signingKey);
CertificateValidator.validateCertificateAndVerifyInitializedSignature(signature, signingKeys, PGPainless.getPolicy());
}
public static void main(String[] args) throws IOException {
// CHECKSTYLE:OFF
PGPPublicKeyRing keys = TestKeys.getEmilPublicKeyRing();

View file

@ -4,23 +4,23 @@
package org.pgpainless.signature;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.fail;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import org.pgpainless.PGPainless;
import org.pgpainless.exception.SignatureValidationException;
import org.pgpainless.policy.Policy;
import org.pgpainless.signature.consumer.CertificateValidator;
import org.pgpainless.decryption_verification.ConsumerOptions;
import org.pgpainless.decryption_verification.DecryptionStream;
import org.pgpainless.decryption_verification.MessageMetadata;
import org.pgpainless.util.TestAllImplementations;
/**
@ -47,12 +47,9 @@ public class BindingSignatureSubpacketsTest {
"-----END PGP SIGNATURE-----\n";
private static final String data = "Hello World :)";
private Date validationDate = new Date();
private Policy policy = PGPainless.getPolicy();
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void baseCase() throws IOException {
public void baseCase() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -113,7 +110,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void subkeyBindingIssuerFpOnly() throws IOException {
public void subkeyBindingIssuerFpOnly() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -174,7 +171,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void subkeyBindingIssuerV6IssuerFp() throws IOException {
public void subkeyBindingIssuerV6IssuerFp() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -235,7 +232,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void subkeyBindingIssuerFakeIssuer() throws IOException {
public void subkeyBindingIssuerFakeIssuer() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -296,7 +293,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void subkeyBindingFakeIssuerIssuer() throws IOException {
public void subkeyBindingFakeIssuerIssuer() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -357,7 +354,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void subkeyBindingFakeIssuer() throws IOException {
public void subkeyBindingFakeIssuer() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -418,7 +415,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void subkeyBindingNoIssuer() throws IOException {
public void subkeyBindingNoIssuer() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -478,7 +475,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void unknownSubpacketHashed() throws IOException {
public void unknownSubpacketHashed() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -539,7 +536,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void subkeyBindingUnknownCriticalSubpacket() throws IOException {
public void subkeyBindingUnknownCriticalSubpacket() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -600,7 +597,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void subkeyBindingUnknownSubpacketUnhashed() throws IOException {
public void subkeyBindingUnknownSubpacketUnhashed() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -661,7 +658,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void subkeyBindingUnknownCriticalSubpacketUnhashed() throws IOException {
public void subkeyBindingUnknownCriticalSubpacketUnhashed() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -722,7 +719,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void subkeyBindingUnknownNotationHashed() throws IOException {
public void subkeyBindingUnknownNotationHashed() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -784,7 +781,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void subkeyBindingCriticalUnknownNotationHashed() throws IOException {
public void subkeyBindingCriticalUnknownNotationHashed() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -846,7 +843,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void subkeyBindingUnknownNotationUnhashed() throws IOException {
public void subkeyBindingUnknownNotationUnhashed() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -908,7 +905,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void subkeyBindingCriticalUnknownNotationUnhashed() throws IOException {
public void subkeyBindingCriticalUnknownNotationUnhashed() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -970,7 +967,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void subkeyBindingBackSigFakeBackSig() throws IOException {
public void subkeyBindingBackSigFakeBackSig() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1042,7 +1039,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void subkeyBindingFakeBackSigBackSig() throws IOException {
public void subkeyBindingFakeBackSigBackSig() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1114,7 +1111,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void primaryBindingIssuerFpOnly() throws IOException {
public void primaryBindingIssuerFpOnly() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1175,7 +1172,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void primaryBindingIssuerV6IssuerFp() throws IOException {
public void primaryBindingIssuerV6IssuerFp() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1236,7 +1233,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void primaryBindingIssuerFakeIssuer() throws IOException {
public void primaryBindingIssuerFakeIssuer() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1297,7 +1294,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void primaryBindingFakeIssuerIssuer() throws IOException {
public void primaryBindingFakeIssuerIssuer() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1358,7 +1355,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void primaryBindingFakeIssuer() throws IOException {
public void primaryBindingFakeIssuer() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1419,7 +1416,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void primaryBindingNoIssuer() throws IOException {
public void primaryBindingNoIssuer() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1479,7 +1476,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void primaryBindingUnknownSubpacketHashed() throws IOException {
public void primaryBindingUnknownSubpacketHashed() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1540,7 +1537,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void primaryBindingCriticalUnknownSubpacketHashed() throws IOException {
public void primaryBindingCriticalUnknownSubpacketHashed() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1601,7 +1598,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void primaryBindingUnknownSubpacketUnhashed() throws IOException {
public void primaryBindingUnknownSubpacketUnhashed() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1662,7 +1659,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void primaryBindingCriticalUnknownSubpacketUnhashed() throws IOException {
public void primaryBindingCriticalUnknownSubpacketUnhashed() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1723,7 +1720,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void primaryBindingUnknownNotationHashed() throws IOException {
public void primaryBindingUnknownNotationHashed() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1785,7 +1782,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void primaryBindingCriticalUnknownNotationHashed() throws IOException {
public void primaryBindingCriticalUnknownNotationHashed() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1847,7 +1844,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void primaryBindingUnknownNotationUnhashed() throws IOException {
public void primaryBindingUnknownNotationUnhashed() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1909,7 +1906,7 @@ public class BindingSignatureSubpacketsTest {
@TestTemplate
@ExtendWith(TestAllImplementations.class)
public void primaryBindingCriticalUnknownNotationUnhashed() throws IOException {
public void primaryBindingCriticalUnknownNotationUnhashed() throws IOException, PGPException {
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"\n" +
@ -1969,27 +1966,38 @@ public class BindingSignatureSubpacketsTest {
expectSignatureValidationSucceeds(key, "Critical unknown notation is acceptable in unhashed area of primary key binding sig.");
}
private void expectSignatureValidationSucceeds(String key, String message) throws IOException {
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key);
PGPSignature signature = SignatureUtils.readSignatures(sig).get(0);
private void expectSignatureValidationSucceeds(String key, String message) throws IOException, PGPException {
PGPainless api = PGPainless.getInstance();
OpenPGPCertificate certificate = api.readKey().parseCertificate(key);
try {
CertificateValidator.validateCertificateAndVerifyUninitializedSignature(signature, getSignedData(data), publicKeys, policy, validationDate);
} catch (SignatureValidationException e) {
// CHECKSTYLE:OFF
e.printStackTrace();
// CHECKSTYLE:ON
fail(message + ": " + e.getMessage());
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify().onInputStream(getSignedData(data))
.withOptions(ConsumerOptions.get(api)
.addVerificationCert(certificate)
.addVerificationOfDetachedSignatures(SignatureUtils.readSignatures(sig)));
Streams.drain(decryptionStream);
decryptionStream.close();
MessageMetadata metadata = decryptionStream.getMetadata();
if (!metadata.getRejectedSignatures().isEmpty()) {
throw metadata.getRejectedSignatures().get(0).getValidationException();
}
assertTrue(decryptionStream.getMetadata().isVerifiedSignedBy(certificate),
message);
}
private void expectSignatureValidationFails(String key, String message) throws IOException {
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key);
PGPSignature signature = SignatureUtils.readSignatures(sig).get(0);
private void expectSignatureValidationFails(String key, String message) throws IOException, PGPException {
PGPainless api = PGPainless.getInstance();
OpenPGPCertificate certificate = api.readKey().parseCertificate(key);
assertThrows(SignatureValidationException.class, () ->
CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
signature, getSignedData(data), publicKeys, policy, validationDate),
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify().onInputStream(getSignedData(data))
.withOptions(ConsumerOptions.get(api)
.addVerificationCert(certificate)
.addVerificationOfDetachedSignatures(SignatureUtils.readSignatures(sig)));
Streams.drain(decryptionStream);
decryptionStream.close();
assertFalse(decryptionStream.getMetadata().isVerifiedSignedBy(certificate),
message);
}

View file

@ -17,6 +17,7 @@ import java.util.Date;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestTemplate;
@ -27,7 +28,6 @@ import org.pgpainless.decryption_verification.DecryptionStream;
import org.pgpainless.decryption_verification.MessageMetadata;
import org.pgpainless.exception.SignatureValidationException;
import org.pgpainless.policy.Policy;
import org.pgpainless.signature.consumer.CertificateValidator;
import org.pgpainless.util.TestAllImplementations;
public class CertificateValidatorTest {
@ -170,16 +170,16 @@ public class CertificateValidatorTest {
Date validationDate = new Date();
String data = "Hello, World";
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate),
"Signature predates primary key");
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
unboundSubkey, getSignedData(data), publicKeys, policy, validationDate),
"Primary key hard revoked");
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
primaryKeyRevoked, getSignedData(data), publicKeys, policy, validationDate),
"Primary key hard revoked");
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
primaryKeyRevalidated, getSignedData(data), publicKeys, policy, validationDate),
"Primary key hard revoked");
}
@ -321,16 +321,16 @@ public class CertificateValidatorTest {
Date validationDate = new Date();
String data = "Hello, World";
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate),
"Signature predates primary key");
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
unboundSubkey, getSignedData(data), publicKeys, policy, validationDate),
"Signing key unbound + hard revocation");
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
revokedSubkey, getSignedData(data), publicKeys, policy, validationDate),
"Primary key is hard revoked");
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
revalidatedSubkey, getSignedData(data), publicKeys, policy, validationDate),
"Primary key is hard revoked");
}
@ -473,16 +473,16 @@ public class CertificateValidatorTest {
Date validationDate = new Date();
String data = "Hello World :)";
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate),
"Signature predates primary key");
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
unboundKey, getSignedData(data), publicKeys, policy, validationDate),
"Signing key unbound + hard revocation");
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
afterHardRevocation, getSignedData(data), publicKeys, policy, validationDate),
"Hard revocation invalidates key at all times");
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
afterRevalidation, getSignedData(data), publicKeys, policy, validationDate),
"Hard revocation invalidates key at all times");
}
@ -624,22 +624,22 @@ public class CertificateValidatorTest {
String data = "Hello, World";
// Sig not valid, as it predates the signing key creation time
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
predatesPrimaryKey, getSignedData(data), publicKeys, policy, predatesPrimaryKey.getCreationTime()),
"Signature predates primary key creation date");
// Sig valid
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertDoesNotThrow(() -> verify(
keyIsValid, getSignedData(data), publicKeys, policy, keyIsValid.getCreationTime()),
"Signature is valid");
// Sig not valid, as the signing key is revoked
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
keyIsRevoked, getSignedData(data), publicKeys, policy, keyIsRevoked.getCreationTime()),
"Signing key is revoked at this point");
// Sig valid, as the signing key is revalidated
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertDoesNotThrow(() -> verify(
keyIsRevalidated, getSignedData(data), publicKeys, policy, keyIsRevalidated.getCreationTime()),
"Signature is valid, as signing key is revalidated");
}
@ -782,17 +782,17 @@ public class CertificateValidatorTest {
String data = "Hello, World";
Date validationDate = new Date();
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate),
"Signature predates primary key creation date");
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
keyNotBound, getSignedData(data), publicKeys, policy, validationDate),
"Signing key is not bound at this point");
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
keyRevoked, getSignedData(data), publicKeys, policy, validationDate),
"Signing key is revoked at this point");
assertDoesNotThrow(() ->
CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
verify(
valid, getSignedData(data), publicKeys, policy, validationDate),
"Signing key is revalidated");
}
@ -935,17 +935,17 @@ public class CertificateValidatorTest {
Date validationDate = new Date();
String data = "Hello, World";
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate),
"Signature predates primary key creation date");
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertDoesNotThrow(() -> verify(
valid, getSignedData(data), publicKeys, policy, validationDate),
"Signature is valid");
assertThrows(SignatureValidationException.class, () ->
CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
verify(
revoked, getSignedData(data), publicKeys, policy, validationDate),
"Primary key is revoked");
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertDoesNotThrow(() -> verify(
revalidated, getSignedData(data), publicKeys, policy, validationDate),
"Primary key is re-legitimized");
}
@ -1275,47 +1275,66 @@ public class CertificateValidatorTest {
Date validationDate = new Date();
String data = "Hello World :)";
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
sigAT0, getSignedData(data), keysA, policy, validationDate),
"Signature predates key creation time");
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertDoesNotThrow(() -> verify(
sigAT1_T2, getSignedData(data), keysA, policy, validationDate),
"Key valid");
assertThrows(SignatureValidationException.class, () ->
CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
verify(
sigAT2_T3, getSignedData(data), keysA, policy, validationDate),
"Key is not valid, as subkey binding expired");
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertDoesNotThrow(() -> verify(
sigAT3_now, getSignedData(data), keysA, policy, validationDate),
"Key is valid again");
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
sigBT0, getSignedData(data), keysB, policy, validationDate),
"Signature predates key creation time");
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertDoesNotThrow(() -> verify(
sigBT1_T2, getSignedData(data), keysB, policy, validationDate),
"Key is valid");
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
sigBT2_T3, getSignedData(data), keysB, policy, validationDate),
"Primary key is not signing-capable");
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertDoesNotThrow(() -> verify(
sigBT3_now, getSignedData(data), keysB, policy, validationDate),
"Key is valid again");
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
sigCT0, getSignedData(data), keysC, policy, validationDate),
"Signature predates key creation time");
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertDoesNotThrow(() -> verify(
sigCT1_T2, getSignedData(data), keysC, policy, validationDate),
"Key is valid");
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertThrows(SignatureValidationException.class, () -> verify(
sigCT2_T3, getSignedData(data), keysC, policy, validationDate),
"Key is revoked");
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
assertDoesNotThrow(() -> verify(
sigCT3_now, getSignedData(data), keysC, policy, validationDate),
"Key is valid again");
}
private void verify(PGPSignature signature, InputStream dataIn, PGPPublicKeyRing cert, Policy policy, Date validationDate) throws PGPException, IOException {
PGPainless api = PGPainless.getInstance();
OpenPGPCertificate certificate = api.toCertificate(cert);
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify()
.onInputStream(dataIn)
.withOptions(ConsumerOptions.get(api)
.addVerificationOfDetachedSignature(signature)
.addVerificationCert(certificate));
Streams.drain(decryptionStream);
decryptionStream.close();
MessageMetadata metadata = decryptionStream.getMetadata();
if (metadata.hasRejectedSignatures()) {
throw metadata.getRejectedSignatures().get(0).getValidationException();
}
}
private static InputStream getSignedData(String data) {
return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
}

View file

@ -8,17 +8,23 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.TestTemplate;
import org.junit.jupiter.api.extension.ExtendWith;
import org.pgpainless.PGPainless;
import org.pgpainless.decryption_verification.ConsumerOptions;
import org.pgpainless.decryption_verification.DecryptionStream;
import org.pgpainless.decryption_verification.MessageMetadata;
import org.pgpainless.exception.SignatureValidationException;
import org.pgpainless.signature.consumer.CertificateValidator;
import org.pgpainless.policy.Policy;
import org.pgpainless.util.TestAllImplementations;
public class KeyRevocationTest {
@ -153,16 +159,16 @@ public class KeyRevocationTest {
PGPSignature t2t3 = SignatureUtils.readSignatures(sigT2T3).get(0);
PGPSignature t3now = SignatureUtils.readSignatures(sigT3Now).get(0);
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(t0,
assertThrows(SignatureValidationException.class, () -> verify(t0,
new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)),
publicKeys, PGPainless.getPolicy(), new Date()));
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(t1t2,
assertThrows(SignatureValidationException.class, () -> verify(t1t2,
new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)),
publicKeys, PGPainless.getPolicy(), new Date()));
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(t2t3,
assertThrows(SignatureValidationException.class, () -> verify(t2t3,
new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)),
publicKeys, PGPainless.getPolicy(), new Date()));
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(t3now,
assertThrows(SignatureValidationException.class, () -> verify(t3now,
new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)),
publicKeys, PGPainless.getPolicy(), new Date()));
}
@ -255,8 +261,29 @@ public class KeyRevocationTest {
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key);
PGPSignature signature = SignatureUtils.readSignatures(sig).get(0);
CertificateValidator.validateCertificateAndVerifyUninitializedSignature(signature,
verify(signature,
new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)),
publicKeys, PGPainless.getPolicy(), new Date());
}
private void verify(PGPSignature signature, InputStream dataIn, PGPPublicKeyRing cert, Policy policy, Date validationDate) throws PGPException, IOException {
PGPainless api = PGPainless.getInstance();
OpenPGPCertificate certificate = api.toCertificate(cert);
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify()
.onInputStream(dataIn)
.withOptions(ConsumerOptions.get(api)
.addVerificationOfDetachedSignature(signature)
.addVerificationCert(certificate));
Streams.drain(decryptionStream);
decryptionStream.close();
MessageMetadata metadata = decryptionStream.getMetadata();
if (metadata.hasRejectedSignatures()) {
throw metadata.getRejectedSignatures().get(0).getValidationException();
}
}
}