2021-10-20 21:27:59 +02:00
|
|
|
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
|
|
package org.pgpainless.signature.builder;
|
|
|
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
|
|
|
import org.bouncycastle.openpgp.PGPException;
|
|
|
|
import org.bouncycastle.openpgp.PGPPrivateKey;
|
|
|
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
|
|
|
import org.bouncycastle.openpgp.PGPSecretKey;
|
2021-10-22 15:42:08 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPSignature;
|
2021-10-20 21:27:59 +02:00
|
|
|
import org.bouncycastle.openpgp.PGPSignatureGenerator;
|
|
|
|
import org.pgpainless.PGPainless;
|
|
|
|
import org.pgpainless.algorithm.HashAlgorithm;
|
|
|
|
import org.pgpainless.algorithm.SignatureType;
|
|
|
|
import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator;
|
|
|
|
import org.pgpainless.exception.WrongPassphraseException;
|
|
|
|
import org.pgpainless.implementation.ImplementationFactory;
|
|
|
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
|
|
|
import org.pgpainless.key.protection.UnlockSecretKey;
|
|
|
|
import org.pgpainless.key.util.OpenPgpKeyAttributeUtil;
|
2021-11-09 12:21:57 +01:00
|
|
|
import org.pgpainless.signature.subpackets.SignatureSubpackets;
|
|
|
|
import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper;
|
2021-10-20 21:27:59 +02:00
|
|
|
|
2021-11-12 15:07:09 +01:00
|
|
|
import javax.annotation.Nonnull;
|
|
|
|
|
2021-10-20 21:27:59 +02:00
|
|
|
public abstract class AbstractSignatureBuilder<B extends AbstractSignatureBuilder<B>> {
|
|
|
|
protected final PGPPrivateKey privateSigningKey;
|
|
|
|
protected final PGPPublicKey publicSigningKey;
|
|
|
|
|
|
|
|
protected HashAlgorithm hashAlgorithm;
|
|
|
|
protected SignatureType signatureType;
|
|
|
|
|
2021-11-09 12:21:57 +01:00
|
|
|
protected SignatureSubpackets unhashedSubpackets;
|
|
|
|
protected SignatureSubpackets hashedSubpackets;
|
2021-10-20 21:27:59 +02:00
|
|
|
|
2021-11-12 15:07:09 +01:00
|
|
|
protected AbstractSignatureBuilder(SignatureType signatureType,
|
|
|
|
PGPSecretKey signingKey,
|
|
|
|
SecretKeyRingProtector protector,
|
|
|
|
HashAlgorithm hashAlgorithm,
|
|
|
|
SignatureSubpackets hashedSubpackets,
|
|
|
|
SignatureSubpackets unhashedSubpackets)
|
2021-10-20 21:27:59 +02:00
|
|
|
throws WrongPassphraseException {
|
|
|
|
if (!isValidSignatureType(signatureType)) {
|
|
|
|
throw new IllegalArgumentException("Invalid signature type.");
|
|
|
|
}
|
|
|
|
this.signatureType = signatureType;
|
|
|
|
this.privateSigningKey = UnlockSecretKey.unlockSecretKey(signingKey, protector);
|
|
|
|
this.publicSigningKey = signingKey.getPublicKey();
|
2021-11-12 15:07:09 +01:00
|
|
|
this.hashAlgorithm = hashAlgorithm;
|
|
|
|
this.hashedSubpackets = hashedSubpackets;
|
|
|
|
this.unhashedSubpackets = unhashedSubpackets;
|
|
|
|
}
|
2021-10-20 21:27:59 +02:00
|
|
|
|
2021-11-12 15:07:09 +01:00
|
|
|
public AbstractSignatureBuilder(SignatureType signatureType, PGPSecretKey signingKey, SecretKeyRingProtector protector)
|
|
|
|
throws WrongPassphraseException {
|
|
|
|
this(
|
|
|
|
signatureType,
|
|
|
|
signingKey,
|
|
|
|
protector,
|
|
|
|
negotiateHashAlgorithm(signingKey.getPublicKey()),
|
|
|
|
SignatureSubpackets.createHashedSubpackets(signingKey.getPublicKey()),
|
|
|
|
SignatureSubpackets.createEmptySubpackets()
|
|
|
|
);
|
2021-10-20 21:27:59 +02:00
|
|
|
}
|
|
|
|
|
2021-11-06 15:40:10 +01:00
|
|
|
public AbstractSignatureBuilder(PGPSecretKey certificationKey, SecretKeyRingProtector protector, PGPSignature archetypeSignature)
|
|
|
|
throws WrongPassphraseException {
|
2021-11-12 15:07:09 +01:00
|
|
|
this(
|
|
|
|
SignatureType.valueOf(archetypeSignature.getSignatureType()),
|
|
|
|
certificationKey,
|
|
|
|
protector,
|
|
|
|
negotiateHashAlgorithm(certificationKey.getPublicKey()),
|
|
|
|
SignatureSubpackets.refreshHashedSubpackets(certificationKey.getPublicKey(), archetypeSignature),
|
|
|
|
SignatureSubpackets.refreshUnhashedSubpackets(archetypeSignature)
|
|
|
|
);
|
2021-10-22 15:42:08 +02:00
|
|
|
}
|
|
|
|
|
2021-11-06 15:40:10 +01:00
|
|
|
/**
|
|
|
|
* Negotiate a {@link HashAlgorithm} to be used when creating the signature.
|
|
|
|
*
|
|
|
|
* @param publicKey signing public key
|
|
|
|
* @return hash algorithm
|
|
|
|
*/
|
2021-11-12 15:07:09 +01:00
|
|
|
protected static HashAlgorithm negotiateHashAlgorithm(PGPPublicKey publicKey) {
|
2021-10-20 21:27:59 +02:00
|
|
|
Set<HashAlgorithm> hashAlgorithmPreferences = OpenPgpKeyAttributeUtil.getOrGuessPreferredHashAlgorithms(publicKey);
|
|
|
|
return HashAlgorithmNegotiator.negotiateSignatureHashAlgorithm(PGPainless.getPolicy())
|
|
|
|
.negotiateHashAlgorithm(hashAlgorithmPreferences);
|
|
|
|
}
|
|
|
|
|
2021-11-12 15:07:09 +01:00
|
|
|
public B overrideHashAlgorithm(@Nonnull HashAlgorithm hashAlgorithm) {
|
|
|
|
this.hashAlgorithm = hashAlgorithm;
|
|
|
|
return (B) this;
|
|
|
|
}
|
|
|
|
|
2021-11-06 15:40:10 +01:00
|
|
|
/**
|
|
|
|
* Set the builders {@link SignatureType}.
|
|
|
|
* Note that only those types who are valid for the concrete subclass of this {@link AbstractSignatureBuilder}
|
|
|
|
* are allowed. Invalid choices result in an {@link IllegalArgumentException} to be thrown.
|
|
|
|
*
|
|
|
|
* @param type signature type
|
|
|
|
* @return builder
|
|
|
|
*/
|
2021-10-20 21:27:59 +02:00
|
|
|
public B setSignatureType(SignatureType type) {
|
|
|
|
if (!isValidSignatureType(type)) {
|
|
|
|
throw new IllegalArgumentException("Invalid signature type: " + type);
|
|
|
|
}
|
|
|
|
this.signatureType = type;
|
|
|
|
return (B) this;
|
|
|
|
}
|
|
|
|
|
2021-11-06 15:40:10 +01:00
|
|
|
/**
|
|
|
|
* Build an instance of {@link PGPSignatureGenerator} initialized with the signing key
|
|
|
|
* and with hashed and unhashed subpackets.
|
|
|
|
*
|
|
|
|
* @return pgp signature generator
|
|
|
|
* @throws PGPException
|
|
|
|
*/
|
2021-10-20 21:27:59 +02:00
|
|
|
protected PGPSignatureGenerator buildAndInitSignatureGenerator() throws PGPException {
|
|
|
|
PGPSignatureGenerator generator = new PGPSignatureGenerator(
|
|
|
|
ImplementationFactory.getInstance().getPGPContentSignerBuilder(
|
|
|
|
publicSigningKey.getAlgorithm(), hashAlgorithm.getAlgorithmId()
|
|
|
|
)
|
|
|
|
);
|
2021-11-09 12:21:57 +01:00
|
|
|
generator.setUnhashedSubpackets(SignatureSubpacketsHelper.toVector(unhashedSubpackets));
|
|
|
|
generator.setHashedSubpackets(SignatureSubpacketsHelper.toVector(hashedSubpackets));
|
2021-10-20 21:27:59 +02:00
|
|
|
generator.init(signatureType.getCode(), privateSigningKey);
|
|
|
|
return generator;
|
|
|
|
}
|
|
|
|
|
2021-11-06 15:40:10 +01:00
|
|
|
/**
|
|
|
|
* Return true if the given {@link SignatureType} is a valid choice for the concrete implementation
|
|
|
|
* of {@link AbstractSignatureBuilder}.
|
|
|
|
*
|
|
|
|
* @param type type
|
|
|
|
* @return return true if valid, false otherwise
|
|
|
|
*/
|
2021-10-20 21:27:59 +02:00
|
|
|
protected abstract boolean isValidSignatureType(SignatureType type);
|
|
|
|
}
|