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);
}