1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-11-23 12:52:07 +01:00

Add methods to sign messages with custom subpackets

This commit is contained in:
Paul Schaub 2021-11-22 19:20:04 +01:00
parent 51311581f9
commit 50f565dd8c
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
2 changed files with 69 additions and 12 deletions

View file

@ -10,6 +10,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable;
import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPrivateKey; import org.bouncycastle.openpgp.PGPPrivateKey;
@ -18,7 +19,6 @@ import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignatureGenerator; import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder; import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder;
import org.pgpainless.PGPainless; import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.DocumentSignatureType; import org.pgpainless.algorithm.DocumentSignatureType;
@ -33,6 +33,9 @@ import org.pgpainless.key.info.KeyRingInfo;
import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.key.protection.UnlockSecretKey; import org.pgpainless.key.protection.UnlockSecretKey;
import org.pgpainless.policy.Policy; import org.pgpainless.policy.Policy;
import org.pgpainless.signature.subpackets.BaseSignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpackets;
import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper;
public final class SigningOptions { public final class SigningOptions {
@ -152,6 +155,31 @@ public final class SigningOptions {
String userId, String userId,
DocumentSignatureType signatureType) DocumentSignatureType signatureType)
throws KeyValidationError, PGPException { throws KeyValidationError, PGPException {
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 {
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 KeyValidationError(userId, keyRingInfo.getLatestUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId));
@ -168,7 +196,7 @@ public final class SigningOptions {
Set<HashAlgorithm> hashAlgorithms = userId != null ? keyRingInfo.getPreferredHashAlgorithms(userId) Set<HashAlgorithm> hashAlgorithms = userId != null ? keyRingInfo.getPreferredHashAlgorithms(userId)
: keyRingInfo.getPreferredHashAlgorithms(signingPubKey.getKeyID()); : keyRingInfo.getPreferredHashAlgorithms(signingPubKey.getKeyID());
HashAlgorithm hashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, PGPainless.getPolicy()); HashAlgorithm hashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, PGPainless.getPolicy());
addSigningMethod(secretKey, signingSubkey, hashAlgorithm, signatureType, false); addSigningMethod(secretKey, signingSubkey, subpacketsCallback, hashAlgorithm, signatureType, false);
} }
return this; return this;
@ -232,6 +260,31 @@ public final class SigningOptions {
String userId, String userId,
DocumentSignatureType signatureType) DocumentSignatureType signatureType)
throws PGPException { throws PGPException {
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 {
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 KeyValidationError(userId, keyRingInfo.getLatestUserIdCertification(userId), keyRingInfo.getUserIdRevocation(userId));
@ -248,7 +301,7 @@ public final class SigningOptions {
Set<HashAlgorithm> hashAlgorithms = userId != null ? keyRingInfo.getPreferredHashAlgorithms(userId) Set<HashAlgorithm> hashAlgorithms = userId != null ? keyRingInfo.getPreferredHashAlgorithms(userId)
: keyRingInfo.getPreferredHashAlgorithms(signingPubKey.getKeyID()); : keyRingInfo.getPreferredHashAlgorithms(signingPubKey.getKeyID());
HashAlgorithm hashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, PGPainless.getPolicy()); HashAlgorithm hashAlgorithm = negotiateHashAlgorithm(hashAlgorithms, PGPainless.getPolicy());
addSigningMethod(secretKey, signingSubkey, hashAlgorithm, signatureType, true); addSigningMethod(secretKey, signingSubkey, subpacketCallback, hashAlgorithm, signatureType, true);
} }
return this; return this;
@ -256,6 +309,7 @@ public final class SigningOptions {
private void addSigningMethod(PGPSecretKeyRing secretKey, private void addSigningMethod(PGPSecretKeyRing secretKey,
PGPPrivateKey signingSubkey, PGPPrivateKey signingSubkey,
@Nullable BaseSignatureSubpackets.Callback subpacketCallback,
HashAlgorithm hashAlgorithm, HashAlgorithm hashAlgorithm,
DocumentSignatureType signatureType, DocumentSignatureType signatureType,
boolean detached) boolean detached)
@ -263,7 +317,17 @@ public final class SigningOptions {
SubkeyIdentifier signingKeyIdentifier = new SubkeyIdentifier(secretKey, signingSubkey.getKeyID()); SubkeyIdentifier signingKeyIdentifier = new SubkeyIdentifier(secretKey, signingSubkey.getKeyID());
PGPSecretKey signingSecretKey = secretKey.getSecretKey(signingSubkey.getKeyID()); PGPSecretKey signingSecretKey = secretKey.getSecretKey(signingSubkey.getKeyID());
PGPSignatureGenerator generator = createSignatureGenerator(signingSubkey, hashAlgorithm, signatureType); PGPSignatureGenerator generator = createSignatureGenerator(signingSubkey, hashAlgorithm, signatureType);
generator.setUnhashedSubpackets(unhashedSubpackets(signingSecretKey).generate());
// 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));
SigningMethod signingMethod = detached ? SigningMethod signingMethod = detached ?
SigningMethod.detachedSignature(generator, hashAlgorithm) : SigningMethod.detachedSignature(generator, hashAlgorithm) :
SigningMethod.inlineSignature(generator, hashAlgorithm); SigningMethod.inlineSignature(generator, hashAlgorithm);
@ -304,13 +368,6 @@ public final class SigningOptions {
return signatureGenerator; return signatureGenerator;
} }
private PGPSignatureSubpacketGenerator unhashedSubpackets(PGPSecretKey key) {
PGPSignatureSubpacketGenerator generator = new PGPSignatureSubpacketGenerator();
generator.setIssuerKeyID(false, key.getKeyID());
generator.setIssuerFingerprint(false, key);
return generator;
}
/** /**
* Return a map of key-ids and signing methods. * Return a map of key-ids and signing methods.
* For internal use. * For internal use.

View file

@ -28,7 +28,7 @@ import org.pgpainless.algorithm.PublicKeyAlgorithm;
public interface BaseSignatureSubpackets { public interface BaseSignatureSubpackets {
interface Callback extends SignatureSubpacketCallback<SignatureSubpackets> { interface Callback extends SignatureSubpacketCallback<BaseSignatureSubpackets> {
} }