-
Notifications
You must be signed in to change notification settings - Fork 1.3k
utils: use CertUtils.generateRandomKeyPair to create SSH keypair #12708
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 4.22
Are you sure you want to change the base?
Changes from all commits
a9627b4
aa95bc0
7be5a86
738ec86
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -20,14 +20,20 @@ | |||||||||||||||
| package com.cloud.utils.ssh; | ||||||||||||||||
|
|
||||||||||||||||
| import java.io.ByteArrayOutputStream; | ||||||||||||||||
| import java.io.StringWriter; | ||||||||||||||||
| import java.math.BigInteger; | ||||||||||||||||
| import java.nio.ByteBuffer; | ||||||||||||||||
| import java.nio.charset.StandardCharsets; | ||||||||||||||||
| import java.security.KeyPair; | ||||||||||||||||
| import java.security.MessageDigest; | ||||||||||||||||
| import java.security.NoSuchAlgorithmException; | ||||||||||||||||
| import java.security.NoSuchProviderException; | ||||||||||||||||
| import java.security.interfaces.RSAPublicKey; | ||||||||||||||||
|
|
||||||||||||||||
| import org.apache.cloudstack.utils.security.CertUtils; | ||||||||||||||||
| import org.apache.commons.codec.binary.Base64; | ||||||||||||||||
|
|
||||||||||||||||
| import com.jcraft.jsch.JSch; | ||||||||||||||||
| import com.jcraft.jsch.JSchException; | ||||||||||||||||
| import com.jcraft.jsch.KeyPair; | ||||||||||||||||
| import org.bouncycastle.util.io.pem.PemObject; | ||||||||||||||||
| import org.bouncycastle.util.io.pem.PemWriter; | ||||||||||||||||
|
|
||||||||||||||||
| public class SSHKeysHelper { | ||||||||||||||||
|
|
||||||||||||||||
|
|
@@ -45,8 +51,8 @@ private static String toHexString(byte[] b) { | |||||||||||||||
|
|
||||||||||||||||
| public SSHKeysHelper(Integer keyLength) { | ||||||||||||||||
| try { | ||||||||||||||||
| keyPair = KeyPair.genKeyPair(new JSch(), KeyPair.RSA, keyLength); | ||||||||||||||||
| } catch (JSchException e) { | ||||||||||||||||
| keyPair = CertUtils.generateRandomKeyPair(keyLength); | ||||||||||||||||
| } catch (NoSuchAlgorithmException | NoSuchProviderException e) { | ||||||||||||||||
| e.printStackTrace(); | ||||||||||||||||
| } | ||||||||||||||||
| } | ||||||||||||||||
|
|
@@ -105,17 +111,54 @@ public static String getPublicKeyFromKeyMaterial(String keyMaterial) { | |||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| public String getPublicKey() { | ||||||||||||||||
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||||||||||||||||
| keyPair.writePublicKey(baos, ""); | ||||||||||||||||
| if (keyPair == null || keyPair.getPublic() == null) { | ||||||||||||||||
| return null; | ||||||||||||||||
| } | ||||||||||||||||
| try { | ||||||||||||||||
| RSAPublicKey rsaPublicKey = (RSAPublicKey) keyPair.getPublic(); | ||||||||||||||||
|
|
||||||||||||||||
| ByteArrayOutputStream buffer = new ByteArrayOutputStream(); | ||||||||||||||||
|
|
||||||||||||||||
| return baos.toString(); | ||||||||||||||||
| writeString(buffer,"ssh-rsa"); | ||||||||||||||||
| writeBigInt(buffer, rsaPublicKey.getPublicExponent()); | ||||||||||||||||
| writeBigInt(buffer, rsaPublicKey.getModulus()); | ||||||||||||||||
|
|
||||||||||||||||
| String base64 = Base64.encodeBase64String(buffer.toByteArray()); | ||||||||||||||||
|
|
||||||||||||||||
| return "ssh-rsa " + base64; | ||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||
| e.printStackTrace(); | ||||||||||||||||
| } | ||||||||||||||||
| return null; | ||||||||||||||||
| } | ||||||||||||||||
weizhouapache marked this conversation as resolved.
Show resolved
Hide resolved
weizhouapache marked this conversation as resolved.
Show resolved
Hide resolved
Comment on lines
113
to
133
|
||||||||||||||||
|
|
||||||||||||||||
| public String getPrivateKey() { | ||||||||||||||||
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||||||||||||||||
| keyPair.writePrivateKey(baos); | ||||||||||||||||
| private static void writeString(ByteArrayOutputStream out, String str) throws Exception { | ||||||||||||||||
| byte[] data = str.getBytes(StandardCharsets.UTF_8); | ||||||||||||||||
| out.write(ByteBuffer.allocate(4).putInt(data.length).array()); | ||||||||||||||||
| out.write(data); | ||||||||||||||||
| } | ||||||||||||||||
|
|
||||||||||||||||
| private static void writeBigInt(ByteArrayOutputStream out, BigInteger value) throws Exception { | ||||||||||||||||
| byte[] data = value.toByteArray(); | ||||||||||||||||
| out.write(ByteBuffer.allocate(4).putInt(data.length).array()); | ||||||||||||||||
| out.write(data); | ||||||||||||||||
| } | ||||||||||||||||
weizhouapache marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||
|
|
||||||||||||||||
| return baos.toString(); | ||||||||||||||||
| public String getPrivateKey() { | ||||||||||||||||
| if (keyPair == null || keyPair.getPrivate() == null) { | ||||||||||||||||
| return null; | ||||||||||||||||
| } | ||||||||||||||||
| try { | ||||||||||||||||
| final PemObject pemObject = new PemObject("RSA PRIVATE KEY", keyPair.getPrivate().getEncoded()); | ||||||||||||||||
| final StringWriter sw = new StringWriter(); | ||||||||||||||||
| try (final PemWriter pw = new PemWriter(sw)) { | ||||||||||||||||
| pw.writeObject(pemObject); | ||||||||||||||||
| } | ||||||||||||||||
| return sw.toString(); | ||||||||||||||||
|
Comment on lines
+152
to
+157
|
||||||||||||||||
| final PemObject pemObject = new PemObject("RSA PRIVATE KEY", keyPair.getPrivate().getEncoded()); | |
| final StringWriter sw = new StringWriter(); | |
| try (final PemWriter pw = new PemWriter(sw)) { | |
| pw.writeObject(pemObject); | |
| } | |
| return sw.toString(); | |
| return CertUtils.privateKeyToPem(keyPair.getPrivate()); |
weizhouapache marked this conversation as resolved.
Show resolved
Hide resolved
weizhouapache marked this conversation as resolved.
Show resolved
Hide resolved
weizhouapache marked this conversation as resolved.
Show resolved
Hide resolved
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -19,8 +19,14 @@ | |||||||||
|
|
||||||||||
| package com.cloud.utils.ssh; | ||||||||||
|
|
||||||||||
| import static org.junit.Assert.assertEquals; | ||||||||||
| import static org.junit.Assert.assertNotNull; | ||||||||||
| import static org.junit.Assert.assertNull; | ||||||||||
| import static org.junit.Assert.assertTrue; | ||||||||||
|
|
||||||||||
| import java.nio.charset.StandardCharsets; | ||||||||||
| import java.util.Base64; | ||||||||||
|
|
||||||||||
| import org.junit.Test; | ||||||||||
|
|
||||||||||
| public class SSHKeysHelperTest { | ||||||||||
|
|
@@ -70,4 +76,56 @@ public void dsaKeyTest() { | |||||||||
| assertTrue("fc:6e:ef:31:93:f8:92:2b:a9:03:c7:06:90:f5:ec:bb".equals(fingerprint)); | ||||||||||
|
|
||||||||||
| } | ||||||||||
|
|
||||||||||
| @Test | ||||||||||
| public void getPublicKeyFromKeyMaterialShouldHandleSupportedPrefixes() { | ||||||||||
| assertEquals("ecdsa-sha2-nistp256 AAAA", SSHKeysHelper.getPublicKeyFromKeyMaterial("ecdsa-sha2-nistp256 AAAA comment")); | ||||||||||
| assertEquals("ecdsa-sha2-nistp384 AAAA", SSHKeysHelper.getPublicKeyFromKeyMaterial("ecdsa-sha2-nistp384 AAAA comment")); | ||||||||||
| assertEquals("ecdsa-sha2-nistp521 AAAA", SSHKeysHelper.getPublicKeyFromKeyMaterial("ecdsa-sha2-nistp521 AAAA comment")); | ||||||||||
| assertEquals("ssh-ed25519 AAAA", SSHKeysHelper.getPublicKeyFromKeyMaterial("ssh-ed25519 AAAA comment")); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| @Test | ||||||||||
| public void getPublicKeyFromKeyMaterialShouldParseBase64EncodedMaterial() { | ||||||||||
| String keyMaterial = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAITestKeyData comment"; | ||||||||||
| String encoded = Base64.getEncoder().encodeToString(keyMaterial.getBytes(StandardCharsets.UTF_8)); | ||||||||||
|
|
||||||||||
| assertEquals("ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAITestKeyData", SSHKeysHelper.getPublicKeyFromKeyMaterial(encoded)); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| @Test | ||||||||||
| public void getPublicKeyFromKeyMaterialShouldReturnNullForInvalidFormats() { | ||||||||||
| assertNull(SSHKeysHelper.getPublicKeyFromKeyMaterial("not-a-valid-key")); | ||||||||||
| assertNull(SSHKeysHelper.getPublicKeyFromKeyMaterial("ssh-unknown AAAA")); | ||||||||||
| assertNull(SSHKeysHelper.getPublicKeyFromKeyMaterial("ssh-rsa")); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| @Test(expected = RuntimeException.class) | ||||||||||
| public void getPublicKeyFingerprintShouldThrowForInvalidPublicKey() { | ||||||||||
| SSHKeysHelper.getPublicKeyFingerprint("invalid-key-format"); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| @Test | ||||||||||
| public void generatedKeysShouldBeWellFormedAndFingerprintConsistent() { | ||||||||||
| SSHKeysHelper helper = new SSHKeysHelper(2048); | ||||||||||
|
|
||||||||||
| String publicKey = helper.getPublicKey(); | ||||||||||
| String privateKey = helper.getPrivateKey(); | ||||||||||
| String fingerprint = helper.getPublicKeyFingerPrint(); | ||||||||||
|
|
||||||||||
| assertNotNull(publicKey); | ||||||||||
| assertTrue(publicKey.startsWith("ssh-rsa ")); | ||||||||||
|
|
||||||||||
| String[] keyParts = publicKey.split(" "); | ||||||||||
| assertEquals(2, keyParts.length); | ||||||||||
|
|
||||||||||
| assertNotNull(privateKey); | ||||||||||
| assertTrue(privateKey.contains("BEGIN RSA PRIVATE KEY")); | ||||||||||
| assertTrue(privateKey.contains("END RSA PRIVATE KEY")); | ||||||||||
|
Comment on lines
+123
to
+124
|
||||||||||
| assertTrue(privateKey.contains("BEGIN RSA PRIVATE KEY")); | |
| assertTrue(privateKey.contains("END RSA PRIVATE KEY")); | |
| assertTrue(privateKey.contains("BEGIN PRIVATE KEY")); | |
| assertTrue(privateKey.contains("END PRIVATE KEY")); |
Uh oh!
There was an error while loading. Please reload this page.