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:
parent
63d1f855de
commit
4c180bbd59
8 changed files with 191 additions and 490 deletions
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
) {}
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue