mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-26 06:12:06 +01:00
Create dedicated KeyException class for key-related exceptions.
This commit is contained in:
parent
6b3f37796c
commit
a22336a795
13 changed files with 186 additions and 68 deletions
|
@ -23,6 +23,7 @@ import org.bouncycastle.openpgp.operator.PBEKeyEncryptionMethodGenerator;
|
||||||
import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator;
|
import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator;
|
||||||
import org.pgpainless.algorithm.EncryptionPurpose;
|
import org.pgpainless.algorithm.EncryptionPurpose;
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
|
import org.pgpainless.exception.KeyException;
|
||||||
import org.pgpainless.implementation.ImplementationFactory;
|
import org.pgpainless.implementation.ImplementationFactory;
|
||||||
import org.pgpainless.key.OpenPgpFingerprint;
|
import org.pgpainless.key.OpenPgpFingerprint;
|
||||||
import org.pgpainless.key.SubkeyIdentifier;
|
import org.pgpainless.key.SubkeyIdentifier;
|
||||||
|
@ -154,7 +155,7 @@ public class EncryptionOptions {
|
||||||
List<PGPPublicKey> encryptionSubkeys = encryptionKeySelectionStrategy
|
List<PGPPublicKey> encryptionSubkeys = encryptionKeySelectionStrategy
|
||||||
.selectEncryptionSubkeys(info.getEncryptionSubkeys(userId, purpose));
|
.selectEncryptionSubkeys(info.getEncryptionSubkeys(userId, purpose));
|
||||||
if (encryptionSubkeys.isEmpty()) {
|
if (encryptionSubkeys.isEmpty()) {
|
||||||
throw new IllegalArgumentException("Key has no suitable encryption subkeys.");
|
throw new KeyException.UnacceptableEncryptionKeyException(OpenPgpFingerprint.of(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PGPPublicKey encryptionSubkey : encryptionSubkeys) {
|
for (PGPPublicKey encryptionSubkey : encryptionSubkeys) {
|
||||||
|
@ -193,16 +194,16 @@ public class EncryptionOptions {
|
||||||
try {
|
try {
|
||||||
primaryKeyExpiration = info.getPrimaryKeyExpirationDate();
|
primaryKeyExpiration = info.getPrimaryKeyExpirationDate();
|
||||||
} catch (NoSuchElementException e) {
|
} catch (NoSuchElementException e) {
|
||||||
throw new IllegalArgumentException("Provided key " + OpenPgpFingerprint.of(key) + " does not have a valid/acceptable signature carrying a primary key expiration date.");
|
throw new KeyException.UnacceptableSelfSignatureException(OpenPgpFingerprint.of(key));
|
||||||
}
|
}
|
||||||
if (primaryKeyExpiration != null && primaryKeyExpiration.before(evaluationDate)) {
|
if (primaryKeyExpiration != null && primaryKeyExpiration.before(evaluationDate)) {
|
||||||
throw new IllegalArgumentException("Provided key " + OpenPgpFingerprint.of(key) + " is expired: " + primaryKeyExpiration);
|
throw new KeyException.ExpiredKeyException(OpenPgpFingerprint.of(key), primaryKeyExpiration);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<PGPPublicKey> encryptionSubkeys = encryptionKeySelectionStrategy
|
List<PGPPublicKey> encryptionSubkeys = encryptionKeySelectionStrategy
|
||||||
.selectEncryptionSubkeys(info.getEncryptionSubkeys(purpose));
|
.selectEncryptionSubkeys(info.getEncryptionSubkeys(purpose));
|
||||||
if (encryptionSubkeys.isEmpty()) {
|
if (encryptionSubkeys.isEmpty()) {
|
||||||
throw new IllegalArgumentException("Key " + OpenPgpFingerprint.of(key) + " has no suitable encryption subkeys.");
|
throw new KeyException.UnacceptableEncryptionKeyException(OpenPgpFingerprint.of(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PGPPublicKey encryptionSubkey : encryptionSubkeys) {
|
for (PGPPublicKey encryptionSubkey : encryptionSubkeys) {
|
||||||
|
|
|
@ -24,8 +24,7 @@ import org.pgpainless.algorithm.DocumentSignatureType;
|
||||||
import org.pgpainless.algorithm.HashAlgorithm;
|
import org.pgpainless.algorithm.HashAlgorithm;
|
||||||
import org.pgpainless.algorithm.PublicKeyAlgorithm;
|
import org.pgpainless.algorithm.PublicKeyAlgorithm;
|
||||||
import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator;
|
import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator;
|
||||||
import org.pgpainless.exception.KeyCannotSignException;
|
import org.pgpainless.exception.KeyException;
|
||||||
import org.pgpainless.exception.KeyValidationError;
|
|
||||||
import org.pgpainless.implementation.ImplementationFactory;
|
import org.pgpainless.implementation.ImplementationFactory;
|
||||||
import org.pgpainless.key.OpenPgpFingerprint;
|
import org.pgpainless.key.OpenPgpFingerprint;
|
||||||
import org.pgpainless.key.SubkeyIdentifier;
|
import org.pgpainless.key.SubkeyIdentifier;
|
||||||
|
@ -120,13 +119,13 @@ public final class SigningOptions {
|
||||||
* @param signingKeys collection of signing keys
|
* @param signingKeys collection of signing keys
|
||||||
* @param signatureType type of signature (binary, canonical text)
|
* @param signatureType type of signature (binary, canonical text)
|
||||||
* @return this
|
* @return this
|
||||||
* @throws KeyValidationError if something is wrong with any of the keys
|
* @throws KeyException if something is wrong with any of the keys
|
||||||
* @throws PGPException if any of the keys cannot be unlocked or a signing method cannot be created
|
* @throws PGPException if any of the keys cannot be unlocked or a signing method cannot be created
|
||||||
*/
|
*/
|
||||||
public SigningOptions addInlineSignatures(SecretKeyRingProtector secrectKeyDecryptor,
|
public SigningOptions addInlineSignatures(SecretKeyRingProtector secrectKeyDecryptor,
|
||||||
Iterable<PGPSecretKeyRing> signingKeys,
|
Iterable<PGPSecretKeyRing> signingKeys,
|
||||||
DocumentSignatureType signatureType)
|
DocumentSignatureType signatureType)
|
||||||
throws KeyValidationError, PGPException {
|
throws KeyException, PGPException {
|
||||||
for (PGPSecretKeyRing signingKey : signingKeys) {
|
for (PGPSecretKeyRing signingKey : signingKeys) {
|
||||||
addInlineSignature(secrectKeyDecryptor, signingKey, signatureType);
|
addInlineSignature(secrectKeyDecryptor, signingKey, signatureType);
|
||||||
}
|
}
|
||||||
|
@ -141,14 +140,14 @@ public final class SigningOptions {
|
||||||
* @param secretKeyDecryptor decryptor to unlock the signing secret key
|
* @param secretKeyDecryptor decryptor to unlock the signing secret key
|
||||||
* @param secretKey signing key
|
* @param secretKey signing key
|
||||||
* @param signatureType type of signature (binary, canonical text)
|
* @param signatureType type of signature (binary, canonical text)
|
||||||
* @throws KeyValidationError if something is wrong with the key
|
* @throws KeyException if something is wrong with the key
|
||||||
* @throws PGPException if the key cannot be unlocked or the signing method cannot be created
|
* @throws PGPException if the key cannot be unlocked or the signing method cannot be created
|
||||||
* @return this
|
* @return this
|
||||||
*/
|
*/
|
||||||
public SigningOptions addInlineSignature(SecretKeyRingProtector secretKeyDecryptor,
|
public SigningOptions addInlineSignature(SecretKeyRingProtector secretKeyDecryptor,
|
||||||
PGPSecretKeyRing secretKey,
|
PGPSecretKeyRing secretKey,
|
||||||
DocumentSignatureType signatureType)
|
DocumentSignatureType signatureType)
|
||||||
throws KeyValidationError, PGPException {
|
throws KeyException, PGPException {
|
||||||
return addInlineSignature(secretKeyDecryptor, secretKey, null, signatureType);
|
return addInlineSignature(secretKeyDecryptor, secretKey, null, signatureType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,14 +163,14 @@ public final class SigningOptions {
|
||||||
* @param userId user-id of the signer
|
* @param userId user-id of the signer
|
||||||
* @param signatureType signature type (binary, canonical text)
|
* @param signatureType signature type (binary, canonical text)
|
||||||
* @return this
|
* @return this
|
||||||
* @throws KeyValidationError if the key is invalid
|
* @throws KeyException if the key is invalid
|
||||||
* @throws PGPException if the key cannot be unlocked or the signing method cannot be created
|
* @throws PGPException if the key cannot be unlocked or the signing method cannot be created
|
||||||
*/
|
*/
|
||||||
public SigningOptions addInlineSignature(SecretKeyRingProtector secretKeyDecryptor,
|
public SigningOptions addInlineSignature(SecretKeyRingProtector secretKeyDecryptor,
|
||||||
PGPSecretKeyRing secretKey,
|
PGPSecretKeyRing secretKey,
|
||||||
String userId,
|
String userId,
|
||||||
DocumentSignatureType signatureType)
|
DocumentSignatureType signatureType)
|
||||||
throws KeyValidationError, PGPException {
|
throws KeyException, PGPException {
|
||||||
return addInlineSignature(secretKeyDecryptor, secretKey, userId, signatureType, null);
|
return addInlineSignature(secretKeyDecryptor, secretKey, userId, signatureType, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +187,8 @@ public final class SigningOptions {
|
||||||
* @param signatureType signature type (binary, canonical text)
|
* @param signatureType signature type (binary, canonical text)
|
||||||
* @param subpacketsCallback callback to modify the hashed and unhashed subpackets of the signature
|
* @param subpacketsCallback callback to modify the hashed and unhashed subpackets of the signature
|
||||||
* @return this
|
* @return this
|
||||||
* @throws KeyValidationError if the key is invalid
|
* @throws KeyException
|
||||||
|
* if the key is invalid
|
||||||
* @throws PGPException if the key cannot be unlocked or the signing method cannot be created
|
* @throws PGPException if the key cannot be unlocked or the signing method cannot be created
|
||||||
*/
|
*/
|
||||||
public SigningOptions addInlineSignature(SecretKeyRingProtector secretKeyDecryptor,
|
public SigningOptions addInlineSignature(SecretKeyRingProtector secretKeyDecryptor,
|
||||||
|
@ -196,19 +196,27 @@ public final class SigningOptions {
|
||||||
String userId,
|
String userId,
|
||||||
DocumentSignatureType signatureType,
|
DocumentSignatureType signatureType,
|
||||||
@Nullable BaseSignatureSubpackets.Callback subpacketsCallback)
|
@Nullable BaseSignatureSubpackets.Callback subpacketsCallback)
|
||||||
throws KeyValidationError, PGPException {
|
throws KeyException, PGPException {
|
||||||
KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date());
|
KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date());
|
||||||
if (userId != null && !keyRingInfo.isUserIdValid(userId)) {
|
if (userId != null && !keyRingInfo.isUserIdValid(userId)) {
|
||||||
throw new KeyValidationError(userId, keyRingInfo.getLatestUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId));
|
throw new KeyException.UnboundUserIdException(
|
||||||
|
OpenPgpFingerprint.of(secretKey),
|
||||||
|
userId,
|
||||||
|
keyRingInfo.getLatestUserIdCertification(userId),
|
||||||
|
keyRingInfo.getUserIdRevocation(userId)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<PGPPublicKey> signingPubKeys = keyRingInfo.getSigningSubkeys();
|
List<PGPPublicKey> signingPubKeys = keyRingInfo.getSigningSubkeys();
|
||||||
if (signingPubKeys.isEmpty()) {
|
if (signingPubKeys.isEmpty()) {
|
||||||
throw new KeyCannotSignException("Key " + OpenPgpFingerprint.of(secretKey) + " has no valid signing key.");
|
throw new KeyException.UnacceptableSigningKeyException(OpenPgpFingerprint.of(secretKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PGPPublicKey signingPubKey : signingPubKeys) {
|
for (PGPPublicKey signingPubKey : signingPubKeys) {
|
||||||
PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID());
|
PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID());
|
||||||
|
if (signingSecKey == null) {
|
||||||
|
throw new KeyException.MissingSecretKeyException(OpenPgpFingerprint.of(secretKey), signingPubKey.getKeyID());
|
||||||
|
}
|
||||||
PGPPrivateKey signingSubkey = UnlockSecretKey.unlockSecretKey(signingSecKey, secretKeyDecryptor);
|
PGPPrivateKey signingSubkey = UnlockSecretKey.unlockSecretKey(signingSecKey, secretKeyDecryptor);
|
||||||
Set<HashAlgorithm> hashAlgorithms = userId != null ? keyRingInfo.getPreferredHashAlgorithms(userId)
|
Set<HashAlgorithm> hashAlgorithms = userId != null ? keyRingInfo.getPreferredHashAlgorithms(userId)
|
||||||
: keyRingInfo.getPreferredHashAlgorithms(signingPubKey.getKeyID());
|
: keyRingInfo.getPreferredHashAlgorithms(signingPubKey.getKeyID());
|
||||||
|
@ -304,18 +312,23 @@ public final class SigningOptions {
|
||||||
throws PGPException {
|
throws PGPException {
|
||||||
KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date());
|
KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date());
|
||||||
if (userId != null && !keyRingInfo.isUserIdValid(userId)) {
|
if (userId != null && !keyRingInfo.isUserIdValid(userId)) {
|
||||||
throw new KeyValidationError(userId, keyRingInfo.getLatestUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId));
|
throw new KeyException.UnboundUserIdException(
|
||||||
|
OpenPgpFingerprint.of(secretKey),
|
||||||
|
userId,
|
||||||
|
keyRingInfo.getLatestUserIdCertification(userId),
|
||||||
|
keyRingInfo.getUserIdRevocation(userId)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<PGPPublicKey> signingPubKeys = keyRingInfo.getSigningSubkeys();
|
List<PGPPublicKey> signingPubKeys = keyRingInfo.getSigningSubkeys();
|
||||||
if (signingPubKeys.isEmpty()) {
|
if (signingPubKeys.isEmpty()) {
|
||||||
throw new KeyCannotSignException("Key has no valid signing key.");
|
throw new KeyException.UnacceptableSigningKeyException(OpenPgpFingerprint.of(secretKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (PGPPublicKey signingPubKey : signingPubKeys) {
|
for (PGPPublicKey signingPubKey : signingPubKeys) {
|
||||||
PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID());
|
PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID());
|
||||||
if (signingSecKey == null) {
|
if (signingSecKey == null) {
|
||||||
throw new PGPException("Missing secret key for signing key " + Long.toHexString(signingPubKey.getKeyID()));
|
throw new KeyException.MissingSecretKeyException(OpenPgpFingerprint.of(secretKey), signingPubKey.getKeyID());
|
||||||
}
|
}
|
||||||
PGPPrivateKey signingSubkey = signingSecKey.extractPrivateKey(
|
PGPPrivateKey signingSubkey = signingSecKey.extractPrivateKey(
|
||||||
secretKeyDecryptor.getDecryptor(signingPubKey.getKeyID()));
|
secretKeyDecryptor.getDecryptor(signingPubKey.getKeyID()));
|
||||||
|
@ -340,8 +353,9 @@ public final class SigningOptions {
|
||||||
PublicKeyAlgorithm publicKeyAlgorithm = PublicKeyAlgorithm.requireFromId(signingSecretKey.getPublicKey().getAlgorithm());
|
PublicKeyAlgorithm publicKeyAlgorithm = PublicKeyAlgorithm.requireFromId(signingSecretKey.getPublicKey().getAlgorithm());
|
||||||
int bitStrength = secretKey.getPublicKey().getBitStrength();
|
int bitStrength = secretKey.getPublicKey().getBitStrength();
|
||||||
if (!PGPainless.getPolicy().getPublicKeyAlgorithmPolicy().isAcceptable(publicKeyAlgorithm, bitStrength)) {
|
if (!PGPainless.getPolicy().getPublicKeyAlgorithmPolicy().isAcceptable(publicKeyAlgorithm, bitStrength)) {
|
||||||
throw new IllegalArgumentException("Public key algorithm policy violation: " +
|
throw new KeyException.UnacceptableSigningKeyException(
|
||||||
publicKeyAlgorithm + " with bit strength " + bitStrength + " is not acceptable.");
|
new KeyException.PublicKeyAlgorithmPolicyException(
|
||||||
|
OpenPgpFingerprint.of(secretKey), signingSecretKey.getKeyID(), publicKeyAlgorithm, bitStrength));
|
||||||
}
|
}
|
||||||
|
|
||||||
PGPSignatureGenerator generator = createSignatureGenerator(signingSubkey, hashAlgorithm, signatureType);
|
PGPSignatureGenerator generator = createSignatureGenerator(signingSubkey, hashAlgorithm, signatureType);
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package org.pgpainless.exception;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
|
|
||||||
public class KeyCannotSignException extends PGPException {
|
|
||||||
public KeyCannotSignException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.exception;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
|
import org.pgpainless.algorithm.PublicKeyAlgorithm;
|
||||||
|
import org.pgpainless.key.OpenPgpFingerprint;
|
||||||
|
import org.pgpainless.util.DateUtil;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public abstract class KeyException extends RuntimeException {
|
||||||
|
|
||||||
|
private final OpenPgpFingerprint fingerprint;
|
||||||
|
|
||||||
|
protected KeyException(@Nonnull String message, @Nonnull OpenPgpFingerprint fingerprint) {
|
||||||
|
super(message);
|
||||||
|
this.fingerprint = fingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected KeyException(@Nonnull String message, @Nonnull OpenPgpFingerprint fingerprint, @Nonnull Throwable underlying) {
|
||||||
|
super(message, underlying);
|
||||||
|
this.fingerprint = fingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OpenPgpFingerprint getFingerprint() {
|
||||||
|
return fingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ExpiredKeyException extends KeyException {
|
||||||
|
|
||||||
|
public ExpiredKeyException(@Nonnull OpenPgpFingerprint fingerprint, @Nonnull Date expirationDate) {
|
||||||
|
super("Key " + fingerprint + " is expired. Expiration date: " + DateUtil.formatUTCDate(expirationDate), fingerprint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UnacceptableEncryptionKeyException extends KeyException {
|
||||||
|
|
||||||
|
public UnacceptableEncryptionKeyException(@Nonnull OpenPgpFingerprint fingerprint) {
|
||||||
|
super("Key " + fingerprint + " has no acceptable encryption key.", fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnacceptableEncryptionKeyException(@Nonnull PublicKeyAlgorithmPolicyException reason) {
|
||||||
|
super("Key " + reason.getFingerprint() + " has no acceptable encryption key.", reason.getFingerprint(), reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UnacceptableSigningKeyException extends KeyException {
|
||||||
|
|
||||||
|
public UnacceptableSigningKeyException(@Nonnull OpenPgpFingerprint fingerprint) {
|
||||||
|
super("Key " + fingerprint + " has no acceptable signing key.", fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnacceptableSigningKeyException(@Nonnull PublicKeyAlgorithmPolicyException reason) {
|
||||||
|
super("Key " + reason.getFingerprint() + " has no acceptable signing key.", reason.getFingerprint(), reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UnacceptableSelfSignatureException extends KeyException {
|
||||||
|
|
||||||
|
public UnacceptableSelfSignatureException(@Nonnull OpenPgpFingerprint fingerprint) {
|
||||||
|
super("Key " + fingerprint + " does not have a valid/acceptable signature to derive an expiration date from.", fingerprint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class MissingSecretKeyException extends KeyException {
|
||||||
|
|
||||||
|
private final long missingSecretKeyId;
|
||||||
|
|
||||||
|
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 long getMissingSecretKeyId() {
|
||||||
|
return missingSecretKeyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PublicKeyAlgorithmPolicyException extends KeyException {
|
||||||
|
|
||||||
|
private final long 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 long getViolatingSubkeyId() {
|
||||||
|
return violatingSubkeyId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class UnboundUserIdException extends KeyException {
|
||||||
|
|
||||||
|
public UnboundUserIdException(@Nonnull OpenPgpFingerprint fingerprint, @Nonnull String userId,
|
||||||
|
@Nullable PGPSignature userIdSignature, @Nullable PGPSignature userIdRevocation) {
|
||||||
|
super(errorMessage(fingerprint, userId, userIdSignature, userIdRevocation), fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String errorMessage(@Nonnull OpenPgpFingerprint fingerprint, @Nonnull String userId,
|
||||||
|
@Nullable PGPSignature userIdSignature, @Nullable PGPSignature userIdRevocation) {
|
||||||
|
String errorMessage = "UserID '" + userId + "' is not valid for key " + fingerprint + ": ";
|
||||||
|
if (userIdSignature == null) {
|
||||||
|
return errorMessage + "Missing binding signature.";
|
||||||
|
}
|
||||||
|
if (userIdRevocation != null) {
|
||||||
|
return errorMessage + "UserID is revoked.";
|
||||||
|
}
|
||||||
|
return errorMessage + "Unacceptable binding signature.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,10 @@
|
||||||
|
|
||||||
package org.pgpainless.exception;
|
package org.pgpainless.exception;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception gets thrown, when the integrity of an OpenPGP key is broken.
|
||||||
|
* That could happen on accident, or during an active attack, so take this exception seriously.
|
||||||
|
*/
|
||||||
public class KeyIntegrityException extends AssertionError {
|
public class KeyIntegrityException extends AssertionError {
|
||||||
|
|
||||||
public KeyIntegrityException() {
|
public KeyIntegrityException() {
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package org.pgpainless.exception;
|
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
|
||||||
|
|
||||||
public class KeyValidationError extends AssertionError {
|
|
||||||
|
|
||||||
public KeyValidationError(String userId, PGPSignature userIdSig, PGPSignature userIdRevocation) {
|
|
||||||
super("User-ID '" + userId + "' is not valid: Sig: " + userIdSig + " Rev: " + userIdRevocation);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -36,7 +36,7 @@ import org.pgpainless.algorithm.HashAlgorithm;
|
||||||
import org.pgpainless.algorithm.KeyFlag;
|
import org.pgpainless.algorithm.KeyFlag;
|
||||||
import org.pgpainless.algorithm.PublicKeyAlgorithm;
|
import org.pgpainless.algorithm.PublicKeyAlgorithm;
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
import org.pgpainless.exception.KeyValidationError;
|
import org.pgpainless.exception.KeyException;
|
||||||
import org.pgpainless.key.OpenPgpFingerprint;
|
import org.pgpainless.key.OpenPgpFingerprint;
|
||||||
import org.pgpainless.key.SubkeyIdentifier;
|
import org.pgpainless.key.SubkeyIdentifier;
|
||||||
import org.pgpainless.key.util.RevocationAttributes;
|
import org.pgpainless.key.util.RevocationAttributes;
|
||||||
|
@ -949,7 +949,12 @@ public class KeyRingInfo {
|
||||||
*/
|
*/
|
||||||
public @Nonnull List<PGPPublicKey> getEncryptionSubkeys(String userId, EncryptionPurpose purpose) {
|
public @Nonnull List<PGPPublicKey> getEncryptionSubkeys(String userId, EncryptionPurpose purpose) {
|
||||||
if (userId != null && !isUserIdValid(userId)) {
|
if (userId != null && !isUserIdValid(userId)) {
|
||||||
throw new KeyValidationError(userId, getLatestUserIdCertification(userId), getUserIdRevocation(userId));
|
throw new KeyException.UnboundUserIdException(
|
||||||
|
OpenPgpFingerprint.of(keys),
|
||||||
|
userId,
|
||||||
|
getLatestUserIdCertification(userId),
|
||||||
|
getUserIdRevocation(userId)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return getEncryptionSubkeys(purpose);
|
return getEncryptionSubkeys(purpose);
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
import org.pgpainless.decryption_verification.ConsumerOptions;
|
import org.pgpainless.decryption_verification.ConsumerOptions;
|
||||||
import org.pgpainless.decryption_verification.DecryptionStream;
|
import org.pgpainless.decryption_verification.DecryptionStream;
|
||||||
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
||||||
|
import org.pgpainless.exception.KeyException;
|
||||||
import org.pgpainless.key.SubkeyIdentifier;
|
import org.pgpainless.key.SubkeyIdentifier;
|
||||||
import org.pgpainless.key.TestKeys;
|
import org.pgpainless.key.TestKeys;
|
||||||
import org.pgpainless.key.generation.KeySpec;
|
import org.pgpainless.key.generation.KeySpec;
|
||||||
|
@ -326,7 +327,7 @@ public class EncryptDecryptTest {
|
||||||
|
|
||||||
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key);
|
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(key);
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class, () ->
|
assertThrows(KeyException.UnacceptableEncryptionKeyException.class, () ->
|
||||||
EncryptionOptions.encryptCommunications()
|
EncryptionOptions.encryptCommunications()
|
||||||
.addRecipient(publicKeys));
|
.addRecipient(publicKeys));
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.algorithm.KeyFlag;
|
import org.pgpainless.algorithm.KeyFlag;
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
import org.pgpainless.exception.KeyValidationError;
|
import org.pgpainless.exception.KeyException;
|
||||||
import org.pgpainless.key.SubkeyIdentifier;
|
import org.pgpainless.key.SubkeyIdentifier;
|
||||||
import org.pgpainless.key.generation.KeySpec;
|
import org.pgpainless.key.generation.KeySpec;
|
||||||
import org.pgpainless.key.generation.type.KeyType;
|
import org.pgpainless.key.generation.type.KeyType;
|
||||||
|
@ -132,14 +132,14 @@ public class EncryptionOptionsTest {
|
||||||
.build();
|
.build();
|
||||||
PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys);
|
PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys);
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> options.addRecipient(publicKeys));
|
assertThrows(KeyException.UnacceptableEncryptionKeyException.class, () -> options.addRecipient(publicKeys));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEncryptionKeySelectionStrategyEmpty_ThrowsAssertionError() {
|
public void testEncryptionKeySelectionStrategyEmpty_ThrowsAssertionError() {
|
||||||
EncryptionOptions options = new EncryptionOptions();
|
EncryptionOptions options = new EncryptionOptions();
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class,
|
assertThrows(KeyException.UnacceptableEncryptionKeyException.class,
|
||||||
() -> options.addRecipient(publicKeys, new EncryptionOptions.EncryptionKeySelector() {
|
() -> options.addRecipient(publicKeys, new EncryptionOptions.EncryptionKeySelector() {
|
||||||
@Override
|
@Override
|
||||||
public List<PGPPublicKey> selectEncryptionSubkeys(List<PGPPublicKey> encryptionCapableKeys) {
|
public List<PGPPublicKey> selectEncryptionSubkeys(List<PGPPublicKey> encryptionCapableKeys) {
|
||||||
|
@ -147,7 +147,7 @@ public class EncryptionOptionsTest {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class,
|
assertThrows(KeyException.UnacceptableEncryptionKeyException.class,
|
||||||
() -> options.addRecipient(publicKeys, "test@pgpainless.org", new EncryptionOptions.EncryptionKeySelector() {
|
() -> options.addRecipient(publicKeys, "test@pgpainless.org", new EncryptionOptions.EncryptionKeySelector() {
|
||||||
@Override
|
@Override
|
||||||
public List<PGPPublicKey> selectEncryptionSubkeys(List<PGPPublicKey> encryptionCapableKeys) {
|
public List<PGPPublicKey> selectEncryptionSubkeys(List<PGPPublicKey> encryptionCapableKeys) {
|
||||||
|
@ -180,6 +180,6 @@ public class EncryptionOptionsTest {
|
||||||
@Test
|
@Test
|
||||||
public void testAddRecipient_withInvalidUserId() {
|
public void testAddRecipient_withInvalidUserId() {
|
||||||
EncryptionOptions options = new EncryptionOptions();
|
EncryptionOptions options = new EncryptionOptions();
|
||||||
assertThrows(KeyValidationError.class, () -> options.addRecipient(publicKeys, "invalid@user.id"));
|
assertThrows(KeyException.UnboundUserIdException.class, () -> options.addRecipient(publicKeys, "invalid@user.id"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,8 +35,7 @@ import org.pgpainless.algorithm.KeyFlag;
|
||||||
import org.pgpainless.decryption_verification.ConsumerOptions;
|
import org.pgpainless.decryption_verification.ConsumerOptions;
|
||||||
import org.pgpainless.decryption_verification.DecryptionStream;
|
import org.pgpainless.decryption_verification.DecryptionStream;
|
||||||
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
||||||
import org.pgpainless.exception.KeyCannotSignException;
|
import org.pgpainless.exception.KeyException;
|
||||||
import org.pgpainless.exception.KeyValidationError;
|
|
||||||
import org.pgpainless.key.SubkeyIdentifier;
|
import org.pgpainless.key.SubkeyIdentifier;
|
||||||
import org.pgpainless.key.TestKeys;
|
import org.pgpainless.key.TestKeys;
|
||||||
import org.pgpainless.key.generation.KeySpec;
|
import org.pgpainless.key.generation.KeySpec;
|
||||||
|
@ -45,9 +44,9 @@ import org.pgpainless.key.generation.type.eddsa.EdDSACurve;
|
||||||
import org.pgpainless.key.info.KeyRingInfo;
|
import org.pgpainless.key.info.KeyRingInfo;
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
import org.pgpainless.key.util.KeyRingUtils;
|
import org.pgpainless.key.util.KeyRingUtils;
|
||||||
import org.pgpainless.util.TestAllImplementations;
|
|
||||||
import org.pgpainless.util.MultiMap;
|
import org.pgpainless.util.MultiMap;
|
||||||
import org.pgpainless.util.Passphrase;
|
import org.pgpainless.util.Passphrase;
|
||||||
|
import org.pgpainless.util.TestAllImplementations;
|
||||||
|
|
||||||
public class SigningTest {
|
public class SigningTest {
|
||||||
|
|
||||||
|
@ -125,7 +124,7 @@ public class SigningTest {
|
||||||
|
|
||||||
SigningOptions opts = new SigningOptions();
|
SigningOptions opts = new SigningOptions();
|
||||||
// "bob" is not a valid user-id
|
// "bob" is not a valid user-id
|
||||||
assertThrows(KeyValidationError.class,
|
assertThrows(KeyException.UnboundUserIdException.class,
|
||||||
() -> opts.addInlineSignature(protector, secretKeys, "bob",
|
() -> opts.addInlineSignature(protector, secretKeys, "bob",
|
||||||
DocumentSignatureType.CANONICAL_TEXT_DOCUMENT));
|
DocumentSignatureType.CANONICAL_TEXT_DOCUMENT));
|
||||||
}
|
}
|
||||||
|
@ -146,7 +145,7 @@ public class SigningTest {
|
||||||
|
|
||||||
SigningOptions opts = new SigningOptions();
|
SigningOptions opts = new SigningOptions();
|
||||||
// "alice" has been revoked
|
// "alice" has been revoked
|
||||||
assertThrows(KeyValidationError.class,
|
assertThrows(KeyException.UnboundUserIdException.class,
|
||||||
() -> opts.addInlineSignature(protector, fSecretKeys, "alice",
|
() -> opts.addInlineSignature(protector, fSecretKeys, "alice",
|
||||||
DocumentSignatureType.CANONICAL_TEXT_DOCUMENT));
|
DocumentSignatureType.CANONICAL_TEXT_DOCUMENT));
|
||||||
}
|
}
|
||||||
|
@ -253,9 +252,9 @@ public class SigningTest {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
SigningOptions options = new SigningOptions();
|
SigningOptions options = new SigningOptions();
|
||||||
assertThrows(KeyCannotSignException.class, () -> options.addDetachedSignature(
|
assertThrows(KeyException.UnacceptableSigningKeyException.class, () -> options.addDetachedSignature(
|
||||||
SecretKeyRingProtector.unprotectedKeys(), secretKeys, DocumentSignatureType.BINARY_DOCUMENT));
|
SecretKeyRingProtector.unprotectedKeys(), secretKeys, DocumentSignatureType.BINARY_DOCUMENT));
|
||||||
assertThrows(KeyCannotSignException.class, () -> options.addInlineSignature(
|
assertThrows(KeyException.UnacceptableSigningKeyException.class, () -> options.addInlineSignature(
|
||||||
SecretKeyRingProtector.unprotectedKeys(), secretKeys, DocumentSignatureType.BINARY_DOCUMENT));
|
SecretKeyRingProtector.unprotectedKeys(), secretKeys, DocumentSignatureType.BINARY_DOCUMENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,10 +269,10 @@ public class SigningTest {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
SigningOptions options = new SigningOptions();
|
SigningOptions options = new SigningOptions();
|
||||||
assertThrows(KeyValidationError.class, () ->
|
assertThrows(KeyException.UnboundUserIdException.class, () ->
|
||||||
options.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys, "Bob",
|
options.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys, "Bob",
|
||||||
DocumentSignatureType.BINARY_DOCUMENT));
|
DocumentSignatureType.BINARY_DOCUMENT));
|
||||||
assertThrows(KeyValidationError.class, () ->
|
assertThrows(KeyException.UnboundUserIdException.class, () ->
|
||||||
options.addInlineSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys, "Bob",
|
options.addInlineSignature(SecretKeyRingProtector.unprotectedKeys(), secretKeys, "Bob",
|
||||||
DocumentSignatureType.BINARY_DOCUMENT));
|
DocumentSignatureType.BINARY_DOCUMENT));
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.pgpainless.encryption_signing.EncryptionOptions;
|
||||||
import org.pgpainless.encryption_signing.EncryptionResult;
|
import org.pgpainless.encryption_signing.EncryptionResult;
|
||||||
import org.pgpainless.encryption_signing.EncryptionStream;
|
import org.pgpainless.encryption_signing.EncryptionStream;
|
||||||
import org.pgpainless.encryption_signing.ProducerOptions;
|
import org.pgpainless.encryption_signing.ProducerOptions;
|
||||||
|
import org.pgpainless.exception.KeyException;
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
import org.pgpainless.key.SubkeyIdentifier;
|
import org.pgpainless.key.SubkeyIdentifier;
|
||||||
|
|
||||||
|
@ -124,7 +125,7 @@ public class CertificateExpirationTest {
|
||||||
"-----END PGP PUBLIC KEY BLOCK-----\n";
|
"-----END PGP PUBLIC KEY BLOCK-----\n";
|
||||||
|
|
||||||
PGPPublicKeyRing cert = PGPainless.readKeyRing().publicKeyRing(CERT);
|
PGPPublicKeyRing cert = PGPainless.readKeyRing().publicKeyRing(CERT);
|
||||||
assertThrows(IllegalArgumentException.class, () -> encrypt(cert));
|
assertThrows(KeyException.ExpiredKeyException.class, () -> encrypt(cert));
|
||||||
}
|
}
|
||||||
|
|
||||||
private EncryptionResult encrypt(PGPPublicKeyRing certificate) throws PGPException, IOException {
|
private EncryptionResult encrypt(PGPPublicKeyRing certificate) throws PGPException, IOException {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.algorithm.KeyFlag;
|
import org.pgpainless.algorithm.KeyFlag;
|
||||||
import org.pgpainless.encryption_signing.EncryptionOptions;
|
import org.pgpainless.encryption_signing.EncryptionOptions;
|
||||||
|
import org.pgpainless.exception.KeyException;
|
||||||
import org.pgpainless.key.generation.KeySpec;
|
import org.pgpainless.key.generation.KeySpec;
|
||||||
import org.pgpainless.key.generation.type.KeyType;
|
import org.pgpainless.key.generation.type.KeyType;
|
||||||
import org.pgpainless.key.generation.type.rsa.RsaLength;
|
import org.pgpainless.key.generation.type.rsa.RsaLength;
|
||||||
|
@ -38,7 +39,7 @@ public class TestEncryptCommsStorageFlagsDifferentiated {
|
||||||
|
|
||||||
PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys);
|
PGPPublicKeyRing publicKeys = KeyRingUtils.publicKeyRingFrom(secretKeys);
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> EncryptionOptions.encryptCommunications()
|
assertThrows(KeyException.UnacceptableEncryptionKeyException.class, () -> EncryptionOptions.encryptCommunications()
|
||||||
.addRecipient(publicKeys));
|
.addRecipient(publicKeys));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import org.pgpainless.encryption_signing.EncryptionResult;
|
||||||
import org.pgpainless.encryption_signing.EncryptionStream;
|
import org.pgpainless.encryption_signing.EncryptionStream;
|
||||||
import org.pgpainless.encryption_signing.ProducerOptions;
|
import org.pgpainless.encryption_signing.ProducerOptions;
|
||||||
import org.pgpainless.encryption_signing.SigningOptions;
|
import org.pgpainless.encryption_signing.SigningOptions;
|
||||||
|
import org.pgpainless.exception.KeyException;
|
||||||
import org.pgpainless.key.SubkeyIdentifier;
|
import org.pgpainless.key.SubkeyIdentifier;
|
||||||
import org.pgpainless.key.info.KeyRingInfo;
|
import org.pgpainless.key.info.KeyRingInfo;
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
|
@ -63,7 +64,7 @@ public class SignImpl implements Sign {
|
||||||
}
|
}
|
||||||
signingOptions.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), key, modeToSigType(mode));
|
signingOptions.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), key, modeToSigType(mode));
|
||||||
}
|
}
|
||||||
} catch (PGPException e) {
|
} catch (PGPException | KeyException e) {
|
||||||
throw new SOPGPException.BadData(e);
|
throw new SOPGPException.BadData(e);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
|
Loading…
Reference in a new issue