Compare commits

..

27 Commits

Author SHA1 Message Date
Shikhar Bhushan
16a363fef6 [maven-release-plugin] prepare release v0.7.0 2012-01-08 09:32:41 +00:00
Shikhar Bhushan
9b0d39a798 Remove the SFTPClient.getFileTansfer() method that has typo altogether as this is a simple change to make for clients. 2012-01-08 09:30:05 +00:00
Shikhar Bhushan
81e36153d7 wrapping 2012-01-08 09:10:07 +00:00
Shikhar Bhushan
3026be282a Refactored the local port forwarding API; give caller control over initializing and cleaning up the server socket used.
Also removed 'server socket factory' stuff from SocketClient.
2012-01-05 22:26:44 +00:00
Shikhar Bhushan
8eedeb25fa Merge pull request #50 from iocanel/master
Remove bouncycastle version range from OSGi metadata.
2012-01-02 06:52:50 -08:00
Ioannis Canellos
de11880648 Removed package version from bouncycastle imports 2012-01-02 15:04:12 +02:00
Shikhar Bhushan
1ff4772f3f update id string 2011-12-20 11:12:35 +00:00
Shikhar Bhushan
22a5ffe735 fix for #47 - should send data down rather than sitting around waiting for an adjustment if there is window space available 2011-12-20 10:41:49 +00:00
Shikhar Bhushan
7a77f85ced docfix 2011-12-20 10:37:32 +00:00
Shikhar Bhushan
0002fe8b40 Made some Buffer subclasses final 2011-12-19 22:52:41 +00:00
Shikhar Bhushan
3028e7f218 Fix logging of window adjustments 2011-12-19 21:56:44 +00:00
Shikhar Bhushan
333e1cb7b8 small cleanup 2011-12-04 19:10:34 +00:00
Shikhar Bhushan
945d430916 Removed deprecated Session.Command's methods - getOutputAsString() and getErrorAsString() 2011-12-04 18:26:05 +00:00
Shikhar Bhushan
73b903784a Next version will be 0.7.0 2011-12-04 18:24:00 +00:00
Shikhar Bhushan
7d53649a85 . 2011-12-04 18:23:15 +00:00
Shikhar Bhushan
e193db9a14 Fix example in SSHClient doc 2011-12-04 18:14:27 +00:00
Shikhar Bhushan
a942edb911 Add SFTPClient.getFileTransfer() and deprecate getFileTansfer() which has typo 2011-12-04 18:11:30 +00:00
Shikhar Bhushan
137a7f5956 (reformat) 2011-12-04 17:48:42 +00:00
Shikhar Bhushan
718ff503df Merge pull request #41 from hierynomus/known-hosts
OpenSSH Known hosts format re-implemented
2011-12-04 09:35:58 -08:00
Shikhar Bhushan
d933b2538e Upgrade maven-bundle-plugin to 2.3.6 to fix #37 [https://issues.apache.org/jira/browse/FELIX-3058] 2011-12-04 17:10:33 +00:00
Shikhar Bhushan
ea6f9ceed2 Correct version 2011-12-04 13:54:19 +00:00
rws
07c61b14e8 Change SocketClient to public so that SSHClient can be mocked for testing. 2011-12-04 21:51:58 +08:00
hierynomus
4b175e6938 Re-implemented OpenSSHKnownHostsVerifier to deal with the real format 2011-11-10 09:51:18 +01:00
Shikhar Bhushan
f7e47cffa0 [maven-release-plugin] prepare for next development iteration 2011-10-02 22:17:46 +01:00
Shikhar Bhushan
42dddc7f7e [maven-release-plugin] prepare release v0.6.1 2011-10-02 22:17:36 +01:00
Shikhar Bhushan
f1b3dbb102 Restore mutual exclusion of sendChannelRequest() and gotResponse() in AbstractChannel (but rather than make methods synchronized do it on the queue, which itself doesn't need to be thread-safe). Regression due to 1a2351c5ee. Fixes #35. 2011-10-02 09:47:49 +01:00
Shikhar Bhushan
f83bf2cd3f [maven-release-plugin] prepare for next development iteration 2011-09-26 12:41:23 +01:00
21 changed files with 596 additions and 421 deletions

View File

@@ -6,7 +6,7 @@
<groupId>net.schmizz</groupId>
<artifactId>sshj</artifactId>
<packaging>bundle</packaging>
<version>0.6.0</version>
<version>0.7.0</version>
<name>sshj</name>
<description>SSHv2 library for Java</description>
@@ -169,7 +169,7 @@
<plugin>
<groupId>org.apache.felix</groupId>
<artifactId>maven-bundle-plugin</artifactId>
<version>2.3.5</version>
<version>2.3.6</version>
<extensions>true</extensions>
<configuration>
<instructions>
@@ -178,7 +178,7 @@
javax.crypto*,
com.jcraft.jzlib*;version="[1.0,2)",
org.slf4j*;version="[1.6,2)",
org.bouncycastle*;version="[1.4,2)",
org.bouncycastle*,
*
</Import-Package>
<Export-Package>net.schmizz.*</Export-Package>

View File

@@ -16,9 +16,11 @@
package examples;
import net.schmizz.sshj.SSHClient;
import net.schmizz.sshj.connection.channel.direct.LocalPortForwarder;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
/**
* This example demonstrates local port forwarding, i.e. when we listen on a particular address and port; and forward
@@ -41,8 +43,16 @@ public class LocalPF {
* _We_ listen on localhost:8080 and forward all connections on to server, which then forwards it to
* google.com:80
*/
ssh.newLocalPortForwarder(new InetSocketAddress("localhost", 8080), "google.com", 80)
.listen();
final LocalPortForwarder.Parameters params
= new LocalPortForwarder.Parameters("0.0.0.0", 8080, "google.com", 80);
final ServerSocket ss = new ServerSocket();
ss.setReuseAddress(true);
ss.bind(new InetSocketAddress(params.getLocalHost(), params.getLocalPort()));
try {
ssh.newLocalPortForwarder(params, ss).listen();
} finally {
ss.close();
}
} finally {
ssh.disconnect();

View File

@@ -73,15 +73,20 @@ import java.util.List;
* A {@link Config} that is initialized as follows. Items marked with an asterisk are added to the config only if
* BouncyCastle is in the classpath.
* <p/>
* <ul> <li>{@link ConfigImpl#setKeyExchangeFactories Key exchange}: {@link DHG14}*, {@link DHG1}</li> <li>{@link
* ConfigImpl#setCipherFactories Ciphers} [1]: {@link AES128CTR}, {@link AES192CTR}, {@link AES256CTR}, {@link
* <ul>
* <li>{@link ConfigImpl#setKeyExchangeFactories Key exchange}: {@link DHG14}*, {@link DHG1}</li>
* <li>{@link ConfigImpl#setCipherFactories Ciphers} [1]: {@link AES128CTR}, {@link AES192CTR}, {@link AES256CTR},
* {@link
* AES128CBC}, {@link AES192CBC}, {@link AES256CBC}, {@link AES192CBC}, {@link TripleDESCBC}, {@link BlowfishCBC}</li>
* <li>{@link ConfigImpl#setMACFactories MAC}: {@link HMACSHA1}, {@link HMACSHA196}, {@link HMACMD5}, {@link
* HMACMD596}</li> <li>{@link ConfigImpl#setCompressionFactories Compression}: {@link NoneCompression}</li> <li>{@link
* ConfigImpl#setSignatureFactories Signature}: {@link SignatureRSA}, {@link SignatureDSA}</li> <li>{@link
* ConfigImpl#setRandomFactory PRNG}: {@link BouncyCastleRandom}* or {@link JCERandom}</li> <li>{@link
* ConfigImpl#setFileKeyProviderFactories Key file support}: {@link PKCS8KeyFile}*, {@link OpenSSHKeyFile}*</li>
* <li>{@link ConfigImpl#setVersion Client version}: {@code "NET_3_0"}</li> </ul>
* HMACMD596}</li>
* <li>{@link ConfigImpl#setCompressionFactories Compression}: {@link NoneCompression}</li>
* <li>{@link ConfigImpl#setSignatureFactories Signature}: {@link SignatureRSA}, {@link SignatureDSA}</li>
* <li>{@link ConfigImpl#setRandomFactory PRNG}: {@link BouncyCastleRandom}* or {@link JCERandom}</li>
* <li>{@link ConfigImpl#setFileKeyProviderFactories Key file support}: {@link PKCS8KeyFile}*, {@link
* OpenSSHKeyFile}*</li>
* <li>{@link ConfigImpl#setVersion Client version}: {@code "NET_3_0"}</li>
* </ul>
* <p/>
* [1] It is worth noting that Sun's JRE does not have the unlimited cryptography extension enabled by default. This
* prevents using ciphers with strength greater than 128.
@@ -91,7 +96,7 @@ public class DefaultConfig
private final Logger log = LoggerFactory.getLogger(getClass());
private static final String VERSION = "SSHJ_0_6_0";
private static final String VERSION = "SSHJ_0_7";
public DefaultConfig() {
setVersion(VERSION);
@@ -113,7 +118,8 @@ public class DefaultConfig
}
protected void initRandomFactory(boolean bouncyCastleRegistered) {
setRandomFactory(new SingletonRandomFactory(bouncyCastleRegistered ? new BouncyCastleRandom.Factory() : new JCERandom.Factory()));
setRandomFactory(new SingletonRandomFactory(bouncyCastleRegistered
? new BouncyCastleRandom.Factory() : new JCERandom.Factory()));
}
protected void initFileKeyProviderFactories(boolean bouncyCastleRegistered) {
@@ -161,7 +167,7 @@ public class DefaultConfig
protected void initMACFactories() {
setMACFactories(new HMACSHA1.Factory(), new HMACSHA196.Factory(), new HMACMD5.Factory(),
new HMACMD596.Factory());
new HMACMD596.Factory());
}
protected void initCompressionFactories() {

View File

@@ -64,7 +64,7 @@ import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.net.SocketAddress;
import java.net.ServerSocket;
import java.security.KeyPair;
import java.security.PublicKey;
import java.util.Arrays;
@@ -92,8 +92,8 @@ import java.util.List;
* <em>A simple example:</em>
* <p/>
* <pre>
* client = new SSHClient();
* client.initUserKnownHosts();
* final SSHClient client = new SSHClient();
* client.loadKnownHosts();
* client.connect(&quot;hostname&quot;);
* try {
* client.authPassword(&quot;username&quot;, &quot;password&quot;);
@@ -476,8 +476,8 @@ public class SSHClient
throws IOException {
final File loc = new File(location);
final FileKeyProvider.Format format = KeyProviderUtil.detectKeyFileFormat(loc);
final FileKeyProvider fkp = Factory.Named.Util.create(trans.getConfig().getFileKeyProviderFactories(), format
.toString());
final FileKeyProvider fkp =
Factory.Named.Util.create(trans.getConfig().getFileKeyProviderFactories(), format.toString());
if (fkp == null)
throw new SSHException("No provider available for " + format + " key file");
fkp.init(loc, passwordFinder);
@@ -520,8 +520,8 @@ public class SSHClient
public KeyProvider loadKeys(String privateKey, String publicKey, PasswordFinder passwordFinder)
throws IOException {
final FileKeyProvider.Format format = KeyProviderUtil.detectKeyFileFormat(privateKey, publicKey != null);
final FileKeyProvider fkp = Factory.Named.Util.create(trans.getConfig().getFileKeyProviderFactories(), format
.toString());
final FileKeyProvider fkp =
Factory.Named.Util.create(trans.getConfig().getFileKeyProviderFactories(), format.toString());
if (fkp == null)
throw new SSHException("No provider available for " + format + " key file");
fkp.init(privateKey, publicKey, passwordFinder);
@@ -568,23 +568,21 @@ public class SSHClient
}
/**
* Create a {@link LocalPortForwarder} that will listen on {@code address} and forward incoming connections to the
* server; which will further forward them to {@code host:port}.
* Create a {@link LocalPortForwarder} that will listen based on {@code parameters} using the bound
* {@code serverSocket} and forward incoming connections to the server; which will further forward them to
* {@code host:port}.
* <p/>
* The returned forwarder's {@link LocalPortForwarder#listen() listen()} method should be called to actually start
* listening, this method just creates an instance.
*
* @param address defines where the {@link LocalPortForwarder} listens
* @param host hostname to which the server will forward
* @param port the port at {@code hostname} to which the server wil forward
* @param parameters parameters for the forwarding setup
* @param serverSocket bound server socket
*
* @return a {@link LocalPortForwarder}
*
* @throws IOException if there is an error opening a local server socket
*/
public LocalPortForwarder newLocalPortForwarder(SocketAddress address, String host, int port)
throws IOException {
return new LocalPortForwarder(getServerSocketFactory(), conn, address, host, port);
public LocalPortForwarder newLocalPortForwarder(LocalPortForwarder.Parameters parameters,
ServerSocket serverSocket) {
return new LocalPortForwarder(conn, parameters, serverSocket);
}
/**

View File

@@ -35,7 +35,6 @@
*/
package net.schmizz.sshj;
import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import java.io.IOException;
import java.io.InputStream;
@@ -44,8 +43,7 @@ import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
abstract class SocketClient {
public abstract class SocketClient {
private final int defaultPort;
@@ -54,7 +52,6 @@ abstract class SocketClient {
private OutputStream output;
private SocketFactory socketFactory = SocketFactory.getDefault();
private ServerSocketFactory serverSocketFactory = ServerSocketFactory.getDefault();
private static final int DEFAULT_CONNECT_TIMEOUT = 0;
private int connectTimeout = DEFAULT_CONNECT_TIMEOUT;
@@ -158,17 +155,6 @@ abstract class SocketClient {
return socketFactory;
}
public void setServerSocketFactory(ServerSocketFactory factory) {
if (factory == null)
serverSocketFactory = ServerSocketFactory.getDefault();
else
serverSocketFactory = factory;
}
public ServerSocketFactory getServerSocketFactory() {
return serverSocketFactory;
}
public int getConnectTimeout() {
return connectTimeout;
}

View File

@@ -51,7 +51,7 @@ public class Buffer<T extends Buffer<T>> {
}
}
public static class PlainBuffer
public static final class PlainBuffer
extends Buffer<PlainBuffer> {
public PlainBuffer() {

View File

@@ -37,7 +37,7 @@ package net.schmizz.sshj.common;
import java.util.Arrays;
public class SSHPacket
public final class SSHPacket
extends Buffer<SSHPacket> {
public SSHPacket() {

View File

@@ -53,8 +53,8 @@ import org.slf4j.LoggerFactory;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
@@ -76,7 +76,7 @@ public abstract class AbstractChannel
/** Remote recipient ID */
private int recipient;
private final Queue<Event<ConnectionException>> chanReqResponseEvents = new ConcurrentLinkedQueue<Event<ConnectionException>>();
private final Queue<Event<ConnectionException>> chanReqResponseEvents = new LinkedList<Event<ConnectionException>>();
/* The lock used by to create the open & close events */
private final ReentrantLock lock = new ReentrantLock();
@@ -368,33 +368,37 @@ public abstract class AbstractChannel
Buffer.PlainBuffer reqSpecific)
throws TransportException {
log.info("Sending channel request for `{}`", reqType);
trans.write(
newBuffer(Message.CHANNEL_REQUEST)
.putString(reqType)
.putBoolean(wantReply)
.putBuffer(reqSpecific)
);
synchronized (chanReqResponseEvents) {
trans.write(
newBuffer(Message.CHANNEL_REQUEST)
.putString(reqType)
.putBoolean(wantReply)
.putBuffer(reqSpecific)
);
Event<ConnectionException> responseEvent = null;
if (wantReply) {
responseEvent = new Event<ConnectionException>("chan#" + id + " / " + "chanreq for " + reqType,
ConnectionException.chainer);
chanReqResponseEvents.add(responseEvent);
Event<ConnectionException> responseEvent = null;
if (wantReply) {
responseEvent = new Event<ConnectionException>("chan#" + id + " / " + "chanreq for " + reqType,
ConnectionException.chainer);
chanReqResponseEvents.add(responseEvent);
}
return responseEvent;
}
return responseEvent;
}
private void gotResponse(boolean success)
throws ConnectionException {
final Event<ConnectionException> responseEvent = chanReqResponseEvents.poll();
if (responseEvent != null) {
if (success)
responseEvent.set();
else
responseEvent.deliverError(new ConnectionException("Request failed"));
} else
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR,
"Received response to channel request when none was requested");
synchronized (chanReqResponseEvents) {
final Event<ConnectionException> responseEvent = chanReqResponseEvents.poll();
if (responseEvent != null) {
if (success)
responseEvent.set();
else
responseEvent.deliverError(new ConnectionException("Request failed"));
} else
throw new ConnectionException(DisconnectReason.PROTOCOL_ERROR,
"Received response to channel request when none was requested");
}
}
private synchronized void gotEOF()

View File

@@ -35,12 +35,14 @@
*/
package net.schmizz.sshj.connection.channel;
import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.ErrorNotifiable;
import net.schmizz.sshj.common.Message;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.transport.Transport;
import net.schmizz.sshj.transport.TransportException;
import java.io.IOException;
import java.io.OutputStream;
@@ -56,26 +58,92 @@ public final class ChannelOutputStream
private final Channel chan;
private final Transport trans;
private final Window.Remote win;
private final SSHPacket buffer = new SSHPacket();
private final DataBuffer buffer = new DataBuffer();
private final byte[] b = new byte[1];
private int bufferLength;
private boolean closed;
private SSHException error;
private final class DataBuffer {
private final int headerOffset;
private final int dataOffset;
private final SSHPacket packet = new SSHPacket(Message.CHANNEL_DATA);
private final Buffer.PlainBuffer leftOvers = new Buffer.PlainBuffer();
DataBuffer() {
headerOffset = packet.rpos();
packet.putUInt32(0); // recipient
packet.putUInt32(0); // data length
dataOffset = packet.wpos();
}
int write(byte[] data, int off, int len)
throws TransportException, ConnectionException {
final int bufferSize = packet.wpos() - dataOffset;
if (bufferSize >= win.getMaxPacketSize()) {
flush(bufferSize);
return 0;
} else {
final int n = Math.min(len - off, win.getMaxPacketSize() - bufferSize);
packet.putRawBytes(data, off, n);
return n;
}
}
void flush()
throws TransportException, ConnectionException {
flush(packet.wpos() - dataOffset);
}
void flush(int bufferSize)
throws TransportException, ConnectionException {
while (bufferSize > 0) {
int remoteWindowSize = win.getSize();
if (remoteWindowSize == 0)
remoteWindowSize = win.awaitExpansion(remoteWindowSize);
// We can only write the min. of
// a) how much data we have
// b) the max packet size
// c) what the current window size will allow
final int writeNow = Math.min(bufferSize, Math.min(win.getMaxPacketSize(), remoteWindowSize));
packet.wpos(headerOffset);
packet.putMessageID(Message.CHANNEL_DATA);
packet.putUInt32(chan.getRecipient());
packet.putUInt32(writeNow);
packet.wpos(dataOffset + writeNow);
final int leftOverBytes = bufferSize - writeNow;
if (leftOverBytes > 0) {
leftOvers.putRawBytes(packet.array(), packet.wpos(), leftOverBytes);
}
trans.write(packet);
win.consume(writeNow);
packet.rpos(headerOffset);
packet.wpos(dataOffset);
if (leftOverBytes > 0) {
packet.putBuffer(leftOvers);
leftOvers.clear();
}
bufferSize = leftOverBytes;
}
}
}
public ChannelOutputStream(Channel chan, Transport trans, Window.Remote win) {
this.chan = chan;
this.trans = trans;
this.win = win;
prepBuffer();
}
private void prepBuffer() {
bufferLength = 0;
buffer.rpos(5);
buffer.wpos(5);
buffer.putMessageID(Message.CHANNEL_DATA);
buffer.putUInt32(0); // meant to be recipient
buffer.putUInt32(0); // meant to be data length
}
@Override
@@ -86,19 +154,13 @@ public final class ChannelOutputStream
}
@Override
public synchronized void write(byte[] data, int off, int len)
public synchronized void write(final byte[] data, int off, int len)
throws IOException {
checkClose();
while (len > 0) {
final int x = Math.min(len, win.getMaxPacketSize() - bufferLength);
if (x <= 0) {
flush();
continue;
}
buffer.putRawBytes(data, off, x);
bufferLength += x;
off += x;
len -= x;
final int n = buffer.write(data, off, len);
off += n;
len -= n;
}
}
@@ -107,55 +169,44 @@ public final class ChannelOutputStream
this.error = error;
}
private synchronized void checkClose()
private void checkClose()
throws SSHException {
if (closed)
if (closed) {
if (error != null)
throw error;
else
throw new ConnectionException("Stream closed");
}
}
@Override
public synchronized void close()
throws IOException {
if (!closed)
if (!closed) {
try {
flush();
buffer.flush();
chan.sendEOF();
} finally {
setClosed();
}
}
}
public synchronized void setClosed() {
closed = true;
}
/**
* Send all data currently buffered. If window space is exhausted in the process, this will block
* until it is expanded by the server.
*
* @throws IOException
*/
@Override
public synchronized void flush()
throws IOException {
checkClose();
if (bufferLength <= 0) // No data to send
return;
putRecipientAndLength();
try {
win.waitAndConsume(bufferLength);
trans.write(buffer);
} finally {
prepBuffer();
}
}
private void putRecipientAndLength() {
final int origPos = buffer.wpos();
buffer.wpos(6);
buffer.putUInt32(chan.getRecipient());
buffer.putUInt32(bufferLength);
buffer.wpos(origPos);
buffer.flush();
}
@Override

View File

@@ -37,8 +37,8 @@ public abstract class Window {
public void expand(int inc) {
synchronized (lock) {
log.debug("Increasing by {} up to {}", inc, size);
size += inc;
log.debug("Increasing by {} up to {}", inc, size);
lock.notifyAll();
}
}
@@ -48,14 +48,16 @@ public abstract class Window {
}
public int getSize() {
return size;
synchronized (lock) {
return size;
}
}
public void consume(int dec)
throws ConnectionException {
synchronized (lock) {
log.debug("Consuming by " + dec + " down to " + size);
size -= dec;
log.debug("Consuming by " + dec + " down to " + size);
if (size < 0)
throw new ConnectionException("Window consumed to below 0");
}
@@ -74,25 +76,25 @@ public abstract class Window {
super(initialWinSize, maxPacketSize);
}
public void waitAndConsume(int howMuch)
public int awaitExpansion(int was)
throws ConnectionException {
synchronized (lock) {
while (size < howMuch) {
log.debug("Waiting, need window space for {} bytes", howMuch);
while (size <= was) {
log.debug("Waiting, need size to grow from {} bytes", was);
try {
lock.wait();
} catch (InterruptedException ie) {
throw new ConnectionException(ie);
}
}
consume(howMuch);
return size;
}
}
public void consume(int howMuch) {
try {
super.consume(howMuch);
} catch (ConnectionException e) {
} catch (ConnectionException e) { // It's a bug if we consume more than remote allowed
throw new SSHRuntimeException(e);
}
}

View File

@@ -19,104 +19,104 @@ import net.schmizz.concurrent.Event;
import net.schmizz.sshj.common.SSHPacket;
import net.schmizz.sshj.common.StreamCopier;
import net.schmizz.sshj.connection.Connection;
import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.channel.SocketStreamCopyMonitor;
import net.schmizz.sshj.transport.TransportException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ServerSocketFactory;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.concurrent.TimeUnit;
public class LocalPortForwarder {
private class DirectTCPIPChannel
extends AbstractDirectChannel {
public static class Parameters {
private final Socket sock;
private final String localHost;
private final int localPort;
private final String remoteHost;
private final int remotePort;
private DirectTCPIPChannel(Connection conn, Socket sock) {
super(conn, "direct-tcpip");
this.sock = sock;
public Parameters(String localHost, int localPort, String remoteHost, int remotePort) {
this.localHost = localHost;
this.localPort = localPort;
this.remoteHost = remoteHost;
this.remotePort = remotePort;
}
private void start()
public String getRemoteHost() {
return remoteHost;
}
public int getRemotePort() {
return remotePort;
}
public String getLocalHost() {
return localHost;
}
public int getLocalPort() {
return localPort;
}
}
public static class DirectTCPIPChannel
extends AbstractDirectChannel {
protected final Socket socket;
protected final Parameters parameters;
public DirectTCPIPChannel(Connection conn, Socket socket, Parameters parameters) {
super(conn, "direct-tcpip");
this.socket = socket;
this.parameters = parameters;
}
protected void start()
throws IOException {
sock.setSendBufferSize(getLocalMaxPacketSize());
sock.setReceiveBufferSize(getRemoteMaxPacketSize());
final Event<IOException> soc2chan = new StreamCopier(sock.getInputStream(), getOutputStream())
socket.setSendBufferSize(getLocalMaxPacketSize());
socket.setReceiveBufferSize(getRemoteMaxPacketSize());
final Event<IOException> soc2chan = new StreamCopier(socket.getInputStream(), getOutputStream())
.bufSize(getRemoteMaxPacketSize())
.spawnDaemon("soc2chan");
final Event<IOException> chan2soc = new StreamCopier(getInputStream(), sock.getOutputStream())
final Event<IOException> chan2soc = new StreamCopier(getInputStream(), socket.getOutputStream())
.bufSize(getLocalMaxPacketSize())
.spawnDaemon("chan2soc");
SocketStreamCopyMonitor.monitor(5, TimeUnit.SECONDS, soc2chan, chan2soc, this, sock);
SocketStreamCopyMonitor.monitor(5, TimeUnit.SECONDS, soc2chan, chan2soc, this, socket);
}
@Override
protected SSHPacket buildOpenReq() {
return super.buildOpenReq()
.putString(host)
.putUInt32(port)
.putString(ss.getInetAddress().getHostAddress())
.putUInt32(ss.getLocalPort());
.putString(parameters.getRemoteHost())
.putUInt32(parameters.getRemotePort())
.putString(parameters.getLocalHost())
.putUInt32(parameters.getLocalPort());
}
}
private final Logger log = LoggerFactory.getLogger(getClass());
private final Logger log = LoggerFactory.getLogger(LocalPortForwarder.class);
private final Connection conn;
private final ServerSocket ss;
private final String host;
private final int port;
private final Parameters parameters;
private final ServerSocket serverSocket;
/**
* Create a local port forwarder with specified binding ({@code listeningAddr}. It does not, however, start
* listening unless {@link #listen() explicitly told to}. The {@link javax.net.ServerSocketFactory#getDefault()
* default} server socket factory is used.
*
* @param conn {@link Connection} implementation
* @param listeningAddr {@link SocketAddress} this forwarder will listen on, if {@code null} then an ephemeral port
* and valid local address will be picked to bind the server socket
* @param host what host the SSH server will further forward to
* @param port port on {@code toHost}
*
* @throws IOException if there is an error binding on specified {@code listeningAddr}
*/
public LocalPortForwarder(Connection conn, SocketAddress listeningAddr, String host, int port)
throws IOException {
this(ServerSocketFactory.getDefault(), conn, listeningAddr, host, port);
}
/**
* Create a local port forwarder with specified binding ({@code listeningAddr}. It does not, however, start
* listening unless {@link #listen() explicitly told to}.
*
* @param ssf factory to use for creating the server socket
* @param conn {@link Connection} implementation
* @param listeningAddr {@link SocketAddress} this forwarder will listen on, if {@code null} then an ephemeral port
* and valid local address will be picked to bind the server socket
* @param host what host the SSH server will further forward to
* @param port port on {@code toHost}
*
* @throws IOException if there is an error binding on specified {@code listeningAddr}
*/
public LocalPortForwarder(ServerSocketFactory ssf, Connection conn, SocketAddress listeningAddr, String host, int port)
throws IOException {
public LocalPortForwarder(Connection conn, Parameters parameters, ServerSocket serverSocket) {
this.conn = conn;
this.host = host;
this.port = port;
this.ss = ssf.createServerSocket();
ss.setReceiveBufferSize(conn.getMaxPacketSize());
ss.bind(listeningAddr);
this.parameters = parameters;
this.serverSocket = serverSocket;
}
/** @return the address to which this forwarder is bound for listening */
public SocketAddress getListeningAddress() {
return ss.getLocalSocketAddress();
protected DirectTCPIPChannel openChannel(Socket socket)
throws TransportException, ConnectionException {
final DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, socket, parameters);
chan.open();
return chan;
}
/**
@@ -126,14 +126,11 @@ public class LocalPortForwarder {
*/
public void listen()
throws IOException {
log.info("Listening on {}", ss.getLocalSocketAddress());
Socket sock;
log.info("Listening on {}", serverSocket.getLocalSocketAddress());
while (!Thread.currentThread().isInterrupted()) {
sock = ss.accept();
log.info("Got connection from {}", sock.getRemoteSocketAddress());
DirectTCPIPChannel chan = new DirectTCPIPChannel(conn, sock);
chan.open();
chan.start();
final Socket socket = serverSocket.accept();
log.info("Got connection from {}", socket.getRemoteSocketAddress());
openChannel(socket).start();
}
log.info("Interrupted!");
}

View File

@@ -19,7 +19,6 @@ import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.channel.Channel;
import net.schmizz.sshj.transport.TransportException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
@@ -81,14 +80,6 @@ public interface Session
void signal(Signal signal)
throws TransportException;
@Deprecated
String getOutputAsString()
throws IOException;
@Deprecated
String getErrorAsString()
throws IOException;
}
/** Shell API. */

View File

@@ -46,7 +46,6 @@ import net.schmizz.sshj.connection.ConnectionException;
import net.schmizz.sshj.connection.channel.ChannelInputStream;
import net.schmizz.sshj.transport.TransportException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Map;
@@ -255,18 +254,4 @@ public class SessionChannel
throw new SSHRuntimeException("This session channel is all used up");
}
@Override
@Deprecated
public String getOutputAsString()
throws IOException {
return IOUtils.readFully(getInputStream()).toString();
}
@Override
@Deprecated
public String getErrorAsString()
throws IOException {
return IOUtils.readFully(getErrorStream()).toString();
}
}

View File

@@ -17,7 +17,7 @@ package net.schmizz.sshj.sftp;
import net.schmizz.concurrent.Promise;
public class Request
public final class Request
extends SFTPPacket<Request> {
private final PacketType type;

View File

@@ -17,7 +17,7 @@ package net.schmizz.sshj.sftp;
import net.schmizz.sshj.common.Buffer;
public class Response
public final class Response
extends SFTPPacket<Response> {
public static enum StatusCode {

View File

@@ -47,7 +47,7 @@ public class SFTPClient
return engine;
}
public SFTPFileTransfer getFileTansfer() {
public SFTPFileTransfer getFileTransfer() {
return xfer;
}

View File

@@ -48,7 +48,7 @@ public class ConsoleKnownHostsVerifier
}
if (response.equalsIgnoreCase(YES)) {
try {
entries().add(new SimpleEntry(hostname, key));
entries().add(new SimpleEntry(null, hostname, KeyType.fromKey(key), key));
write();
console.printf("Warning: Permanently added '%s' (%s) to the list of known hosts.\n", hostname, type);
} catch (IOException e) {
@@ -60,7 +60,7 @@ public class ConsoleKnownHostsVerifier
}
@Override
protected boolean hostKeyChangedAction(Entry entry, String hostname, PublicKey key) {
protected boolean hostKeyChangedAction(HostEntry entry, String hostname, PublicKey key) {
final KeyType type = KeyType.fromKey(key);
final String fp = SecurityUtils.getFingerprint(key);
final String path = getFile().getAbsolutePath();

View File

@@ -20,6 +20,7 @@ import net.schmizz.sshj.common.Buffer;
import net.schmizz.sshj.common.IOUtils;
import net.schmizz.sshj.common.KeyType;
import net.schmizz.sshj.common.SSHException;
import net.schmizz.sshj.common.SecurityUtils;
import net.schmizz.sshj.transport.mac.HMACSHA1;
import net.schmizz.sshj.transport.mac.MAC;
import org.slf4j.Logger;
@@ -31,7 +32,10 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.RSAPublicKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -44,187 +48,11 @@ import java.util.List;
public class OpenSSHKnownHosts
implements HostKeyVerifier {
public static abstract class Entry {
private KeyType type;
private PublicKey key;
private String sKey;
protected void init(PublicKey key)
throws SSHException {
this.key = key;
this.type = KeyType.fromKey(key);
if (type == KeyType.UNKNOWN)
throw new SSHException("Unknown key type for key: " + key);
}
protected void init(String typeString, String keyString)
throws SSHException {
this.sKey = keyString;
this.type = KeyType.fromString(typeString);
if (type == KeyType.UNKNOWN)
throw new SSHException("Unknown key type: " + typeString);
}
public KeyType getType() {
return type;
}
public PublicKey getKey()
throws IOException {
if (key == null) {
key = new Buffer.PlainBuffer(Base64.decode(sKey)).readPublicKey();
}
return key;
}
protected String getKeyString() {
if (sKey == null) {
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer().putPublicKey(key);
sKey = Base64.encodeBytes(buf.array(), buf.rpos(), buf.available());
}
return sKey;
}
public String getLine() {
final StringBuilder line = new StringBuilder();
line.append(getHostPart());
line.append(" ").append(type.toString());
line.append(" ").append(getKeyString());
return line.toString();
}
@Override
public String toString() {
return "KnownHostsEntry{host=" + getHostPart() + "; type=" + type + "}";
}
protected abstract String getHostPart();
public abstract boolean appliesTo(String host)
throws IOException;
}
public static class SimpleEntry
extends Entry {
private final List<String> hosts;
public SimpleEntry(String host, PublicKey key)
throws SSHException {
this(Arrays.asList(host), key);
}
public SimpleEntry(List<String> hosts, PublicKey key)
throws SSHException {
this.hosts = hosts;
init(key);
}
public SimpleEntry(String line)
throws SSHException {
final String[] parts = line.split(" ");
if (parts.length != 3)
throw new SSHException("Line parts not 3: " + line);
hosts = Arrays.asList(parts[0].split(","));
init(parts[1], parts[2]);
}
@Override
public boolean appliesTo(String host) {
for (String h : hosts)
if (host.equals(h))
return true;
return false;
}
@Override
protected String getHostPart() {
final StringBuilder sb = new StringBuilder();
for (String host : hosts) {
if (sb.length() > 0) // a host already in there
sb.append(",");
sb.append(host);
}
return sb.toString();
}
}
public static class HashedEntry
extends Entry {
private final MAC sha1 = new HMACSHA1();
private String salt;
private byte[] saltyBytes;
private final String hashedHost;
public HashedEntry(String host, PublicKey key)
throws IOException {
{
saltyBytes = new byte[sha1.getBlockSize()];
new java.util.Random().nextBytes(saltyBytes);
}
this.hashedHost = hashHost(host);
init(key);
}
public HashedEntry(String line)
throws IOException {
final String[] parts = line.split(" ");
if (parts.length != 3)
throw new SSHException("Line parts not 3: " + line);
hashedHost = parts[0];
{
final String[] hostParts = hashedHost.split("\\|");
if (hostParts.length != 4)
throw new SSHException("Unrecognized format for hashed hostname");
salt = hostParts[2];
}
init(parts[1], parts[2]);
}
@Override
public boolean appliesTo(String host)
throws IOException {
return hashedHost.equals(hashHost(host));
}
private String hashHost(String host)
throws IOException {
sha1.init(getSaltyBytes());
return "|1|" + getSalt() + "|" + Base64.encodeBytes(sha1.doFinal(host.getBytes(IOUtils.UTF8)));
}
private byte[] getSaltyBytes()
throws IOException {
if (saltyBytes == null) {
saltyBytes = Base64.decode(salt);
}
return saltyBytes;
}
private String getSalt() {
if (salt == null) {
salt = Base64.encodeBytes(saltyBytes);
}
return salt;
}
@Override
protected String getHostPart() {
return hashedHost;
}
}
private static final Logger LOG = LoggerFactory.getLogger(OpenSSHKnownHosts.class);
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final File khFile;
protected final List<Entry> entries = new ArrayList<Entry>();
protected final List<HostEntry> entries = new ArrayList<HostEntry>();
public OpenSSHKnownHosts(File khFile)
throws IOException {
@@ -236,7 +64,10 @@ public class OpenSSHKnownHosts
String line;
while ((line = br.readLine()) != null)
try {
entries.add(isHashed(line) ? new HashedEntry(line) : new SimpleEntry(line));
HostEntry entry = EntryFactory.parseEntry(line);
if (entry != null) {
entries.add(entry);
}
} catch (SSHException ignore) {
log.debug("Bad line ({}): {} ", ignore.toString(), line);
}
@@ -253,19 +84,22 @@ public class OpenSSHKnownHosts
@Override
public boolean verify(final String hostname, final int port, final PublicKey key) {
final KeyType type = KeyType.fromKey(key);
if (type == KeyType.UNKNOWN)
return false;
final String adjustedHostname = (port != 22) ? "[" + hostname + "]:" + port : hostname;
for (Entry e : entries)
for (HostEntry e : entries) {
try {
if (e.getType() == type && e.appliesTo(adjustedHostname))
return key.equals(e.getKey()) || hostKeyChangedAction(e, adjustedHostname, key);
if (e.appliesTo(type, adjustedHostname))
return e.verify(key) || hostKeyChangedAction(e, adjustedHostname, key);
} catch (IOException ioe) {
log.error("Error with {}: {}", e, ioe);
return false;
}
}
return hostKeyUnverifiableAction(adjustedHostname, key);
}
@@ -273,12 +107,12 @@ public class OpenSSHKnownHosts
return false;
}
protected boolean hostKeyChangedAction(Entry entry, String hostname, PublicKey key) {
protected boolean hostKeyChangedAction(HostEntry entry, String hostname, PublicKey key) {
log.warn("Host key for `{}` has changed!", hostname);
return false;
}
public List<Entry> entries() {
public List<HostEntry> entries() {
return entries;
}
@@ -288,7 +122,7 @@ public class OpenSSHKnownHosts
throws IOException {
final BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(khFile));
try {
for (Entry entry : entries)
for (HostEntry entry : entries)
bos.write((entry.getLine() + LS).getBytes(IOUtils.UTF8));
} finally {
bos.close();
@@ -300,8 +134,280 @@ public class OpenSSHKnownHosts
return sshDir.exists() ? sshDir : null;
}
public static boolean isHashed(String line) {
return line.startsWith("|1|");
/**
* Each line in these files contains the following fields: markers
* (optional), hostnames, bits, exponent, modulus, comment. The fields are
* separated by spaces.
* <p/>
* The marker is optional, but if it is present then it must be one of
* ``@cert-authority'', to indicate that the line contains a certification
* authority (CA) key, or ``@revoked'', to indicate that the key contained
* on the line is revoked and must not ever be accepted. Only one marker
* should be used on a key line.
* <p/>
* Hostnames is a comma-separated list of patterns (`*' and `?' act as
* wildcards); each pattern in turn is matched against the canonical host
* name (when authenticating a client) or against the user-supplied name
* (when authenticating a server). A pattern may also be preceded by `!' to
* indicate negation: if the host name matches a negated pattern, it is not
* accepted (by that line) even if it matched another pattern on the line.
* A hostname or address may optionally be enclosed within `[' and `]'
* brackets then followed by `:' and a non-standard port number.
* <p/>
* Alternately, hostnames may be stored in a hashed form which hides host
* names and addresses should the file's contents be disclosed. Hashed
* hostnames start with a `|' character. Only one hashed hostname may
* appear on a single line and none of the above negation or wildcard
* operators may be applied.
* <p/>
* Bits, exponent, and modulus are taken directly from the RSA host key;
* they can be obtained, for example, from /etc/ssh/ssh_host_key.pub. The
* optional comment field continues to the end of the line, and is not used.
* <p/>
* Lines starting with `#' and empty lines are ignored as comments.
*/
public static class EntryFactory {
public static HostEntry parseEntry(String line)
throws IOException {
if (isComment(line)) {
return new CommentEntry(line);
}
final String[] split = line.split(" ");
int i = 0;
final Marker marker = Marker.fromString(split[i]);
if (marker != null) {
i++;
}
final String hostnames = split[i++];
final String sType = split[i++];
KeyType type = KeyType.fromString(sType);
PublicKey key;
if (type != KeyType.UNKNOWN) {
final String sKey = split[i++];
key = getKey(sKey);
} else if (isBits(sType)) {
type = KeyType.RSA;
// int bits = Integer.valueOf(sType);
final BigInteger e = new BigInteger(split[i++]);
final BigInteger n = new BigInteger(split[i++]);
try {
final KeyFactory keyFactory = SecurityUtils.getKeyFactory("RSA");
key = keyFactory.generatePublic(new RSAPublicKeySpec(n, e));
} catch (Exception ex) {
LOG.error("Error reading entry `{}`, could not create key", line, ex);
return null;
}
} else {
LOG.error("Error reading entry `{}`, could not determine type", line);
return null;
}
if (isHashed(hostnames)) {
return new HashedEntry(marker, hostnames, type, key);
} else {
return new SimpleEntry(marker, hostnames, type, key);
}
}
private static PublicKey getKey(String sKey)
throws IOException {
return new Buffer.PlainBuffer(Base64.decode(sKey)).readPublicKey();
}
private static boolean isBits(String type) {
try {
Integer.parseInt(type);
return true;
} catch (NumberFormatException e) {
return false;
}
}
private static boolean isComment(String line) {
return line.isEmpty() || line.startsWith("#");
}
public static boolean isHashed(String line) {
return line.startsWith("|1|");
}
}
public interface HostEntry {
boolean appliesTo(KeyType type, String host)
throws IOException;
boolean verify(PublicKey key)
throws IOException;
String getLine();
}
public static class CommentEntry
implements HostEntry {
private final String comment;
public CommentEntry(String comment) {
this.comment = comment;
}
@Override
public boolean appliesTo(KeyType type, String host) {
return false;
}
@Override
public boolean verify(PublicKey key) {
return false;
}
@Override
public String getLine() {
return comment;
}
}
public static abstract class AbstractEntry
implements HostEntry {
protected final OpenSSHKnownHosts.Marker marker;
protected final KeyType type;
protected final PublicKey key;
public AbstractEntry(Marker marker, KeyType type, PublicKey key) {
this.marker = marker;
this.type = type;
this.key = key;
}
@Override
public boolean verify(PublicKey key)
throws IOException {
return key.equals(this.key) && marker != Marker.REVOKED;
}
public String getLine() {
final StringBuilder line = new StringBuilder();
if (marker != null) line.append(marker.getMarkerString()).append(" ");
line.append(getHostPart());
line.append(" ").append(type.toString());
line.append(" ").append(getKeyString());
return line.toString();
}
private String getKeyString() {
final Buffer.PlainBuffer buf = new Buffer.PlainBuffer().putPublicKey(key);
return Base64.encodeBytes(buf.array(), buf.rpos(), buf.available());
}
protected abstract String getHostPart();
}
public static class SimpleEntry
extends AbstractEntry {
private final List<String> hosts;
private final String hostnames;
public SimpleEntry(Marker marker, String hostnames, KeyType type, PublicKey key) {
super(marker, type, key);
this.hostnames = hostnames;
hosts = Arrays.asList(hostnames.split(","));
}
@Override
protected String getHostPart() {
return hostnames;
}
@Override
public boolean appliesTo(KeyType type, String host)
throws IOException {
return type == this.type && hostnames.contains(host);
}
}
public static class HashedEntry
extends AbstractEntry {
private final MAC sha1 = new HMACSHA1();
private final String hashedHost;
private final String salt;
private byte[] saltyBytes;
public HashedEntry(Marker marker, String hash, KeyType type, PublicKey key)
throws SSHException {
super(marker, type, key);
this.hashedHost = hash;
{
final String[] hostParts = hashedHost.split("\\|");
if (hostParts.length != 4)
throw new SSHException("Unrecognized format for hashed hostname");
salt = hostParts[2];
}
}
@Override
public boolean appliesTo(KeyType type, String host)
throws IOException {
return this.type == type && hashedHost.equals(hashHost(host));
}
private String hashHost(String host)
throws IOException {
sha1.init(getSaltyBytes());
return "|1|" + salt + "|" + Base64.encodeBytes(sha1.doFinal(host.getBytes(IOUtils.UTF8)));
}
private byte[] getSaltyBytes()
throws IOException {
if (saltyBytes == null) {
saltyBytes = Base64.decode(salt);
}
return saltyBytes;
}
@Override
public String getLine() {
return null;
}
@Override
protected String getHostPart() {
return hashedHost;
}
}
public enum Marker {
CA_CERT("@cert-authority"),
REVOKED("@revoked");
private final String sMarker;
Marker(String sMarker) {
this.sMarker = sMarker;
}
public String getMarkerString() {
return sMarker;
}
public static Marker fromString(String str) {
for (Marker m: values())
if (m.sMarker.equals(str))
return m;
return null;
}
}
}

View File

@@ -49,7 +49,7 @@ public interface UserAuth {
* Returns the authentication banner (if any). In some cases this is available even before the first authentication
* request has been made.
*
* @return the banner, or {@code null} if none was received
* @return the banner, or an empty string if none was received
*/
String getBanner();

View File

@@ -17,15 +17,23 @@ package net.schmizz.sshj.transport.verification;
import net.schmizz.sshj.util.KeyUtil;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.UnknownHostException;
import java.security.GeneralSecurityException;
import java.security.PublicKey;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
public class OpenSSHKnownHostsTest {
@@ -33,23 +41,53 @@ public class OpenSSHKnownHostsTest {
// BasicConfigurator.configure(new ConsoleAppender(new PatternLayout("%d [%-15.15t] %-5p %-30.30c{1} - %m%n")));
// }
private OpenSSHKnownHosts kh;
@Rule
public TemporaryFolder temp = new TemporaryFolder();
@Before
public void setUp()
throws IOException, GeneralSecurityException {
kh = new OpenSSHKnownHosts(new File("src/test/resources/known_hosts"));
// kh = new OpenSSHKnownHosts(new File("src/test/resources/known_hosts"));
}
@Test
public void testLocalhostEntry()
throws UnknownHostException, GeneralSecurityException {
public File writeKnownHosts(String line) throws IOException {
File known_hosts = temp.newFile("known_hosts");
FileWriter fileWriter = new FileWriter(known_hosts);
BufferedWriter writer = new BufferedWriter(fileWriter);
writer.write(line);
writer.write("\r\n");
writer.flush();
writer.close();
return known_hosts;
}
}
@Test
public void shouldAddCommentForEmptyLine() throws IOException {
File file = writeKnownHosts("");
OpenSSHKnownHosts openSSHKnownHosts = new OpenSSHKnownHosts(file);
assertThat(openSSHKnownHosts.entries().size(), equalTo(1));
assertThat(openSSHKnownHosts.entries().get(0), instanceOf(OpenSSHKnownHosts.CommentEntry.class));
}
@Test
public void shouldAddCommentForCommentLine() throws IOException {
File file = writeKnownHosts("# this is a comment");
OpenSSHKnownHosts openSSHKnownHosts = new OpenSSHKnownHosts(file);
assertThat(openSSHKnownHosts.entries().size(), equalTo(1));
assertThat(openSSHKnownHosts.entries().get(0), instanceOf(OpenSSHKnownHosts.CommentEntry.class));
}
//
// @Test
// public void testLocalhostEntry()
// throws UnknownHostException, GeneralSecurityException {
//
// }
//
@Test
public void testSchmizzEntry()
throws UnknownHostException, GeneralSecurityException {
throws IOException, GeneralSecurityException {
OpenSSHKnownHosts kh = new OpenSSHKnownHosts(new File("src/test/resources/known_hosts"));
final PublicKey key = KeyUtil
.newRSAPublicKey(
"e8ff4797075a861db9d2319960a836b2746ada3da514955d2921f2c6a6c9895cbd557f604e43772b6303e3cab2ad82d83b21acdef4edb72524f9c2bef893335115acacfe2989bcbb2e978e4fedc8abc090363e205d975c1fdc35e55ba4daa4b5d5ab7a22c40f547a4a0fd1c683dfff10551c708ff8c34ea4e175cb9bf2313865308fa23601e5a610e2f76838be7ded3b4d3a2c49d2d40fa20db51d1cc8ab20d330bb0dadb88b1a12853f0ecb7c7632947b098dcf435a54566bcf92befd55e03ee2a57d17524cd3d59d6e800c66059067e5eb6edb81946b3286950748240ec9afa4389f9b62bc92f94ec0fba9e64d6dc2f455f816016a4c5f3d507382ed5d3365",

View File

@@ -1,3 +1,4 @@
schmizz.net,69.163.155.180 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6P9Hlwdahh250jGZYKg2snRq2j2lFJVdKSHyxqbJiVy9VX9gTkN3K2MD48qyrYLYOyGs3vTttyUk+cK++JMzURWsrP4piby7LpeOT+3Iq8CQNj4gXZdcH9w15Vuk2qS11at6IsQPVHpKD9HGg9//EFUccI/4w06k4XXLm/IxOGUwj6I2AeWmEOL3aDi+fe07TTosSdLUD6INtR0cyKsg0zC7Da24ixoShT8Oy3x2MpR7CY3PQ1pUVmvPkr79VeA+4qV9F1JM09WdboAMZgWQZ+XrbtuBlGsyhpUHSCQOya+kOJ+bYryS+U7A+6nmTW3C9FX4FgFqTF89UHOC7V0zZQ==
Above we have a plain line, Below we have a hashed line, This is a garbage line.
# Above we have a plain line, Below we have a hashed line, Last is a v1 line, This is a garbage line.
|1|dy7xSefq6NmJms6AzANG3w45W28=|SSCTlHs4pZbc2uaRoPvjyEAHE1g= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAu64GJcCkdtckPGt8uKTyhG1ShT1Np1kh10eE49imQ4Nh9Y/IrSPzDtYUAazQ88ABc2NffuOKkdn2qtUwZ1ulfcdNfN3oTim3BiVHqa041pKG0L+onQe8Bo+CaG5KBLy/C24eNGM9EcfQvDQOnq1eD3lnR/l8fFckldzjfxZgar0yT9Bb3pwp50oN+1wSEINJEHOgMIW8kZBQmyNr/B+b7yX+Y1s1vuYIP/i4WimCVmkdi9G87Ga8w7GxKalRD2QOG6Xms2YWRQDN6M/MOn4tda3EKolbWkctEWcQf/PcVJffTH4Wv5f0RjVyrQv4ha4FZcNAv6RkRd9WkiCsiTKioQ==
test.com,1.1.1.1 2048 35 22017496617994656680820635966392838863613340434802393112245951008866692373218840197754553998457793202561151141246686162285550121243768846314646395880632789308110750881198697743542374668273149584280424505890648953477691795864456749782348425425954366277600319096366690719901119774784695056100331902394094537054256611668966698242432417382422091372756244612839068092471592121759862971414741954991375710930168229171638843329213652899594987626853020377726482288618521941129157643483558764875338089684351824791983007780922947554898825663693324944982594850256042689880090306493029526546183035567296830604572253312294059766327