1

I'm trying to generate an OpenPGP keyring using a DSA and a ECDH keypair combination using Bouncy Castle's Java APIs. In order not to rely on NIST curves, I have chosen Curve25519 to be the function I want to use. However, upon keyring export I get an IllegalArgumentException:

java.lang.IllegalArgumentException: illegal object in getInstance: org.bouncycastle.asn1.DLSequence
   at: org.bouncycastle.asn1.ASN1ObjectIdentifier.getInstance(Unknown Source)
   at: org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter.getPGPPublicKey(Unknown Source)
   at: org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyConverter.getPGPPublicKey(Unknown Source)
   at: org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair.getPublicKey(Unknown Source)
   at: org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPair.(Unknown Source)

The code I'm using to generate both keypairs is as follows:

String identity, passphrase;        // User input
OutputStream secretOut, publicOut;  // File streams

try {
    KeyPairGenerator dsaKeygen  = KeyPairGenerator.getInstance ("DSA", "BC");
    KeyPairGenerator ecdhKeygen = KeyPairGenerator.getInstance ("ECDH", "BC");

    dsaKeygen.initialize (2048);
    KeyPair dsaKeypair = dsaKeygen.generateKeyPair ();

    X9ECParameters ecParams = CustomNamedCurves.getByName ("Curve25519");
    ECParameterSpec ecSpecs = new ECParameterSpec (
        ecParams.getCurve (),
        ecParams.getG (),
        ecParams.getN (),
        ecParams.getH (),
        ecParams.getSeed ()
    );

    ecdhKeygen.initialize (ecSpecs, new SecureRandom ());
    KeyPair ecdhKeypair = ecdhKeygen.generateKeyPair ();

    PGPKeyPair dsaPgpKeypair  = new JcaPGPKeyPair (PGPPublicKey.DSA, dsaKeypair, new Date ());
    PGPKeyPair ecdhPgpKeypair = new JcaPGPKeyPair (PGPPublicKey.ECDH, ecdhKeypair, new Date ()); // Fails

    PGPSignatureSubpacketGenerator flagsGen = new PGPSignatureSubpacketGenerator ();
    flagsGen.setKeyFlags (
        true,
        KeyFlags.CERTIFY_OTHER | 
            KeyFlags.SIGN_DATA | 
            KeyFlags.ENCRYPT_COMMS |
            KeyFlags.ENCRYPT_STORAGE
    );

    PGPDigestCalculator sha1Calculator = new JcaPGPDigestCalculatorProviderBuilder ()
        .build ()
        .get (HashAlgorithmTags.SHA1);

    PGPKeyRingGenerator keyRingGenerator = new PGPKeyRingGenerator (
        PGPSignature.POSITIVE_CERTIFICATION,
        dsaPgpKeypair,
        identity,
        sha1Calculator,
        flagsGenerator.generate (),
        null,
        new JcaPGPContentSignerBuilder (dsaPgpKeypair.getPublicKey ().getAlgorithm (), AlgorithmTags.SHA1),
        new JcePBESecretKeyEncryptorBuilder (PGPEncryptedDate.AES_256, sha1Calculator)
            .setProvider ("BC")
            .build (passphrase.toCharArray ())
    );

    keyRingGenerator.addSubKey (ecdhPgpKeypair);

    keyRingGenerator.generateSecretKeyRing ().encode (secretOut);
    secretOut.close ();

    keyRingGenerator.generatePublicKeyRing ().encode (publicOut);
    publicOut.close ();
} catch (Exception e) {
    // obligatory exception handling
}

For reference: The code generating the Curve25519 key I adapted from this GitHub issue and the code generating and exporting the whole keyring I adapted from this Bouncy Castle example.

However, above code works and exports an importable keyring (well, not importable into GnuPG because SHA1 is only supported by Bouncy Castle for key checksum generation) using curves from the ECNamedCurveTable, for example prime256v1.

Why does the conversion between a JCA keypair and an OpenPGP keypair fail for keys created using Curve25519, but not those created with specs from ECNamedCurveTable? Am I missing something or are custom curves not supported by Bouncy Castle for OpenPGP keys?

Alexander Leithner
  • 3,169
  • 22
  • 33
  • I have the same problem: Ec25519 is not in the OpenPGP RFC! It is a draft: https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-04 . So it isn't implemented yet. It would be nice to find a workaround for Java using Ec25519. – User8461 Jan 16 '20 at 00:44

0 Answers0