Skip to content

Instantly share code, notes, and snippets.

@wernerb
Created July 15, 2014 15:46
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save wernerb/3795be58d27829512272 to your computer and use it in GitHub Desktop.
Save wernerb/3795be58d27829512272 to your computer and use it in GitHub Desktop.
Generate SSH public keypair from SSH RSA/DSA/EC private key in Scala
import java.io.{DataOutputStream, ByteArrayOutputStream, StringReader}
import java.security.interfaces.{ECPublicKey, DSAParams, DSAPublicKey, RSAPublicKey}
import java.security._
import java.security.spec.X509EncodedKeySpec
import org.apache.commons.codec.binary.Base64
import org.bouncycastle.openssl.{PEMKeyPair, PEMParser}
object genPubSSHKey {
/**
* Generates a public key from a SSH private key.
* @param privateKey A RSA/DSA/EC private key
* @return SSH encoded public key
*/
def getPublicKey(privateKey: String): String = {
val kp: PEMKeyPair = new PEMParser(new StringReader(privateKey)).readObject().asInstanceOf[PEMKeyPair]
val x509 = new X509EncodedKeySpec(kp.getPublicKeyInfo.getEncoded)
val keyFactory = if (privateKey.startsWith("-----BEGIN RSA PRIVATE KEY-----")) {
KeyFactory.getInstance("RSA")
} else if (privateKey.startsWith("-----BEGIN DSA PRIVATE KEY-----")) {
KeyFactory.getInstance("DSA")
} else if (privateKey.startsWith("-----BEGIN EC PRIVATE KEY-----")) {
KeyFactory.getInstance("EC")
} else {
throw new Exception("Incompatible SSH key algorithm")
}
encodePublicKey(keyFactory.generatePublic(x509),"ozone")
}
private def encodePublicKey(publicKey: PublicKey, user: String): String = {
var publicKeyEncoded: String = null
if (publicKey.getAlgorithm == "RSA") {
val rsaPublicKey: RSAPublicKey = publicKey.asInstanceOf[RSAPublicKey]
val byteOs: ByteArrayOutputStream = new ByteArrayOutputStream
val dos: DataOutputStream = new DataOutputStream(byteOs)
dos.writeInt("ssh-rsa".getBytes.length)
dos.write("ssh-rsa".getBytes)
dos.writeInt(rsaPublicKey.getPublicExponent.toByteArray.length)
dos.write(rsaPublicKey.getPublicExponent.toByteArray)
dos.writeInt(rsaPublicKey.getModulus.toByteArray.length)
dos.write(rsaPublicKey.getModulus.toByteArray)
publicKeyEncoded = new String(Base64.encodeBase64(byteOs.toByteArray))
s"ssh-rsa $publicKeyEncoded user"
}
else if (publicKey.getAlgorithm == "DSA") {
val dsaPublicKey: DSAPublicKey = publicKey.asInstanceOf[DSAPublicKey]
val dsaParams: DSAParams = dsaPublicKey.getParams
val byteOs: ByteArrayOutputStream = new ByteArrayOutputStream
val dos: DataOutputStream = new DataOutputStream(byteOs)
dos.writeInt("ssh-dss".getBytes.length)
dos.write("ssh-dss".getBytes)
dos.writeInt(dsaParams.getP.toByteArray.length)
dos.write(dsaParams.getP.toByteArray)
dos.writeInt(dsaParams.getQ.toByteArray.length)
dos.write(dsaParams.getQ.toByteArray)
dos.writeInt(dsaParams.getG.toByteArray.length)
dos.write(dsaParams.getG.toByteArray)
dos.writeInt(dsaPublicKey.getY.toByteArray.length)
dos.write(dsaPublicKey.getY.toByteArray)
publicKeyEncoded = new String(Base64.encodeBase64(byteOs.toByteArray))
s"ssh-dss $publicKeyEncoded $user"
} else if (publicKey.getAlgorithm == "EC") {
val ecPublicKey: ECPublicKey = publicKey.asInstanceOf[ECPublicKey]
val byteOs: ByteArrayOutputStream = new ByteArrayOutputStream
val dos: DataOutputStream = new DataOutputStream(byteOs)
val curveName = ecPublicKey.getParams.getCurve.getField.getFieldSize match {
case 256 => "nistp256"
case 384 => "nistp384"
case 521 => "nistp521"
case _ => throw new Exception("Unknown curvesize")
}
val fullName = s"ecdsa-sha2-$curveName"
dos.writeInt(fullName.getBytes.length)
dos.write(fullName.getBytes)
dos.writeInt(curveName.getBytes.length)
dos.write(curveName.getBytes)
val group = ecPublicKey.getW
val elementSize: Int = (ecPublicKey.getParams.getCurve.getField.getFieldSize + 7) / 8
val M: Array[Byte] = new Array[Byte](2 * elementSize + 1)
M(0) = 0x04.toByte
val affineX: Array[Byte] = group.getAffineX.toByteArray.dropWhile(_ == 0x00)
Array.copy(affineX, 0, M, 1 + elementSize - affineX.length, affineX.length)
val affineY: Array[Byte] = group.getAffineY.toByteArray.dropWhile(_ == 0x00)
Array.copy(affineY, 0, M, 1 + elementSize + elementSize - affineY.length, affineY.length)
dos.writeInt(M.length)
dos.write(M)
publicKeyEncoded = new String(Base64.encodeBase64(byteOs.toByteArray))
s"$fullName $publicKeyEncoded $user"
}
else {
throw new IllegalArgumentException("Unknown public key encoding: " + publicKey.getAlgorithm)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment