2021-10-07 15:48:52 +02:00
|
|
|
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2021-05-06 00:04:03 +02:00
|
|
|
package org.pgpainless.encryption_signing;
|
|
|
|
|
|
|
|
import java.util.Collections;
|
|
|
|
import java.util.Date;
|
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
2021-05-25 13:52:45 +02:00
|
|
|
import java.util.Set;
|
2021-11-22 19:20:04 +01:00
|
|
|
import javax.annotation.Nullable;
|
2021-05-06 00:04:03 +02:00
|
|
|
|
|
|
|
import org.bouncycastle.openpgp.PGPException;
|
|
|
|
import org.bouncycastle.openpgp.PGPPrivateKey;
|
|
|
|
import org.bouncycastle.openpgp.PGPPublicKey;
|
|
|
|
import org.bouncycastle.openpgp.PGPSecretKey;
|
|
|
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
|
|
|
import org.bouncycastle.openpgp.PGPSignatureGenerator;
|
|
|
|
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
|
2021-05-25 13:52:45 +02:00
|
|
|
import org.pgpainless.PGPainless;
|
2021-05-06 00:04:03 +02:00
|
|
|
import org.pgpainless.algorithm.DocumentSignatureType;
|
|
|
|
import org.pgpainless.algorithm.HashAlgorithm;
|
2021-12-02 15:10:53 +01:00
|
|
|
import org.pgpainless.algorithm.PublicKeyAlgorithm;
|
2021-10-20 21:26:47 +02:00
|
|
|
import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator;
|
2021-07-15 16:55:13 +02:00
|
|
|
import org.pgpainless.exception.KeyCannotSignException;
|
2021-08-15 15:21:14 +02:00
|
|
|
import org.pgpainless.exception.KeyValidationError;
|
2021-05-06 00:04:03 +02:00
|
|
|
import org.pgpainless.implementation.ImplementationFactory;
|
2021-10-27 17:38:25 +02:00
|
|
|
import org.pgpainless.key.OpenPgpFingerprint;
|
2021-05-06 00:04:03 +02:00
|
|
|
import org.pgpainless.key.SubkeyIdentifier;
|
|
|
|
import org.pgpainless.key.info.KeyRingInfo;
|
|
|
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
2021-07-15 16:55:13 +02:00
|
|
|
import org.pgpainless.key.protection.UnlockSecretKey;
|
2021-05-25 13:52:45 +02:00
|
|
|
import org.pgpainless.policy.Policy;
|
2021-11-22 19:20:04 +01:00
|
|
|
import org.pgpainless.signature.subpackets.BaseSignatureSubpackets;
|
|
|
|
import org.pgpainless.signature.subpackets.SignatureSubpackets;
|
|
|
|
import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper;
|
2021-05-06 00:04:03 +02:00
|
|
|
|
|
|
|
public final class SigningOptions {
|
|
|
|
|
2021-05-25 13:52:45 +02:00
|
|
|
/**
|
|
|
|
* A method of signing.
|
|
|
|
*/
|
2021-05-06 00:04:03 +02:00
|
|
|
public static final class SigningMethod {
|
|
|
|
private final PGPSignatureGenerator signatureGenerator;
|
|
|
|
private final boolean detached;
|
2021-09-27 17:10:00 +02:00
|
|
|
private final HashAlgorithm hashAlgorithm;
|
2021-05-06 00:04:03 +02:00
|
|
|
|
2021-09-27 17:10:00 +02:00
|
|
|
private SigningMethod(PGPSignatureGenerator signatureGenerator, boolean detached, HashAlgorithm hashAlgorithm) {
|
2021-05-06 00:04:03 +02:00
|
|
|
this.signatureGenerator = signatureGenerator;
|
|
|
|
this.detached = detached;
|
2021-09-27 17:10:00 +02:00
|
|
|
this.hashAlgorithm = hashAlgorithm;
|
2021-05-06 00:04:03 +02:00
|
|
|
}
|
|
|
|
|
2021-05-25 13:52:45 +02:00
|
|
|
/**
|
|
|
|
* Inline-signature method.
|
|
|
|
* The resulting signature will be written into the message itself, together with a one-pass-signature packet.
|
|
|
|
*
|
|
|
|
* @param signatureGenerator signature generator
|
|
|
|
* @return inline signing method
|
|
|
|
*/
|
2021-09-27 17:10:00 +02:00
|
|
|
public static SigningMethod inlineSignature(PGPSignatureGenerator signatureGenerator, HashAlgorithm hashAlgorithm) {
|
|
|
|
return new SigningMethod(signatureGenerator, false, hashAlgorithm);
|
2021-05-06 00:04:03 +02:00
|
|
|
}
|
|
|
|
|
2021-05-25 13:52:45 +02:00
|
|
|
/**
|
|
|
|
* Detached signing method.
|
|
|
|
* The resulting signature will not be added to the message, and instead can be distributed separately
|
|
|
|
* to the signed message.
|
|
|
|
*
|
|
|
|
* @param signatureGenerator signature generator
|
|
|
|
* @return detached signing method
|
|
|
|
*/
|
2021-09-27 17:10:00 +02:00
|
|
|
public static SigningMethod detachedSignature(PGPSignatureGenerator signatureGenerator, HashAlgorithm hashAlgorithm) {
|
|
|
|
return new SigningMethod(signatureGenerator, true, hashAlgorithm);
|
2021-05-06 00:04:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isDetached() {
|
|
|
|
return detached;
|
|
|
|
}
|
|
|
|
|
|
|
|
public PGPSignatureGenerator getSignatureGenerator() {
|
|
|
|
return signatureGenerator;
|
|
|
|
}
|
2021-09-27 17:10:00 +02:00
|
|
|
|
|
|
|
public HashAlgorithm getHashAlgorithm() {
|
|
|
|
return hashAlgorithm;
|
|
|
|
}
|
2021-05-06 00:04:03 +02:00
|
|
|
}
|
|
|
|
|
2021-05-25 13:52:45 +02:00
|
|
|
private final Map<SubkeyIdentifier, SigningMethod> signingMethods = new HashMap<>();
|
2021-05-06 00:04:03 +02:00
|
|
|
private HashAlgorithm hashAlgorithmOverride;
|
|
|
|
|
2021-06-29 16:43:37 +02:00
|
|
|
public static SigningOptions get() {
|
|
|
|
return new SigningOptions();
|
|
|
|
}
|
|
|
|
|
2021-05-27 13:55:18 +02:00
|
|
|
/**
|
|
|
|
* Add inline signatures with all secret key rings in the provided secret key ring collection.
|
|
|
|
*
|
|
|
|
* @param secrectKeyDecryptor decryptor to unlock the signing secret keys
|
|
|
|
* @param signingKeys collection of signing keys
|
|
|
|
* @param signatureType type of signature (binary, canonical text)
|
|
|
|
* @return this
|
2021-08-15 15:21:14 +02:00
|
|
|
* @throws KeyValidationError if something is wrong with any of the keys
|
2021-05-27 13:55:18 +02:00
|
|
|
* @throws PGPException if any of the keys cannot be unlocked or a signing method cannot be created
|
|
|
|
*/
|
|
|
|
public SigningOptions addInlineSignatures(SecretKeyRingProtector secrectKeyDecryptor,
|
2021-12-13 01:14:55 +01:00
|
|
|
Iterable<PGPSecretKeyRing> signingKeys,
|
2021-05-27 13:55:18 +02:00
|
|
|
DocumentSignatureType signatureType)
|
2021-08-15 15:21:14 +02:00
|
|
|
throws KeyValidationError, PGPException {
|
2021-05-27 13:55:18 +02:00
|
|
|
for (PGPSecretKeyRing signingKey : signingKeys) {
|
|
|
|
addInlineSignature(secrectKeyDecryptor, signingKey, signatureType);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-05-25 13:52:45 +02:00
|
|
|
/**
|
|
|
|
* Add an inline-signature.
|
|
|
|
* Inline signatures are being embedded into the message itself and can be processed in one pass, thanks to the use
|
|
|
|
* of one-pass-signature packets.
|
|
|
|
*
|
|
|
|
* @param secretKeyDecryptor decryptor to unlock the signing secret key
|
|
|
|
* @param secretKey signing key
|
|
|
|
* @param signatureType type of signature (binary, canonical text)
|
2021-08-15 15:21:14 +02:00
|
|
|
* @throws KeyValidationError if something is wrong with the key
|
2021-05-25 13:52:45 +02:00
|
|
|
* @throws PGPException if the key cannot be unlocked or the signing method cannot be created
|
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
public SigningOptions addInlineSignature(SecretKeyRingProtector secretKeyDecryptor,
|
|
|
|
PGPSecretKeyRing secretKey,
|
|
|
|
DocumentSignatureType signatureType)
|
2021-08-15 15:21:14 +02:00
|
|
|
throws KeyValidationError, PGPException {
|
2021-05-25 13:52:45 +02:00
|
|
|
return addInlineSignature(secretKeyDecryptor, secretKey, null, signatureType);
|
2021-05-06 00:04:03 +02:00
|
|
|
}
|
|
|
|
|
2021-05-25 13:52:45 +02:00
|
|
|
/**
|
|
|
|
* Add an inline-signature.
|
|
|
|
* Inline signatures are being embedded into the message itself and can be processed in one pass, thanks to the use
|
|
|
|
* of one-pass-signature packets.
|
|
|
|
*
|
|
|
|
* This method uses the passed in user-id to select user-specific hash algorithms.
|
|
|
|
*
|
|
|
|
* @param secretKeyDecryptor decryptor to unlock the signing secret key
|
|
|
|
* @param secretKey signing key
|
|
|
|
* @param userId user-id of the signer
|
|
|
|
* @param signatureType signature type (binary, canonical text)
|
|
|
|
* @return this
|
2021-08-15 15:21:14 +02:00
|
|
|
* @throws KeyValidationError if the key is invalid
|
2021-05-25 13:52:45 +02:00
|
|
|
* @throws PGPException if the key cannot be unlocked or the signing method cannot be created
|
|
|
|
*/
|
|
|
|
public SigningOptions addInlineSignature(SecretKeyRingProtector secretKeyDecryptor,
|
|
|
|
PGPSecretKeyRing secretKey,
|
|
|
|
String userId,
|
|
|
|
DocumentSignatureType signatureType)
|
2021-08-15 15:21:14 +02:00
|
|
|
throws KeyValidationError, PGPException {
|
2021-11-22 19:20:04 +01:00
|
|
|
return addInlineSignature(secretKeyDecryptor, secretKey, userId, signatureType, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add an inline-signature.
|
|
|
|
* Inline signatures are being embedded into the message itself and can be processed in one pass, thanks to the use
|
|
|
|
* of one-pass-signature packets.
|
|
|
|
*
|
|
|
|
* This method uses the passed in user-id to select user-specific hash algorithms.
|
|
|
|
*
|
|
|
|
* @param secretKeyDecryptor decryptor to unlock the signing secret key
|
|
|
|
* @param secretKey signing key
|
|
|
|
* @param userId user-id of the signer
|
|
|
|
* @param signatureType signature type (binary, canonical text)
|
|
|
|
* @param subpacketsCallback callback to modify the hashed and unhashed subpackets of the signature
|
|
|
|
* @return this
|
|
|
|
* @throws KeyValidationError if the key is invalid
|
|
|
|
* @throws PGPException if the key cannot be unlocked or the signing method cannot be created
|
|
|
|
*/
|
|
|
|
public SigningOptions addInlineSignature(SecretKeyRingProtector secretKeyDecryptor,
|
|
|
|
PGPSecretKeyRing secretKey,
|
|
|
|
String userId,
|
|
|
|
DocumentSignatureType signatureType,
|
|
|
|
@Nullable BaseSignatureSubpackets.Callback subpacketsCallback)
|
|
|
|
throws KeyValidationError, PGPException {
|
2021-05-06 00:04:03 +02:00
|
|
|
KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date());
|
2021-08-15 15:21:14 +02:00
|
|
|
if (userId != null && !keyRingInfo.isUserIdValid(userId)) {
|
|
|
|
throw new KeyValidationError(userId, keyRingInfo.getLatestUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId));
|
2021-05-06 00:04:03 +02:00
|
|
|
}
|
|
|
|
|
2021-05-24 16:10:26 +02:00
|
|
|
List<PGPPublicKey> signingPubKeys = keyRingInfo.getSigningSubkeys();
|
|
|
|
if (signingPubKeys.isEmpty()) {
|
2021-10-27 17:38:25 +02:00
|
|
|
throw new KeyCannotSignException("Key " + OpenPgpFingerprint.of(secretKey) + " has no valid signing key.");
|
2021-05-06 00:04:03 +02:00
|
|
|
}
|
2021-05-24 16:10:26 +02:00
|
|
|
|
|
|
|
for (PGPPublicKey signingPubKey : signingPubKeys) {
|
|
|
|
PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID());
|
2021-07-15 16:55:13 +02:00
|
|
|
PGPPrivateKey signingSubkey = UnlockSecretKey.unlockSecretKey(signingSecKey, secretKeyDecryptor);
|
2021-11-15 13:02:26 +01:00
|
|
|
Set<HashAlgorithm> hashAlgorithms = userId != null ? keyRingInfo.getPreferredHashAlgorithms(userId)
|
|
|
|
: keyRingInfo.getPreferredHashAlgorithms(signingPubKey.getKeyID());
|
2021-05-25 13:52:45 +02:00
|
|
|
HashAlgorithm hashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, PGPainless.getPolicy());
|
2021-11-22 19:20:04 +01:00
|
|
|
addSigningMethod(secretKey, signingSubkey, subpacketsCallback, hashAlgorithm, signatureType, false);
|
2021-05-24 16:10:26 +02:00
|
|
|
}
|
2021-05-25 13:52:45 +02:00
|
|
|
|
|
|
|
return this;
|
2021-05-06 00:04:03 +02:00
|
|
|
}
|
|
|
|
|
2021-05-27 13:55:18 +02:00
|
|
|
/**
|
|
|
|
* Add detached signatures with all key rings from the provided secret key ring collection.
|
|
|
|
*
|
|
|
|
* @param secretKeyDecryptor decryptor to unlock the secret signing keys
|
|
|
|
* @param signingKeys collection of signing key rings
|
|
|
|
* @param signatureType type of the signature (binary, canonical text)
|
|
|
|
* @return this
|
|
|
|
* @throws PGPException if any of the keys cannot be validated or unlocked, or if any signing method cannot be created
|
|
|
|
*/
|
|
|
|
public SigningOptions addDetachedSignatures(SecretKeyRingProtector secretKeyDecryptor,
|
2021-12-13 01:14:55 +01:00
|
|
|
Iterable<PGPSecretKeyRing> signingKeys,
|
2021-05-27 13:55:18 +02:00
|
|
|
DocumentSignatureType signatureType)
|
|
|
|
throws PGPException {
|
|
|
|
for (PGPSecretKeyRing signingKey : signingKeys) {
|
|
|
|
addDetachedSignature(secretKeyDecryptor, signingKey, signatureType);
|
|
|
|
}
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-05-25 13:52:45 +02:00
|
|
|
/**
|
|
|
|
* Create a detached signature.
|
|
|
|
* Detached signatures are not being added into the PGP message itself.
|
|
|
|
* Instead they can be distributed separately to the message.
|
|
|
|
* Detached signatures are useful if the data that is being signed shall not be modified (eg. when signing a file).
|
|
|
|
*
|
|
|
|
* @param secretKeyDecryptor decryptor to unlock the secret signing key
|
|
|
|
* @param secretKey signing key
|
|
|
|
* @param signatureType type of data that is signed (binary, canonical text)
|
|
|
|
* @throws PGPException if the key cannot be validated or unlocked, or if no signature method can be created
|
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
public SigningOptions addDetachedSignature(SecretKeyRingProtector secretKeyDecryptor,
|
|
|
|
PGPSecretKeyRing secretKey,
|
|
|
|
DocumentSignatureType signatureType)
|
2021-05-20 13:42:52 +02:00
|
|
|
throws PGPException {
|
2021-05-25 13:52:45 +02:00
|
|
|
return addDetachedSignature(secretKeyDecryptor, secretKey, null, signatureType);
|
2021-05-20 13:42:52 +02:00
|
|
|
}
|
|
|
|
|
2021-05-25 13:52:45 +02:00
|
|
|
/**
|
|
|
|
* Create a detached signature.
|
|
|
|
* Detached signatures are not being added into the PGP message itself.
|
|
|
|
* Instead they can be distributed separately to the message.
|
|
|
|
* Detached signatures are useful if the data that is being signed shall not be modified (eg. when signing a file).
|
|
|
|
*
|
|
|
|
* This method uses the passed in user-id to select user-specific hash algorithms.
|
|
|
|
*
|
|
|
|
* @param secretKeyDecryptor decryptor to unlock the secret signing key
|
|
|
|
* @param secretKey signing key
|
|
|
|
* @param userId user-id
|
|
|
|
* @param signatureType type of data that is signed (binary, canonical text)
|
|
|
|
* @throws PGPException if the key cannot be validated or unlocked, or if no signature method can be created
|
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
public SigningOptions addDetachedSignature(SecretKeyRingProtector secretKeyDecryptor,
|
|
|
|
PGPSecretKeyRing secretKey,
|
|
|
|
String userId,
|
|
|
|
DocumentSignatureType signatureType)
|
2021-05-20 13:42:52 +02:00
|
|
|
throws PGPException {
|
2021-11-22 19:20:04 +01:00
|
|
|
return addDetachedSignature(secretKeyDecryptor, secretKey, userId, signatureType, null);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a detached signature.
|
|
|
|
* Detached signatures are not being added into the PGP message itself.
|
|
|
|
* Instead they can be distributed separately to the message.
|
|
|
|
* Detached signatures are useful if the data that is being signed shall not be modified (eg. when signing a file).
|
|
|
|
*
|
|
|
|
* This method uses the passed in user-id to select user-specific hash algorithms.
|
|
|
|
*
|
|
|
|
* @param secretKeyDecryptor decryptor to unlock the secret signing key
|
|
|
|
* @param secretKey signing key
|
|
|
|
* @param userId user-id
|
|
|
|
* @param signatureType type of data that is signed (binary, canonical text)
|
|
|
|
* @param subpacketCallback callback to modify hashed and unhashed subpackets of the signature
|
|
|
|
* @throws PGPException if the key cannot be validated or unlocked, or if no signature method can be created
|
|
|
|
* @return this
|
|
|
|
*/
|
|
|
|
public SigningOptions addDetachedSignature(SecretKeyRingProtector secretKeyDecryptor,
|
|
|
|
PGPSecretKeyRing secretKey,
|
|
|
|
String userId,
|
|
|
|
DocumentSignatureType signatureType,
|
|
|
|
@Nullable BaseSignatureSubpackets.Callback subpacketCallback)
|
|
|
|
throws PGPException {
|
2021-05-20 13:42:52 +02:00
|
|
|
KeyRingInfo keyRingInfo = new KeyRingInfo(secretKey, new Date());
|
2021-08-15 15:21:14 +02:00
|
|
|
if (userId != null && !keyRingInfo.isUserIdValid(userId)) {
|
|
|
|
throw new KeyValidationError(userId, keyRingInfo.getLatestUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId));
|
2021-05-20 13:42:52 +02:00
|
|
|
}
|
|
|
|
|
2021-05-24 16:10:26 +02:00
|
|
|
List<PGPPublicKey> signingPubKeys = keyRingInfo.getSigningSubkeys();
|
|
|
|
if (signingPubKeys.isEmpty()) {
|
2021-07-15 16:55:13 +02:00
|
|
|
throw new KeyCannotSignException("Key has no valid signing key.");
|
2021-05-20 13:42:52 +02:00
|
|
|
}
|
2021-05-24 16:10:26 +02:00
|
|
|
|
|
|
|
for (PGPPublicKey signingPubKey : signingPubKeys) {
|
|
|
|
PGPSecretKey signingSecKey = secretKey.getSecretKey(signingPubKey.getKeyID());
|
2021-12-02 15:10:53 +01:00
|
|
|
if (signingSecKey == null) {
|
|
|
|
throw new PGPException("Missing secret key for signing key " + Long.toHexString(signingPubKey.getKeyID()));
|
|
|
|
}
|
|
|
|
PGPPrivateKey signingSubkey = signingSecKey.extractPrivateKey(
|
|
|
|
secretKeyDecryptor.getDecryptor(signingPubKey.getKeyID()));
|
2021-11-15 13:02:26 +01:00
|
|
|
Set<HashAlgorithm> hashAlgorithms = userId != null ? keyRingInfo.getPreferredHashAlgorithms(userId)
|
|
|
|
: keyRingInfo.getPreferredHashAlgorithms(signingPubKey.getKeyID());
|
2021-05-25 13:52:45 +02:00
|
|
|
HashAlgorithm hashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, PGPainless.getPolicy());
|
2021-11-22 19:20:04 +01:00
|
|
|
addSigningMethod(secretKey, signingSubkey, subpacketCallback, hashAlgorithm, signatureType, true);
|
2021-05-24 16:10:26 +02:00
|
|
|
}
|
2021-05-25 13:52:45 +02:00
|
|
|
|
|
|
|
return this;
|
2021-05-20 13:42:52 +02:00
|
|
|
}
|
|
|
|
|
2021-05-06 00:04:03 +02:00
|
|
|
private void addSigningMethod(PGPSecretKeyRing secretKey,
|
|
|
|
PGPPrivateKey signingSubkey,
|
2021-11-22 19:20:04 +01:00
|
|
|
@Nullable BaseSignatureSubpackets.Callback subpacketCallback,
|
2021-05-06 00:04:03 +02:00
|
|
|
HashAlgorithm hashAlgorithm,
|
|
|
|
DocumentSignatureType signatureType,
|
|
|
|
boolean detached)
|
|
|
|
throws PGPException {
|
|
|
|
SubkeyIdentifier signingKeyIdentifier = new SubkeyIdentifier(secretKey, signingSubkey.getKeyID());
|
2021-08-01 16:03:30 +02:00
|
|
|
PGPSecretKey signingSecretKey = secretKey.getSecretKey(signingSubkey.getKeyID());
|
2021-12-02 15:10:53 +01:00
|
|
|
PublicKeyAlgorithm publicKeyAlgorithm = PublicKeyAlgorithm.fromId(signingSecretKey.getPublicKey().getAlgorithm());
|
|
|
|
int bitStrength = secretKey.getPublicKey().getBitStrength();
|
|
|
|
if (!PGPainless.getPolicy().getPublicKeyAlgorithmPolicy().isAcceptable(publicKeyAlgorithm, bitStrength)) {
|
|
|
|
throw new IllegalArgumentException("Public key algorithm policy violation: " +
|
|
|
|
publicKeyAlgorithm + " with bit strength " + bitStrength + " is not acceptable.");
|
|
|
|
}
|
|
|
|
|
2021-08-01 16:03:30 +02:00
|
|
|
PGPSignatureGenerator generator = createSignatureGenerator(signingSubkey, hashAlgorithm, signatureType);
|
2021-11-22 19:20:04 +01:00
|
|
|
|
|
|
|
// Subpackets
|
|
|
|
SignatureSubpackets hashedSubpackets = SignatureSubpackets.createHashedSubpackets(signingSecretKey.getPublicKey());
|
|
|
|
SignatureSubpackets unhashedSubpackets = SignatureSubpackets.createEmptySubpackets();
|
|
|
|
if (subpacketCallback != null) {
|
|
|
|
subpacketCallback.modifyHashedSubpackets(hashedSubpackets);
|
|
|
|
subpacketCallback.modifyUnhashedSubpackets(unhashedSubpackets);
|
|
|
|
}
|
|
|
|
generator.setHashedSubpackets(SignatureSubpacketsHelper.toVector(hashedSubpackets));
|
|
|
|
generator.setUnhashedSubpackets(SignatureSubpacketsHelper.toVector(unhashedSubpackets));
|
|
|
|
|
2021-09-27 17:10:00 +02:00
|
|
|
SigningMethod signingMethod = detached ?
|
|
|
|
SigningMethod.detachedSignature(generator, hashAlgorithm) :
|
|
|
|
SigningMethod.inlineSignature(generator, hashAlgorithm);
|
2021-05-06 00:04:03 +02:00
|
|
|
signingMethods.put(signingKeyIdentifier, signingMethod);
|
|
|
|
}
|
|
|
|
|
2021-05-25 13:52:45 +02:00
|
|
|
/**
|
|
|
|
* Negotiate, which hash algorithm to use.
|
|
|
|
*
|
2021-10-20 21:26:47 +02:00
|
|
|
* This method gives the highest priority to the algorithm override, which can be set via {@link #overrideHashAlgorithm(HashAlgorithm)}.
|
2021-05-25 13:52:45 +02:00
|
|
|
* After that, the signing keys hash algorithm preferences are iterated to find the first acceptable algorithm.
|
|
|
|
* Lastly, should no acceptable algorithm be found, the {@link Policy Policies} default signature hash algorithm is
|
|
|
|
* used as a fallback.
|
|
|
|
*
|
|
|
|
* @param preferences preferences
|
|
|
|
* @param policy policy
|
|
|
|
* @return selected hash algorithm
|
|
|
|
*/
|
|
|
|
private HashAlgorithm negotiateHashAlgorithm(Set<HashAlgorithm> preferences, Policy policy) {
|
|
|
|
if (hashAlgorithmOverride != null) {
|
|
|
|
return hashAlgorithmOverride;
|
|
|
|
}
|
|
|
|
|
2021-10-20 21:26:47 +02:00
|
|
|
return HashAlgorithmNegotiator.negotiateSignatureHashAlgorithm(policy)
|
|
|
|
.negotiateHashAlgorithm(preferences);
|
2021-05-25 13:52:45 +02:00
|
|
|
}
|
|
|
|
|
2021-08-01 16:03:30 +02:00
|
|
|
private PGPSignatureGenerator createSignatureGenerator(PGPPrivateKey privateKey,
|
2021-05-06 00:04:03 +02:00
|
|
|
HashAlgorithm hashAlgorithm,
|
|
|
|
DocumentSignatureType signatureType)
|
|
|
|
throws PGPException {
|
|
|
|
int publicKeyAlgorithm = privateKey.getPublicKeyPacket().getAlgorithm();
|
|
|
|
PGPContentSignerBuilder signerBuilder = ImplementationFactory.getInstance()
|
|
|
|
.getPGPContentSignerBuilder(publicKeyAlgorithm, hashAlgorithm.getAlgorithmId());
|
|
|
|
PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(signerBuilder);
|
|
|
|
signatureGenerator.init(signatureType.getSignatureType().getCode(), privateKey);
|
|
|
|
|
|
|
|
return signatureGenerator;
|
|
|
|
}
|
|
|
|
|
2021-05-25 13:52:45 +02:00
|
|
|
/**
|
|
|
|
* Return a map of key-ids and signing methods.
|
|
|
|
* For internal use.
|
|
|
|
*
|
|
|
|
* @return signing methods
|
|
|
|
*/
|
2021-05-06 00:04:03 +02:00
|
|
|
public Map<SubkeyIdentifier, SigningMethod> getSigningMethods() {
|
|
|
|
return Collections.unmodifiableMap(signingMethods);
|
|
|
|
}
|
|
|
|
|
2021-05-25 13:52:45 +02:00
|
|
|
/**
|
|
|
|
* Override hash algorithm negotiation by dictating which hash algorithm needs to be used.
|
|
|
|
* If no override has been set, an accetable algorithm will be negotiated instead.
|
|
|
|
*
|
|
|
|
* Note: To override the hash algorithm for signing, call this method *before* calling
|
|
|
|
* {@link #addInlineSignature(SecretKeyRingProtector, PGPSecretKeyRing, DocumentSignatureType)} or
|
|
|
|
* {@link #addDetachedSignature(SecretKeyRingProtector, PGPSecretKeyRing, DocumentSignatureType)}.
|
|
|
|
*
|
|
|
|
* @param hashAlgorithmOverride override hash algorithm
|
|
|
|
* @return this
|
|
|
|
*/
|
2021-05-06 00:04:03 +02:00
|
|
|
public SigningOptions overrideHashAlgorithm(HashAlgorithm hashAlgorithmOverride) {
|
|
|
|
this.hashAlgorithmOverride = hashAlgorithmOverride;
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2021-05-25 13:52:45 +02:00
|
|
|
/**
|
|
|
|
* Return the hash algorithm override (or null if no override is set).
|
|
|
|
*
|
|
|
|
* @return hash algorithm override
|
|
|
|
*/
|
2021-05-06 00:04:03 +02:00
|
|
|
public HashAlgorithm getHashAlgorithmOverride() {
|
|
|
|
return hashAlgorithmOverride;
|
|
|
|
}
|
|
|
|
}
|