TaihouKai
TaihouKai

Reputation: 371

Convert Ed25519 public key to x25519 public key in Java

I'm trying to convert Ed25519 public key to x25519 public key in Java, using the lazysodium package:

import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PublicKey;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;

import com.goterl.lazysodium.LazySodium;
import com.goterl.lazysodium.LazySodiumJava;
import com.goterl.lazysodium.SodiumJava;

public class KeyOps {

    public static PublicKey ed25519ToX25519(PublicKey ed25519PublicKey) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
        byte[] x25519PublicKeyBytes = new byte[32];
        LazySodium lazySodium = new LazySodiumJava(new SodiumJava());
        byte[] ed25519PublicKeyBytes = new byte[32];
        System.arraycopy(ed25519PublicKey.getEncoded(), 12, ed25519PublicKeyBytes, 0, 32);
        lazySodium.convertPublicKeyEd25519ToCurve25519(x25519PublicKeyBytes, ed25519PublicKeyBytes);
        return KeyFactory.getInstance("X25519", "BC").generatePublic(new X509EncodedKeySpec(x25519PublicKeyBytes));
    }

}

... where ed25519PublicKey will be generated like:

KeyPair keyPair = KeyPairGenerator.getInstance("Ed25519", "BC").generateKeyPair();
PublicKey ed25519PublicKey = keyPair.getPublic();

However, I'm getting random errors, like one of below:

An exception occured while executing the Java class. encoded key spec not recognized: failed to construct sequence from byte[]: Extra data detected in stream

or

An exception occured while executing the Java class. encoded key spec not recognized: failed to construct sequence from byte[]: corrupted stream - out of bounds length found: 57 >= 54

(57 and 54 can be other values)

May I know what's wrong here? How do I convert Ed25519 pk to x25519 pk, and convert it back to PublicKey format?

Upvotes: 0

Views: 158

Answers (1)

TaihouKai
TaihouKai

Reputation: 371

OK I figured it out.

I have no idea how to encode X25519 pk with X509EncodedKeySpec natively in Java, so I construct it manually...

    public static PublicKey ed25519ToX25519(PublicKey ed25519PublicKey) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException {
        byte[] x25519PublicKeyBytes = new byte[32];
        LazySodium lazySodium = new LazySodiumJava(new SodiumJava());
        byte[] ed25519PublicKeyBytes = new byte[32];
        System.arraycopy(ed25519PublicKey.getEncoded(), 12, ed25519PublicKeyBytes, 0, 32);
        boolean conversion_success = lazySodium.convertPublicKeyEd25519ToCurve25519(x25519PublicKeyBytes, ed25519PublicKeyBytes);
        if (!conversion_success) {
            System.out.println("Conversion failed!");
            System.exit(1);
        }

        byte[] x509Header = new byte[]{
            0x30, 0x2a, // SEQUENCE, length 42
            0x30, 0x05, // SEQUENCE, length 5
            0x06, 0x03, 0x2b, 0x65, 0x6e, // OID for X25519
            0x03, 0x21, 0x00 // BIT STRING, length 33
        };
        // Combine the header and the key bytes
        byte[] x509EncodedKey = new byte[x509Header.length + x25519PublicKeyBytes.length];
        System.arraycopy(x509Header, 0, x509EncodedKey, 0, x509Header.length);
        System.arraycopy(x25519PublicKeyBytes, 0, x509EncodedKey, x509Header.length, x25519PublicKeyBytes.length);

        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(x509EncodedKey);
        KeyFactory keyFactory = KeyFactory.getInstance("X25519");
        PublicKey x25519PublicKey = keyFactory.generatePublic(keySpec);

        return x25519PublicKey;
    }

Upvotes: 0

Related Questions