Search code examples
javaprivate-keyed25519

String to PrivateKey Ed25519


I have a private key as a String and I want to convert it to PrivateKey

String privateKey = "Y2E3YjYwYzRjMDRjMjk1ZDQ5ZTQzM2RlMTdjZjVkNGE0NGFjYzJmM2IzOWExNWZhMjViNGE4ZWJiZDBiMDVkYTIwNGU4MWE3ZWZmMTQ0NGE2ZmM2NjExNzRmNTY4M2I0YmYyMTk5YTkyY2UzOWRkZjdmMzhkNTFjYTNmM2Q3ZDU";

byte[] pkcs8EncodedBytes = Base64.getDecoder().decode(privateKey);

PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pkcs8EncodedBytes);

PrivateKey pkey = KeyFactory.getInstance("Ed25519") //NoSuchAlgorithmException
                            .generatePrivate(keySpec);

But I get this error:

java.security.NoSuchAlgorithmException: Ed25519 KeyFactory not available

at KeyFactory.getInstance("Ed25519")

I'm using Java-10


Solution

  • The posted key is double encoded, first hex, then Base64. Note that the double encoding is not necessary. If the key is Base64 and hex decoded, the result is a 64 bytes key.

    From this 64 bytes key the first 32 bytes are the secret key and the following 32 bytes are the public key. More details about this format can be found here.

    Import, signing and verification with BouncyCastle are possible with Ed25519PrivateKeyParameters, Ed25519PublicKeyParameters and Ed25519Signer classes:

    import java.nio.ByteBuffer;
    import java.nio.charset.StandardCharsets;
    import java.util.Base64;
    import org.bouncycastle.crypto.Signer;
    import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
    import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters;
    import org.bouncycastle.crypto.signers.Ed25519Signer;
    import org.bouncycastle.util.encoders.Hex;
    
    // Base64, hex decode
    String keyHexBase64 ="Y2E3YjYwYzRjMDRjMjk1ZDQ5ZTQzM2RlMTdjZjVkNGE0NGFjYzJmM2IzOWExNWZhMjViNGE4ZWJiZDBiMDVkYTIwNGU4MWE3ZWZmMTQ0NGE2ZmM2NjExNzRmNTY4M2I0YmYyMTk5YTkyY2UzOWRkZjdmMzhkNTFjYTNmM2Q3ZDU";
    byte[] key = Hex.decode(new String(Base64.getDecoder().decode(keyHexBase64), StandardCharsets.UTF_8));
    
    // Separate secret and public key
    ByteBuffer keyBuffer = ByteBuffer.wrap(key);
    byte[] secretKey = new byte[32];
    keyBuffer.get(secretKey);
    byte[] publicKey = new byte[keyBuffer.remaining()];
    keyBuffer.get(publicKey);
    
    // Signing
    byte[] message = "The quick brown fox jumps over the lazy dog".getBytes(StandardCharsets.UTF_8);
    Ed25519PrivateKeyParameters secretKeyParameters = new Ed25519PrivateKeyParameters(secretKey, 0);
    Signer signer = new Ed25519Signer();
    signer.init(true, secretKeyParameters);
    signer.update(message, 0, message.length);
    byte[] signature = signer.generateSignature();
    System.out.println("Signature (hex): " + Hex.toHexString(signature));
    
    // Verification
    Ed25519PublicKeyParameters publicKeyParameters = new Ed25519PublicKeyParameters(publicKey, 0);
    Signer verifier = new Ed25519Signer();
    verifier.init(false, publicKeyParameters);
    verifier.update(message, 0, message.length);
    boolean verified = verifier.verifySignature(signature); // Signature (hex): 2aa31bb14799a00ac1129bdd6773a8481f0fd7e829d59f6fccc81021bf21e397dc5d17362d342615a5500598542586cad8891f984bdb90ec0c80b48eb638df07
    System.out.println("Verification: " + verified); // Verification: true
    

    Regarding BouncyCastle, bcprov-jdk15on is required, which can be loaded from the Maven repository or from the BouncyCastle website.