From 176574df504417e64ba83578b9fbe73180b696b0 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sat, 20 Nov 2021 16:07:27 +0100 Subject: [PATCH] Wip --- .../key/generation/KeyRingBuilder.java | 41 +---- .../pgpainless/key/generation/KeySpec.java | 2 +- .../secretkeyring/SecretKeyRingEditor.java | 167 ++++++++---------- .../SecretKeyRingEditorInterface.java | 26 ++- .../org/pgpainless/key/util/KeyRingUtils.java | 127 +++++++++++-- .../signature/builder/SignatureFactory.java | 121 +------------ .../org/pgpainless/example/ModifyKeys.java | 2 +- ...ithModifiedBindingSignatureSubpackets.java | 41 ++--- .../key/modification/RevokeSubKeyTest.java | 10 +- .../SubkeyBindingSignatureBuilderTest.java | 75 -------- 10 files changed, 238 insertions(+), 374 deletions(-) delete mode 100644 pgpainless-core/src/test/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilderTest.java diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilder.java index 731efef3..13baf553 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilder.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeyRingBuilder.java @@ -42,7 +42,6 @@ import org.pgpainless.algorithm.SignatureType; import org.pgpainless.algorithm.SymmetricKeyAlgorithm; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.generation.type.KeyType; -import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.protection.UnlockSecretKey; import org.pgpainless.provider.ProviderFactory; import org.pgpainless.signature.subpackets.SignatureSubpackets; @@ -133,6 +132,8 @@ public class KeyRingBuilder implements KeyRingBuilderInterface { PGPKeyPair certKey = generateKeyPair(primaryKeySpec); PGPContentSignerBuilder signer = buildContentSigner(certKey); PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(signer); + + // Prepare primary user-id sig SignatureSubpackets hashedSubPacketGenerator = primaryKeySpec.getSubpacketGenerator(); hashedSubPacketGenerator.setPrimaryUserId(); if (expirationDate != null) { @@ -142,9 +143,7 @@ public class KeyRingBuilder implements KeyRingBuilderInterface { SignatureSubpacketsHelper.applyTo(hashedSubPacketGenerator, generator); PGPSignatureSubpacketVector hashedSubPackets = generator.generate(); - // Generator which the user can get the key pair from PGPKeyRingGenerator ringGenerator = buildRingGenerator(certKey, signer, keyFingerprintCalculator, hashedSubPackets, secretKeyEncryptor); - addSubKeys(certKey, ringGenerator); // Generate secret key ring with only primary user id @@ -155,10 +154,10 @@ public class KeyRingBuilder implements KeyRingBuilderInterface { // Attempt to add additional user-ids to the primary public key PGPPublicKey primaryPubKey = secretKeys.next().getPublicKey(); PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(secretKeyRing.getSecretKey(), secretKeyDecryptor); - Iterator additionalUserIds = userIds.iterator(); - additionalUserIds.next(); // Skip primary user id - while (additionalUserIds.hasNext()) { - String additionalUserId = additionalUserIds.next(); + Iterator userIdIterator = this.userIds.iterator(); + userIdIterator.next(); // Skip primary user id + while (userIdIterator.hasNext()) { + String additionalUserId = userIdIterator.next(); signatureGenerator.init(SignatureType.POSITIVE_CERTIFICATION.getCode(), privateKey); PGPSignature additionalUserIdSignature = signatureGenerator.generateCertification(additionalUserId, primaryPubKey); @@ -175,7 +174,6 @@ public class KeyRingBuilder implements KeyRingBuilderInterface { secretKeyList.add(secretKeys.next()); } secretKeyRing = new PGPSecretKeyRing(secretKeyList); - return secretKeyRing; } @@ -266,31 +264,4 @@ public class KeyRingBuilder implements KeyRingBuilderInterface { PGPKeyPair pgpKeyPair = ImplementationFactory.getInstance().getPGPKeyPair(type.getAlgorithm(), keyPair, new Date()); return pgpKeyPair; } - - private static SecretKeyRingProtector protectorFromPassphrase(@Nonnull Passphrase passphrase, long keyId) { - if (!passphrase.isValid()) { - throw new IllegalStateException("Passphrase has been cleared."); - } - if (passphrase.isEmpty()) { - return SecretKeyRingProtector.unprotectedKeys(); - } else { - return SecretKeyRingProtector.unlockSingleKeyWith(passphrase, keyId); - } - } - - public static PGPSecretKey generatePGPSecretKey(KeySpec keySpec, @Nonnull Passphrase passphrase, boolean isPrimary) - throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { - PGPDigestCalculator keyFingerprintCalculator = ImplementationFactory.getInstance() - .getV4FingerprintCalculator(); - PGPKeyPair keyPair = generateKeyPair(keySpec); - - SecretKeyRingProtector protector = protectorFromPassphrase(passphrase, keyPair.getKeyID()); - - return new PGPSecretKey( - keyPair.getPrivateKey(), - keyPair.getPublicKey(), - keyFingerprintCalculator, - isPrimary, - protector.getEncryptor(keyPair.getKeyID())); - } } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java index 2249c854..5032c83a 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/generation/KeySpec.java @@ -47,7 +47,7 @@ public class KeySpec { } @Nonnull - SignatureSubpackets getSubpacketGenerator() { + public SignatureSubpackets getSubpacketGenerator() { return subpacketGenerator; } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java index 3aab17c9..1a6585b9 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditor.java @@ -26,7 +26,6 @@ import org.bouncycastle.openpgp.PGPKeyPair; import org.bouncycastle.openpgp.PGPKeyRingGenerator; import org.bouncycastle.openpgp.PGPPrivateKey; import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSignature; @@ -36,11 +35,12 @@ import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder; -import org.bouncycastle.openpgp.operator.PGPDigestCalculator; import org.pgpainless.PGPainless; +import org.pgpainless.algorithm.HashAlgorithm; import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.algorithm.PublicKeyAlgorithm; import org.pgpainless.algorithm.SignatureType; +import org.pgpainless.algorithm.negotiation.HashAlgorithmNegotiator; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.OpenPgpFingerprint; import org.pgpainless.key.generation.KeyRingBuilder; @@ -57,14 +57,14 @@ import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvi import org.pgpainless.key.util.KeyRingUtils; import org.pgpainless.key.util.RevocationAttributes; import org.pgpainless.signature.SignatureUtils; -import org.pgpainless.signature.builder.PrimaryKeyBindingSignatureBuilder; import org.pgpainless.signature.builder.RevocationSignatureBuilder; import org.pgpainless.signature.builder.SelfSignatureBuilder; import org.pgpainless.signature.builder.SignatureFactory; -import org.pgpainless.signature.builder.SubkeyBindingSignatureBuilder; import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets; import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; import org.pgpainless.signature.subpackets.SignatureSubpacketGeneratorUtil; +import org.pgpainless.signature.subpackets.SignatureSubpackets; +import org.pgpainless.signature.subpackets.SignatureSubpacketsHelper; import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; import org.pgpainless.util.CollectionUtils; import org.pgpainless.util.Passphrase; @@ -95,33 +95,21 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { SecretKeyRingProtector protector) throws PGPException { userId = sanitizeUserId(userId); - List secretKeyList = new ArrayList<>(); - Iterator secretKeyIterator = secretKeyRing.getSecretKeys(); + // user-id certifications live on the primary key + PGPSecretKey primaryKey = secretKeyRing.getSecretKey(); - // add user-id certificate to primary key - PGPSecretKey primaryKey = secretKeyIterator.next(); - PGPPublicKey publicKey = primaryKey.getPublicKey(); + // retain key flags from previous signature KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing); List keyFlags = info.getKeyFlagsOf(info.getKeyId()); + SelfSignatureBuilder builder = SignatureFactory.selfCertifyUserId( primaryKey, protector, signatureSubpacketCallback, - keyFlags.toArray(new KeyFlag[0])); + keyFlags); builder.setSignatureType(SignatureType.POSITIVE_CERTIFICATION); - builder.applyCallback(signatureSubpacketCallback); - PGPSignature signature = builder.build(publicKey, userId); - - publicKey = PGPPublicKey.addCertification(publicKey, - userId, signature); - primaryKey = PGPSecretKey.replacePublicKey(primaryKey, publicKey); - secretKeyList.add(primaryKey); - - while (secretKeyIterator.hasNext()) { - secretKeyList.add(secretKeyIterator.next()); - } - - secretKeyRing = new PGPSecretKeyRing(secretKeyList); + PGPSignature signature = builder.build(primaryKey.getPublicKey(), userId); + secretKeyRing = KeyRingUtils.injectCertification(secretKeyRing, userId, signature); return this; } @@ -138,51 +126,30 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { public SecretKeyRingEditorInterface addSubKey(@Nonnull KeySpec keySpec, @Nonnull Passphrase subKeyPassphrase, SecretKeyRingProtector secretKeyRingProtector) - throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException { + throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException { + + PGPKeyPair keyPair = KeyRingBuilder.generateKeyPair(keySpec); - PGPSecretKey secretSubKey = generateSubKey(keySpec, subKeyPassphrase); SecretKeyRingProtector subKeyProtector = PasswordBasedSecretKeyRingProtector - .forKey(secretSubKey, subKeyPassphrase); - PGPSignatureSubpacketVector hashedSubpackets = keySpec.getSubpackets(); - PGPSignatureSubpacketVector unhashedSubpackets = null; + .forKeyId(keyPair.getKeyID(), subKeyPassphrase); - return addSubKey(secretSubKey, hashedSubpackets, unhashedSubpackets, subKeyProtector, secretKeyRingProtector); + SelfSignatureSubpackets.Callback callback = new SelfSignatureSubpackets.Callback() { + @Override + public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) { + SignatureSubpacketsHelper.applyFrom(keySpec.getSubpackets(), (SignatureSubpackets) hashedSubpackets); + } + }; + + List keyFlags = KeyFlag.fromBitmask(keySpec.getSubpackets().getKeyFlags()); + KeyFlag firstFlag = keyFlags.remove(0); + KeyFlag[] otherFlags = keyFlags.toArray(new KeyFlag[0]); + + return addSubKey(keyPair, callback, subKeyProtector, secretKeyRingProtector, firstFlag, otherFlags); } @Override - @Deprecated - public SecretKeyRingEditorInterface addSubKey(PGPSecretKey secretSubKey, - PGPSignatureSubpacketVector hashedSubpackets, - PGPSignatureSubpacketVector unhashedSubpackets, - SecretKeyRingProtector subKeyProtector, - SecretKeyRingProtector keyRingProtector) - throws PGPException { - - PGPPublicKey primaryKey = secretKeyRing.getSecretKey().getPublicKey(); - - PBESecretKeyDecryptor ringDecryptor = keyRingProtector.getDecryptor(primaryKey.getKeyID()); - PBESecretKeyEncryptor subKeyEncryptor = subKeyProtector.getEncryptor(secretSubKey.getKeyID()); - - PGPDigestCalculator digestCalculator = ImplementationFactory.getInstance().getV4FingerprintCalculator(); - PGPContentSignerBuilder contentSignerBuilder = - SignatureUtils.getPgpContentSignerBuilderForKey(primaryKey); - - PGPPrivateKey privateSubKey = UnlockSecretKey.unlockSecretKey(secretSubKey, subKeyProtector); - PGPKeyPair subKeyPair = new PGPKeyPair(secretSubKey.getPublicKey(), privateSubKey); - - PGPKeyRingGenerator keyRingGenerator = new PGPKeyRingGenerator( - secretKeyRing, ringDecryptor, digestCalculator, contentSignerBuilder, subKeyEncryptor); - - keyRingGenerator.addSubKey(subKeyPair, hashedSubpackets, unhashedSubpackets); - secretKeyRing = keyRingGenerator.generateSecretKeyRing(); - - return this; - } - - @Override - public SecretKeyRingEditorInterface addSubKey(PGPSecretKey subkey, + public SecretKeyRingEditorInterface addSubKey(PGPKeyPair subkey, @Nullable SelfSignatureSubpackets.Callback bindingSignatureCallback, - @Nullable SelfSignatureSubpackets.Callback backSignatureCallback, SecretKeyRingProtector subkeyProtector, SecretKeyRingProtector primaryKeyProtector, KeyFlag keyFlag, @@ -193,36 +160,49 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { SignatureSubpacketsUtil.assureKeyCanCarryFlags(subkeyAlgorithm); PGPSecretKey primaryKey = secretKeyRing.getSecretKey(); - SubkeyBindingSignatureBuilder bindingSigBuilder = - new SubkeyBindingSignatureBuilder(primaryKey, primaryKeyProtector); + KeyRingInfo info = PGPainless.inspectKeyRing(secretKeyRing); + PublicKeyAlgorithm signingKeyAlgorithm = PublicKeyAlgorithm.fromId(primaryKey.getPublicKey().getAlgorithm()); + HashAlgorithm hashAlgorithm = HashAlgorithmNegotiator + .negotiateSignatureHashAlgorithm(PGPainless.getPolicy()) + .negotiateHashAlgorithm(info.getPreferredHashAlgorithms()); - bindingSigBuilder.applyCallback(bindingSignatureCallback); - bindingSigBuilder.getHashedSubpackets().setKeyFlags(flags); + // While we'd like to rely on our own BindingSignatureBuilder implementation, + // unfortunately we have to use BCs PGPKeyRingGenerator class since there is no public constructor + // for subkeys. See https://github.com/bcgit/bc-java/pull/1063 + PGPKeyRingGenerator ringGenerator = new PGPKeyRingGenerator( + secretKeyRing, + primaryKeyProtector.getDecryptor(primaryKey.getKeyID()), + ImplementationFactory.getInstance().getV4FingerprintCalculator(), + ImplementationFactory.getInstance().getPGPContentSignerBuilder( + signingKeyAlgorithm, hashAlgorithm), + subkeyProtector.getEncryptor(subkey.getKeyID())); + + SelfSignatureSubpackets hashedSubpackets = SignatureSubpackets.createHashedSubpackets(primaryKey.getPublicKey()); + SelfSignatureSubpackets unhashedSubpackets = SignatureSubpackets.createEmptySubpackets(); + hashedSubpackets.setKeyFlags(flags); + + if (bindingSignatureCallback != null) { + bindingSignatureCallback.modifyHashedSubpackets(hashedSubpackets); + bindingSignatureCallback.modifyUnhashedSubpackets(unhashedSubpackets); + } boolean isSigningKey = CollectionUtils.contains(flags, KeyFlag.SIGN_DATA) || CollectionUtils.contains(flags, KeyFlag.CERTIFY_OTHER); + PGPContentSignerBuilder primaryKeyBindingSigner = null; if (isSigningKey) { - // Add embedded back-signature made by subkey over primary key - PrimaryKeyBindingSignatureBuilder backSigBuilder = - new PrimaryKeyBindingSignatureBuilder(subkey, subkeyProtector); - backSigBuilder.applyCallback(backSignatureCallback); - PGPSignature backSig = backSigBuilder.build(primaryKey.getPublicKey()); - bindingSigBuilder.getHashedSubpackets().addEmbeddedSignature(backSig); + primaryKeyBindingSigner = ImplementationFactory.getInstance().getPGPContentSignerBuilder(subkeyAlgorithm, hashAlgorithm); } - PGPSignature bindingSig = bindingSigBuilder.build(subkey.getPublicKey()); - subkey = KeyRingUtils.secretKeyPlusSignature(subkey, bindingSig); - secretKeyRing = KeyRingUtils.secretKeysPlusSecretKey(secretKeyRing, subkey); + ringGenerator.addSubKey(subkey, + SignatureSubpacketsHelper.toVector((SignatureSubpackets) hashedSubpackets), + SignatureSubpacketsHelper.toVector((SignatureSubpackets) unhashedSubpackets), + primaryKeyBindingSigner); + + secretKeyRing = ringGenerator.generateSecretKeyRing(); return this; } - private PGPSecretKey generateSubKey(@Nonnull KeySpec keySpec, - @Nonnull Passphrase subKeyPassphrase) - throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { - return KeyRingBuilder.generatePGPSecretKey(keySpec, subKeyPassphrase, false); - } - @Override public SecretKeyRingEditorInterface revoke(SecretKeyRingProtector secretKeyRingProtector, @Nullable RevocationAttributes revocationAttributes) @@ -252,15 +232,13 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { SecretKeyRingProtector secretKeyRingProtector, @Nullable RevocationSignatureSubpackets.Callback subpacketsCallback) throws PGPException { + // retrieve subkey to be revoked PGPPublicKey revokeeSubKey = KeyRingUtils.requirePublicKeyFrom(secretKeyRing, keyID); + // create revocation PGPSignature subKeyRevocation = generateRevocation(secretKeyRingProtector, revokeeSubKey, subpacketsCallback); - revokeeSubKey = PGPPublicKey.addCertification(revokeeSubKey, subKeyRevocation); - - // Inject revoked public key into key ring - PGPPublicKeyRing publicKeyRing = KeyRingUtils.publicKeyRingFrom(secretKeyRing); - publicKeyRing = PGPPublicKeyRing.insertPublicKey(publicKeyRing, revokeeSubKey); - secretKeyRing = PGPSecretKeyRing.replacePublicKeys(secretKeyRing, publicKeyRing); + // inject revocation sig into key ring + secretKeyRing = KeyRingUtils.injectCertification(secretKeyRing, revokeeSubKey, subKeyRevocation); return this; } @@ -285,6 +263,16 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { return generateRevocation(secretKeyRingProtector, revokeeSubkey, callback); } + @Override + public PGPSignature createRevocationCertificate( + long subkeyId, + SecretKeyRingProtector secretKeyRingProtector, + @Nullable RevocationSignatureSubpackets.Callback certificateSubpacketsCallback) + throws PGPException { + PGPPublicKey revokeeSubkey = KeyRingUtils.requirePublicKeyFrom(secretKeyRing, subkeyId); + return generateRevocation(secretKeyRingProtector, revokeeSubkey, certificateSubpacketsCallback); + } + private PGPSignature generateRevocation(SecretKeyRingProtector protector, PGPPublicKey revokeeSubKey, @Nullable RevocationSignatureSubpackets.Callback callback) @@ -371,12 +359,7 @@ public class SecretKeyRingEditor implements SecretKeyRingEditorInterface { signatureBuilder.applyCallback(callback); PGPSignature revocationSignature = signatureBuilder.build(userId); - primaryPublicKey = PGPPublicKey.addCertification(primaryPublicKey, userId, revocationSignature); - - PGPPublicKeyRing publicKeyRing = KeyRingUtils.publicKeyRingFrom(secretKeyRing); - publicKeyRing = PGPPublicKeyRing.insertPublicKey(publicKeyRing, primaryPublicKey); - secretKeyRing = PGPSecretKeyRing.replacePublicKeys(secretKeyRing, publicKeyRing); - + secretKeyRing = KeyRingUtils.injectCertification(secretKeyRing, userId, revocationSignature); return this; } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.java b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.java index 41a395f9..f2a64190 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/modification/secretkeyring/SecretKeyRingEditorInterface.java @@ -12,10 +12,9 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPKeyPair; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSignature; -import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.key.OpenPgpFingerprint; import org.pgpainless.key.generation.KeySpec; @@ -66,18 +65,10 @@ public interface SecretKeyRingEditorInterface { SecretKeyRingEditorInterface addSubKey(@Nonnull KeySpec keySpec, @Nullable Passphrase subKeyPassphrase, SecretKeyRingProtector secretKeyRingProtector) - throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException; + throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException; - @Deprecated - SecretKeyRingEditorInterface addSubKey(PGPSecretKey subKey, - PGPSignatureSubpacketVector hashedSubpackets, - PGPSignatureSubpacketVector unhashedSubpackets, - SecretKeyRingProtector subKeyProtector, SecretKeyRingProtector keyRingProtector) - throws PGPException; - - SecretKeyRingEditorInterface addSubKey(PGPSecretKey subkey, + SecretKeyRingEditorInterface addSubKey(PGPKeyPair subkey, @Nullable SelfSignatureSubpackets.Callback bindingSignatureCallback, - @Nullable SelfSignatureSubpackets.Callback backSignatureCallback, SecretKeyRingProtector subkeyProtector, SecretKeyRingProtector primaryKeyProtector, KeyFlag keyFlag, @@ -244,17 +235,22 @@ public interface SecretKeyRingEditorInterface { * @return revocation certificate */ PGPSignature createRevocationCertificate(SecretKeyRingProtector secretKeyRingProtector, - RevocationAttributes revocationAttributes) + @Nullable RevocationAttributes revocationAttributes) throws PGPException; PGPSignature createRevocationCertificate(long subkeyId, SecretKeyRingProtector secretKeyRingProtector, - RevocationAttributes revocationAttributes) + @Nullable RevocationAttributes revocationAttributes) + throws PGPException; + + PGPSignature createRevocationCertificate(long subkeyId, + SecretKeyRingProtector secretKeyRingProtector, + @Nullable RevocationSignatureSubpackets.Callback certificateSubpacketsCallback) throws PGPException; default PGPSignature createRevocationCertificate(OpenPgpFingerprint subkeyFingerprint, SecretKeyRingProtector secretKeyRingProtector, - RevocationAttributes revocationAttributes) + @Nullable RevocationAttributes revocationAttributes) throws PGPException { return createRevocationCertificate(subkeyFingerprint.getKeyId(), secretKeyRingProtector, revocationAttributes); } diff --git a/pgpainless-core/src/main/java/org/pgpainless/key/util/KeyRingUtils.java b/pgpainless-core/src/main/java/org/pgpainless/key/util/KeyRingUtils.java index 7398edcd..4afbfd6a 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/key/util/KeyRingUtils.java +++ b/pgpainless-core/src/main/java/org/pgpainless/key/util/KeyRingUtils.java @@ -22,9 +22,10 @@ import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRingCollection; import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector; +import org.pgpainless.PGPainless; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.key.protection.UnlockSecretKey; -import org.pgpainless.util.CollectionUtils; public final class KeyRingUtils { @@ -203,22 +204,121 @@ public final class KeyRingUtils { return publicKeys; } - public static PGPSecretKeyRing secretKeysPlusPublicKey(PGPSecretKeyRing secretKeys, PGPPublicKey subkey) { - PGPPublicKeyRing publicKeys = publicKeyRingFrom(secretKeys); - PGPPublicKeyRing newPublicKeys = publicKeysPlusPublicKey(publicKeys, subkey); - PGPSecretKeyRing newSecretKeys = PGPSecretKeyRing.replacePublicKeys(secretKeys, newPublicKeys); - return newSecretKeys; + public static T injectCertification(T keyRing, PGPPublicKey certifiedKey, PGPSignature certification) { + PGPSecretKeyRing secretKeys = null; + PGPPublicKeyRing publicKeys; + if (keyRing instanceof PGPSecretKeyRing) { + secretKeys = (PGPSecretKeyRing) keyRing; + publicKeys = PGPainless.extractCertificate(secretKeys); + } else { + publicKeys = (PGPPublicKeyRing) keyRing; + } + + certifiedKey = PGPPublicKey.addCertification(certifiedKey, certification); + List publicKeyList = new ArrayList<>(); + Iterator publicKeyIterator = publicKeys.iterator(); + boolean added = false; + while (publicKeyIterator.hasNext()) { + PGPPublicKey key = publicKeyIterator.next(); + if (key.getKeyID() == certifiedKey.getKeyID()) { + added = true; + publicKeyList.add(certifiedKey); + } else { + publicKeyList.add(key); + } + } + if (!added) { + throw new NoSuchElementException("Cannot find public key with id " + Long.toHexString(certifiedKey.getKeyID()) + " in the provided key ring."); + } + + publicKeys = new PGPPublicKeyRing(publicKeyList); + if (secretKeys == null) { + return (T) publicKeys; + } else { + secretKeys = PGPSecretKeyRing.replacePublicKeys(secretKeys, publicKeys); + return (T) secretKeys; + } } - public static PGPPublicKeyRing publicKeysPlusPublicKey(PGPPublicKeyRing publicKeys, PGPPublicKey subkey) { - List publicKeyList = CollectionUtils.iteratorToList(publicKeys.getPublicKeys()); - publicKeyList.add(subkey); - PGPPublicKeyRing newPublicKeys = new PGPPublicKeyRing(publicKeyList); - return newPublicKeys; + public static T injectCertification(T keyRing, String userId, PGPSignature certification) { + PGPSecretKeyRing secretKeys = null; + PGPPublicKeyRing publicKeys; + if (keyRing instanceof PGPSecretKeyRing) { + secretKeys = (PGPSecretKeyRing) keyRing; + publicKeys = PGPainless.extractCertificate(secretKeys); + } else { + publicKeys = (PGPPublicKeyRing) keyRing; + } + + Iterator publicKeyIterator = publicKeys.iterator(); + PGPPublicKey primaryKey = publicKeyIterator.next(); + primaryKey = PGPPublicKey.addCertification(primaryKey, userId, certification); + + List publicKeyList = new ArrayList<>(); + publicKeyList.add(primaryKey); + while (publicKeyIterator.hasNext()) { + publicKeyList.add(publicKeyIterator.next()); + } + + publicKeys = new PGPPublicKeyRing(publicKeyList); + if (secretKeys == null) { + return (T) publicKeys; + } else { + secretKeys = PGPSecretKeyRing.replacePublicKeys(secretKeys, publicKeys); + return (T) secretKeys; + } } - public static PGPSecretKeyRing secretKeysPlusSecretKey(PGPSecretKeyRing secretKeys, PGPSecretKey subkey) { - return PGPSecretKeyRing.insertSecretKey(secretKeys, subkey); + public static T injectCertification(T keyRing, PGPUserAttributeSubpacketVector userAttributes, PGPSignature certification) { + PGPSecretKeyRing secretKeys = null; + PGPPublicKeyRing publicKeys; + if (keyRing instanceof PGPSecretKeyRing) { + secretKeys = (PGPSecretKeyRing) keyRing; + publicKeys = PGPainless.extractCertificate(secretKeys); + } else { + publicKeys = (PGPPublicKeyRing) keyRing; + } + + Iterator publicKeyIterator = publicKeys.iterator(); + PGPPublicKey primaryKey = publicKeyIterator.next(); + primaryKey = PGPPublicKey.addCertification(primaryKey, userAttributes, certification); + + List publicKeyList = new ArrayList<>(); + publicKeyList.add(primaryKey); + while (publicKeyIterator.hasNext()) { + publicKeyList.add(publicKeyIterator.next()); + } + + publicKeys = new PGPPublicKeyRing(publicKeyList); + if (secretKeys == null) { + return (T) publicKeys; + } else { + secretKeys = PGPSecretKeyRing.replacePublicKeys(secretKeys, publicKeys); + return (T) secretKeys; + } + } + + public static T keysPlusPublicKey(T keyRing, PGPPublicKey publicKey) { + PGPSecretKeyRing secretKeys = null; + PGPPublicKeyRing publicKeys; + if (keyRing instanceof PGPSecretKeyRing) { + secretKeys = (PGPSecretKeyRing) keyRing; + publicKeys = PGPainless.extractCertificate(secretKeys); + } else { + publicKeys = (PGPPublicKeyRing) keyRing; + } + + publicKeys = PGPPublicKeyRing.insertPublicKey(publicKeys, publicKey); + if (secretKeys == null) { + return (T) publicKeys; + } else { + secretKeys = PGPSecretKeyRing.replacePublicKeys(secretKeys, publicKeys); + return (T) secretKeys; + } + } + + public static PGPSecretKeyRing keysPlusSecretKey(PGPSecretKeyRing secretKeys, PGPSecretKey secretKey) { + return PGPSecretKeyRing.insertSecretKey(secretKeys, secretKey); } public static PGPSecretKey secretKeyPlusSignature(PGPSecretKey secretKey, PGPSignature signature) { @@ -227,4 +327,5 @@ public final class KeyRingUtils { PGPSecretKey newSecretKey = PGPSecretKey.replacePublicKey(secretKey, publicKey); return newSecretKey; } + } diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SignatureFactory.java b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SignatureFactory.java index f9a7d540..9bcac504 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SignatureFactory.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/builder/SignatureFactory.java @@ -4,18 +4,14 @@ package org.pgpainless.signature.builder; -import java.io.IOException; +import java.util.List; import javax.annotation.Nullable; -import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSignature; import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.algorithm.SignatureType; import org.pgpainless.exception.WrongPassphraseException; import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.signature.subpackets.BaseSignatureSubpackets; -import org.pgpainless.signature.subpackets.CertificationSubpackets; import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; public final class SignatureFactory { @@ -24,66 +20,14 @@ public final class SignatureFactory { } - public static SubkeyBindingSignatureBuilder bindNonSigningSubkey( + public static SelfSignatureBuilder selfCertifyUserId( PGPSecretKey primaryKey, SecretKeyRingProtector primaryKeyProtector, - @Nullable SelfSignatureSubpackets.Callback subkeyBindingSubpacketsCallback, - KeyFlag... flags) throws WrongPassphraseException { - if (hasSignDataFlag(flags)) { - throw new IllegalArgumentException("Binding a subkey with SIGN_DATA flag requires primary key backsig.\n" + - "Please use the method bindSigningSubkey() instead."); - } - - return bindSubkey(primaryKey, primaryKeyProtector, subkeyBindingSubpacketsCallback, flags); - } - - public static SubkeyBindingSignatureBuilder bindSigningSubkey( - PGPSecretKey primaryKey, - SecretKeyRingProtector primaryKeyProtector, - PGPSecretKey subkey, - SecretKeyRingProtector subkeyProtector, - @Nullable SelfSignatureSubpackets.Callback subkeyBindingSubpacketsCallback, - @Nullable SelfSignatureSubpackets.Callback primaryKeyBindingSubpacketsCallback, - KeyFlag... flags) - throws PGPException, IOException { - - SubkeyBindingSignatureBuilder subkeyBinder = bindSubkey(primaryKey, primaryKeyProtector, subkeyBindingSubpacketsCallback, flags); - - if (hasSignDataFlag(flags)) { - PGPSignature backsig = bindPrimaryKey( - subkey, subkeyProtector, primaryKeyBindingSubpacketsCallback) - .build(primaryKey.getPublicKey()); - subkeyBinder.getHashedSubpackets().addEmbeddedSignature(backsig); - } - - return subkeyBinder; - } - - private static SubkeyBindingSignatureBuilder bindSubkey(PGPSecretKey primaryKey, - SecretKeyRingProtector primaryKeyProtector, - @Nullable SelfSignatureSubpackets.Callback subkeyBindingSubpacketsCallback, - KeyFlag... flags) throws WrongPassphraseException { - if (flags.length == 0) { - throw new IllegalArgumentException("Keyflags for subkey binding cannot be empty."); - } - SubkeyBindingSignatureBuilder subkeyBinder = new SubkeyBindingSignatureBuilder(primaryKey, primaryKeyProtector); - SelfSignatureSubpackets hashedSubpackets = subkeyBinder.getHashedSubpackets(); - hashedSubpackets.setKeyFlags(flags); - - subkeyBinder.applyCallback(subkeyBindingSubpacketsCallback); - - return subkeyBinder; - } - - public static PrimaryKeyBindingSignatureBuilder bindPrimaryKey( - PGPSecretKey subkey, - SecretKeyRingProtector subkeyProtector, - @Nullable SelfSignatureSubpackets.Callback primaryKeyBindingSubpacketsCallback) throws WrongPassphraseException { - PrimaryKeyBindingSignatureBuilder primaryKeyBinder = new PrimaryKeyBindingSignatureBuilder(subkey, subkeyProtector); - - primaryKeyBinder.applyCallback(primaryKeyBindingSubpacketsCallback); - - return primaryKeyBinder; + @Nullable SelfSignatureSubpackets.Callback selfSignatureCallback, + List keyFlags) + throws WrongPassphraseException { + KeyFlag[] keyFlagArray = keyFlags.toArray(new KeyFlag[0]); + return selfCertifyUserId(primaryKey, primaryKeyProtector, selfSignatureCallback, keyFlagArray); } public static SelfSignatureBuilder selfCertifyUserId( @@ -100,55 +44,4 @@ public final class SignatureFactory { return certifier; } - public static SelfSignatureBuilder renewSelfCertification( - PGPSecretKey primaryKey, - SecretKeyRingProtector primaryKeyProtector, - @Nullable SelfSignatureSubpackets.Callback selfSignatureCallback, - PGPSignature oldCertification) throws WrongPassphraseException { - SelfSignatureBuilder certifier = new SelfSignatureBuilder( - primaryKey, primaryKeyProtector, oldCertification); - - certifier.applyCallback(selfSignatureCallback); - - return certifier; - } - - public static CertificationSignatureBuilder certifyUserId( - PGPSecretKey signingKey, - SecretKeyRingProtector signingKeyProtector, - @Nullable CertificationSubpackets.Callback subpacketsCallback) - throws WrongPassphraseException { - CertificationSignatureBuilder certifier = new CertificationSignatureBuilder(signingKey, signingKeyProtector); - - certifier.applyCallback(subpacketsCallback); - - return certifier; - } - - public static UniversalSignatureBuilder universalSignature( - SignatureType signatureType, - PGPSecretKey signingKey, - SecretKeyRingProtector signingKeyProtector, - @Nullable BaseSignatureSubpackets.Callback callback) - throws WrongPassphraseException { - UniversalSignatureBuilder builder = - new UniversalSignatureBuilder(signatureType, signingKey, signingKeyProtector); - - builder.applyCallback(callback); - - return builder; - } - - private static boolean hasSignDataFlag(KeyFlag... flags) { - if (flags == null) { - return false; - } - for (KeyFlag flag : flags) { - if (flag == KeyFlag.SIGN_DATA) { - return true; - } - } - return false; - } - } diff --git a/pgpainless-core/src/test/java/org/pgpainless/example/ModifyKeys.java b/pgpainless-core/src/test/java/org/pgpainless/example/ModifyKeys.java index c98d03ad..51d1d2a6 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/example/ModifyKeys.java +++ b/pgpainless-core/src/test/java/org/pgpainless/example/ModifyKeys.java @@ -170,7 +170,7 @@ public class ModifyKeys { * @throws NoSuchAlgorithmException */ @Test - public void addSubkey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException { + public void addSubkey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException { // Protector for unlocking the existing secret key SecretKeyRingProtector protector = SecretKeyRingProtector.unlockAllKeysWith(Passphrase.fromPassword(originalPassphrase), secretKey); Passphrase subkeyPassphrase = Passphrase.fromPassword("subk3yP4ssphr4s3"); diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/modification/AddSubkeyWithModifiedBindingSignatureSubpackets.java b/pgpainless-core/src/test/java/org/pgpainless/key/modification/AddSubkeyWithModifiedBindingSignatureSubpackets.java index 4306298d..4e1e5c18 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/modification/AddSubkeyWithModifiedBindingSignatureSubpackets.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/modification/AddSubkeyWithModifiedBindingSignatureSubpackets.java @@ -16,28 +16,28 @@ import java.util.List; import org.bouncycastle.bcpg.sig.NotationData; import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyPair; import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSignature; import org.junit.JUtils; import org.junit.jupiter.api.Test; import org.pgpainless.PGPainless; -import org.pgpainless.algorithm.EncryptionPurpose; import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.key.generation.KeyRingBuilder; import org.pgpainless.key.generation.KeySpec; import org.pgpainless.key.generation.type.KeyType; -import org.pgpainless.key.generation.type.xdh.XDHSpec; +import org.pgpainless.key.generation.type.eddsa.EdDSACurve; import org.pgpainless.key.info.KeyRingInfo; import org.pgpainless.key.protection.SecretKeyRingProtector; import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; -import org.pgpainless.util.Passphrase; public class AddSubkeyWithModifiedBindingSignatureSubpackets { + public static long MILLIS_IN_SEC = 1000; + @Test public void bindEncryptionSubkeyAndModifyBindingSignatureHashedSubpackets() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException { SecretKeyRingProtector protector = SecretKeyRingProtector.unprotectedKeys(); @@ -45,29 +45,30 @@ public class AddSubkeyWithModifiedBindingSignatureSubpackets { .modernKeyRing("Alice ", null); KeyRingInfo before = PGPainless.inspectKeyRing(secretKeys); - PGPSecretKey secretSubkey = KeyRingBuilder.generatePGPSecretKey( - KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS).build(), - Passphrase.emptyPassphrase(), false); + PGPKeyPair secretSubkey = KeyRingBuilder.generateKeyPair( + KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.SIGN_DATA).build()); + long secondsUntilExpiration = 1000; secretKeys = PGPainless.modifyKeyRing(secretKeys) .addSubKey(secretSubkey, new SelfSignatureSubpackets.Callback() { - @Override - public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) { - hashedSubpackets.setKeyExpirationTime(true, 1000); - hashedSubpackets.addNotationData(false, "test@test.test", "test"); - } - }, null, SecretKeyRingProtector.unprotectedKeys(), protector, KeyFlag.ENCRYPT_COMMS) + @Override + public void modifyHashedSubpackets(SelfSignatureSubpackets hashedSubpackets) { + hashedSubpackets.setKeyExpirationTime(true, secondsUntilExpiration); + hashedSubpackets.addNotationData(false, "test@test.test", "test"); + } + }, SecretKeyRingProtector.unprotectedKeys(), protector, KeyFlag.SIGN_DATA) .done(); KeyRingInfo after = PGPainless.inspectKeyRing(secretKeys); + List signingKeys = after.getSigningSubkeys(); + signingKeys.removeAll(before.getSigningSubkeys()); + assertFalse(signingKeys.isEmpty()); - List encryptionKeys = after.getEncryptionSubkeys(EncryptionPurpose.COMMUNICATIONS); - encryptionKeys.removeAll(before.getEncryptionSubkeys(EncryptionPurpose.COMMUNICATIONS)); - assertFalse(encryptionKeys.isEmpty()); - assertEquals(1, encryptionKeys.size()); - - PGPPublicKey newKey = encryptionKeys.get(0); - JUtils.assertEquals(new Date().getTime() + 1000 * 1000, after.getSubkeyExpirationDate(new OpenPgpV4Fingerprint(newKey)).getTime(), 2000); + PGPPublicKey newKey = signingKeys.get(0); + Date now = new Date(); + JUtils.assertEquals( + now.getTime() + MILLIS_IN_SEC * secondsUntilExpiration, + after.getSubkeyExpirationDate(new OpenPgpV4Fingerprint(newKey)).getTime(), 2 * MILLIS_IN_SEC); assertTrue(newKey.getSignatures().hasNext()); PGPSignature binding = newKey.getSignatures().next(); List notations = SignatureSubpacketsUtil.getHashedNotationData(binding); diff --git a/pgpainless-core/src/test/java/org/pgpainless/key/modification/RevokeSubKeyTest.java b/pgpainless-core/src/test/java/org/pgpainless/key/modification/RevokeSubKeyTest.java index f78cc32f..421c9ecf 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/key/modification/RevokeSubKeyTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/key/modification/RevokeSubKeyTest.java @@ -39,7 +39,6 @@ import org.pgpainless.key.util.RevocationAttributes; import org.pgpainless.signature.SignatureUtils; import org.pgpainless.signature.subpackets.RevocationSignatureSubpackets; import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; -import org.pgpainless.util.ArmorUtils; import org.pgpainless.util.Passphrase; public class RevokeSubKeyTest { @@ -82,11 +81,6 @@ public class RevokeSubKeyTest { .withReason(RevocationAttributes.Reason.KEY_RETIRED) .withDescription("Key no longer used.")); - // CHECKSTYLE:OFF - System.out.println("Revocation Certificate:"); - System.out.println(ArmorUtils.toAsciiArmoredString(revocationCertificate.getEncoded())); - // CHECKSTYLE:ON - PGPPublicKey publicKey = secretKeys.getPublicKey(); assertFalse(publicKey.hasRevocation()); @@ -107,8 +101,8 @@ public class RevokeSubKeyTest { .forKey(secretKeys, Passphrase.fromPassword("password123")); SecretKeyRingEditorInterface editor = PGPainless.modifyKeyRing(secretKeys); - PGPSignature keyRevocation = editor.createRevocationCertificate(primaryKey.getKeyID(), protector, null); - PGPSignature subkeyRevocation = editor.createRevocationCertificate(subKey.getKeyID(), protector, null); + PGPSignature keyRevocation = editor.createRevocationCertificate(primaryKey.getKeyID(), protector, (RevocationAttributes) null); + PGPSignature subkeyRevocation = editor.createRevocationCertificate(subKey.getKeyID(), protector, (RevocationAttributes) null); assertEquals(SignatureType.KEY_REVOCATION.getCode(), keyRevocation.getSignatureType()); assertEquals(SignatureType.SUBKEY_REVOCATION.getCode(), subkeyRevocation.getSignatureType()); diff --git a/pgpainless-core/src/test/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilderTest.java b/pgpainless-core/src/test/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilderTest.java deleted file mode 100644 index 4218563b..00000000 --- a/pgpainless-core/src/test/java/org/pgpainless/signature/builder/SubkeyBindingSignatureBuilderTest.java +++ /dev/null @@ -1,75 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.signature.builder; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -import java.io.IOException; -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; -import java.util.Iterator; -import java.util.List; - -import org.bouncycastle.bcpg.sig.NotationData; -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.openpgp.PGPSignature; -import org.junit.jupiter.api.Test; -import org.pgpainless.PGPainless; -import org.pgpainless.algorithm.EncryptionPurpose; -import org.pgpainless.algorithm.KeyFlag; -import org.pgpainless.key.info.KeyRingInfo; -import org.pgpainless.key.protection.SecretKeyRingProtector; -import org.pgpainless.signature.subpackets.SelfSignatureSubpackets; -import org.pgpainless.signature.subpackets.SignatureSubpacketsUtil; -import org.pgpainless.util.Passphrase; - -public class SubkeyBindingSignatureBuilderTest { - - @Test - public void testBindSubkeyWithCustomNotation() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException { - PGPSecretKeyRing secretKey = PGPainless.generateKeyRing() - .modernKeyRing("Alice ", "passphrase"); - KeyRingInfo info = PGPainless.inspectKeyRing(secretKey); - List previousSubkeys = info.getEncryptionSubkeys(EncryptionPurpose.ANY); - SecretKeyRingProtector protector = SecretKeyRingProtector.unlockAllKeysWith(Passphrase.fromPassword("passphrase"), secretKey); - - PGPSecretKeyRing tempSubkeyRing = PGPainless.generateKeyRing() - .modernKeyRing("Subkeys", null); - PGPPublicKey subkeyPub = PGPainless.inspectKeyRing(tempSubkeyRing) - .getEncryptionSubkeys(EncryptionPurpose.ANY).get(0); - PGPSecretKey subkeySec = tempSubkeyRing.getSecretKey(subkeyPub.getKeyID()); - - PGPSignature binding = SignatureFactory.bindNonSigningSubkey( - secretKey.getSecretKey(), protector, - new SelfSignatureSubpackets.Callback() { - @Override - public void modifyHashedSubpackets(SelfSignatureSubpackets subpackets) { - subpackets.addNotationData(false, "testnotation@pgpainless.org", "hello-world"); - } - }, KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE) - .build(subkeyPub); - - subkeyPub = PGPPublicKey.addCertification(subkeyPub, binding); - subkeySec = PGPSecretKey.replacePublicKey(subkeySec, subkeyPub); - secretKey = PGPSecretKeyRing.insertSecretKey(secretKey, subkeySec); - - info = PGPainless.inspectKeyRing(secretKey); - List nextSubkeys = info.getEncryptionSubkeys(EncryptionPurpose.ANY); - assertEquals(previousSubkeys.size() + 1, nextSubkeys.size()); - subkeyPub = secretKey.getPublicKey(subkeyPub.getKeyID()); - Iterator newBindingSigs = subkeyPub.getSignaturesForKeyID(secretKey.getPublicKey().getKeyID()); - PGPSignature bindingSig = newBindingSigs.next(); - assertNotNull(bindingSig); - List notations = SignatureSubpacketsUtil.getHashedNotationData(bindingSig); - - assertEquals(1, notations.size()); - assertEquals("testnotation@pgpainless.org", notations.get(0).getNotationName()); - assertEquals("hello-world", notations.get(0).getNotationValue()); - } -}