0

I'm generating Ed25519 key pairs in my Kotlin app using the BouncyCastle library and have two requirements that are easy to implement in isolation, but seemingly difficult to do in tandem:

  • Provide the key pair as a JCE KeyPair instance for use with a third-party SSH library
  • Provide the public key in OpenSSH .pub format for the user to copy and paste into a git repository provider such as GitHub (i.e. ssh-ed25519 <encoded key> <comment>)

I have two options for generating the keys using BouncyCastle, each makes only one of these requirements easy.

Generate directly using BouncyCastle generator

val generator = Ed25519KeyPairGenerator()
generator.init(Ed25519KeyGenerationParameters(SecureRandom()))
val pair = generator.generateKeyPair()

This gives me a key containing Ed25519PublicKeyParameters, which makes it super easy to get the OpenSSH .pub format using OpenSSHPublicKeyUtil provided by BouncyCastle:

"ssh-ed25519 " + toBase64(OpenSSHPublicKeyUtil.encodePublicKey(publicKey))

...but there is no obvious way to get to a JCE KeyPair from here. The BouncyCastle JCE implementation seems to use BCEdDSAPublicKey and BCEdDSAPrivateKey as wrapper classes for exactly this purpose, but their constructors are package-private.

Generate using BouncyCastle as a JCE security provider

Security.addProvider(BouncyCastleProvider())
val keyPairGenerator: KeyPairGenerator = KeyPairGenerator.getInstance(EdDSAParameterSpec.Ed25519, BouncyCastleProvider.PROVIDER_NAME)
keyPairGenerator.initialize(EdDSAParameterSpec(EdDSAParameterSpec.Ed25519), SecureRandom())
val pair = keyPairGenerator.generateKeyPair()

This gives me the JCE KeyPair I'm looking for, but no obvious way to convert it to OpenSSH .pub format. The answers in this RSA-specific question all only support DSA/RSA, or suggest libraries that also don't seem to be able to handle the Ed25519 keys. I have tried:

  • Apache SSHD
  • SSHJ
  • Jsch

What I think I need

Any one of:

  • A way to convert from BouncyCastle's AsymmetricCipherKeyPair to a JCE KeyPair
  • A way to get a Ed25519PublicKeyParameters instance from a BCEdDSAPublicKey wrapper so I can use BouncyCastle's OpenSSH utility method
  • Another way to output a BouncyCastle generated Ed25519 public key from a KeyPair in OpenSSH format
  • Another way/library to generate an Ed25519 keypair that will support my two requirements
Mike Rippon
  • 564
  • 6
  • 11

1 Answers1

1

Hack using reflection (wrapped into getter of extension property), following way #2 (get a Ed25519PublicKeyParameters instance from a BCEdDSAPublicKey):

val BCEdDSAPublicKey.pubKey
    get() = BCEdDSAPublicKey::class.declaredMemberProperties
        .find { it.returnType.javaType == AsymmetricKeyParameter::class.java }!!
        .apply { isAccessible = true }
        .get(this) as AsymmetricKeyParameter