EdDSA OctetKeyPair from JDK 15's EdECPublicKey and EdECPrivateKey

Issue #393 new
Former user created an issue

The JEP 339 linked in your documentation should be available now.

Is there a way or a workaround to convert a EdDSA key pair generated with JDK 15's KeyPairGenerator or a EdDSA key pair loaded from a key store generated with JDK 15's keytool to a JWK?

I considered constructing an OctetKeyPair from the information provided in the EdEC*Key, but I could not find the x parameter required by the OctetKeyPair constructor. Java only provides isXOdd.

If there is no way, I suggest this as an enhancement to the OctetKeyPair.Builder.

Comments (6)

  1. Bart Beton

    I need this too :). These keys are more and more common because they are more effiencient than for example RSA and still assymetric. Since java 15 supports them, can nimbus too?

  2. Rafael Kaczmarek

    Here’s a workaround to deal with the java security EdEC*Key generated by the keypairgenerator or when fetched from java keystore (using e.g. BC) that i adopted from a solution that i found here

    I’m using this to create the JWK for signing JWTs with the CA microservice. The keypair is generated with algorithm Ed25519.

    private JWK createJWK(...)
                throws CertificateStorageException, IOException, JOSEException {
            final KeyStore.PrivateKeyEntry privateKeyEntry = certificateStorage.getEntry(...);
            final EdECPrivateKey ecPrivateKey = (EdECPrivateKey) privateKeyEntry.getPrivateKey();
            final EdECPublicKey publicKey = (EdECPublicKey) privateKeyEntry.getCertificate().getPublicKey();
            final NamedParameterSpec namedParameterSpec = ecPrivateKey.getParams();
    
            /*
             * Public key is:
             *
             * SEQUENCE (2 elem)
             *   SEQUENCE (1 elem)
             *     OBJECT IDENTIFIER
             *   BIT STRING (n bit) <-- x value
             *
             */
            final ASN1Sequence pubPrim = (ASN1Sequence) ASN1Sequence.fromByteArray(publicKey.getEncoded());
            final byte[] x = ((ASN1BitString) pubPrim.getObjectAt(1)).getOctets();
    
            /*
             * Private key is:
             *
             * SEQUENCE (4 elem)
             *   INTEGER
             *   SEQUENCE (1 elem)
             *     OBJECT IDENTIFIER
             *   OCTET STRING (1 elem)
             *     OCTET STRING (n byte) <-- d value
             *   OCTET STRING (n byte) <-- (x value)
             *
             */
            final ASN1Sequence privPrim = (ASN1Sequence) ASN1Sequence.fromByteArray(ecPrivateKey.getEncoded());
            byte[] d = ((ASN1OctetString) privPrim.getObjectAt(2)).getOctets();
    
            // Both the public and private keys should be the same length.
            // For some reason, sometimes the private key is double-wrapped in OctetStrings and we need to unpack that.
            if (x.length < d.length) {
                d = ((ASN1OctetString) ASN1OctetString.fromByteArray(d)).getOctets();
            }
    
            return new OctetKeyPair
                    .Builder(
                    Curve.parse(namedParameterSpec.getName()),
                    Base64URL.encode(x)
            )
                    .keyID(...)
                    .algorithm(Algorithm.parse("EdDSA"))
                    .keyOperations(Set.of(KeyOperation.SIGN, KeyOperation.VERIFY))
                    .keyUse(KeyUse.SIGNATURE)
                    .d(Base64URL.encode(d))
                    .build();
        }
    

  3. Vladimir Dzhuvinov

    The Numbus lib currently targets Java 7, which means Java 15’s APIs are out of reach.

    We’ll probably need a separate module / dependency on top of the core lib to handle EdDSA in JDK 15.

  4. Vladimir Dzhuvinov

    @Bart: Chiefly to cater for the Android ecosystem, which API level is behind what the current JDKs have.

    Occasionally we also get requests to support Java 6, to accommodate legacy IBM web applications.

  5. Log in to comment