Created
July 15, 2014 15:46
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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