mirror of
https://github.com/pgpainless/pgpainless.git
synced 2025-09-09 10:19:39 +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 openpgp.openPgpKeyId
|
||||||
import org.bouncycastle.bcpg.BCPGInputStream
|
import org.bouncycastle.bcpg.BCPGInputStream
|
||||||
import org.bouncycastle.bcpg.CompressionAlgorithmTags
|
import org.bouncycastle.bcpg.CompressionAlgorithmTags
|
||||||
|
import org.bouncycastle.bcpg.KeyIdentifier
|
||||||
import org.bouncycastle.bcpg.UnsupportedPacketVersionException
|
import org.bouncycastle.bcpg.UnsupportedPacketVersionException
|
||||||
import org.bouncycastle.openpgp.PGPCompressedData
|
import org.bouncycastle.openpgp.PGPCompressedData
|
||||||
import org.bouncycastle.openpgp.PGPEncryptedData
|
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.OpenPGPPrivateKey
|
||||||
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey
|
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey
|
||||||
import org.bouncycastle.openpgp.api.OpenPGPSignature.OpenPGPDocumentSignature
|
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.PBEDataDecryptorFactory
|
||||||
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory
|
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory
|
||||||
import org.bouncycastle.util.io.TeeInputStream
|
import org.bouncycastle.util.io.TeeInputStream
|
||||||
|
@ -60,8 +62,6 @@ import org.pgpainless.exception.UnacceptableAlgorithmException
|
||||||
import org.pgpainless.exception.WrongPassphraseException
|
import org.pgpainless.exception.WrongPassphraseException
|
||||||
import org.pgpainless.key.SubkeyIdentifier
|
import org.pgpainless.key.SubkeyIdentifier
|
||||||
import org.pgpainless.key.protection.UnlockSecretKey.Companion.unlockSecretKey
|
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.OnePassSignatureCheck
|
||||||
import org.pgpainless.signature.consumer.SignatureValidator
|
import org.pgpainless.signature.consumer.SignatureValidator
|
||||||
import org.pgpainless.util.ArmoredInputStreamFactory
|
import org.pgpainless.util.ArmoredInputStreamFactory
|
||||||
|
@ -312,8 +312,7 @@ class OpenPgpMessageInputStream(
|
||||||
signatures
|
signatures
|
||||||
.leaveNesting() // TODO: Only leave nesting if all OPSs of the nesting layer are
|
.leaveNesting() // TODO: Only leave nesting if all OPSs of the nesting layer are
|
||||||
// dealt with
|
// dealt with
|
||||||
signatures.addCorrespondingOnePassSignature(
|
signatures.addCorrespondingOnePassSignature(signature, layerMetadata)
|
||||||
signature, layerMetadata, api.algorithmPolicy)
|
|
||||||
} else {
|
} else {
|
||||||
LOGGER.debug(
|
LOGGER.debug(
|
||||||
"Prepended Signature Packet by key ${keyId.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.")
|
"Prepended Signature Packet by key ${keyId.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.")
|
||||||
|
@ -618,7 +617,7 @@ class OpenPgpMessageInputStream(
|
||||||
throw RuntimeException(e)
|
throw RuntimeException(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
signatures.finish(layerMetadata, api.algorithmPolicy)
|
signatures.finish(layerMetadata)
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
@ -645,7 +644,7 @@ class OpenPgpMessageInputStream(
|
||||||
throw RuntimeException(e)
|
throw RuntimeException(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
signatures.finish(layerMetadata, api.algorithmPolicy)
|
signatures.finish(layerMetadata)
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
@ -832,15 +831,11 @@ class OpenPgpMessageInputStream(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addCorrespondingOnePassSignature(
|
fun addCorrespondingOnePassSignature(signature: PGPSignature, layer: Layer) {
|
||||||
signature: PGPSignature,
|
|
||||||
layer: Layer,
|
|
||||||
policy: Policy
|
|
||||||
) {
|
|
||||||
var found = false
|
var found = false
|
||||||
val keyId = signature.issuerKeyId
|
|
||||||
for ((i, check) in onePassSignatures.withIndex().reversed()) {
|
for ((i, check) in onePassSignatures.withIndex().reversed()) {
|
||||||
if (check.onePassSignature.keyID != keyId) {
|
if (!KeyIdentifier.matches(
|
||||||
|
signature.keyIdentifiers, check.onePassSignature.keyIdentifier, true)) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
found = true
|
found = true
|
||||||
|
@ -848,8 +843,11 @@ class OpenPgpMessageInputStream(
|
||||||
if (check.signature != null) {
|
if (check.signature != null) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
check.signature = signature
|
check.signature = signature
|
||||||
|
|
||||||
|
val documentSignature =
|
||||||
|
OpenPGPDocumentSignature(
|
||||||
|
signature, check.verificationKeys.getSigningKeyFor(signature))
|
||||||
val verification =
|
val verification =
|
||||||
SignatureVerification(
|
SignatureVerification(
|
||||||
signature,
|
signature,
|
||||||
|
@ -861,11 +859,15 @@ class OpenPgpMessageInputStream(
|
||||||
SignatureValidator.signatureWasCreatedInBounds(
|
SignatureValidator.signatureWasCreatedInBounds(
|
||||||
options.getVerifyNotBefore(), options.getVerifyNotAfter())
|
options.getVerifyNotBefore(), options.getVerifyNotAfter())
|
||||||
.verify(signature)
|
.verify(signature)
|
||||||
CertificateValidator.validateCertificateAndVerifyOnePassSignature(check, policy)
|
if (documentSignature.verify(check.onePassSignature) &&
|
||||||
LOGGER.debug("Acceptable signature by key ${verification.signingKey}")
|
documentSignature.isValid(api.implementation.policy())) {
|
||||||
layer.addVerifiedOnePassSignature(verification)
|
layer.addVerifiedOnePassSignature(verification)
|
||||||
|
} else {
|
||||||
|
throw SignatureValidationException("Incorrect OnePassSignature.")
|
||||||
|
}
|
||||||
|
} catch (e: MalformedOpenPGPSignatureException) {
|
||||||
|
throw SignatureValidationException("Malformed OnePassSignature.", e)
|
||||||
} catch (e: SignatureValidationException) {
|
} catch (e: SignatureValidationException) {
|
||||||
LOGGER.debug("Rejected signature by key ${verification.signingKey}", e)
|
|
||||||
layer.addRejectedOnePassSignature(
|
layer.addRejectedOnePassSignature(
|
||||||
SignatureVerification.Failure(verification, e))
|
SignatureVerification.Failure(verification, e))
|
||||||
}
|
}
|
||||||
|
@ -874,7 +876,7 @@ class OpenPgpMessageInputStream(
|
||||||
|
|
||||||
if (!found) {
|
if (!found) {
|
||||||
LOGGER.debug(
|
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(
|
inbandSignaturesWithMissingCert.add(
|
||||||
SignatureVerification.Failure(
|
SignatureVerification.Failure(
|
||||||
signature, null, SignatureValidationException("Missing verification key.")))
|
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) {
|
for (detached in detachedSignatures) {
|
||||||
val verification =
|
val verification =
|
||||||
SignatureVerification(detached.signature, SubkeyIdentifier(detached.issuer))
|
SignatureVerification(detached.signature, SubkeyIdentifier(detached.issuer))
|
||||||
|
@ -971,12 +973,14 @@ class OpenPgpMessageInputStream(
|
||||||
SignatureValidator.signatureWasCreatedInBounds(
|
SignatureValidator.signatureWasCreatedInBounds(
|
||||||
options.getVerifyNotBefore(), options.getVerifyNotAfter())
|
options.getVerifyNotBefore(), options.getVerifyNotAfter())
|
||||||
.verify(detached.signature)
|
.verify(detached.signature)
|
||||||
CertificateValidator.validateCertificateAndVerifyInitializedSignature(
|
if (detached.verify() && detached.isValid(api.implementation.policy())) {
|
||||||
detached.signature, detached.issuerCertificate.pgpPublicKeyRing, policy)
|
layer.addVerifiedDetachedSignature(verification)
|
||||||
LOGGER.debug("Acceptable signature by key ${verification.signingKey}")
|
} else {
|
||||||
layer.addVerifiedDetachedSignature(verification)
|
throw SignatureValidationException("Incorrect detached signature.")
|
||||||
|
}
|
||||||
|
} catch (e: MalformedOpenPGPSignatureException) {
|
||||||
|
throw SignatureValidationException("Malformed detached signature.", e)
|
||||||
} catch (e: SignatureValidationException) {
|
} catch (e: SignatureValidationException) {
|
||||||
LOGGER.debug("Rejected signature by key ${verification.signingKey}", e)
|
|
||||||
layer.addRejectedDetachedSignature(
|
layer.addRejectedDetachedSignature(
|
||||||
SignatureVerification.Failure(verification, e))
|
SignatureVerification.Failure(verification, e))
|
||||||
}
|
}
|
||||||
|
@ -989,10 +993,13 @@ class OpenPgpMessageInputStream(
|
||||||
SignatureValidator.signatureWasCreatedInBounds(
|
SignatureValidator.signatureWasCreatedInBounds(
|
||||||
options.getVerifyNotBefore(), options.getVerifyNotAfter())
|
options.getVerifyNotBefore(), options.getVerifyNotAfter())
|
||||||
.verify(prepended.signature)
|
.verify(prepended.signature)
|
||||||
CertificateValidator.validateCertificateAndVerifyInitializedSignature(
|
if (prepended.verify() && prepended.isValid(api.implementation.policy())) {
|
||||||
prepended.signature, prepended.issuerCertificate.pgpPublicKeyRing, policy)
|
layer.addVerifiedPrependedSignature(verification)
|
||||||
LOGGER.debug("Acceptable signature by key ${verification.signingKey}")
|
} else {
|
||||||
layer.addVerifiedPrependedSignature(verification)
|
throw SignatureValidationException("Incorrect prepended signature.")
|
||||||
|
}
|
||||||
|
} catch (e: MalformedOpenPGPSignatureException) {
|
||||||
|
throw SignatureValidationException("Malformed prepended signature.", e)
|
||||||
} catch (e: SignatureValidationException) {
|
} catch (e: SignatureValidationException) {
|
||||||
LOGGER.debug("Rejected signature by key ${verification.signingKey}", e)
|
LOGGER.debug("Rejected signature by key ${verification.signingKey}", e)
|
||||||
layer.addRejectedPrependedSignature(
|
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
|
@Test
|
||||||
public void assertModifiedDSAKeyThrowsKeyIntegrityException() throws IOException {
|
public void assertModifiedDSAKeyThrowsKeyIntegrityException() throws IOException {
|
||||||
System.out.println(DSA);
|
|
||||||
PGPainless api = PGPainless.newInstance();
|
PGPainless api = PGPainless.newInstance();
|
||||||
Policy policy = api.getAlgorithmPolicy();
|
Policy policy = api.getAlgorithmPolicy();
|
||||||
policy.setEnableKeyParameterValidation(true);
|
policy.setEnableKeyParameterValidation(true);
|
||||||
|
|
|
@ -19,7 +19,6 @@ import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKey;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
|
@ -36,9 +35,7 @@ import org.pgpainless.encryption_signing.SigningOptions;
|
||||||
import org.pgpainless.exception.WrongConsumingMethodException;
|
import org.pgpainless.exception.WrongConsumingMethodException;
|
||||||
import org.pgpainless.key.TestKeys;
|
import org.pgpainless.key.TestKeys;
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
import org.pgpainless.signature.consumer.CertificateValidator;
|
|
||||||
import org.pgpainless.signature.SignatureUtils;
|
import org.pgpainless.signature.SignatureUtils;
|
||||||
import org.pgpainless.signature.consumer.SignatureVerifier;
|
|
||||||
import org.pgpainless.util.ArmorUtils;
|
import org.pgpainless.util.ArmorUtils;
|
||||||
import org.pgpainless.util.TestUtils;
|
import org.pgpainless.util.TestUtils;
|
||||||
|
|
||||||
|
@ -136,19 +133,6 @@ public class CleartextSignatureVerificationTest {
|
||||||
assertArrayEquals(MESSAGE_BODY, bytes.toByteArray());
|
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 {
|
public static void main(String[] args) throws IOException {
|
||||||
// CHECKSTYLE:OFF
|
// CHECKSTYLE:OFF
|
||||||
PGPPublicKeyRing keys = TestKeys.getEmilPublicKeyRing();
|
PGPPublicKeyRing keys = TestKeys.getEmilPublicKeyRing();
|
||||||
|
|
|
@ -4,23 +4,23 @@
|
||||||
|
|
||||||
package org.pgpainless.signature;
|
package org.pgpainless.signature;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
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.TestTemplate;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.exception.SignatureValidationException;
|
import org.pgpainless.decryption_verification.ConsumerOptions;
|
||||||
import org.pgpainless.policy.Policy;
|
import org.pgpainless.decryption_verification.DecryptionStream;
|
||||||
import org.pgpainless.signature.consumer.CertificateValidator;
|
import org.pgpainless.decryption_verification.MessageMetadata;
|
||||||
import org.pgpainless.util.TestAllImplementations;
|
import org.pgpainless.util.TestAllImplementations;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,12 +47,9 @@ public class BindingSignatureSubpacketsTest {
|
||||||
"-----END PGP SIGNATURE-----\n";
|
"-----END PGP SIGNATURE-----\n";
|
||||||
private static final String data = "Hello World :)";
|
private static final String data = "Hello World :)";
|
||||||
|
|
||||||
private Date validationDate = new Date();
|
|
||||||
private Policy policy = PGPainless.getPolicy();
|
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void baseCase() throws IOException {
|
public void baseCase() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -113,7 +110,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void subkeyBindingIssuerFpOnly() throws IOException {
|
public void subkeyBindingIssuerFpOnly() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -174,7 +171,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void subkeyBindingIssuerV6IssuerFp() throws IOException {
|
public void subkeyBindingIssuerV6IssuerFp() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -235,7 +232,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void subkeyBindingIssuerFakeIssuer() throws IOException {
|
public void subkeyBindingIssuerFakeIssuer() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -296,7 +293,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void subkeyBindingFakeIssuerIssuer() throws IOException {
|
public void subkeyBindingFakeIssuerIssuer() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -357,7 +354,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void subkeyBindingFakeIssuer() throws IOException {
|
public void subkeyBindingFakeIssuer() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -418,7 +415,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void subkeyBindingNoIssuer() throws IOException {
|
public void subkeyBindingNoIssuer() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -478,7 +475,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void unknownSubpacketHashed() throws IOException {
|
public void unknownSubpacketHashed() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -539,7 +536,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void subkeyBindingUnknownCriticalSubpacket() throws IOException {
|
public void subkeyBindingUnknownCriticalSubpacket() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -600,7 +597,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void subkeyBindingUnknownSubpacketUnhashed() throws IOException {
|
public void subkeyBindingUnknownSubpacketUnhashed() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -661,7 +658,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void subkeyBindingUnknownCriticalSubpacketUnhashed() throws IOException {
|
public void subkeyBindingUnknownCriticalSubpacketUnhashed() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -722,7 +719,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void subkeyBindingUnknownNotationHashed() throws IOException {
|
public void subkeyBindingUnknownNotationHashed() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -784,7 +781,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void subkeyBindingCriticalUnknownNotationHashed() throws IOException {
|
public void subkeyBindingCriticalUnknownNotationHashed() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -846,7 +843,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void subkeyBindingUnknownNotationUnhashed() throws IOException {
|
public void subkeyBindingUnknownNotationUnhashed() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -908,7 +905,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void subkeyBindingCriticalUnknownNotationUnhashed() throws IOException {
|
public void subkeyBindingCriticalUnknownNotationUnhashed() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -970,7 +967,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void subkeyBindingBackSigFakeBackSig() throws IOException {
|
public void subkeyBindingBackSigFakeBackSig() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1042,7 +1039,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void subkeyBindingFakeBackSigBackSig() throws IOException {
|
public void subkeyBindingFakeBackSigBackSig() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1114,7 +1111,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void primaryBindingIssuerFpOnly() throws IOException {
|
public void primaryBindingIssuerFpOnly() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1175,7 +1172,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void primaryBindingIssuerV6IssuerFp() throws IOException {
|
public void primaryBindingIssuerV6IssuerFp() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1236,7 +1233,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void primaryBindingIssuerFakeIssuer() throws IOException {
|
public void primaryBindingIssuerFakeIssuer() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1297,7 +1294,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void primaryBindingFakeIssuerIssuer() throws IOException {
|
public void primaryBindingFakeIssuerIssuer() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1358,7 +1355,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void primaryBindingFakeIssuer() throws IOException {
|
public void primaryBindingFakeIssuer() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1419,7 +1416,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void primaryBindingNoIssuer() throws IOException {
|
public void primaryBindingNoIssuer() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1479,7 +1476,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void primaryBindingUnknownSubpacketHashed() throws IOException {
|
public void primaryBindingUnknownSubpacketHashed() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1540,7 +1537,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void primaryBindingCriticalUnknownSubpacketHashed() throws IOException {
|
public void primaryBindingCriticalUnknownSubpacketHashed() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1601,7 +1598,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void primaryBindingUnknownSubpacketUnhashed() throws IOException {
|
public void primaryBindingUnknownSubpacketUnhashed() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1662,7 +1659,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void primaryBindingCriticalUnknownSubpacketUnhashed() throws IOException {
|
public void primaryBindingCriticalUnknownSubpacketUnhashed() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1723,7 +1720,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void primaryBindingUnknownNotationHashed() throws IOException {
|
public void primaryBindingUnknownNotationHashed() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1785,7 +1782,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void primaryBindingCriticalUnknownNotationHashed() throws IOException {
|
public void primaryBindingCriticalUnknownNotationHashed() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1847,7 +1844,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void primaryBindingUnknownNotationUnhashed() throws IOException {
|
public void primaryBindingUnknownNotationUnhashed() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1909,7 +1906,7 @@ public class BindingSignatureSubpacketsTest {
|
||||||
|
|
||||||
@TestTemplate
|
@TestTemplate
|
||||||
@ExtendWith(TestAllImplementations.class)
|
@ExtendWith(TestAllImplementations.class)
|
||||||
public void primaryBindingCriticalUnknownNotationUnhashed() throws IOException {
|
public void primaryBindingCriticalUnknownNotationUnhashed() throws IOException, PGPException {
|
||||||
|
|
||||||
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
String key = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -1969,27 +1966,38 @@ public class BindingSignatureSubpacketsTest {
|
||||||
expectSignatureValidationSucceeds(key, "Critical unknown notation is acceptable in unhashed area of primary key binding sig.");
|
expectSignatureValidationSucceeds(key, "Critical unknown notation is acceptable in unhashed area of primary key binding sig.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void expectSignatureValidationSucceeds(String key, String message) throws IOException {
|
private void expectSignatureValidationSucceeds(String key, String message) throws IOException, PGPException {
|
||||||
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key);
|
PGPainless api = PGPainless.getInstance();
|
||||||
PGPSignature signature = SignatureUtils.readSignatures(sig).get(0);
|
OpenPGPCertificate certificate = api.readKey().parseCertificate(key);
|
||||||
|
|
||||||
try {
|
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify().onInputStream(getSignedData(data))
|
||||||
CertificateValidator.validateCertificateAndVerifyUninitializedSignature(signature, getSignedData(data), publicKeys, policy, validationDate);
|
.withOptions(ConsumerOptions.get(api)
|
||||||
} catch (SignatureValidationException e) {
|
.addVerificationCert(certificate)
|
||||||
// CHECKSTYLE:OFF
|
.addVerificationOfDetachedSignatures(SignatureUtils.readSignatures(sig)));
|
||||||
e.printStackTrace();
|
|
||||||
// CHECKSTYLE:ON
|
Streams.drain(decryptionStream);
|
||||||
fail(message + ": " + e.getMessage());
|
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 {
|
private void expectSignatureValidationFails(String key, String message) throws IOException, PGPException {
|
||||||
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key);
|
PGPainless api = PGPainless.getInstance();
|
||||||
PGPSignature signature = SignatureUtils.readSignatures(sig).get(0);
|
OpenPGPCertificate certificate = api.readKey().parseCertificate(key);
|
||||||
|
|
||||||
assertThrows(SignatureValidationException.class, () ->
|
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify().onInputStream(getSignedData(data))
|
||||||
CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
.withOptions(ConsumerOptions.get(api)
|
||||||
signature, getSignedData(data), publicKeys, policy, validationDate),
|
.addVerificationCert(certificate)
|
||||||
|
.addVerificationOfDetachedSignatures(SignatureUtils.readSignatures(sig)));
|
||||||
|
|
||||||
|
Streams.drain(decryptionStream);
|
||||||
|
decryptionStream.close();
|
||||||
|
assertFalse(decryptionStream.getMetadata().isVerifiedSignedBy(certificate),
|
||||||
message);
|
message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import java.util.Date;
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
|
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
|
||||||
import org.bouncycastle.util.io.Streams;
|
import org.bouncycastle.util.io.Streams;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.TestTemplate;
|
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.decryption_verification.MessageMetadata;
|
||||||
import org.pgpainless.exception.SignatureValidationException;
|
import org.pgpainless.exception.SignatureValidationException;
|
||||||
import org.pgpainless.policy.Policy;
|
import org.pgpainless.policy.Policy;
|
||||||
import org.pgpainless.signature.consumer.CertificateValidator;
|
|
||||||
import org.pgpainless.util.TestAllImplementations;
|
import org.pgpainless.util.TestAllImplementations;
|
||||||
|
|
||||||
public class CertificateValidatorTest {
|
public class CertificateValidatorTest {
|
||||||
|
@ -170,16 +170,16 @@ public class CertificateValidatorTest {
|
||||||
Date validationDate = new Date();
|
Date validationDate = new Date();
|
||||||
String data = "Hello, World";
|
String data = "Hello, World";
|
||||||
|
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate),
|
predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Signature predates primary key");
|
"Signature predates primary key");
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
unboundSubkey, getSignedData(data), publicKeys, policy, validationDate),
|
unboundSubkey, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Primary key hard revoked");
|
"Primary key hard revoked");
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
primaryKeyRevoked, getSignedData(data), publicKeys, policy, validationDate),
|
primaryKeyRevoked, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Primary key hard revoked");
|
"Primary key hard revoked");
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
primaryKeyRevalidated, getSignedData(data), publicKeys, policy, validationDate),
|
primaryKeyRevalidated, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Primary key hard revoked");
|
"Primary key hard revoked");
|
||||||
}
|
}
|
||||||
|
@ -321,16 +321,16 @@ public class CertificateValidatorTest {
|
||||||
Date validationDate = new Date();
|
Date validationDate = new Date();
|
||||||
String data = "Hello, World";
|
String data = "Hello, World";
|
||||||
|
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate),
|
predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Signature predates primary key");
|
"Signature predates primary key");
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
unboundSubkey, getSignedData(data), publicKeys, policy, validationDate),
|
unboundSubkey, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Signing key unbound + hard revocation");
|
"Signing key unbound + hard revocation");
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
revokedSubkey, getSignedData(data), publicKeys, policy, validationDate),
|
revokedSubkey, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Primary key is hard revoked");
|
"Primary key is hard revoked");
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
revalidatedSubkey, getSignedData(data), publicKeys, policy, validationDate),
|
revalidatedSubkey, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Primary key is hard revoked");
|
"Primary key is hard revoked");
|
||||||
}
|
}
|
||||||
|
@ -473,16 +473,16 @@ public class CertificateValidatorTest {
|
||||||
Date validationDate = new Date();
|
Date validationDate = new Date();
|
||||||
String data = "Hello World :)";
|
String data = "Hello World :)";
|
||||||
|
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate),
|
predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Signature predates primary key");
|
"Signature predates primary key");
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
unboundKey, getSignedData(data), publicKeys, policy, validationDate),
|
unboundKey, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Signing key unbound + hard revocation");
|
"Signing key unbound + hard revocation");
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
afterHardRevocation, getSignedData(data), publicKeys, policy, validationDate),
|
afterHardRevocation, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Hard revocation invalidates key at all times");
|
"Hard revocation invalidates key at all times");
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
afterRevalidation, getSignedData(data), publicKeys, policy, validationDate),
|
afterRevalidation, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Hard revocation invalidates key at all times");
|
"Hard revocation invalidates key at all times");
|
||||||
}
|
}
|
||||||
|
@ -624,22 +624,22 @@ public class CertificateValidatorTest {
|
||||||
String data = "Hello, World";
|
String data = "Hello, World";
|
||||||
|
|
||||||
// Sig not valid, as it predates the signing key creation time
|
// 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()),
|
predatesPrimaryKey, getSignedData(data), publicKeys, policy, predatesPrimaryKey.getCreationTime()),
|
||||||
"Signature predates primary key creation date");
|
"Signature predates primary key creation date");
|
||||||
|
|
||||||
// Sig valid
|
// Sig valid
|
||||||
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertDoesNotThrow(() -> verify(
|
||||||
keyIsValid, getSignedData(data), publicKeys, policy, keyIsValid.getCreationTime()),
|
keyIsValid, getSignedData(data), publicKeys, policy, keyIsValid.getCreationTime()),
|
||||||
"Signature is valid");
|
"Signature is valid");
|
||||||
|
|
||||||
// Sig not valid, as the signing key is revoked
|
// 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()),
|
keyIsRevoked, getSignedData(data), publicKeys, policy, keyIsRevoked.getCreationTime()),
|
||||||
"Signing key is revoked at this point");
|
"Signing key is revoked at this point");
|
||||||
|
|
||||||
// Sig valid, as the signing key is revalidated
|
// Sig valid, as the signing key is revalidated
|
||||||
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertDoesNotThrow(() -> verify(
|
||||||
keyIsRevalidated, getSignedData(data), publicKeys, policy, keyIsRevalidated.getCreationTime()),
|
keyIsRevalidated, getSignedData(data), publicKeys, policy, keyIsRevalidated.getCreationTime()),
|
||||||
"Signature is valid, as signing key is revalidated");
|
"Signature is valid, as signing key is revalidated");
|
||||||
}
|
}
|
||||||
|
@ -782,17 +782,17 @@ public class CertificateValidatorTest {
|
||||||
String data = "Hello, World";
|
String data = "Hello, World";
|
||||||
Date validationDate = new Date();
|
Date validationDate = new Date();
|
||||||
|
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate),
|
predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Signature predates primary key creation date");
|
"Signature predates primary key creation date");
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
keyNotBound, getSignedData(data), publicKeys, policy, validationDate),
|
keyNotBound, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Signing key is not bound at this point");
|
"Signing key is not bound at this point");
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
keyRevoked, getSignedData(data), publicKeys, policy, validationDate),
|
keyRevoked, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Signing key is revoked at this point");
|
"Signing key is revoked at this point");
|
||||||
assertDoesNotThrow(() ->
|
assertDoesNotThrow(() ->
|
||||||
CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
verify(
|
||||||
valid, getSignedData(data), publicKeys, policy, validationDate),
|
valid, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Signing key is revalidated");
|
"Signing key is revalidated");
|
||||||
}
|
}
|
||||||
|
@ -935,17 +935,17 @@ public class CertificateValidatorTest {
|
||||||
Date validationDate = new Date();
|
Date validationDate = new Date();
|
||||||
String data = "Hello, World";
|
String data = "Hello, World";
|
||||||
|
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate),
|
predatesPrimaryKey, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Signature predates primary key creation date");
|
"Signature predates primary key creation date");
|
||||||
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertDoesNotThrow(() -> verify(
|
||||||
valid, getSignedData(data), publicKeys, policy, validationDate),
|
valid, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Signature is valid");
|
"Signature is valid");
|
||||||
assertThrows(SignatureValidationException.class, () ->
|
assertThrows(SignatureValidationException.class, () ->
|
||||||
CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
verify(
|
||||||
revoked, getSignedData(data), publicKeys, policy, validationDate),
|
revoked, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Primary key is revoked");
|
"Primary key is revoked");
|
||||||
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertDoesNotThrow(() -> verify(
|
||||||
revalidated, getSignedData(data), publicKeys, policy, validationDate),
|
revalidated, getSignedData(data), publicKeys, policy, validationDate),
|
||||||
"Primary key is re-legitimized");
|
"Primary key is re-legitimized");
|
||||||
}
|
}
|
||||||
|
@ -1275,47 +1275,66 @@ public class CertificateValidatorTest {
|
||||||
Date validationDate = new Date();
|
Date validationDate = new Date();
|
||||||
String data = "Hello World :)";
|
String data = "Hello World :)";
|
||||||
|
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
sigAT0, getSignedData(data), keysA, policy, validationDate),
|
sigAT0, getSignedData(data), keysA, policy, validationDate),
|
||||||
"Signature predates key creation time");
|
"Signature predates key creation time");
|
||||||
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertDoesNotThrow(() -> verify(
|
||||||
sigAT1_T2, getSignedData(data), keysA, policy, validationDate),
|
sigAT1_T2, getSignedData(data), keysA, policy, validationDate),
|
||||||
"Key valid");
|
"Key valid");
|
||||||
assertThrows(SignatureValidationException.class, () ->
|
assertThrows(SignatureValidationException.class, () ->
|
||||||
CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
verify(
|
||||||
sigAT2_T3, getSignedData(data), keysA, policy, validationDate),
|
sigAT2_T3, getSignedData(data), keysA, policy, validationDate),
|
||||||
"Key is not valid, as subkey binding expired");
|
"Key is not valid, as subkey binding expired");
|
||||||
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertDoesNotThrow(() -> verify(
|
||||||
sigAT3_now, getSignedData(data), keysA, policy, validationDate),
|
sigAT3_now, getSignedData(data), keysA, policy, validationDate),
|
||||||
"Key is valid again");
|
"Key is valid again");
|
||||||
|
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
sigBT0, getSignedData(data), keysB, policy, validationDate),
|
sigBT0, getSignedData(data), keysB, policy, validationDate),
|
||||||
"Signature predates key creation time");
|
"Signature predates key creation time");
|
||||||
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertDoesNotThrow(() -> verify(
|
||||||
sigBT1_T2, getSignedData(data), keysB, policy, validationDate),
|
sigBT1_T2, getSignedData(data), keysB, policy, validationDate),
|
||||||
"Key is valid");
|
"Key is valid");
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
sigBT2_T3, getSignedData(data), keysB, policy, validationDate),
|
sigBT2_T3, getSignedData(data), keysB, policy, validationDate),
|
||||||
"Primary key is not signing-capable");
|
"Primary key is not signing-capable");
|
||||||
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertDoesNotThrow(() -> verify(
|
||||||
sigBT3_now, getSignedData(data), keysB, policy, validationDate),
|
sigBT3_now, getSignedData(data), keysB, policy, validationDate),
|
||||||
"Key is valid again");
|
"Key is valid again");
|
||||||
|
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
sigCT0, getSignedData(data), keysC, policy, validationDate),
|
sigCT0, getSignedData(data), keysC, policy, validationDate),
|
||||||
"Signature predates key creation time");
|
"Signature predates key creation time");
|
||||||
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertDoesNotThrow(() -> verify(
|
||||||
sigCT1_T2, getSignedData(data), keysC, policy, validationDate),
|
sigCT1_T2, getSignedData(data), keysC, policy, validationDate),
|
||||||
"Key is valid");
|
"Key is valid");
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertThrows(SignatureValidationException.class, () -> verify(
|
||||||
sigCT2_T3, getSignedData(data), keysC, policy, validationDate),
|
sigCT2_T3, getSignedData(data), keysC, policy, validationDate),
|
||||||
"Key is revoked");
|
"Key is revoked");
|
||||||
assertDoesNotThrow(() -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(
|
assertDoesNotThrow(() -> verify(
|
||||||
sigCT3_now, getSignedData(data), keysC, policy, validationDate),
|
sigCT3_now, getSignedData(data), keysC, policy, validationDate),
|
||||||
"Key is valid again");
|
"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) {
|
private static InputStream getSignedData(String data) {
|
||||||
return new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8));
|
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.ByteArrayInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
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.TestTemplate;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.pgpainless.PGPainless;
|
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.exception.SignatureValidationException;
|
||||||
import org.pgpainless.signature.consumer.CertificateValidator;
|
import org.pgpainless.policy.Policy;
|
||||||
import org.pgpainless.util.TestAllImplementations;
|
import org.pgpainless.util.TestAllImplementations;
|
||||||
|
|
||||||
public class KeyRevocationTest {
|
public class KeyRevocationTest {
|
||||||
|
@ -153,16 +159,16 @@ public class KeyRevocationTest {
|
||||||
PGPSignature t2t3 = SignatureUtils.readSignatures(sigT2T3).get(0);
|
PGPSignature t2t3 = SignatureUtils.readSignatures(sigT2T3).get(0);
|
||||||
PGPSignature t3now = SignatureUtils.readSignatures(sigT3Now).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)),
|
new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)),
|
||||||
publicKeys, PGPainless.getPolicy(), new Date()));
|
publicKeys, PGPainless.getPolicy(), new Date()));
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(t1t2,
|
assertThrows(SignatureValidationException.class, () -> verify(t1t2,
|
||||||
new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)),
|
new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)),
|
||||||
publicKeys, PGPainless.getPolicy(), new Date()));
|
publicKeys, PGPainless.getPolicy(), new Date()));
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(t2t3,
|
assertThrows(SignatureValidationException.class, () -> verify(t2t3,
|
||||||
new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)),
|
new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)),
|
||||||
publicKeys, PGPainless.getPolicy(), new Date()));
|
publicKeys, PGPainless.getPolicy(), new Date()));
|
||||||
assertThrows(SignatureValidationException.class, () -> CertificateValidator.validateCertificateAndVerifyUninitializedSignature(t3now,
|
assertThrows(SignatureValidationException.class, () -> verify(t3now,
|
||||||
new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)),
|
new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)),
|
||||||
publicKeys, PGPainless.getPolicy(), new Date()));
|
publicKeys, PGPainless.getPolicy(), new Date()));
|
||||||
}
|
}
|
||||||
|
@ -255,8 +261,29 @@ public class KeyRevocationTest {
|
||||||
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key);
|
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key);
|
||||||
PGPSignature signature = SignatureUtils.readSignatures(sig).get(0);
|
PGPSignature signature = SignatureUtils.readSignatures(sig).get(0);
|
||||||
|
|
||||||
CertificateValidator.validateCertificateAndVerifyUninitializedSignature(signature,
|
verify(signature,
|
||||||
new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)),
|
new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)),
|
||||||
publicKeys, PGPainless.getPolicy(), new Date());
|
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