From 428bda18d72bb9b344e6a27a26262fe3e684beff Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 29 Jul 2026 22:35:05 +0200 Subject: [PATCH] Add PGPKeyPairGenerator factory methods for OpenPGP brainpool keys --- .../openpgp/operator/PGPKeyPairGenerator.java | 97 +++++++ .../openpgp/test/PGPKeyPairGeneratorTest.java | 236 ++++++++++++++++++ 2 files changed, 333 insertions(+) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyPairGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyPairGenerator.java index 2dc54f5c42..9bcb955c42 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyPairGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyPairGenerator.java @@ -2,6 +2,7 @@ import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.sec.SECObjectIdentifiers; +import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPKeyPair; @@ -183,6 +184,7 @@ public abstract PGPKeyPair generateLegacyX25519KeyPair() /** * Generate an ECDH elliptic curve encryption key over the NIST p-256 curve. + *
NIST P-256
is equivalent to
prime256v1
and
secp256r1
. * * @return NIST p-256 ECDSA encryption key pair * @throws PGPException if the key pair cannot be generated @@ -198,6 +200,7 @@ public PGPKeyPair generateNistP256ECDHKeyPair() /** * Generate an ECDH elliptic curve encryption key over the NIST p-384 curve. + *
NIST P-384
is equivalent to
secp384r1
. * * @return NIST p-384 ECDSA encryption key pair * @throws PGPException if the key pair cannot be generated @@ -213,6 +216,7 @@ public PGPKeyPair generateNistP384ECDHKeyPair() /** * Generate an ECDH elliptic curve encryption key over the NIST p-521 curve. + *
NIST P-521
is equivalent to
secp521r1
. * * @return NIST p-521 ECDSA encryption key pair * @throws PGPException if the key pair cannot be generated @@ -226,8 +230,54 @@ public PGPKeyPair generateNistP521ECDHKeyPair() return generateECDHKeyPair(SECObjectIdentifiers.secp521r1); } + /** + * Generate an ECDH elliptic curve encryption key over the brainpoolP256r1 curve. + * + * @return BrainpoolP256r1 ECDH encryption key pair + * @throws PGPException if the key pair cannot be generated + * + * @see + * RFC9580 - ECC Curves for OpenPGP + */ + public PGPKeyPair generateBrainpoolP256r1ECDHKeyPair() + throws PGPException + { + return generateECDHKeyPair(TeleTrusTObjectIdentifiers.brainpoolP256r1); + } + + /** + * Generate an ECDH elliptic curve encryption key over the brainpoolP384r1 curve. + * + * @return BrainpoolP384r1 ECDH encryption key pair + * @throws PGPException if the key pair cannot be generated + * + * @see + * RFC9580 - ECC Curves for OpenPGP + */ + public PGPKeyPair generateBrainpoolP384r1ECDHKeyPair() + throws PGPException + { + return generateECDHKeyPair(TeleTrusTObjectIdentifiers.brainpoolP384r1); + } + + /** + * Generate an ECDH elliptic curve encryption key over the brainpoolP512r1 curve. + * + * @return BrainpoolP512r1 ECDH encryption key pair + * @throws PGPException if the key pair cannot be generated + * + * @see + * RFC9580 - ECC Curves for OpenPGP + */ + public PGPKeyPair generateBrainpoolP512r1ECDHKeyPair() + throws PGPException + { + return generateECDHKeyPair(TeleTrusTObjectIdentifiers.brainpoolP512r1); + } + /** * Generate an ECDSA elliptic curve signing key over the NIST p-256 curve. + *
NIST P-256
is equivalent to
prime256v1
and
secp256r1
. * * @return NIST p-256 ECDSA signing key pair * @throws PGPException if the key pair cannot be generated @@ -243,6 +293,7 @@ public PGPKeyPair generateNistP256ECDSAKeyPair() /** * Generate an ECDSA elliptic curve signing key over the NIST p-384 curve. + *
NIST P-384
is equivalent to
secp384r1
. * * @return NIST p-384 ECDSA signing key pair * @throws PGPException if the key pair cannot be generated @@ -258,6 +309,7 @@ public PGPKeyPair generateNistP384ECDSAKeyPair() /** * Generate an ECDSA elliptic curve signing key over the NIST p-521 curve. + *
NIST P-521
is equivalent to
secp521r1
. * * @return NIST p-521 ECDSA signing key pair * @throws PGPException if the key pair cannot be generated @@ -271,6 +323,51 @@ public PGPKeyPair generateNistP521ECDSAKeyPair() return generateECDSAKeyPair(SECObjectIdentifiers.secp521r1); } + /** + * Generate an ECDSA elliptic curve signing key over the brainpoolP256r1 curve. + * + * @return BrainpoolP256r1 ECDSA signing key pair + * @throws PGPException if the key pair cannot be generated + * + * @see + * RFC9580 - ECC Curves for OpenPGP + */ + public PGPKeyPair generateBrainpoolP256r1ECDSAKeyPair() + throws PGPException + { + return generateECDSAKeyPair(TeleTrusTObjectIdentifiers.brainpoolP256r1); + } + + /** + * Generate an ECDSA elliptic curve signing key over the brainpoolP384r1 curve. + * + * @return BrainpoolP384r1 ECDSA signing key pair + * @throws PGPException if the key pair cannot be generated + * + * @see + * RFC9580 - ECC Curves for OpenPGP + */ + public PGPKeyPair generateBrainpoolP384r1ECDSAKeyPair() + throws PGPException + { + return generateECDSAKeyPair(TeleTrusTObjectIdentifiers.brainpoolP384r1); + } + + /** + * Generate an ECDSA elliptic curve signing key over the brainpoolP512r1 curve. + * + * @return BrainpoolP512r1 ECDSA signing key pair + * @throws PGPException if the key pair cannot be generated + * + * @see + * RFC9580 - ECC Curves for OpenPGP + */ + public PGPKeyPair generateBrainpoolP512r1ECDSAKeyPair() + throws PGPException + { + return generateECDSAKeyPair(TeleTrusTObjectIdentifiers.brainpoolP512r1); + } + /** * Generate an elliptic curve Diffie-Hellman encryption key pair over the curve identified by the given OID. * diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/PGPKeyPairGeneratorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/PGPKeyPairGeneratorTest.java index a0f71064f0..1bb6afe249 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/PGPKeyPairGeneratorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/PGPKeyPairGeneratorTest.java @@ -1,6 +1,7 @@ package org.bouncycastle.openpgp.test; import org.bouncycastle.asn1.sec.SECObjectIdentifiers; +import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; import org.bouncycastle.bcpg.ECDHPublicBCPGKey; import org.bouncycastle.bcpg.ECDSAPublicBCPGKey; import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; @@ -92,6 +93,25 @@ private void performWith(Factory factory) testGenerateV4P521ECDSAKey(factory); testGenerateV6P521ECDSAKey(factory); + + // Brainpool + testGenerateV4BrainpoolP256r1ECDHKey(factory); + testGenerateV6BrainpoolP256r1ECDHKey(factory); + + testGenerateV4BrainpoolP256r1ECDSAKey(factory); + testGenerateV6BrainpoolP256r1ECDSAKey(factory); + + testGenerateV4BrainpoolP384r1ECDHKey(factory); + testGenerateV6BrainpoolP384r1ECDHKey(factory); + + testGenerateV4BrainpoolP384r1ECDSAKey(factory); + testGenerateV6BrainpoolP384r1ECDSAKey(factory); + + testGenerateV4BrainpoolP512r1ECDHKey(factory); + testGenerateV6BrainpoolP512r1ECDHKey(factory); + + testGenerateV4BrainpoolP512r1ECDSAKey(factory); + testGenerateV6BrainpoolP512r1ECDSAKey(factory); } private void testGenerateV4RsaKey(Factory factory) @@ -556,6 +576,222 @@ private void testGenerateV6P521ECDSAKey(Factory factory) kp.getPublicKey().getCreationTime(), creationTime); } + private void testGenerateV4BrainpoolP256r1ECDHKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_4, creationTime); + + PGPKeyPair kp = gen.generateBrainpoolP256r1ECDHKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_4); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDH); + ECDHPublicBCPGKey k = (ECDHPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(TeleTrusTObjectIdentifiers.brainpoolP256r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV6BrainpoolP256r1ECDHKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_6, creationTime); + + PGPKeyPair kp = gen.generateBrainpoolP256r1ECDHKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_6); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDH); + ECDHPublicBCPGKey k = (ECDHPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(TeleTrusTObjectIdentifiers.brainpoolP256r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV4BrainpoolP256r1ECDSAKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_4, creationTime); + + PGPKeyPair kp = gen.generateBrainpoolP256r1ECDSAKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_4); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDSA); + ECDSAPublicBCPGKey k = (ECDSAPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(TeleTrusTObjectIdentifiers.brainpoolP256r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV6BrainpoolP256r1ECDSAKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_6, creationTime); + + PGPKeyPair kp = gen.generateBrainpoolP256r1ECDSAKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_6); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDSA); + ECDSAPublicBCPGKey k = (ECDSAPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(TeleTrusTObjectIdentifiers.brainpoolP256r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV4BrainpoolP384r1ECDHKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_4, creationTime); + + PGPKeyPair kp = gen.generateBrainpoolP384r1ECDHKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_4); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDH); + ECDHPublicBCPGKey k = (ECDHPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(TeleTrusTObjectIdentifiers.brainpoolP384r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV6BrainpoolP384r1ECDHKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_6, creationTime); + + PGPKeyPair kp = gen.generateBrainpoolP384r1ECDHKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_6); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDH); + ECDHPublicBCPGKey k = (ECDHPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(TeleTrusTObjectIdentifiers.brainpoolP384r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV4BrainpoolP384r1ECDSAKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_4, creationTime); + + PGPKeyPair kp = gen.generateBrainpoolP384r1ECDSAKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_4); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDSA); + ECDSAPublicBCPGKey k = (ECDSAPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(TeleTrusTObjectIdentifiers.brainpoolP384r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV6BrainpoolP384r1ECDSAKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_6, creationTime); + + PGPKeyPair kp = gen.generateBrainpoolP384r1ECDSAKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_6); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDSA); + ECDSAPublicBCPGKey k = (ECDSAPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(TeleTrusTObjectIdentifiers.brainpoolP384r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV4BrainpoolP512r1ECDHKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_4, creationTime); + + PGPKeyPair kp = gen.generateBrainpoolP512r1ECDHKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_4); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDH); + ECDHPublicBCPGKey k = (ECDHPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(TeleTrusTObjectIdentifiers.brainpoolP512r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV6BrainpoolP512r1ECDHKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_6, creationTime); + + PGPKeyPair kp = gen.generateBrainpoolP512r1ECDHKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_6); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDH); + ECDHPublicBCPGKey k = (ECDHPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(TeleTrusTObjectIdentifiers.brainpoolP512r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV4BrainpoolP512r1ECDSAKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_4, creationTime); + + PGPKeyPair kp = gen.generateBrainpoolP512r1ECDSAKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_4); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDSA); + ECDSAPublicBCPGKey k = (ECDSAPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(TeleTrusTObjectIdentifiers.brainpoolP512r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV6BrainpoolP512r1ECDSAKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_6, creationTime); + + PGPKeyPair kp = gen.generateBrainpoolP512r1ECDSAKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_6); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDSA); + ECDSAPublicBCPGKey k = (ECDSAPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(TeleTrusTObjectIdentifiers.brainpoolP512r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + public static void main(String[] args) { runTest(new PGPKeyPairGeneratorTest());