From e7570abf44b77eb9a95aafb74fae890746acda6a Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 16 Jun 2025 12:14:20 +0200 Subject: [PATCH 01/13] Add PublicKeyUtils.isEncryptionAlgorithm() --- .../org/bouncycastle/bcpg/PublicKeyUtils.java | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyUtils.java b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyUtils.java index 70213770a4..84d6891834 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyUtils.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyUtils.java @@ -30,26 +30,26 @@ public static boolean isSigningAlgorithm(int publicKeyAlgorithm) } } -// /** -// * Return true, if the public key algorithm that corresponds to the given ID is capable of encryption. -// * @param publicKeyAlgorithm public key algorithm id -// * @return true if algorithm can encrypt -// */ -// public static boolean isEncryptionAlgorithm(int publicKeyAlgorithm) -// { -// switch (publicKeyAlgorithm) -// { -// case PublicKeyAlgorithmTags.RSA_GENERAL: -// case PublicKeyAlgorithmTags.RSA_ENCRYPT: -// case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: -// case PublicKeyAlgorithmTags.ECDH: -// case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: -// case PublicKeyAlgorithmTags.DIFFIE_HELLMAN: -// case PublicKeyAlgorithmTags.X25519: -// case PublicKeyAlgorithmTags.X448: -// return true; -// default: -// return false; -// } -// } + /** + * Return true, if the public key algorithm that corresponds to the given ID is capable of encryption. + * @param publicKeyAlgorithm public key algorithm id + * @return true if algorithm can encrypt + */ + public static boolean isEncryptionAlgorithm(int publicKeyAlgorithm) + { + switch (publicKeyAlgorithm) + { + case PublicKeyAlgorithmTags.RSA_GENERAL: + case PublicKeyAlgorithmTags.RSA_ENCRYPT: + case PublicKeyAlgorithmTags.ELGAMAL_ENCRYPT: + case PublicKeyAlgorithmTags.ECDH: + case PublicKeyAlgorithmTags.ELGAMAL_GENERAL: + case PublicKeyAlgorithmTags.DIFFIE_HELLMAN: + case PublicKeyAlgorithmTags.X25519: + case PublicKeyAlgorithmTags.X448: + return true; + default: + return false; + } + } } From 6509a2594463a3cf721d331a4c052f725d6e6013 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 16 Jun 2025 12:14:59 +0200 Subject: [PATCH 02/13] OpenPGPSignature: Add customizable toAsciiArmoredString() methods --- .../openpgp/api/OpenPGPSignature.java | 71 +++++++++++++++++-- 1 file changed, 64 insertions(+), 7 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java index 0421bdc8c1..6a7432cc0e 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java @@ -452,21 +452,78 @@ protected String getType() public String toAsciiArmoredString() throws IOException { - ByteArrayOutputStream bOut = new ByteArrayOutputStream(); - ArmoredOutputStream.Builder aBuilder = ArmoredOutputStream.builder() + return toAsciiArmoredString(PacketFormat.ROUNDTRIP); + } + + /** + * Return an ASCII armored String representation of the signature. + * If the signature contains issuer information, the fingerprint or key-id of the issuer will be added + * to the ASCII armor as a comment header. + * + * @param packetFormat decide, which packet format to use when encoding the signature + * @return ASCII armored signature + * @throws IOException if the signature cannot be encoded + */ + public String toAsciiArmoredString(PacketFormat packetFormat) + throws IOException + { + ArmoredOutputStream.Builder armorBuilder = ArmoredOutputStream.builder() .clearHeaders(); if (getKeyIdentifier() != null) { - aBuilder.addSplitMultilineComment(getKeyIdentifier().toPrettyPrint()); + armorBuilder.addSplitMultilineComment(getKeyIdentifier().toPrettyPrint()); } - ArmoredOutputStream aOut = aBuilder.build(bOut); - BCPGOutputStream pOut = new BCPGOutputStream(aOut, PacketFormat.CURRENT); - getSignature().encode(pOut); - pOut.close(); + return toAsciiArmoredString(packetFormat, armorBuilder); + } + + /** + * Return an ASCII armored String representation of the signature. + * The ASCII armor can be configured using the passed {@link ArmoredOutputStream.Builder}. + * + * @param packetFormat decide, which packet format to use when encoding the signature + * @param armorBuilder builder for the ASCII armored output stream + * @return ASCII armored signature + * @throws IOException if the signature cannot be encoded + */ + public String toAsciiArmoredString(PacketFormat packetFormat, ArmoredOutputStream.Builder armorBuilder) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ArmoredOutputStream aOut = armorBuilder.build(bOut); + aOut.write(getEncoded(packetFormat)); aOut.close(); return bOut.toString(); } + /** + * Return the binary encoding of the signature. + * + * @return binary encoding + * @throws IOException if the signature cannot be encoded + */ + public byte[] getEncoded() + throws IOException + { + return getEncoded(PacketFormat.ROUNDTRIP); + } + + /** + * Return the binary encoding of the signature. + * + * @param packetFormat decide, which packet format to use when encoding the signature + * @return binary encoding + * @throws IOException if the signature cannot be encoded + */ + public byte[] getEncoded(PacketFormat packetFormat) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut, packetFormat); + signature.encode(pOut); + pOut.close(); + return bOut.toByteArray(); + } + /** * {@link SignatureSubpacket} and the {@link OpenPGPSignature} that contains it. */ From b3d23e3531ed364001cd5ab47c8ca66dfc1a89d8 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 16 Jun 2025 12:19:00 +0200 Subject: [PATCH 03/13] Implement EncryptedDataPacketType.of() and expose PGPEncryptedDataList.getEncryptedData() --- .../openpgp/PGPEncryptedDataList.java | 5 ++ .../openpgp/api/EncryptedDataPacketType.java | 62 +++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedDataList.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedDataList.java index 2f55f519e7..7c2cf62c20 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedDataList.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedDataList.java @@ -154,6 +154,11 @@ public PGPEncryptedData get( return (PGPEncryptedData)methods.get(index); } + public InputStreamPacket getEncryptedData() + { + return data; + } + /** * Gets the number of encryption methods in this list. */ diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/EncryptedDataPacketType.java b/pg/src/main/java/org/bouncycastle/openpgp/api/EncryptedDataPacketType.java index e8f5a67aa8..32165157b7 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/EncryptedDataPacketType.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/EncryptedDataPacketType.java @@ -1,5 +1,13 @@ package org.bouncycastle.openpgp.api; +import org.bouncycastle.bcpg.AEADEncDataPacket; +import org.bouncycastle.bcpg.InputStreamPacket; +import org.bouncycastle.bcpg.SymmetricEncDataPacket; +import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket; +import org.bouncycastle.bcpg.UnsupportedPacketVersionException; +import org.bouncycastle.openpgp.PGPEncryptedDataList; +import org.bouncycastle.openpgp.PGPException; + /** * Encryption Mode. */ @@ -34,4 +42,58 @@ public enum EncryptedDataPacketType * Support for this feature is signalled using {@link org.bouncycastle.bcpg.sig.Features#FEATURE_AEAD_ENCRYPTED_DATA}. */ LIBREPGP_OED // "v5" + ; + + /** + * Detect the type of the PGPEncryptedDataList's encrypted data packet. + * + * @param encDataList encrypted data list + * @return encrypted data packet type + * @throws PGPException if an unexpected data packet is encountered. + */ + public static EncryptedDataPacketType of(PGPEncryptedDataList encDataList) + throws PGPException + { + return of(encDataList.getEncryptedData()); + } + + /** + * Detect the type the provided encrypted data packet. + * + * @param encData encrypted data packet + * @return encrypted data packet type + * @throws PGPException if an unexpected data packet is encountered. + */ + public static EncryptedDataPacketType of(InputStreamPacket encData) + throws PGPException + { + if (encData instanceof SymmetricEncIntegrityPacket) + { + SymmetricEncIntegrityPacket seipd = (SymmetricEncIntegrityPacket) encData; + if (seipd.getVersion() == SymmetricEncIntegrityPacket.VERSION_1) + { + return SEIPDv1; + } + else if (seipd.getVersion() == SymmetricEncIntegrityPacket.VERSION_2) + { + return SEIPDv2; + } + else + { + throw new UnsupportedPacketVersionException("Symmetrically-Encrypted Integrity-Protected Data Packet of unknown version encountered: " + seipd.getVersion()); + } + } + else if (encData instanceof AEADEncDataPacket) + { + return LIBREPGP_OED; + } + else if (encData instanceof SymmetricEncDataPacket) + { + return SED; + } + else + { + throw new PGPException("Unexpected packet type: " + encData.getClass().getName()); + } + } } From 6c9cb4b87d708471606e57e815e677efaf5892d3 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 16 Jun 2025 12:22:55 +0200 Subject: [PATCH 04/13] MessageEncryptionMechanism: Add factory method for SED data, toString() implementation --- .../api/MessageEncryptionMechanism.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/MessageEncryptionMechanism.java b/pg/src/main/java/org/bouncycastle/openpgp/api/MessageEncryptionMechanism.java index ddb5b8666a..af41b12708 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/MessageEncryptionMechanism.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/MessageEncryptionMechanism.java @@ -57,6 +57,13 @@ public static MessageEncryptionMechanism unencrypted() SymmetricKeyAlgorithmTags.NULL, none); } + @Deprecated + public static MessageEncryptionMechanism legacyEncryptedNonIntegrityProtected(int symmetricKeyAlgorithm) + { + int none = 0; + return new MessageEncryptionMechanism(EncryptedDataPacketType.SED, symmetricKeyAlgorithm, none); + } + /** * The data will be encrypted and integrity protected using a SEIPDv1 packet. * @@ -137,4 +144,15 @@ public boolean equals(Object obj) && getSymmetricKeyAlgorithm() == m.getSymmetricKeyAlgorithm() && getAeadAlgorithm() == m.getAeadAlgorithm(); } + + @Override + public String toString() + { + String out = mode.name() + "[cipher: " + symmetricKeyAlgorithm; + if (mode == EncryptedDataPacketType.SEIPDv2 || mode == EncryptedDataPacketType.LIBREPGP_OED) + { + out += " aead: " + aeadAlgorithm; + } + return out + "]"; + } } From c931f24a87d61e5188a796ee6fb8a727141f50ee Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 16 Jun 2025 12:36:25 +0200 Subject: [PATCH 05/13] Turn KeyPairGeneratorCallback into an Interface. Applied the same pattern of moving factory methods into a static Util class as was done in SignatureParameters.Callback --- .../openpgp/api/KeyPairGeneratorCallback.java | 64 ++++++++++--------- .../openpgp/api/OpenPGPKeyEditor.java | 4 +- .../openpgp/api/OpenPGPKeyGenerator.java | 8 +-- 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPairGeneratorCallback.java b/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPairGeneratorCallback.java index 68825e773b..beca35f349 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPairGeneratorCallback.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPairGeneratorCallback.java @@ -7,7 +7,7 @@ /** * Callback to generate a {@link PGPKeyPair} from a {@link PGPKeyPairGenerator} instance. */ -public abstract class KeyPairGeneratorCallback +public interface KeyPairGeneratorCallback { /** * Generate a {@link PGPKeyPair} by calling a factory method on a given generator instance. @@ -16,45 +16,47 @@ public abstract class KeyPairGeneratorCallback * @return generated key pair * @throws PGPException */ - public abstract PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + PGPKeyPair generateFrom(PGPKeyPairGenerator generator) throws PGPException; - public static KeyPairGeneratorCallback primaryKey() - { - return new KeyPairGeneratorCallback() + static class Util { + public static KeyPairGeneratorCallback primaryKey() { - @Override - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException + return new KeyPairGeneratorCallback() { - return generator.generatePrimaryKey(); - } - }; - } + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generatePrimaryKey(); + } + }; + } - public static KeyPairGeneratorCallback encryptionKey() - { - return new KeyPairGeneratorCallback() + public static KeyPairGeneratorCallback encryptionKey() { - @Override - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException + return new KeyPairGeneratorCallback() { - return generator.generateEncryptionSubkey(); - } - }; - } + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateEncryptionSubkey(); + } + }; + } - public static KeyPairGeneratorCallback signingKey() - { - return new KeyPairGeneratorCallback() + public static KeyPairGeneratorCallback signingKey() { - @Override - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException + return new KeyPairGeneratorCallback() { - return generator.generateSigningSubkey(); - } - }; + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateSigningSubkey(); + } + }; + } } } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyEditor.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyEditor.java index 0f9b9a1f59..5a7801bad1 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyEditor.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyEditor.java @@ -184,7 +184,7 @@ public OpenPGPKeyEditor revokeIdentity(OpenPGPCertificate.OpenPGPIdentityCompone public OpenPGPKeyEditor addEncryptionSubkey() throws PGPException { - return addEncryptionSubkey(KeyPairGeneratorCallback.encryptionKey()); + return addEncryptionSubkey(KeyPairGeneratorCallback.Util.encryptionKey()); } public OpenPGPKeyEditor addEncryptionSubkey(KeyPairGeneratorCallback keyGenCallback) @@ -228,7 +228,7 @@ public void operate(PGPSignatureSubpacketGenerator hashedSubpackets) public OpenPGPKeyEditor addSigningSubkey() throws PGPException { - return addSigningSubkey(KeyPairGeneratorCallback.signingKey()); + return addSigningSubkey(KeyPairGeneratorCallback.Util.signingKey()); } public OpenPGPKeyEditor addSigningSubkey(KeyPairGeneratorCallback keyGenCallback) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java index 7ce19c3ad3..612320cd4d 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java @@ -229,7 +229,7 @@ public WithPrimaryKey signOnlyKey() throws PGPException { return withPrimaryKey( - KeyPairGeneratorCallback.primaryKey(), + KeyPairGeneratorCallback.Util.primaryKey(), SignatureParameters.Callback.Util.modifyHashedSubpackets(new SignatureSubpacketsFunction() { @Override @@ -252,7 +252,7 @@ public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpa public WithPrimaryKey withPrimaryKey() throws PGPException { - return withPrimaryKey(KeyPairGeneratorCallback.primaryKey()); + return withPrimaryKey(KeyPairGeneratorCallback.Util.primaryKey()); } /** @@ -408,7 +408,7 @@ public WithPrimaryKey addUserId( public WithPrimaryKey addEncryptionSubkey() throws PGPException { - return addEncryptionSubkey(KeyPairGeneratorCallback.encryptionKey()); + return addEncryptionSubkey(KeyPairGeneratorCallback.Util.encryptionKey()); } /** @@ -500,7 +500,7 @@ public void operate(PGPSignatureSubpacketGenerator hashedSubpackets) public WithPrimaryKey addSigningSubkey() throws PGPException { - return addSigningSubkey(KeyPairGeneratorCallback.signingKey()); + return addSigningSubkey(KeyPairGeneratorCallback.Util.signingKey()); } /** From f129a33bba94faadf6b53e3dd7760954339b10be Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 16 Jun 2025 12:41:14 +0200 Subject: [PATCH 06/13] PGPKeyRing: Add getKeyIdentifier() --- .../main/java/org/bouncycastle/openpgp/PGPKeyRing.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPKeyRing.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPKeyRing.java index c9a7630d1d..8507988deb 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPKeyRing.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPKeyRing.java @@ -93,6 +93,16 @@ static void readUserIDs( } } + /** + * Return the {@link KeyIdentifier} of this key rings primary key. + * + * @return primary key identifier + */ + public KeyIdentifier getKeyIdentifier() + { + return getPublicKey().getKeyIdentifier(); + } + /** * Return the first public key in the ring. In the case of a {@link PGPSecretKeyRing} * this is also the public key of the master key pair. From 2c69b9b529f6498c23c8226efaf434cd49aaa6de Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 16 Jun 2025 12:43:10 +0200 Subject: [PATCH 07/13] PGPPublicKey: getUserIds() -> skip non-UTF8 uids Those are still accessible via getRawUserIds() --- .../main/java/org/bouncycastle/openpgp/PGPPublicKey.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java index 4f69293bcb..a69a7f6335 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java @@ -487,7 +487,14 @@ public Iterator getUserIDs() { if (ids.get(i) instanceof UserIDPacket) { - temp.add(((UserIDPacket)ids.get(i)).getID()); + try + { + temp.add(((UserIDPacket) ids.get(i)).getID()); + } + catch (IllegalArgumentException e) + { + // Skip non-UTF8 user-ids + } } } From 16aeb76c584cc948ce72a31b7444361fdee0db84 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 16 Jun 2025 12:45:10 +0200 Subject: [PATCH 08/13] PGPSecretKey: Properly initialize PGPSignatureGenerator, adding support for v6 --- pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java index b6c814ed2f..d805fbbb37 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java @@ -293,7 +293,7 @@ public PGPSecretKey( // // generate the certification // - PGPSignatureGenerator sGen = new PGPSignatureGenerator(certificationSignerBuilder); + PGPSignatureGenerator sGen = new PGPSignatureGenerator(certificationSignerBuilder, masterKeyPair.getPublicKey()); sGen.init(PGPSignature.SUBKEY_BINDING, masterKeyPair.getPrivateKey()); @@ -302,7 +302,7 @@ public PGPSecretKey( { if (hashedPcks == null) { - PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(certificationSignerBuilder); + PGPSignatureGenerator signatureGenerator = new PGPSignatureGenerator(certificationSignerBuilder, keyPair.getPublicKey()); signatureGenerator.init(PGPSignature.PRIMARYKEY_BINDING, keyPair.getPrivateKey()); @@ -382,7 +382,7 @@ private static PGPPublicKey certifiedPublicKey( try { - sGen = new PGPSignatureGenerator(certificationSignerBuilder); + sGen = new PGPSignatureGenerator(certificationSignerBuilder, keyPair.getPublicKey()); } catch (Exception e) { From cf0e7c4fc7b3c0e45533f85ef30d273e29525b07 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 30 Jun 2025 13:16:52 +0200 Subject: [PATCH 09/13] Fix checkstyle and compilation issues --- .../bouncycastle/openpgp/api/KeyPairGeneratorCallback.java | 3 ++- .../openpgp/api/test/OpenPGPV6KeyGeneratorTest.java | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPairGeneratorCallback.java b/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPairGeneratorCallback.java index beca35f349..60585ff5dc 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPairGeneratorCallback.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPairGeneratorCallback.java @@ -19,7 +19,8 @@ public interface KeyPairGeneratorCallback PGPKeyPair generateFrom(PGPKeyPairGenerator generator) throws PGPException; - static class Util { + static class Util + { public static KeyPairGeneratorCallback primaryKey() { return new KeyPairGeneratorCallback() diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV6KeyGeneratorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV6KeyGeneratorTest.java index 7043bbfef5..87a3dd5b8b 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV6KeyGeneratorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV6KeyGeneratorTest.java @@ -581,7 +581,7 @@ private void testGenerateKeyWithoutSignatures(OpenPGPApi api) { OpenPGPKey key = api.generateKey() .withPrimaryKey( - KeyPairGeneratorCallback.primaryKey(), + KeyPairGeneratorCallback.Util.primaryKey(), // No direct-key sig new SignatureParameters.Callback() { @@ -591,7 +591,7 @@ public SignatureParameters apply(SignatureParameters parameters) { } }) .addSigningSubkey( - KeyPairGeneratorCallback.signingKey(), + KeyPairGeneratorCallback.Util.signingKey(), // No subkey binding sig new SignatureParameters.Callback() { @@ -611,7 +611,7 @@ public SignatureParameters apply(SignatureParameters parameters) } }) .addEncryptionSubkey( - KeyPairGeneratorCallback.encryptionKey(), + KeyPairGeneratorCallback.Util.encryptionKey(), // No subkey binding sig new SignatureParameters.Callback() { From f38e1f6eab36e4af52a948062430753c2ef0c439 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 30 Jun 2025 13:17:10 +0200 Subject: [PATCH 10/13] Properly copy salt when creating new instances of signatures --- .../bouncycastle/openpgp/PGPSignature.java | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java index 7688b94600..4b0beb1e25 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java @@ -1032,19 +1032,39 @@ public static PGPSignature join(PGPSignature sig1, PGPSignature sig2) } SignatureSubpacket[] unhashed = (SignatureSubpacket[])merged.toArray(new SignatureSubpacket[0]); - return new PGPSignature( - new SignaturePacket( - sig1.getVersion(), - sig1.sigPck.hasNewPacketFormat(), - sig1.getSignatureType(), - sig1.getKeyID(), - sig1.getKeyAlgorithm(), - sig1.getHashAlgorithm(), - sig1.getHashedSubPackets().packets, - unhashed, - sig1.getDigestPrefix(), - sig1.sigPck.getSignature() - ) - ); + if (sig1.getVersion() == SignaturePacket.VERSION_6) + { + return new PGPSignature( + new SignaturePacket( + sig1.getVersion(), + sig1.getSignatureType(), + sig1.getKeyID(), + sig1.getKeyAlgorithm(), + sig1.getHashAlgorithm(), + sig1.getHashedSubPackets().packets, + unhashed, + sig1.getDigestPrefix(), + sig1.sigPck.getSignatureBytes(), + sig1.getSalt() + ) + ); + } + else + { + return new PGPSignature( + new SignaturePacket( + sig1.getVersion(), + sig1.sigPck.hasNewPacketFormat(), + sig1.getSignatureType(), + sig1.getKeyID(), + sig1.getKeyAlgorithm(), + sig1.getHashAlgorithm(), + sig1.getHashedSubPackets().packets, + unhashed, + sig1.getDigestPrefix(), + sig1.sigPck.getSignature() + ) + ); + } } } From 7c3285dcbd2a101b22cc15b3f56d00dde742754e Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 30 Jun 2025 13:55:59 +0200 Subject: [PATCH 11/13] Move proper SignaturePacket copying to dedicated method --- .../bouncycastle/bcpg/SignaturePacket.java | 35 ++++++++++++++++++ .../bouncycastle/openpgp/PGPSignature.java | 37 ++----------------- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java b/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java index 853a22090f..9d9b3c7f07 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java @@ -8,6 +8,7 @@ import org.bouncycastle.bcpg.sig.IssuerFingerprint; import org.bouncycastle.bcpg.sig.IssuerKeyID; import org.bouncycastle.bcpg.sig.SignatureCreationTime; +import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; import org.bouncycastle.util.io.Streams; @@ -446,6 +447,40 @@ public SignaturePacket( } } + public static SignaturePacket copyOfWith(SignaturePacket packet, SignatureSubpacket[] unhashedSubpackets) + { + if (packet.getVersion() == SignaturePacket.VERSION_6) + { + return new SignaturePacket( + packet.getVersion(), + packet.getSignatureType(), + packet.getKeyID(), + packet.getKeyAlgorithm(), + packet.getHashAlgorithm(), + packet.getHashedSubPackets(), + unhashedSubpackets, + packet.getFingerPrint(), + packet.getSignatureBytes(), + packet.getSalt() + ); + } + else + { + return new SignaturePacket( + packet.getVersion(), + packet.hasNewPacketFormat(), + packet.getSignatureType(), + packet.getKeyID(), + packet.getKeyAlgorithm(), + packet.getHashAlgorithm(), + packet.getHashedSubPackets(), + unhashedSubpackets, + packet.getFingerPrint(), + packet.getSignature() + ); + } + } + /** * get the version number */ diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java index 4b0beb1e25..9105ad59e5 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java @@ -1032,39 +1032,8 @@ public static PGPSignature join(PGPSignature sig1, PGPSignature sig2) } SignatureSubpacket[] unhashed = (SignatureSubpacket[])merged.toArray(new SignatureSubpacket[0]); - if (sig1.getVersion() == SignaturePacket.VERSION_6) - { - return new PGPSignature( - new SignaturePacket( - sig1.getVersion(), - sig1.getSignatureType(), - sig1.getKeyID(), - sig1.getKeyAlgorithm(), - sig1.getHashAlgorithm(), - sig1.getHashedSubPackets().packets, - unhashed, - sig1.getDigestPrefix(), - sig1.sigPck.getSignatureBytes(), - sig1.getSalt() - ) - ); - } - else - { - return new PGPSignature( - new SignaturePacket( - sig1.getVersion(), - sig1.sigPck.hasNewPacketFormat(), - sig1.getSignatureType(), - sig1.getKeyID(), - sig1.getKeyAlgorithm(), - sig1.getHashAlgorithm(), - sig1.getHashedSubPackets().packets, - unhashed, - sig1.getDigestPrefix(), - sig1.sigPck.getSignature() - ) - ); - } + return new PGPSignature( + SignaturePacket.copyOfWith(sig1.sigPck, unhashed) + ); } } From da8ff5e23c20d3f9f888940b5ba719baa676d6df Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 30 Jun 2025 14:27:12 +0200 Subject: [PATCH 12/13] Fix merging public keys, make sure all keySigs are added to merged key --- pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java index a69a7f6335..2e02fc05aa 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKey.java @@ -1164,7 +1164,7 @@ public static PGPPublicKey join( } // key signatures - joinPgpSignatureList(copy.keySigs, keySigs, true, true); + joinPgpSignatureList(copy.keySigs, keySigs, false, true); // user-ids and id sigs for (int idIdx = 0; idIdx < copy.ids.size(); idIdx++) From a3d0f307584bc233454c143e3befad96092fda8f Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 4 Jul 2025 16:31:29 +0200 Subject: [PATCH 13/13] Implement PGPTrust pendant to TrustPacket Avoids ClassCastException when parsing cert with TrustPacket in PGPObjectFactory --- .../openpgp/PGPObjectFactory.java | 4 ++- .../org/bouncycastle/openpgp/PGPTrust.java | 36 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/PGPTrust.java diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPObjectFactory.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPObjectFactory.java index 852fc2d0f1..6f23b950fe 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPObjectFactory.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPObjectFactory.java @@ -10,10 +10,10 @@ import org.bouncycastle.bcpg.BCPGInputStream; import org.bouncycastle.bcpg.PacketTags; +import org.bouncycastle.bcpg.TrustPacket; import org.bouncycastle.bcpg.UnknownPacket; import org.bouncycastle.bcpg.UnsupportedPacketVersionException; import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator; -import org.bouncycastle.util.Iterable; /** * General class for reading a PGP object stream. @@ -141,6 +141,8 @@ public Object nextObject() return new PGPCompressedData(in); case PacketTags.LITERAL_DATA: return new PGPLiteralData(in); + case PacketTags.TRUST: + return new PGPTrust(in); case PacketTags.PUBLIC_KEY_ENC_SESSION: case PacketTags.SYMMETRIC_KEY_ENC_SESSION: case PacketTags.SYMMETRIC_KEY_ENC: diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPTrust.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPTrust.java new file mode 100644 index 0000000000..d400559c71 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPTrust.java @@ -0,0 +1,36 @@ +package org.bouncycastle.openpgp; + +import org.bouncycastle.bcpg.BCPGInputStream; +import org.bouncycastle.bcpg.TrustPacket; + +import java.io.IOException; +import java.util.Arrays; + +public class PGPTrust +{ + + private final TrustPacket packet; + + public PGPTrust(TrustPacket packet) + { + this.packet = packet; + } + + public PGPTrust(BCPGInputStream inputStream) + throws IOException + { + this((TrustPacket) inputStream.readPacket()); + } + + public TrustPacket getPacket() + { + return packet; + } + + public byte[] getLevelAndTrust() + { + return Arrays.copyOf( + packet.getLevelAndTrustAmount(), + packet.getLevelAndTrustAmount().length); + } +}