How derive ed25519 (eddsa) public key from private key using Java
Asked Answered
B

2

8

I am looking into https://github.com/str4d/ed25519-java library as well as some other libraries like BouncyCastle, but I am so far unsuccessful in generating a public key out from a private key. I am not interested in generating a key pair because I already have a private key and am interested in generating the corresponding public key only.

Bayadere answered 17/11, 2020 at 0:39 Comment(0)
A
8

Using Bouncy Castle (BC) as crypto provider library is always a good choice, and they have a "build in" support for deriving the public key from an existing private key. Please keep in mind that BC does not work with a Private or Public key but instead with Ed25519PrivateKeyParameters and Ed25519PublicKeyParameters but as you can see it's very easy to get them from the encoded keys.

The full program is a little bit longer to prove that the rebuild public key is been able to verify a signature generated by its corresponding private key. Therefore, the main part of the program is to generate and verify a ED25519 signature.

Those two lines are doing what you are asking:

Ed25519PrivateKeyParameters privateKeyRebuild = new Ed25519PrivateKeyParameters(privateKeyEncoded, 0);
Ed25519PublicKeyParameters publicKeyRebuild = privateKeyRebuild.generatePublicKey(); 

The following lines are verifying the signature with the rebuild public key successfully.

output:

ED25519 signature with BC and deriving public key from private key
signature Length  :64 Data:218c6dd5053ee22e94325981cdeb81d623b80715b21495d22ef9d8dbf0c4a097699747bafedbd2fd2bcdfdededb2664ea5b732e2242b7cb92ddd6e51acbed30e
signature correct :true
Rebuild the keys and verify the signature with rebuild public key
signature correct :true

Security warning: the code does not have any exception handling and is for educational purpose only.

code:

import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CryptoException;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator;
import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters;
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
import org.bouncycastle.crypto.signers.Ed25519Signer;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.security.Security;

public class Ed25519SignatureWithPublicKeyDeriving {
    public static void main(String[] args) throws CryptoException {
        System.out.println("ED25519 signature with BC and deriving public key from private key");
        Security.addProvider(new BouncyCastleProvider());
        // generate ed25519 keys
        SecureRandom RANDOM = new SecureRandom();
        Ed25519KeyPairGenerator keyPairGenerator = new Ed25519KeyPairGenerator();
        keyPairGenerator.init(new Ed25519KeyGenerationParameters(RANDOM));
        AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
        Ed25519PrivateKeyParameters privateKey = (Ed25519PrivateKeyParameters) asymmetricCipherKeyPair.getPrivate();
        Ed25519PublicKeyParameters publicKey = (Ed25519PublicKeyParameters) asymmetricCipherKeyPair.getPublic();
        // the message
        byte[] message = "Message to sign".getBytes(StandardCharsets.UTF_8);
        // create the signature
        Signer signer = new Ed25519Signer();
        signer.init(true, privateKey);
        signer.update(message, 0, message.length);
        byte[] signature = signer.generateSignature();
        // verify the signature
        Signer verifier = new Ed25519Signer();
        verifier.init(false, publicKey);
        verifier.update(message, 0, message.length);
        boolean shouldVerify = verifier.verifySignature(signature);
        // output
        System.out.println("signature Length  :" + signature.length + " Data:" + bytesToHex(signature));
        System.out.println("signature correct :" + shouldVerify);

        // derive pub key from private key, here in encoded
        byte[] privateKeyEncoded = privateKey.getEncoded();
        // rebuild the keys
        System.out.println("Rebuild the keys and verify the signature with rebuild public key");
        Ed25519PrivateKeyParameters privateKeyRebuild = new Ed25519PrivateKeyParameters(privateKeyEncoded, 0);
        Ed25519PublicKeyParameters publicKeyRebuild = privateKeyRebuild.generatePublicKey();
        // verify the signature
        Signer verifierDerived = new Ed25519Signer();
        verifierDerived.init(false, publicKeyRebuild);
        verifierDerived.update(message, 0, message.length);
        boolean shouldVerifyDerived = verifierDerived.verifySignature(signature);
        System.out.println("signature correct :" + shouldVerifyDerived);
    }
    private static String bytesToHex(byte[] bytes) {
        StringBuffer result = new StringBuffer();
        for (byte b : bytes) result.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
        return result.toString();
    }
}
Aetna answered 17/11, 2020 at 2:31 Comment(0)
R
4

If you want to avoid or can't use Bouncy Castle for some reason, then use Sun Crypto Service Provider instead (for Java 15 and later). Something like the following should work, where params is either NamedParameterSpec.Ed25519 or NamedParameterSpec.Ed448

public PublicKey getEdDSAPublicKeyFromPrivateKey(NamedParameterSpec params, byte[] privateKey) {

  EdDSAOperations eddsaOperations = new EdDSAOperations(EdDSAParameters.get(InvalidAlgorithmParameterException::new, params));
  EdECPoint edecPublicKeyPoint = eddsaOperations.computePublic(privateKey);
  
  // Use KeyFactory to convert public point to public key
  KeyFactory kf = KeyFactory.getInstance("EdDSA");
  EdECPublicKeySpec pubSpec = new EdECPublicKeySpec(params, edecPublicKeyPoint);
  return kf.generatePublic(pubSpec);
}

Rich answered 13/6, 2022 at 12:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.