You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by gn...@apache.org on 2009/12/04 18:40:20 UTC

svn commit: r887288 - in /mina/sshd/trunk/sshd-core/src: main/java/org/apache/sshd/ main/java/org/apache/sshd/client/auth/ main/java/org/apache/sshd/client/session/ main/java/org/apache/sshd/common/keyprovider/ test/java/org/apache/sshd/

Author: gnodet
Date: Fri Dec  4 17:40:19 2009
New Revision: 887288

URL: http://svn.apache.org/viewvc?rev=887288&view=rev
Log:
SSHD-25: Support key based authentication on the client side

Added:
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
Modified:
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/ClientSession.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshClient.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
    mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
    mina/sshd/trunk/sshd-core/src/test/java/org/apache/sshd/ClientTest.java

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/ClientSession.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/ClientSession.java?rev=887288&r1=887287&r2=887288&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/ClientSession.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/ClientSession.java Fri Dec  4 17:40:19 2009
@@ -19,6 +19,7 @@
 package org.apache.sshd;
 
 import java.io.IOException;
+import java.security.KeyPair;
 import java.security.PublicKey;
 
 import org.apache.sshd.client.future.AuthFuture;
@@ -30,7 +31,7 @@
  * A client session is established using the {@link SshClient}.
  * Once the session has been created, the user has to authenticate
  * using either {@link #authPassword(String, String)} or
- * {@link #authPublicKey(String, java.security.PublicKey)}.
+ * {@link #authPublicKey(String, java.security.KeyPair)}.
  *
  * From this session, channels can be created using the
  * {@link #createChannel(String)} method.  Multiple channels can
@@ -54,7 +55,7 @@
 
     AuthFuture authPassword(String username, String password) throws IOException;
 
-    AuthFuture authPublicKey(String username, PublicKey key) throws IOException;
+    AuthFuture authPublicKey(String username, KeyPair key) throws IOException;
 
     ClientChannel createChannel(String type) throws Exception;
 

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshClient.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshClient.java?rev=887288&r1=887287&r2=887288&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshClient.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshClient.java Fri Dec  4 17:40:19 2009
@@ -18,14 +18,10 @@
  */
 package org.apache.sshd;
 
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Writer;
+import java.io.*;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
+import java.security.KeyPair;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -55,6 +51,7 @@
 import org.apache.sshd.common.cipher.BlowfishCBC;
 import org.apache.sshd.common.cipher.TripleDESCBC;
 import org.apache.sshd.common.compression.CompressionNone;
+import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
 import org.apache.sshd.common.mac.HMACMD5;
 import org.apache.sshd.common.mac.HMACMD596;
 import org.apache.sshd.common.mac.HMACSHA1;
@@ -68,6 +65,8 @@
 import org.apache.sshd.common.util.NoCloseInputStream;
 import org.apache.sshd.common.util.NoCloseOutputStream;
 import org.apache.sshd.common.util.SecurityUtils;
+import org.bouncycastle.openssl.PEMReader;
+import org.bouncycastle.openssl.PasswordFinder;
 
 /**
  * Entry point for the client side of the SSH protocol.
@@ -304,12 +303,46 @@
             ClientSession session = client.connect(host, port).await().getSession();
 
             int ret = ClientSession.WAIT_AUTH;
+
+            List<String> files = new ArrayList<String>();
+            File f = new File(System.getProperty("user.home"), ".ssh/id_dsa");
+            if (f.exists() && f.isFile() && f.canRead()) {
+                files.add(f.getAbsolutePath());
+            }
+            f = new File(System.getProperty("user.home"), ".ssh/id_rsa");
+            if (f.exists() && f.isFile() && f.canRead()) {
+                files.add(f.getAbsolutePath());
+            }
+            KeyPair[] keys = null;
+            try {
+                if (files.size() > 0) {
+                    keys = new FileKeyPairProvider(files.toArray(new String[0]), new PasswordFinder() {
+                        public char[] getPassword() {
+                            try {
+                                System.out.println("Enter password for private key: ");
+                                BufferedReader r = new BufferedReader(new InputStreamReader(System.in));
+                                String password = r.readLine();
+                                return password.toCharArray();
+                            } catch (IOException e) {
+                                return null;
+                            }
+                        }
+                    }).loadKeys();
+                }
+            } catch (Exception e) {
+            }
+            int nbKey = 0;
             while ((ret & ClientSession.WAIT_AUTH) != 0) {
-                System.out.print("Password:");
-                BufferedReader r = new BufferedReader(new InputStreamReader(System.in));
-                String password = r.readLine();
-                session.authPassword(login, password);
-                ret = session.waitFor(ClientSession.WAIT_AUTH | ClientSession.CLOSED | ClientSession.AUTHED, 0);
+                if (keys != null && nbKey < keys.length) {
+                    session.authPublicKey(login, keys[nbKey++]);
+                    ret = session.waitFor(ClientSession.WAIT_AUTH | ClientSession.CLOSED | ClientSession.AUTHED, 0);
+                } else {
+                    System.out.print("Password:");
+                    BufferedReader r = new BufferedReader(new InputStreamReader(System.in));
+                    String password = r.readLine();
+                    session.authPassword(login, password);
+                    ret = session.waitFor(ClientSession.WAIT_AUTH | ClientSession.CLOSED | ClientSession.AUTHED, 0);
+                }
             }
             if ((ret & ClientSession.CLOSED) != 0) {
                 System.err.println("error");

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java?rev=887288&r1=887287&r2=887288&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/SshServer.java Fri Dec  4 17:40:19 2009
@@ -18,9 +18,11 @@
  */
 package org.apache.sshd;
 
+import java.io.File;
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.security.InvalidKeyException;
+import java.security.PublicKey;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.EnumSet;
@@ -420,7 +422,7 @@
         }
 
         System.err.println("Starting SSHD on port " + port);
-
+                                                    
         SshServer sshd = SshServer.setUpDefaultServer();
         sshd.setPort(port);
         if (SecurityUtils.isBouncyCastleRegistered()) {
@@ -440,6 +442,12 @@
                 return username != null && username.equals(password);
             }
         });
+        sshd.setPublickeyAuthenticator(new PublickeyAuthenticator() {
+            public boolean authenticate(String username, PublicKey key, ServerSession session) {
+                //File f = new File("/Users/" + username + "/.ssh/authorized_keys");
+                return true;
+            }
+        });
         sshd.setTcpIpForwardFilter(new TcpIpForwardFilter() {
             public boolean canListen(InetSocketAddress address, ServerSession session) {
                 return true;

Added: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java?rev=887288&view=auto
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java (added)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/auth/UserAuthPublicKey.java Fri Dec  4 17:40:19 2009
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sshd.client.auth;
+
+import org.apache.sshd.client.UserAuth;
+import org.apache.sshd.client.session.ClientSessionImpl;
+import org.apache.sshd.common.KeyPairProvider;
+import org.apache.sshd.common.NamedFactory;
+import org.apache.sshd.common.Signature;
+import org.apache.sshd.common.SshConstants;
+import org.apache.sshd.common.util.Buffer;
+import org.apache.sshd.common.util.BufferUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.security.KeyPair;
+import java.security.PublicKey;
+import java.security.interfaces.DSAPublicKey;
+import java.security.interfaces.RSAPublicKey;
+
+/**
+ * TODO Add javadoc
+ *
+ * @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
+ */
+public class UserAuthPublicKey implements UserAuth {
+
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+
+    public UserAuthPublicKey(ClientSessionImpl session, String username, KeyPair key) throws IOException {
+        try {
+            log.info("Send SSH_MSG_USERAUTH_REQUEST for password");
+            Buffer buffer = session.createBuffer(SshConstants.Message.SSH_MSG_USERAUTH_REQUEST);
+            int pos1 = buffer.wpos() - 1;
+            buffer.putString(username);
+            buffer.putString("ssh-connection");
+            buffer.putString("publickey");
+            buffer.putByte((byte) 1);
+            buffer.putString((key.getPublic() instanceof RSAPublicKey) ? KeyPairProvider.SSH_RSA : KeyPairProvider.SSH_DSS);
+            int pos2 = buffer.wpos();
+            Buffer b = new Buffer();
+            b.putPublicKey(key.getPublic());
+            buffer.putInt(b.available());
+            buffer.putBuffer(b);
+
+            Signature verif = NamedFactory.Utils.create(session.getFactoryManager().getSignatureFactories(), (key.getPublic() instanceof RSAPublicKey) ? KeyPairProvider.SSH_RSA : KeyPairProvider.SSH_DSS);
+            verif.init(key.getPublic(), key.getPrivate());
+
+            Buffer bs = new Buffer();
+            bs.putString(session.getKex().getH());
+            bs.putCommand(SshConstants.Message.SSH_MSG_USERAUTH_REQUEST);
+            bs.putString(username);
+            bs.putString("ssh-connection");
+            bs.putString("publickey");
+            bs.putByte((byte) 1);
+            bs.putString((key.getPublic() instanceof RSAPublicKey) ? KeyPairProvider.SSH_RSA : KeyPairProvider.SSH_DSS);
+            bs.putInt(b.available());
+            bs.putBuffer(b);
+            verif.update(bs.array(), bs.rpos(), bs.available());
+
+            buffer.putBytes(verif.sign());
+
+            session.writePacket(buffer);
+        } catch (IOException e) {
+            throw e;
+        } catch (Exception e) {
+            throw (IOException) new IOException("Error performing public key authentication").initCause(e);
+        }
+    }
+
+    public Result next(Buffer buffer) throws IOException {
+        SshConstants.Message cmd = buffer.getCommand();
+        log.info("Received {}", cmd);
+        if (cmd == SshConstants.Message.SSH_MSG_USERAUTH_SUCCESS) {
+            return Result.Success;
+        } if (cmd == SshConstants.Message.SSH_MSG_USERAUTH_FAILURE) {
+            return Result.Failure;
+        } else {
+            // TODO: check packets
+            return Result.Continued;
+        }
+    }
+
+}
\ No newline at end of file

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java?rev=887288&r1=887287&r2=887288&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/client/session/ClientSessionImpl.java Fri Dec  4 17:40:19 2009
@@ -19,6 +19,7 @@
 package org.apache.sshd.client.session;
 
 import java.io.IOException;
+import java.security.KeyPair;
 import java.security.PublicKey;
 
 import org.apache.mina.core.session.IoSession;
@@ -26,19 +27,17 @@
 import org.apache.sshd.ClientSession;
 import org.apache.sshd.client.UserAuth;
 import org.apache.sshd.client.auth.UserAuthPassword;
+import org.apache.sshd.client.auth.UserAuthPublicKey;
 import org.apache.sshd.client.channel.AbstractClientChannel;
 import org.apache.sshd.client.channel.ChannelShell;
 import org.apache.sshd.client.channel.ChannelExec;
 import org.apache.sshd.client.future.AuthFuture;
 import org.apache.sshd.client.future.DefaultAuthFuture;
-import org.apache.sshd.common.FactoryManager;
-import org.apache.sshd.common.KeyPairProvider;
-import org.apache.sshd.common.NamedFactory;
-import org.apache.sshd.common.SshConstants;
-import org.apache.sshd.common.SshException;
+import org.apache.sshd.common.*;
 import org.apache.sshd.common.future.CloseFuture;
 import org.apache.sshd.common.session.AbstractSession;
 import org.apache.sshd.common.util.Buffer;
+import org.bouncycastle.asn1.x9.X9ECParameters;
 
 /**
  * TODO Add javadoc
@@ -62,6 +61,10 @@
         sendKexInit();
     }
 
+    public KeyExchange getKex() {
+        return kex;
+    }
+
     public AuthFuture authPassword(String username, String password) throws IOException {
         synchronized (lock) {
             if (closeFuture.isClosed()) {
@@ -84,7 +87,7 @@
         }
     }
 
-    public AuthFuture authPublicKey(String username, PublicKey key) throws IOException {
+    public AuthFuture authPublicKey(String username, KeyPair key) throws IOException {
         synchronized (lock) {
             if (closeFuture.isClosed()) {
                 throw new IllegalStateException("Session is closed");
@@ -99,9 +102,10 @@
             if (closeFuture.isClosed()) {
                 throw new IllegalStateException("Session is closed");
             }
-            //authFuture = new DefaultAuthFuture<ClientSession>(this, lock);
-            // TODO: implement public key authentication method
-            throw new UnsupportedOperationException("Not supported yet");
+            authFuture = new DefaultAuthFuture(lock);
+            userAuth = new UserAuthPublicKey(this, username, key);
+            setState(ClientSessionImpl.State.UserAuth);
+            return authFuture;
         }
     }
 
@@ -130,6 +134,12 @@
     }
 
     protected void handleMessage(Buffer buffer) throws Exception {
+        synchronized (lock) {
+            doHandleMessage(buffer);
+        }
+    }
+
+    protected void doHandleMessage(Buffer buffer) throws Exception {
         SshConstants.Message cmd = buffer.getCommand();
         log.debug("Received packet {}", cmd);
         switch (cmd) {

Modified: mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java?rev=887288&r1=887287&r2=887288&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java (original)
+++ mina/sshd/trunk/sshd-core/src/main/java/org/apache/sshd/common/keyprovider/FileKeyPairProvider.java Fri Dec  4 17:40:19 2009
@@ -73,7 +73,7 @@
         this.passwordFinder = passwordFinder;
     }
 
-    protected KeyPair[] loadKeys() {
+    public KeyPair[] loadKeys() {
         if (!SecurityUtils.isBouncyCastleRegistered()) {
             throw new IllegalStateException("BouncyCastle must be registered as a JCE provider");
         }

Modified: mina/sshd/trunk/sshd-core/src/test/java/org/apache/sshd/ClientTest.java
URL: http://svn.apache.org/viewvc/mina/sshd/trunk/sshd-core/src/test/java/org/apache/sshd/ClientTest.java?rev=887288&r1=887287&r2=887288&view=diff
==============================================================================
--- mina/sshd/trunk/sshd-core/src/test/java/org/apache/sshd/ClientTest.java (original)
+++ mina/sshd/trunk/sshd-core/src/test/java/org/apache/sshd/ClientTest.java Fri Dec  4 17:40:19 2009
@@ -23,14 +23,17 @@
 import java.io.PipedInputStream;
 import java.io.PipedOutputStream;
 import java.net.ServerSocket;
+import java.security.KeyPair;
 
 import org.apache.sshd.client.future.AuthFuture;
 import org.apache.sshd.client.future.OpenFuture;
+import org.apache.sshd.common.KeyPairProvider;
 import org.apache.sshd.common.SshException;
 import org.apache.sshd.common.future.CloseFuture;
 import org.apache.sshd.common.keyprovider.FileKeyPairProvider;
 import org.apache.sshd.common.util.BufferUtils;
 import org.apache.sshd.util.BogusPasswordAuthenticator;
+import org.apache.sshd.util.BogusPublickeyAuthenticator;
 import org.apache.sshd.util.EchoShellFactory;
 import org.apache.sshd.util.TeePipedOutputStream;
 import org.junit.After;
@@ -61,6 +64,7 @@
         sshd.setKeyPairProvider(new FileKeyPairProvider(new String[] { "src/test/resources/hostkey.pem" }));
         sshd.setShellFactory(new EchoShellFactory());
         sshd.setPasswordAuthenticator(new BogusPasswordAuthenticator());
+        sshd.setPublickeyAuthenticator(new BogusPublickeyAuthenticator());
         sshd.start();
     }
 
@@ -230,6 +234,17 @@
         assertTrue(closeFuture.isClosed());
     }
 
+    @Test
+    public void testPublicKeyAuth() throws Exception {
+        SshClient client = SshClient.setUpDefaultClient();
+        client.start();
+        ClientSession session = client.connect("localhost", port).await().getSession();
+
+        KeyPair pair = new FileKeyPairProvider(new String[] { "src/test/resources/hostkey.pem" }).loadKey(KeyPairProvider.SSH_RSA);
+
+        assertTrue(session.authPublicKey("smx", pair).await().isSuccess());
+    }
+
     public static void main(String[] args) throws Exception {
         SshClient.main(args);
     }