Mina SSHD Server on Windows Example

The official Mina SSHD server example is oversimplified, so I'd like to work out a more useful example.

PART 1: Add Password Authentication

show source directly:

import java.io.File;
import java.io.IOException;

import org.apache.sshd.server.SshServer;
import org.apache.sshd.server.auth.password.PasswordAuthenticator;
import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider;
import org.apache.sshd.server.session.ServerSession;
import org.apache.sshd.server.shell.ProcessShellFactory;

public class SshServerMock {

	public static void main(String[] args) throws IOException {
		SshServer sshd = SshServer.setUpDefaultServer();

		sshd.setPort(22);

		sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(new File("d:\\hostkey.ser")));
		sshd.setPasswordAuthenticator(new PasswordAuthenticator() {

			@Override
			public boolean authenticate(String u, String p, ServerSession s) {
				return ("sshtest".equals(u) && "sshtest".equals(p));
			}
		});

		sshd.setShellFactory(new ProcessShellFactory(new String[] { "cmd.exe" }));

		sshd.start();

		try {
			Thread.sleep(100000000000l);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}




PART 2: Public Key Authentication

At first we need to know the public key authenticating process, quoted from [3]: 

What really happens is as follows: (1) The server asks for the public key of the client. (2) The public key is passed to PublickeyAuthenticator#authenticate (3) All you are supposed to do in authenticate() is check that this is a public key that you want to allow. (4) If authenticate() returns true, then UserAuthPublicKey#auth will check that a message has been signed with the private key. If it has then authentication has been validated.

Second, we must be aware of the two forms of public key, one is OpenSSH generated public key, second is the java public key in the Mina SSHD API. Therefore, when coding for SSHD server, you will get client's public key in java form (java.security.interfaces.RSAPublicKey), but the server's persisted public key is in OpenSSH form, so we need to transform before validating.

/*
 * http://caffeineiscodefuel.blogspot.hk/2013/04/apache-mina-sshd-
 * publickeyauthenticator.html
 */
class CustomPublicKeyAuthenticator implements PublickeyAuthenticator {

	private static final String knownKey = "{SSH2.PUBLIC.KEY}";

	@Override
	public boolean authenticate(String username, PublicKey key, ServerSession session) {
		if (key instanceof RSAPublicKey) {
			String s1 = new String(encode((RSAPublicKey) key));
			String s2 = new String(Base64.decodeBase64(knownKey.getBytes()));
			return s1.equals(s2); // Returns true if the key matches our known
									// key, this allows auth to proceed.
		}
		return false; // Doesn't handle other key types currently.
	}

	// Converts a Java RSA PK to SSH2 Format.
	public static byte[] encode(RSAPublicKey key) {
		try {
			ByteArrayOutputStream buf = new ByteArrayOutputStream();
			byte[] name = "ssh-rsa".getBytes("US-ASCII");
			write(name, buf);
			write(key.getPublicExponent().toByteArray(), buf);
			write(key.getModulus().toByteArray(), buf);
			return buf.toByteArray();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	private static void write(byte[] str, OutputStream os) throws IOException {
		for (int shift = 24; shift >= 0; shift -= 8)
			os.write((str.length >>> shift) & 0xFF);
		os.write(str);
	}

}


PART 3 Expose Java Service to SSH by Customized ShellFactory

Once you plug in your own authentication logic and custom shell, the sky’s the limit.

The best example for this I found resides in the Mina SSHD unit test source code, the class named 'EchoShellFactory'

/**
 * @author Apache MINA SSHD Project
 */
public class EchoShellFactory implements Factory<Command> {

	public Command create() {
		return new EchoShell();
	}

	public static class EchoShell implements Command, Runnable {

		private InputStream in;
		private OutputStream out;
		private OutputStream err;
		private ExitCallback callback;
		private Environment environment;
		private Thread thread;

		public InputStream getIn() {
			return in;
		}

		public OutputStream getOut() {
			return out;
		}

		public OutputStream getErr() {
			return err;
		}

		public Environment getEnvironment() {
			return environment;
		}

		public void setInputStream(InputStream in) {
			this.in = in;
		}

		public void setOutputStream(OutputStream out) {
			this.out = out;
		}

		public void setErrorStream(OutputStream err) {
			this.err = err;
		}

		public void setExitCallback(ExitCallback callback) {
			this.callback = callback;
		}

		public void start(Environment env) throws IOException {
			environment = env;
			thread = new Thread(this, "EchoShell");
			thread.start();
		}

		public void destroy() {
			thread.interrupt();
		}

		public void run() {
			BufferedReader r = new BufferedReader(new InputStreamReader(in));
			try {
				for (;;) {
					String s = r.readLine();
					if (s == null) {
						return;
					}
					out.write((s + "\r\n").getBytes());   //note the '\r'
					out.flush();
					if ("exit".equals(s)) {
						return;
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				callback.onExit(0);
			}
		}
	}
}
Note that without the '\r', the new line will not start from the beginning, it looks weird. so we'd better add this carriage return character.


PART 4 Configure Putty Line discipline options

With default setting, we can see what we input in Putty pseudo terminal for the echo shell example above. All we need to do is configure the line discipline options, as follows.


References:

[1] http://www.programcreek.com/java-api-examples/index.php?api=org.apache.sshd.SshServer

[2] http://caffeineiscodefuel.blogspot.hk/2013/04/apache-mina-sshd-publickeyauthenticator.html

[3] http://stackoverflow.com/questions/15372360/apache-sshd-public-key-authentication

[4] https://tools.ietf.org/html/rfc4254#section-8

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值