1

I'm using the Lazysodium library (https://terl.gitbook.io/lazysodium/) to access libsodium from Java, specifically for Ed25519 digital signatures.

I'd also like to be able to store key pairs in a standard Java keystore. However, libsodium works with byte arrays rather than JCA Keypair instances so it isn't clear how to achieve this.

In particular, how can you:

  • Convert a byte array Ed25519 key as used with Libsodium into a JCA KeyPair?
  • Convert a JCA Keypair back into the appropriate byte array for libsodium?
mikera
  • 105,238
  • 25
  • 256
  • 415
  • 1
    The conversion of your keys can be done easily with the code provided by @Topaco below, but unfortunately the Java key store types do not accept Ed25519 keys (or certificates) at the time of writing (Java 16), so your best choice is to store the keys locally in encrypted form (coming from the encoded/byte array form). On Android, I recommend the "EncryptedSharedPreferences" for this task. – Michael Fehr Jul 24 '21 at 22:31
  • 1
    I have to correct myself in parts - it is indeed possible to store Ed25519 keys in a Java keystore (tested on Java 11, PKCS12 type). You can' store the private/publlic keys as PrivateKey entry but use the encoded form and store it as **SecretKey** entry. Simply convert the encoded key to a SecretKey with e.g. "SecretKey secretKeyPrivate = new SecretKeySpec(ed25519PrivateKeyBytes, "Ed25519");" and store it with "keyStore.setKeyEntry("ed25519Private", secretKeyPrivate, keyStorePassword, null);". – Michael Fehr Jul 25 '21 at 09:15

1 Answers1

2

The conversion between a raw private Ed25519 key and a java.security.PrivateKey is possible e.g. with BouncyCastle as follows:

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.KeyFactory;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Base64;

import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.edec.EdECObjectIdentifiers;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.util.encoders.Hex;

...
// Generate private test key
PrivateKey privateKey = loadPrivateKey("Ed25519");
byte[] privateKeyBytes = privateKey.getEncoded();
System.out.println(Base64.getEncoder().encodeToString(privateKeyBytes)); // PKCS#8-key, check this in an ASN.1 Parser, e.g. https://lapo.it/asn1js/

// java.security.PrivateKey to raw Ed25519 key
Ed25519PrivateKeyParameters ed25519PrivateKeyParameters = (Ed25519PrivateKeyParameters)PrivateKeyFactory.createKey(privateKeyBytes);
byte[] rawKey = ed25519PrivateKeyParameters.getEncoded();
System.out.println(Hex.toHexString(rawKey)); // equals the raw 32 bytes key from the PKCS#8 key

// Raw Ed25519 key to java.security.PrivateKey
KeyFactory keyFactory = KeyFactory.getInstance("Ed25519");
PrivateKeyInfo privateKeyInfo = new PrivateKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), new DEROctetString(rawKey));
PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(privateKeyInfo.getEncoded());
PrivateKey privateKeyReloaded = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
byte[] privateKeyBytesReloaded = privateKeyReloaded.getEncoded();
System.out.println(Base64.getEncoder().encodeToString(privateKeyBytesReloaded)); // equals the PKCS#8 key from above

where the private test key was generated with:

private static PrivateKey loadPrivateKey(String algorithm) throws Exception {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(algorithm);
    KeyPair keyPair = keyPairGenerator.generateKeyPair();
    return keyPair.getPrivate();
}

For X25519 it is analogous, replacing all Ed25519 with X25519.

A conversion between a raw public X25519 key and a java.security.PublicKey (and vice versa) can be found here. For a raw public Ed25519 key, the procedure is analogous, where each X25519 has to be replaced by Ed25519.

However, I have not tested whether Ed25519 or X25519 keys can be stored in the keystore.

Topaco
  • 40,594
  • 4
  • 35
  • 62
  • You should add that the KeyPairGenerator only works on Java 15+ as you mentioned in your linked answer (X25519), on my Java 11 I'm getting an "Exception in thread "main" java.security.NoSuchAlgorithmException: Ed25519 KeyPairGenerator not available" error. – Michael Fehr Jul 24 '21 at 13:56
  • @MichaelFehr - I see it as the duty of users with old versions to check if a certain feature is already present in their old version. No offense intended, but if I had to take into account every version, I would have to constantly determine the release dates of methods/features. I assume the version that the OP specifies, or if no version is specified, the most recent. – Topaco Jul 24 '21 at 18:40