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.
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();
}
}
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);
}
© 2022 - 2024 — McMap. All rights reserved.