From dcb78ddedf5deda5db422dba98590517bbe7ce0f Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 17 Feb 2025 13:01:46 +0100 Subject: [PATCH] Improve KeyExceptions --- .../pgpainless/exception/KeyException.java | 82 ++++++++++++++++--- .../encryption_signing/EncryptionOptions.kt | 11 +-- .../encryption_signing/SigningOptions.kt | 42 +++++----- .../org/pgpainless/key/OpenPgpFingerprint.kt | 12 +-- 4 files changed, 99 insertions(+), 48 deletions(-) diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/KeyException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/KeyException.java index 65d27390..6664dea7 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/exception/KeyException.java +++ b/pgpainless-core/src/main/java/org/pgpainless/exception/KeyException.java @@ -4,7 +4,9 @@ package org.pgpainless.exception; +import org.bouncycastle.bcpg.KeyIdentifier; import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.api.OpenPGPCertificate; import org.pgpainless.algorithm.PublicKeyAlgorithm; import org.pgpainless.key.OpenPgpFingerprint; import org.pgpainless.util.DateUtil; @@ -33,6 +35,10 @@ public abstract class KeyException extends RuntimeException { public static class ExpiredKeyException extends KeyException { + public ExpiredKeyException(@Nonnull OpenPGPCertificate cert, @Nonnull Date expirationDate) { + this(OpenPgpFingerprint.of(cert), expirationDate); + } + public ExpiredKeyException(@Nonnull OpenPgpFingerprint fingerprint, @Nonnull Date expirationDate) { super("Key " + fingerprint + " is expired. Expiration date: " + DateUtil.formatUTCDate(expirationDate), fingerprint); } @@ -43,10 +49,29 @@ public abstract class KeyException extends RuntimeException { public RevokedKeyException(@Nonnull OpenPgpFingerprint fingerprint) { super("Key " + fingerprint + " appears to be revoked.", fingerprint); } + + public RevokedKeyException(@Nonnull OpenPGPCertificate.OpenPGPComponentKey key) { + super("Subkey " + key.getKeyIdentifier() + " appears to be revoked.", + OpenPgpFingerprint.of(key)); + } + + public RevokedKeyException(@Nonnull OpenPGPCertificate cert) { + super("Key or certificate " + cert.getKeyIdentifier() + " appears to be revoked.", + OpenPgpFingerprint.of(cert)); + } } public static class UnacceptableEncryptionKeyException extends KeyException { + public UnacceptableEncryptionKeyException(@Nonnull OpenPGPCertificate cert) { + this(OpenPgpFingerprint.of(cert)); + } + + public UnacceptableEncryptionKeyException(@Nonnull OpenPGPCertificate.OpenPGPComponentKey subkey) { + super("Subkey " + subkey.getKeyIdentifier() + " is not an acceptable encryption key.", + OpenPgpFingerprint.of(subkey)); + } + public UnacceptableEncryptionKeyException(@Nonnull OpenPgpFingerprint fingerprint) { super("Key " + fingerprint + " has no acceptable encryption key.", fingerprint); } @@ -58,6 +83,14 @@ public abstract class KeyException extends RuntimeException { public static class UnacceptableSigningKeyException extends KeyException { + public UnacceptableSigningKeyException(OpenPGPCertificate certificate) { + this(OpenPgpFingerprint.of(certificate)); + } + + public UnacceptableSigningKeyException(OpenPGPCertificate.OpenPGPComponentKey subkey) { + this(OpenPgpFingerprint.of(subkey)); + } + public UnacceptableSigningKeyException(@Nonnull OpenPgpFingerprint fingerprint) { super("Key " + fingerprint + " has no acceptable signing key.", fingerprint); } @@ -76,6 +109,10 @@ public abstract class KeyException extends RuntimeException { public static class UnacceptableSelfSignatureException extends KeyException { + public UnacceptableSelfSignatureException(@Nonnull OpenPGPCertificate cert) { + this(OpenPgpFingerprint.of(cert)); + } + public UnacceptableSelfSignatureException(@Nonnull OpenPgpFingerprint fingerprint) { super("Key " + fingerprint + " does not have a valid/acceptable signature to derive an expiration date from.", fingerprint); } @@ -83,29 +120,50 @@ public abstract class KeyException extends RuntimeException { public static class MissingSecretKeyException extends KeyException { - private final long missingSecretKeyId; + private final KeyIdentifier missingSecretKeyIdentifier; - public MissingSecretKeyException(@Nonnull OpenPgpFingerprint fingerprint, long keyId) { - super("Key " + fingerprint + " does not contain a secret key for public key " + Long.toHexString(keyId), fingerprint); - this.missingSecretKeyId = keyId; + public MissingSecretKeyException(@Nonnull OpenPGPCertificate.OpenPGPComponentKey publicKey) { + this(OpenPgpFingerprint.of(publicKey.getCertificate()), publicKey.getKeyIdentifier()); } - public long getMissingSecretKeyId() { - return missingSecretKeyId; + public MissingSecretKeyException(@Nonnull OpenPgpFingerprint fingerprint, KeyIdentifier keyIdentifier) { + super("Key " + fingerprint + " does not contain a secret key for public key " + keyIdentifier, fingerprint); + this.missingSecretKeyIdentifier = keyIdentifier; + } + + @Deprecated + public MissingSecretKeyException(@Nonnull OpenPgpFingerprint fingerprint, long keyId) { + this(fingerprint, new KeyIdentifier(keyId)); + } + + public KeyIdentifier getMissingSecretKeyIdentifier() { + return missingSecretKeyIdentifier; } } public static class PublicKeyAlgorithmPolicyException extends KeyException { - private final long violatingSubkeyId; + private final KeyIdentifier violatingSubkeyId; - public PublicKeyAlgorithmPolicyException(@Nonnull OpenPgpFingerprint fingerprint, long keyId, @Nonnull PublicKeyAlgorithm algorithm, int bitSize) { - super("Subkey " + Long.toHexString(keyId) + " of key " + fingerprint + " is violating the Public Key Algorithm Policy:\n" + - algorithm + " of size " + bitSize + " is not acceptable.", fingerprint); - this.violatingSubkeyId = keyId; + public PublicKeyAlgorithmPolicyException(@Nonnull OpenPGPCertificate.OpenPGPComponentKey subkey, + @Nonnull PublicKeyAlgorithm algorithm, + int bitSize) { + super("Subkey " + subkey.getKeyIdentifier() + " of key " + subkey.getCertificate().getKeyIdentifier() + + " is violating the Public Key Algorithm Policy:\n" + + algorithm + " of size " + bitSize + " is not acceptable.", OpenPgpFingerprint.of(subkey)); + this.violatingSubkeyId = subkey.getKeyIdentifier(); } - public long getViolatingSubkeyId() { + public PublicKeyAlgorithmPolicyException(@Nonnull OpenPgpFingerprint fingerprint, + long keyId, + @Nonnull PublicKeyAlgorithm algorithm, + int bitSize) { + super("Subkey " + Long.toHexString(keyId) + " of key " + fingerprint + " is violating the Public Key Algorithm Policy:\n" + + algorithm + " of size " + bitSize + " is not acceptable.", fingerprint); + this.violatingSubkeyId = new KeyIdentifier(keyId); + } + + public KeyIdentifier getViolatingSubkeyId() { return violatingSubkeyId; } } diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionOptions.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionOptions.kt index 71093fe4..d130a4f1 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionOptions.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionOptions.kt @@ -17,7 +17,6 @@ import org.pgpainless.bouncycastle.extensions.toOpenPGPCertificate import org.pgpainless.encryption_signing.EncryptionOptions.EncryptionKeySelector import org.pgpainless.exception.KeyException.* import org.pgpainless.implementation.ImplementationFactory -import org.pgpainless.key.OpenPgpFingerprint import org.pgpainless.key.SubkeyIdentifier import org.pgpainless.key.info.KeyAccessor import org.pgpainless.key.info.KeyRingInfo @@ -197,7 +196,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) { encryptionKeySelector.selectEncryptionSubkeys( info.getEncryptionSubkeys(userId, purpose)) if (subkeys.isEmpty()) { - throw UnacceptableEncryptionKeyException(OpenPgpFingerprint.of(cert)) + throw UnacceptableEncryptionKeyException(cert) } for (subkey in subkeys) { @@ -295,13 +294,11 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) { try { info.primaryKeyExpirationDate } catch (e: NoSuchElementException) { - throw UnacceptableSelfSignatureException( - OpenPgpFingerprint.of(cert)) + throw UnacceptableSelfSignatureException(cert) } if (primaryKeyExpiration != null && primaryKeyExpiration < evaluationDate) { - throw ExpiredKeyException( - OpenPgpFingerprint.of(cert), primaryKeyExpiration) + throw ExpiredKeyException(cert, primaryKeyExpiration) } var encryptionSubkeys = selector.selectEncryptionSubkeys(info.getEncryptionSubkeys(purpose)) @@ -318,7 +315,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) { } if (encryptionSubkeys.isEmpty()) { - throw UnacceptableEncryptionKeyException(OpenPgpFingerprint.of(cert)) + throw UnacceptableEncryptionKeyException(cert) } for (subkey in encryptionSubkeys) { diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt index 41b9a448..cc675619 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/SigningOptions.kt @@ -157,14 +157,13 @@ class SigningOptions { val signingPubKeys = keyRingInfo.signingSubkeys if (signingPubKeys.isEmpty()) { - throw UnacceptableSigningKeyException(of(signingKey)) + throw UnacceptableSigningKeyException(signingKey) } for (signingPubKey in signingPubKeys) { val signingSecKey: OpenPGPSecretKey = signingKey.getSecretKey(signingPubKey) - ?: throw MissingSecretKeyException( - of(signingKey), signingPubKey.keyIdentifier.keyId) + ?: throw MissingSecretKeyException(signingPubKey) val signingPrivKey: OpenPGPPrivateKey = unlockSecretKey(signingSecKey, signingKeyProtector) val hashAlgorithms = @@ -220,12 +219,11 @@ class SigningOptions { val keyRingInfo = inspectKeyRing(openPGPKey, evaluationDate) val signingPubKeys = keyRingInfo.signingSubkeys if (signingPubKeys.isEmpty()) { - throw UnacceptableSigningKeyException(of(openPGPKey)) + throw UnacceptableSigningKeyException(openPGPKey) } if (!signingPubKeys.any { it.keyIdentifier.matches(signingKey.keyIdentifier) }) { - throw MissingSecretKeyException( - of(openPGPKey), signingKey.keyIdentifier.keyId) + throw MissingSecretKeyException(signingKey) } val signingPrivKey = unlockSecretKey(signingKey, signingKeyProtector) @@ -258,13 +256,16 @@ class SigningOptions { keyId: Long, signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT, subpacketsCallback: Callback? = null - ) = - addInlineSignature( + ): SigningOptions { + val key = signingKey.toOpenPGPKey() + val subkeyIdentifier = KeyIdentifier(keyId) + return addInlineSignature( signingKeyProtector, - signingKey.toOpenPGPKey().getSecretKey(KeyIdentifier(keyId)) - ?: throw MissingSecretKeyException(of(signingKey), keyId), + key.getSecretKey(subkeyIdentifier) + ?: throw MissingSecretKeyException(of(signingKey), subkeyIdentifier), signatureType, subpacketsCallback) + } /** * Add detached signatures with all key rings from the provided secret key ring collection. @@ -332,14 +333,13 @@ class SigningOptions { val signingPubKeys = keyRingInfo.signingSubkeys if (signingPubKeys.isEmpty()) { - throw UnacceptableSigningKeyException(of(signingKey)) + throw UnacceptableSigningKeyException(signingKey) } for (signingPubKey in signingPubKeys) { val signingSecKey: OpenPGPSecretKey = signingKey.getSecretKey(signingPubKey.keyIdentifier) - ?: throw MissingSecretKeyException( - of(signingKey), signingPubKey.keyIdentifier.keyId) + ?: throw MissingSecretKeyException(signingPubKey) addDetachedSignature( signingKeyProtector, signingSecKey, userId, signatureType, subpacketCallback) } @@ -421,14 +421,17 @@ class SigningOptions { keyId: Long, signatureType: DocumentSignatureType = DocumentSignatureType.BINARY_DOCUMENT, subpacketsCallback: Callback? = null - ) = - addDetachedSignature( + ): SigningOptions { + val key = signingKey.toOpenPGPKey() + val signingKeyIdentifier = KeyIdentifier(keyId) + return addDetachedSignature( signingKeyProtector, - signingKey.toOpenPGPKey().getSecretKey(KeyIdentifier(keyId)) - ?: throw MissingSecretKeyException(of(signingKey), keyId), + key.getSecretKey(signingKeyIdentifier) + ?: throw MissingSecretKeyException(of(key), signingKeyIdentifier), null, signatureType, subpacketsCallback) + } private fun addSigningMethod( signingKey: OpenPGPPrivateKey, @@ -443,10 +446,7 @@ class SigningOptions { if (!getPolicy().publicKeyAlgorithmPolicy.isAcceptable(publicKeyAlgorithm, bitStrength)) { throw UnacceptableSigningKeyException( PublicKeyAlgorithmPolicyException( - of(signingKey), - signingSecretKey.keyID, - publicKeyAlgorithm, - bitStrength)) + signingKey.secretKey, publicKeyAlgorithm, bitStrength)) } val generator: PGPSignatureGenerator = diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpFingerprint.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpFingerprint.kt index 5352e67e..31347f99 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpFingerprint.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/key/OpenPgpFingerprint.kt @@ -132,17 +132,13 @@ abstract class OpenPgpFingerprint : CharSequence, Comparable */ @JvmStatic fun of(keys: PGPKeyRing): OpenPgpFingerprint = of(keys.publicKey) - /** - * Return the [OpenPgpFingerprint] of the primary key of the given [OpenPGPCertificate]. - */ + /** Return the [OpenPgpFingerprint] of the primary key of the given [OpenPGPCertificate]. */ @JvmStatic fun of(cert: OpenPGPCertificate): OpenPgpFingerprint = of(cert.pgpPublicKeyRing) - /** - * Return the [OpenPgpFingerprint] of the given [OpenPGPComponentKey]. - */ - @JvmStatic fun of (key: OpenPGPComponentKey): OpenPgpFingerprint = of(key.pgpPublicKey) + /** Return the [OpenPgpFingerprint] of the given [OpenPGPComponentKey]. */ + @JvmStatic fun of(key: OpenPGPComponentKey): OpenPgpFingerprint = of(key.pgpPublicKey) - @JvmStatic fun of (key: OpenPGPPrivateKey): OpenPgpFingerprint = of(key.secretKey) + @JvmStatic fun of(key: OpenPGPPrivateKey): OpenPgpFingerprint = of(key.secretKey) /** * Try to parse an [OpenPgpFingerprint] from the given fingerprint string. If the trimmed