Improve SSH client.
authorMathieu Baudier <mbaudier@argeo.org>
Sun, 8 Sep 2019 15:36:55 +0000 (17:36 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Sun, 8 Sep 2019 15:36:55 +0000 (17:36 +0200)
org.argeo.core/.gitignore
org.argeo.core/src/org/argeo/ssh/AbstractSsh.java
org.argeo.core/src/org/argeo/ssh/SimpleSshServer.java
org.argeo.core/src/org/argeo/ssh/Ssh.java
org.argeo.core/src/org/argeo/ssh/SshKeyPair.java
org.argeo.core/src/org/argeo/sync/fs/SshSync.java

index 09e3bc9b241c477ea341af9ee029becad0c2148c..c1639dd78c02f56dd2d8dc09969446b58f7aee6e 100644 (file)
@@ -1,2 +1,4 @@
 /bin/
 /target/
+id_rsa
+id_rsa.pub
index 88b28b525290a113f94250b90458df06ef94bade..588c16b01bd426a8a08e20aca5d051a8d9686cbb 100644 (file)
@@ -3,19 +3,15 @@ package org.argeo.ssh;
 import java.io.Console;
 import java.io.IOException;
 import java.net.URI;
-import java.security.GeneralSecurityException;
-import java.security.KeyPair;
 import java.util.Arrays;
 import java.util.Scanner;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.sshd.client.SshClient;
-import org.apache.sshd.client.config.keys.ClientIdentityLoader;
 import org.apache.sshd.client.future.ConnectFuture;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.client.subsystem.sftp.fs.SftpFileSystemProvider;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
 
 abstract class AbstractSsh {
        private final static Log log = LogFactory.getLog(AbstractSsh.class);
@@ -48,6 +44,7 @@ abstract class AbstractSsh {
                return sftpFileSystemProvider;
        }
 
+       @SuppressWarnings("restriction")
        void authenticate() {
                try {
                        if (sshKeyPair != null) {
@@ -59,8 +56,9 @@ abstract class AbstractSsh {
                                        Console console = System.console();
                                        if (console == null) {// IDE
                                                System.out.print("Password: ");
-                                               Scanner s = new Scanner(System.in);
-                                               password = s.next();
+                                               try (Scanner s = new Scanner(System.in)) {
+                                                       password = s.next();
+                                               }
                                        } else {
                                                console.printf("Password: ");
                                                char[] pwd = console.readPassword();
@@ -99,6 +97,7 @@ abstract class AbstractSsh {
                openSession(uri.getUserInfo(), uri.getHost(), uri.getPort() > 0 ? uri.getPort() : null);
        }
 
+       @SuppressWarnings("restriction")
        void openSession(String login, String host, Integer port) {
                if (session != null)
                        throw new IllegalStateException("Session is already open");
@@ -132,6 +131,7 @@ abstract class AbstractSsh {
                }
        }
 
+       @SuppressWarnings("restriction")
        void closeSession() {
                if (session == null)
                        throw new IllegalStateException("No session is open");
index c1f90efe99b0444b7be8cd75ba3267cfa4f61469..e63b989a43cf5a024f6c4b3e76abab6fa5f04c81 100644 (file)
@@ -11,6 +11,7 @@ import org.apache.sshd.server.shell.ProcessShellFactory;
 import org.argeo.util.os.OS;
 
 /** A simple SSH server with some defaults. Supports SCP. */
+@SuppressWarnings("restriction")
 public class SimpleSshServer {
        private Integer port;
        private Path hostKeyPath;
index 584294cc07228c91052597ecfec9c36ca7e334b5..0443098770ab8f44155fb7dec1d7c1157092d3af 100644 (file)
@@ -19,6 +19,7 @@ import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.common.util.io.NoCloseInputStream;
 import org.apache.sshd.common.util.io.NoCloseOutputStream;
 
+@SuppressWarnings("restriction")
 public class Ssh extends AbstractSsh {
        private final URI uri;
 
@@ -83,6 +84,10 @@ public class Ssh extends AbstractSsh {
                }
        }
 
+       public URI getUri() {
+               return uri;
+       }
+
        public static Options getOptions() {
                Options options = new Options();
 //             options.addOption("p", true, "port");
index 8feca58f95048693c01647819cb7589cce446b54..f9b34859885b1e077d0b91a1846a2426e8ab802b 100644 (file)
@@ -1,8 +1,14 @@
 package org.argeo.ssh;
 
+import java.io.IOException;
+import java.io.InputStreamReader;
 import java.io.Reader;
 import java.io.StringReader;
 import java.io.StringWriter;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.security.GeneralSecurityException;
 import java.security.KeyFactory;
 import java.security.KeyPair;
@@ -28,6 +34,8 @@ import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
 
 @SuppressWarnings("restriction")
 public class SshKeyPair {
+       public final static String RSA_KEY_TYPE = "ssh-rsa";
+
        private PublicKey publicKey;
        private PrivateKey privateKey;
        private KeyPair keyPair;
@@ -54,7 +62,7 @@ public class SshKeyPair {
                return PublicKeyEntry.toString(publicKey);
        }
 
-       public String getPrivateKeyAsString(char[] password) {
+       public String getPrivateKeyAsPemString(char[] password) {
                try {
                        Object obj;
 
@@ -79,9 +87,36 @@ public class SshKeyPair {
                }
        }
 
+       public static SshKeyPair loadOrGenerate(Path privateKeyPath, int size, char[] password) {
+               try {
+                       SshKeyPair sshKeyPair;
+                       if (Files.exists(privateKeyPath)) {
+//                             String privateKeyStr = new String(Files.readAllBytes(privateKeyPath), StandardCharsets.US_ASCII);
+                               sshKeyPair = load(
+                                               new InputStreamReader(Files.newInputStream(privateKeyPath), StandardCharsets.US_ASCII),
+                                               password);
+                               // TOD make sure public key is consistemt
+                       } else {
+                               sshKeyPair = generate(size);
+                               Files.write(privateKeyPath,
+                                               sshKeyPair.getPrivateKeyAsPemString(password).getBytes(StandardCharsets.US_ASCII));
+                               Path publicKeyPath = privateKeyPath.resolveSibling(privateKeyPath.getFileName() + ".pub");
+                               Files.write(publicKeyPath,
+                                               sshKeyPair.getPublicKeyAsOpenSshString().getBytes(StandardCharsets.US_ASCII));
+                       }
+                       return sshKeyPair;
+               } catch (IOException e) {
+                       throw new RuntimeException("Cannot read or write private key " + privateKeyPath, e);
+               }
+       }
+
        public static SshKeyPair generate(int size) {
+               return generate(RSA_KEY_TYPE, size);
+       }
+
+       public static SshKeyPair generate(String keyType, int size) {
                try {
-                       KeyPair keyPair = KeyUtils.generateKeyPair("ssh-rsa", size);
+                       KeyPair keyPair = KeyUtils.generateKeyPair(keyType, size);
                        PublicKey publicKey = keyPair.getPublic();
                        PrivateKey privateKey = keyPair.getPrivate();
                        return new SshKeyPair(publicKey, privateKey);
@@ -93,7 +128,7 @@ public class SshKeyPair {
        public static SshKeyPair load(Reader reader, char[] password) {
                try (PEMParser pemParser = new PEMParser(reader)) {
                        Object object = pemParser.readObject();
-                       JcaPEMKeyConverter converter = new JcaPEMKeyConverter().setProvider("BC");
+                       JcaPEMKeyConverter converter = new JcaPEMKeyConverter();// .setProvider("BC");
                        KeyPair kp;
                        if (object instanceof PKCS8EncryptedPrivateKeyInfo) {
                                // Encrypted key - we will use provided password
@@ -125,22 +160,23 @@ public class SshKeyPair {
        }
 
        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()));
+               Path privateKeyPath = Paths.get(System.getProperty("user.dir") + "/id_rsa");
+               SshKeyPair skp = SshKeyPair.loadOrGenerate(privateKeyPath, 1024, null);
+               System.out.println("Public:\n" + skp.getPublicKeyAsOpenSshString());
+               System.out.println("Private (plain):\n" + skp.getPrivateKeyAsPemString(null));
+               System.out.println("Private (encrypted):\n" + skp.getPrivateKeyAsPemString("demo".toCharArray()));
+
+               StringReader reader = new StringReader(skp.getPrivateKeyAsPemString(null));
+               skp = SshKeyPair.load(reader, null);
+               System.out.println("Public:\n" + skp.getPublicKeyAsOpenSshString());
+               System.out.println("Private (plain):\n" + skp.getPrivateKeyAsPemString(null));
+               System.out.println("Private (encrypted):\n" + skp.getPrivateKeyAsPemString("demo".toCharArray()));
+
+               reader = new StringReader(skp.getPrivateKeyAsPemString("demo".toCharArray()));
+               skp = SshKeyPair.load(reader, "demo".toCharArray());
+               System.out.println("Public:\n" + skp.getPublicKeyAsOpenSshString());
+               System.out.println("Private (plain):\n" + skp.getPrivateKeyAsPemString(null));
+               System.out.println("Private (encrypted):\n" + skp.getPrivateKeyAsPemString("demo".toCharArray()));
        }
 
 }
index 7fe6670c9ef7627ad949b65ae0fdbbdb6cbca23e..9b15a32ea5ac3a365ec5ff31f4d2f5c424e5b9a6 100644 (file)
@@ -6,13 +6,8 @@ import java.io.OutputStream;
 import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.security.KeyPair;
-import java.security.PublicKey;
-import java.util.HashSet;
-import java.util.List;
 import java.util.Map;
 import java.util.Scanner;
-import java.util.Set;
 
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.logging.Log;
@@ -22,16 +17,10 @@ import org.apache.sshd.agent.SshAgentFactory;
 import org.apache.sshd.agent.local.LocalAgentFactory;
 import org.apache.sshd.agent.unix.UnixAgentFactory;
 import org.apache.sshd.client.SshClient;
-import org.apache.sshd.client.channel.ClientChannel;
-import org.apache.sshd.client.channel.ClientChannelEvent;
-import org.apache.sshd.client.config.keys.ClientIdentityLoader;
 import org.apache.sshd.client.future.ConnectFuture;
 import org.apache.sshd.client.session.ClientSession;
 import org.apache.sshd.client.subsystem.sftp.fs.SftpFileSystem;
 import org.apache.sshd.client.subsystem.sftp.fs.SftpFileSystemProvider;
-import org.apache.sshd.common.config.keys.FilePasswordProvider;
-import org.apache.sshd.common.util.io.NoCloseInputStream;
-import org.apache.sshd.common.util.io.NoCloseOutputStream;
 
 public class SshSync {
        private final static Log log = LogFactory.getLog(SshSync.class);