0

I'm trying to generate keys of type Ed25519 on Android. Right now i use the library org.bouncycastle:bcpkix-jdk15on.

This is my code:

val keyPairGenerator = Ed25519KeyPairGenerator()
keyPairGenerator.init(Ed25519KeyGenerationParameters(SecureRandom()))

val keyPair = keyPairGenerator.generateKeyPair()

val privateKey = (keyPair.private as Ed25519PrivateKeyParameters).encoded
val publicKey = (keyPair.public as Ed25519PublicKeyParameters).encoded

val privateKeyBase64 = Base64.toBase64String(privateKey)
val publicKeyBase64 = Base64.toBase64String(publicKey)
Log.d(TAG, publicKeyBase64)

This print this public key (one example): JU+YTj99pb35tX+pLZAZAzdpwVp7GMGPkX0TcmsC7iQ=

This is an example of a public key I generated with the command line tool ssh-keygen:

ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKuDxTrKvkL3HbXrFuU796bmi9+KbKLTzT0QLumiJFmk example@gmail.com

You can see the payload has not the same size.

Then, when i try to use it on Github, the one generated by ssh-keygen works, but the other one don't. Even if i add ssh-ed25519 and the email.

This is the error message i get: Key is invalid. You must supply a key in OpenSSH public key format.

So how can i generate a key in the right format with Java?

Topaco
  • 40,594
  • 4
  • 35
  • 62
wiiznokes3
  • 91
  • 8
  • 2
    related [How to create an OpenSSH compatible ED25519 key with Bouncy Castle?](https://stackoverflow.com/q/67273915/22471702) – user22471702 Aug 31 '23 at 19:53
  • 1
    The public key conversion is analogous to the private key conversion in the linked post, but using `PublicKeyFactory.createKey()` and `OpenSSHPublicKeyUtil.encodePublicKey()`. The PEM conversion is to be omitted. Instead, a Base64 encoding is sufficient, which gives the central part of the key (`AAAAC3...`) following `ssh-ed25519` (see e.g. [here](https://www.thedigitalcatonline.com/blog/2018/04/25/rsa-keys/#:~:text=The%20OpenSSH%20public%20key%20format) for a format description). – Topaco Aug 31 '23 at 22:53

1 Answers1

2

First, that isn't Java, although it is some language that compiles to run in the JVM and access Java libraries -- I'm guessing Scala or Kotlin, although I'm not familiar enough with either to say which. Second, you can't be using only bcpkix, because the classes you use aren't there; they are in bcprov (or bcprov-ext).

Your problem is the 'blob' in OpenSSH publickey format uses the SSH protocol's encoding for the relevant keytype, which for ed25519 adds prefix lengths and a (redundant) copy of the keytype string. This can be done by hand, but since you're already using Bouncy it can do most of it for you:

// I only know Java, translate back to your language as needed
// invoke keyPairGenerator as you have, with result AsymmetricCipherKeyPair keyPair
byte[] wire = OpenSSHPublicKeyUtils.encodePublicKey(keyPair.getPublic());
String line = "ssh-ed25519 " + Base64.getEncoder().encodeToString(wire) + " comment_field";
// write that to a file, or display it, or whatever you need

For OpenSSH the third component doesn't have to be an email address, it can be any information you want to associate with the key. I don't know if GitHub is stricter here, but as you have a working value you might as well keep it.

dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70