SSH key pair management.
authorMathieu Baudier <mbaudier@argeo.org>
Sun, 8 Sep 2019 13:24:33 +0000 (15:24 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Sun, 8 Sep 2019 13:24:33 +0000 (15:24 +0200)
org.argeo.core/src/org/argeo/ssh/AbstractSsh.java
org.argeo.core/src/org/argeo/ssh/SshKeyPair.java [new file with mode: 0644]

index ae1d6a0d4acabf446948ccbfce60026140c1ef8b..88b28b525290a113f94250b90458df06ef94bade 100644 (file)
@@ -26,6 +26,8 @@ abstract class AbstractSsh {
        private boolean passwordSet = false;
        private ClientSession session;
 
+       private SshKeyPair sshKeyPair;
+
        synchronized SshClient getSshClient() {
                if (sshClient == null) {
                        long begin = System.currentTimeMillis();
@@ -48,21 +50,26 @@ abstract class AbstractSsh {
 
        void authenticate() {
                try {
-                       if (!passwordSet) {
-                               String password;
-                               Console console = System.console();
-                               if (console == null) {// IDE
-                                       System.out.print("Password: ");
-                                       Scanner s = new Scanner(System.in);
-                                       password = s.next();
-                               } else {
-                                       console.printf("Password: ");
-                                       char[] pwd = console.readPassword();
-                                       password = new String(pwd);
-                                       Arrays.fill(pwd, ' ');
+                       if (sshKeyPair != null) {
+                               session.addPublicKeyIdentity(sshKeyPair.asKeyPair());
+                       } else {
+
+                               if (!passwordSet) {
+                                       String password;
+                                       Console console = System.console();
+                                       if (console == null) {// IDE
+                                               System.out.print("Password: ");
+                                               Scanner s = new Scanner(System.in);
+                                               password = s.next();
+                                       } else {
+                                               console.printf("Password: ");
+                                               char[] pwd = console.readPassword();
+                                               password = new String(pwd);
+                                               Arrays.fill(pwd, ' ');
+                                       }
+                                       session.addPasswordIdentity(password);
+                                       passwordSet = true;
                                }
-                               session.addPasswordIdentity(password);
-                               passwordSet = true;
                        }
                        session.auth().verify(1000l);
                } catch (IOException e) {
diff --git a/org.argeo.core/src/org/argeo/ssh/SshKeyPair.java b/org.argeo.core/src/org/argeo/ssh/SshKeyPair.java
new file mode 100644 (file)
index 0000000..8feca58
--- /dev/null
@@ -0,0 +1,146 @@
+package org.argeo.ssh;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.interfaces.RSAPrivateCrtKey;
+import java.security.spec.RSAPublicKeySpec;
+
+import org.apache.sshd.common.config.keys.KeyUtils;
+import org.apache.sshd.common.config.keys.PublicKeyEntry;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.PKCS8Generator;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
+import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
+import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
+import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8EncryptorBuilder;
+import org.bouncycastle.operator.InputDecryptorProvider;
+import org.bouncycastle.operator.OutputEncryptor;
+import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
+
+@SuppressWarnings("restriction")
+public class SshKeyPair {
+       private PublicKey publicKey;
+       private PrivateKey privateKey;
+       private KeyPair keyPair;
+
+       public SshKeyPair(KeyPair keyPair) {
+               super();
+               this.publicKey = keyPair.getPublic();
+               this.privateKey = keyPair.getPrivate();
+               this.keyPair = keyPair;
+       }
+
+       public SshKeyPair(PublicKey publicKey, PrivateKey privateKey) {
+               super();
+               this.publicKey = publicKey;
+               this.privateKey = privateKey;
+               this.keyPair = new KeyPair(publicKey, privateKey);
+       }
+
+       public KeyPair asKeyPair() {
+               return keyPair;
+       }
+
+       public String getPublicKeyAsOpenSshString() {
+               return PublicKeyEntry.toString(publicKey);
+       }
+
+       public String getPrivateKeyAsString(char[] password) {
+               try {
+                       Object obj;
+
+                       if (password != null) {
+                               JceOpenSSLPKCS8EncryptorBuilder encryptorBuilder = new JceOpenSSLPKCS8EncryptorBuilder(
+                                               PKCS8Generator.PBE_SHA1_3DES);
+                               encryptorBuilder.setPasssword(password);
+                               OutputEncryptor oe = encryptorBuilder.build();
+                               JcaPKCS8Generator gen = new JcaPKCS8Generator(privateKey, oe);
+                               obj = gen.generate();
+                       } else {
+                               obj = privateKey;
+                       }
+
+                       StringWriter sw = new StringWriter();
+                       JcaPEMWriter pemWrt = new JcaPEMWriter(sw);
+                       pemWrt.writeObject(obj);
+                       pemWrt.close();
+                       return sw.toString();
+               } catch (Exception e) {
+                       throw new RuntimeException("Cannot convert private key", e);
+               }
+       }
+
+       public static SshKeyPair generate(int size) {
+               try {
+                       KeyPair keyPair = KeyUtils.generateKeyPair("ssh-rsa", size);
+                       PublicKey publicKey = keyPair.getPublic();
+                       PrivateKey privateKey = keyPair.getPrivate();
+                       return new SshKeyPair(publicKey, privateKey);
+               } catch (GeneralSecurityException e) {
+                       throw new RuntimeException("Cannot generate SSH key", e);
+               }
+       }
+
+       public static SshKeyPair load(Reader reader, char[] password) {
+               try (PEMParser pemParser = new PEMParser(reader)) {
+                       Object object = pemParser.readObject();
+                       JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
+                       KeyPair kp;
+                       if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
+                               // Encrypted key - we will use provided password
+                               PKCS8EncryptedPrivateKeyInfo ckp = (PKCS8EncryptedPrivateKeyInfo) object;
+//                             PEMDecryptorProvider decProv = new JcePEMDecryptorProviderBuilder().build(password);
+                               InputDecryptorProvider inputDecryptorProvider = new JceOpenSSLPKCS8DecryptorProviderBuilder()
+                                               .build(password);
+                               PrivateKeyInfo pkInfo = ckp.decryptPrivateKeyInfo(inputDecryptorProvider);
+                               PrivateKey privateKey = converter.getPrivateKey(pkInfo);
+
+                               // generate public key
+                               RSAPrivateCrtKey privk = (RSAPrivateCrtKey) privateKey;
+                               RSAPublicKeySpec publicKeySpec = new java.security.spec.RSAPublicKeySpec(privk.getModulus(),
+                                               privk.getPublicExponent());
+                               KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+                               PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);
+
+                               kp = new KeyPair(publicKey, privateKey);
+                       } else {
+                               // Unencrypted key - no password needed
+//                             PKCS8EncryptedPrivateKeyInfo ukp = (PKCS8EncryptedPrivateKeyInfo) object;
+                               PEMKeyPair pemKp = (PEMKeyPair) object;
+                               kp = converter.getKeyPair(pemKp);
+                       }
+                       return new SshKeyPair(kp);
+               } catch (Exception e) {
+                       throw new RuntimeException("Cannot load private key", e);
+               }
+       }
+
+       public static void main(String args[]) {
+               SshKeyPair okp = SshKeyPair.generate(1024);
+               System.out.println("Public:\n" + okp.getPublicKeyAsOpenSshString());
+               System.out.println("Private (plain):\n" + okp.getPrivateKeyAsString(null));
+               System.out.println("Private (encrypted):\n" + okp.getPrivateKeyAsString("demo".toCharArray()));
+
+               StringReader reader = new StringReader(okp.getPrivateKeyAsString(null));
+               okp = SshKeyPair.load(reader, null);
+               System.out.println("Public:\n" + okp.getPublicKeyAsOpenSshString());
+               System.out.println("Private (plain):\n" + okp.getPrivateKeyAsString(null));
+               System.out.println("Private (encrypted):\n" + okp.getPrivateKeyAsString("demo".toCharArray()));
+
+               reader = new StringReader(okp.getPrivateKeyAsString("demo".toCharArray()));
+               okp = SshKeyPair.load(reader, "demo".toCharArray());
+               System.out.println("Public:\n" + okp.getPublicKeyAsOpenSshString());
+               System.out.println("Private (plain):\n" + okp.getPrivateKeyAsString(null));
+               System.out.println("Private (encrypted):\n" + okp.getPrivateKeyAsString("demo".toCharArray()));
+       }
+
+}