javaed25519

How derive ed25519 public key from private key using Java


As indicate in title, how to derive ed25519 public key from private key (byte[]) using Java 15+ (without dependencies like BouncyCastle).

It's easy with BouncyCastle:

byte[] rawPrivateKey = ...;
Ed25519PrivateKeyParameters privateKeyRebuild = new Ed25519PrivateKeyParameters(rawPrivateKey , 0);
Ed25519PublicKeyParameters publicKeyRebuild = privateKeyRebuild.generatePublicKey();
byte[] rawPublicKey = publicKeyRebuild.getEncoded();

openssl has EVP_PKEY_get_raw_public_key method.

Java 15 support ed25519, but I fail to understand how to use it. Since https://openjdk.org/jeps/396, the ed25519 internals are not accessible, so is there any possibility still ?


Solution

  • You can achieve this with a trick: You generate a key pair and force the key pair generator to use your private key instead of generating a new one. With this procedure, the public key in question is implicitly derived using the built-in EC arithmetic (which you otherwise have no access to, as this is not exposed):

    import java.security.KeyPairGenerator;
    import java.security.SecureRandom;
    import java.security.spec.NamedParameterSpec;
    import java.util.Arrays;
    ...
    byte[] rawPrivateKey = {...}; // 32 bytes raw private key
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("Ed25519");
    keyPairGenerator.initialize(new NamedParameterSpec("Ed25519"), new SecureRandom() {
        public void nextBytes(byte[] bytes) {
            System.arraycopy(rawPrivateKey, 0, bytes, 0, rawPrivateKey.length);
        }
    });
    byte[] spki = keyPairGenerator.generateKeyPair().getPublic().getEncoded(); // public key in SPKI format; the last 32 bytes are the raw public key
    byte[] rawPublicKey = Arrays.copyOfRange(spki, spki.length - 32, spki.length); // 32 bytes raw public key
    

    For the key generation to apply the specified private key, a derivation of SecureRandom is required to overwrite nextBytes() in a suitable way, which is implemented in the code above using an anonymous class.

    This works on Java 15+ without additional libraries. The approach is based on this post, which describes how to extract a public key from the private key for X25519 in this way.