2 * Copyright (c) 2006-2011 Christian Plattner. All rights reserved.
3 * Please refer to the LICENSE.txt for licensing details.
8 import java.io.CharArrayWriter;
10 import java.net.Socket;
11 import java.io.FileReader;
12 import java.io.IOException;
13 import java.net.InetSocketAddress;
14 import java.net.SocketTimeoutException;
15 import java.security.SecureRandom;
16 import java.util.List;
17 import java.util.Vector;
19 import ch.ethz.ssh2.auth.AuthenticationManager;
20 import ch.ethz.ssh2.channel.ChannelManager;
21 import ch.ethz.ssh2.crypto.CryptoWishList;
22 import ch.ethz.ssh2.crypto.cipher.BlockCipherFactory;
23 import ch.ethz.ssh2.crypto.digest.MAC;
24 import ch.ethz.ssh2.packets.PacketIgnore;
25 import ch.ethz.ssh2.transport.KexManager;
26 import ch.ethz.ssh2.transport.TransportManager;
27 import ch.ethz.ssh2.util.TimeoutService;
28 import ch.ethz.ssh2.util.TimeoutService.TimeoutToken;
31 * A <code>Connection</code> is used to establish an encrypted TCP/IP
32 * connection to a SSH-2 server.
36 * <li>creates a {@link #Connection(String) Connection} object.</li>
37 * <li>calls the {@link #connect() connect()} method.</li>
38 * <li>calls some of the authentication methods (e.g., {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}).</li>
39 * <li>calls one or several times the {@link #openSession() openSession()} method.</li>
40 * <li>finally, one must close the connection and release resources with the {@link #close() close()} method.</li>
43 * @author Christian Plattner
44 * @version $Id: Connection.java 69 2013-08-09 06:39:56Z dkocher@sudo.ch $
47 public class Connection
50 * The identifier presented to the SSH-2 server. This is the same
51 * as the "softwareversion" defined in RFC 4253.
53 * <b>NOTE: As per the RFC, the "softwareversion" string MUST consist of printable
54 * US-ASCII characters, with the exception of whitespace characters and the minus sign (-).</b>
56 private String softwareversion = String.format("Ganymed_%s", Version.getSpecification());
58 /* Will be used to generate all random data needed for the current connection.
59 * Note: SecureRandom.nextBytes() is thread safe.
62 private SecureRandom generator;
64 private Socket precreatedSocket;
66 public Connection(Socket socket) {
67 this.precreatedSocket = socket;
68 this.hostname = socket.getInetAddress().getHostName();
69 this.port = socket.getPort();
73 * Unless you know what you are doing, you will never need this.
75 * @return The list of supported cipher algorithms by this implementation.
77 public static synchronized String[] getAvailableCiphers()
79 return BlockCipherFactory.getDefaultCipherList();
83 * Unless you know what you are doing, you will never need this.
85 * @return The list of supported MAC algorthims by this implementation.
87 public static synchronized String[] getAvailableMACs()
89 return MAC.getMacList();
93 * Unless you know what you are doing, you will never need this.
95 * @return The list of supported server host key algorthims by this implementation.
97 public static synchronized String[] getAvailableServerHostKeyAlgorithms()
99 return KexManager.getDefaultServerHostkeyAlgorithmList();
102 private AuthenticationManager am;
104 private boolean authenticated = false;
105 private ChannelManager cm;
107 private CryptoWishList cryptoWishList = new CryptoWishList();
109 private DHGexParameters dhgexpara = new DHGexParameters();
111 private final String hostname;
113 private final int port;
115 private TransportManager tm;
117 private boolean tcpNoDelay = false;
119 private ProxyData proxyData = null;
121 private List<ConnectionMonitor> connectionMonitors = new Vector<ConnectionMonitor>();
124 * Prepares a fresh <code>Connection</code> object which can then be used
125 * to establish a connection to the specified SSH-2 server.
127 * Same as {@link #Connection(String, int) Connection(hostname, 22)}.
129 * @param hostname the hostname of the SSH-2 server.
131 public Connection(String hostname)
137 * Prepares a fresh <code>Connection</code> object which can then be used
138 * to establish a connection to the specified SSH-2 server.
141 * the host where we later want to connect to.
143 * port on the server, normally 22.
145 public Connection(String hostname, int port)
147 this.hostname = hostname;
152 * Prepares a fresh <code>Connection</code> object which can then be used
153 * to establish a connection to the specified SSH-2 server.
156 * the host where we later want to connect to.
158 * port on the server, normally 22.
159 * @param softwareversion
160 * Allows you to set a custom "softwareversion" string as defined in RFC 4253.
161 * <b>NOTE: As per the RFC, the "softwareversion" string MUST consist of printable
162 * US-ASCII characters, with the exception of whitespace characters and the minus sign (-).</b>
164 public Connection(String hostname, int port, String softwareversion)
166 this.hostname = hostname;
168 this.softwareversion = softwareversion;
172 * After a successful connect, one has to authenticate oneself. This method
173 * is based on DSA (it uses DSA to sign a challenge sent by the server).
175 * If the authentication phase is complete, <code>true</code> will be
176 * returned. If the server does not accept the request (or if further
177 * authentication steps are needed), <code>false</code> is returned and
178 * one can retry either by using this or any other authentication method
179 * (use the <code>getRemainingAuthMethods</code> method to get a list of
180 * the remaining possible methods).
183 * A <code>String</code> holding the username.
185 * A <code>String</code> containing the DSA private key of the
186 * user in OpenSSH key format (PEM, you can't miss the
187 * "-----BEGIN DSA PRIVATE KEY-----" tag). The string may contain
190 * If the PEM string is 3DES encrypted ("DES-EDE3-CBC"), then you
191 * must specify the password. Otherwise, this argument will be
192 * ignored and can be set to <code>null</code>.
194 * @return whether the connection is now authenticated.
195 * @throws IOException
197 * @deprecated You should use one of the {@link #authenticateWithPublicKey(String, File, String) authenticateWithPublicKey()}
198 * methods, this method is just a wrapper for it and will
199 * disappear in future builds.
202 public synchronized boolean authenticateWithDSA(String user, String pem, String password) throws IOException
205 throw new IllegalStateException("Connection is not established!");
208 throw new IllegalStateException("Connection is already authenticated!");
211 am = new AuthenticationManager(tm);
214 cm = new ChannelManager(tm);
217 throw new IllegalArgumentException("user argument is null");
220 throw new IllegalArgumentException("pem argument is null");
222 authenticated = am.authenticatePublicKey(user, pem.toCharArray(), password, getOrCreateSecureRND());
224 return authenticated;
228 * A wrapper that calls {@link #authenticateWithKeyboardInteractive(String, String[], InteractiveCallback)
229 * authenticateWithKeyboardInteractivewith} a <code>null</code> submethod list.
232 * A <code>String</code> holding the username.
234 * An <code>InteractiveCallback</code> which will be used to
235 * determine the responses to the questions asked by the server.
236 * @return whether the connection is now authenticated.
237 * @throws IOException
239 public synchronized boolean authenticateWithKeyboardInteractive(String user, InteractiveCallback cb)
242 return authenticateWithKeyboardInteractive(user, null, cb);
246 * After a successful connect, one has to authenticate oneself. This method
247 * is based on "keyboard-interactive", specified in
248 * draft-ietf-secsh-auth-kbdinteract-XX. Basically, you have to define a
249 * callback object which will be feeded with challenges generated by the
250 * server. Answers are then sent back to the server. It is possible that the
251 * callback will be called several times during the invocation of this
252 * method (e.g., if the server replies to the callback's answer(s) with
253 * another challenge...)
255 * If the authentication phase is complete, <code>true</code> will be
256 * returned. If the server does not accept the request (or if further
257 * authentication steps are needed), <code>false</code> is returned and
258 * one can retry either by using this or any other authentication method
259 * (use the <code>getRemainingAuthMethods</code> method to get a list of
260 * the remaining possible methods).
262 * Note: some SSH servers advertise "keyboard-interactive", however, any
263 * interactive request will be denied (without having sent any challenge to
267 * A <code>String</code> holding the username.
269 * An array of submethod names, see
270 * draft-ietf-secsh-auth-kbdinteract-XX. May be <code>null</code>
271 * to indicate an empty list.
273 * An <code>InteractiveCallback</code> which will be used to
274 * determine the responses to the questions asked by the server.
276 * @return whether the connection is now authenticated.
277 * @throws IOException
279 public synchronized boolean authenticateWithKeyboardInteractive(String user, String[] submethods,
280 InteractiveCallback cb) throws IOException
283 throw new IllegalArgumentException("Callback may not ne NULL!");
286 throw new IllegalStateException("Connection is not established!");
289 throw new IllegalStateException("Connection is already authenticated!");
292 am = new AuthenticationManager(tm);
295 cm = new ChannelManager(tm);
298 throw new IllegalArgumentException("user argument is null");
300 authenticated = am.authenticateInteractive(user, submethods, cb);
302 return authenticated;
306 * After a successful connect, one has to authenticate oneself. This method
307 * sends username and password to the server.
309 * If the authentication phase is complete, <code>true</code> will be
310 * returned. If the server does not accept the request (or if further
311 * authentication steps are needed), <code>false</code> is returned and
312 * one can retry either by using this or any other authentication method
313 * (use the <code>getRemainingAuthMethods</code> method to get a list of
314 * the remaining possible methods).
316 * Note: if this method fails, then please double-check that it is actually
317 * offered by the server (use {@link #getRemainingAuthMethods(String) getRemainingAuthMethods()}.
319 * Often, password authentication is disabled, but users are not aware of it.
320 * Many servers only offer "publickey" and "keyboard-interactive". However,
321 * even though "keyboard-interactive" *feels* like password authentication
322 * (e.g., when using the putty or openssh clients) it is *not* the same mechanism.
326 * @return if the connection is now authenticated.
327 * @throws IOException
329 public synchronized boolean authenticateWithPassword(String user, String password) throws IOException
332 throw new IllegalStateException("Connection is not established!");
335 throw new IllegalStateException("Connection is already authenticated!");
338 am = new AuthenticationManager(tm);
341 cm = new ChannelManager(tm);
344 throw new IllegalArgumentException("user argument is null");
346 if (password == null)
347 throw new IllegalArgumentException("password argument is null");
349 authenticated = am.authenticatePassword(user, password);
351 return authenticated;
355 * After a successful connect, one has to authenticate oneself.
356 * This method can be used to explicitly use the special "none"
357 * authentication method (where only a username has to be specified).
359 * Note 1: The "none" method may always be tried by clients, however as by
360 * the specs, the server will not explicitly announce it. In other words,
361 * the "none" token will never show up in the list returned by
362 * {@link #getRemainingAuthMethods(String)}.
364 * Note 2: no matter which one of the authenticateWithXXX() methods
365 * you call, the library will always issue exactly one initial "none"
366 * authentication request to retrieve the initially allowed list of
367 * authentication methods by the server. Please read RFC 4252 for the
370 * If the authentication phase is complete, <code>true</code> will be
371 * returned. If further authentication steps are needed, <code>false</code>
372 * is returned and one can retry by any other authentication method
373 * (use the <code>getRemainingAuthMethods</code> method to get a list of
374 * the remaining possible methods).
377 * @return if the connection is now authenticated.
378 * @throws IOException
380 public synchronized boolean authenticateWithNone(String user) throws IOException
383 throw new IllegalStateException("Connection is not established!");
386 throw new IllegalStateException("Connection is already authenticated!");
389 am = new AuthenticationManager(tm);
392 cm = new ChannelManager(tm);
395 throw new IllegalArgumentException("user argument is null");
397 /* Trigger the sending of the PacketUserauthRequestNone packet */
398 /* (if not already done) */
400 authenticated = am.authenticateNone(user);
402 return authenticated;
406 * After a successful connect, one has to authenticate oneself.
407 * The authentication method "publickey" works by signing a challenge
408 * sent by the server. The signature is either DSA or RSA based - it
409 * just depends on the type of private key you specify, either a DSA
410 * or RSA private key in PEM format. And yes, this is may seem to be a
411 * little confusing, the method is called "publickey" in the SSH-2 protocol
412 * specification, however since we need to generate a signature, you
413 * actually have to supply a private key =).
415 * The private key contained in the PEM file may also be encrypted ("Proc-Type: 4,ENCRYPTED").
416 * The library supports DES-CBC and DES-EDE3-CBC encryption, as well
417 * as the more exotic PEM encrpytions AES-128-CBC, AES-192-CBC and AES-256-CBC.
419 * If the authentication phase is complete, <code>true</code> will be
420 * returned. If the server does not accept the request (or if further
421 * authentication steps are needed), <code>false</code> is returned and
422 * one can retry either by using this or any other authentication method
423 * (use the <code>getRemainingAuthMethods</code> method to get a list of
424 * the remaining possible methods).
426 * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
427 * it is not in the expected format. You have to convert it to the OpenSSH
428 * key format by using the "puttygen" tool (can be downloaded from the Putty
429 * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
430 * functionality to get a proper PEM file.
433 * A <code>String</code> holding the username.
434 * @param pemPrivateKey
435 * A <code>char[]</code> containing a DSA or RSA private key of the
436 * user in OpenSSH key format (PEM, you can't miss the
437 * "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
438 * tag). The char array may contain linebreaks/linefeeds.
440 * If the PEM structure is encrypted ("Proc-Type: 4,ENCRYPTED") then
441 * you must specify a password. Otherwise, this argument will be ignored
442 * and can be set to <code>null</code>.
444 * @return whether the connection is now authenticated.
445 * @throws IOException
447 public synchronized boolean authenticateWithPublicKey(String user, char[] pemPrivateKey, String password)
451 throw new IllegalStateException("Connection is not established!");
454 throw new IllegalStateException("Connection is already authenticated!");
457 am = new AuthenticationManager(tm);
460 cm = new ChannelManager(tm);
463 throw new IllegalArgumentException("user argument is null");
465 if (pemPrivateKey == null)
466 throw new IllegalArgumentException("pemPrivateKey argument is null");
468 authenticated = am.authenticatePublicKey(user, pemPrivateKey, password, getOrCreateSecureRND());
470 return authenticated;
474 * A convenience wrapper function which reads in a private key (PEM format, either DSA or RSA)
475 * and then calls <code>authenticateWithPublicKey(String, char[], String)</code>.
477 * NOTE PUTTY USERS: Event though your key file may start with "-----BEGIN..."
478 * it is not in the expected format. You have to convert it to the OpenSSH
479 * key format by using the "puttygen" tool (can be downloaded from the Putty
480 * website). Simply load your key and then use the "Conversions/Export OpenSSH key"
481 * functionality to get a proper PEM file.
484 * A <code>String</code> holding the username.
486 * A <code>File</code> object pointing to a file containing a DSA or RSA
487 * private key of the user in OpenSSH key format (PEM, you can't miss the
488 * "-----BEGIN DSA PRIVATE KEY-----" or "-----BEGIN RSA PRIVATE KEY-----"
491 * If the PEM file is encrypted then you must specify the password.
492 * Otherwise, this argument will be ignored and can be set to <code>null</code>.
494 * @return whether the connection is now authenticated.
495 * @throws IOException
497 public synchronized boolean authenticateWithPublicKey(String user, File pemFile, String password)
501 throw new IllegalArgumentException("pemFile argument is null");
503 char[] buff = new char[256];
505 CharArrayWriter cw = new CharArrayWriter();
507 FileReader fr = new FileReader(pemFile);
511 int len = fr.read(buff);
514 cw.write(buff, 0, len);
519 return authenticateWithPublicKey(user, cw.toCharArray(), password);
523 * Add a {@link ConnectionMonitor} to this connection. Can be invoked at any time,
524 * but it is best to add connection monitors before invoking
525 * <code>connect()</code> to avoid glitches (e.g., you add a connection monitor after
526 * a successful connect(), but the connection has died in the mean time. Then,
527 * your connection monitor won't be notified.)
529 * You can add as many monitors as you like. If a monitor has already been added, then
530 * this method does nothing.
532 * @see ConnectionMonitor
534 * @param cmon An object implementing the {@link ConnectionMonitor} interface.
536 public synchronized void addConnectionMonitor(ConnectionMonitor cmon)
539 throw new IllegalArgumentException("cmon argument is null");
541 if (!connectionMonitors.contains(cmon))
543 connectionMonitors.add(cmon);
546 tm.setConnectionMonitors(connectionMonitors);
551 * Remove a {@link ConnectionMonitor} from this connection.
554 * @return whether the monitor could be removed
556 public synchronized boolean removeConnectionMonitor(ConnectionMonitor cmon)
559 throw new IllegalArgumentException("cmon argument is null");
561 boolean existed = connectionMonitors.remove(cmon);
564 tm.setConnectionMonitors(connectionMonitors);
570 * Close the connection to the SSH-2 server. All assigned sessions will be
571 * closed, too. Can be called at any time. Don't forget to call this once
572 * you don't need a connection anymore - otherwise the receiver thread may
575 public synchronized void close()
577 Throwable t = new Throwable("Closed due to user request.");
581 public synchronized void close(Throwable t, boolean hard)
584 cm.closeAllChannels();
588 tm.close(t, hard == false);
593 authenticated = false;
597 * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(null, 0, 0)}.
599 * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
600 * @throws IOException
602 public synchronized ConnectionInfo connect() throws IOException
604 return connect(null, 0, 0);
608 * Same as {@link #connect(ServerHostKeyVerifier, int, int) connect(verifier, 0, 0)}.
610 * @return see comments for the {@link #connect(ServerHostKeyVerifier, int, int) connect(ServerHostKeyVerifier, int, int)} method.
611 * @throws IOException
613 public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier) throws IOException
615 return connect(verifier, 0, 0);
619 * Connect to the SSH-2 server and, as soon as the server has presented its
620 * host key, use the {@link ServerHostKeyVerifier#verifyServerHostKey(String,
621 * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()}
622 * method of the <code>verifier</code> to ask for permission to proceed.
623 * If <code>verifier</code> is <code>null</code>, then any host key will be
624 * accepted - this is NOT recommended, since it makes man-in-the-middle attackes
625 * VERY easy (somebody could put a proxy SSH server between you and the real server).
627 * Note: The verifier will be called before doing any crypto calculations
628 * (i.e., diffie-hellman). Therefore, if you don't like the presented host key then
629 * no CPU cycles are wasted (and the evil server has less information about us).
631 * However, it is still possible that the server presented a fake host key: the server
632 * cheated (typically a sign for a man-in-the-middle attack) and is not able to generate
633 * a signature that matches its host key. Don't worry, the library will detect such
634 * a scenario later when checking the signature (the signature cannot be checked before
635 * having completed the diffie-hellman exchange).
637 * Note 2: The {@link ServerHostKeyVerifier#verifyServerHostKey(String,
638 * int, String, byte[]) ServerHostKeyVerifier.verifyServerHostKey()} method
639 * will *NOT* be called from the current thread, the call is being made from a
640 * background thread (there is a background dispatcher thread for every
641 * established connection).
643 * Note 3: This method will block as long as the key exchange of the underlying connection
644 * has not been completed (and you have not specified any timeouts).
646 * Note 4: If you want to re-use a connection object that was successfully connected,
647 * then you must call the {@link #close()} method before invoking <code>connect()</code> again.
650 * An object that implements the
651 * {@link ServerHostKeyVerifier} interface. Pass <code>null</code>
652 * to accept any server host key - NOT recommended.
654 * @param connectTimeout
655 * Connect the underlying TCP socket to the server with the given timeout
656 * value (non-negative, in milliseconds). Zero means no timeout. If a proxy is being
657 * used (see {@link #setProxyData(ProxyData)}), then this timeout is used for the
658 * connection establishment to the proxy.
661 * Timeout for complete connection establishment (non-negative,
662 * in milliseconds). Zero means no timeout. The timeout counts from the
663 * moment you invoke the connect() method and is cancelled as soon as the
664 * first key-exchange round has finished. It is possible that
665 * the timeout event will be fired during the invocation of the
666 * <code>verifier</code> callback, but it will only have an effect after
667 * the <code>verifier</code> returns.
669 * @return A {@link ConnectionInfo} object containing the details of
670 * the established connection.
672 * @throws IOException
673 * If any problem occurs, e.g., the server's host key is not
674 * accepted by the <code>verifier</code> or there is problem during
675 * the initial crypto setup (e.g., the signature sent by the server is wrong).
677 * In case of a timeout (either connectTimeout or kexTimeout)
678 * a SocketTimeoutException is thrown.
680 * An exception may also be thrown if the connection was already successfully
681 * connected (no matter if the connection broke in the mean time) and you invoke
682 * <code>connect()</code> again without having called {@link #close()} first.
684 * If a HTTP proxy is being used and the proxy refuses the connection,
685 * then a {@link HTTPProxyException} may be thrown, which
686 * contains the details returned by the proxy. If the proxy is buggy and does
687 * not return a proper HTTP response, then a normal IOException is thrown instead.
689 public synchronized ConnectionInfo connect(ServerHostKeyVerifier verifier, int connectTimeout, int kexTimeout)
692 final class TimeoutState
694 boolean isCancelled = false;
695 boolean timeoutSocketClosed = false;
699 throw new IOException("Connection to " + hostname + " is already in connected state!");
701 if (connectTimeout < 0)
702 throw new IllegalArgumentException("connectTimeout must be non-negative!");
705 throw new IllegalArgumentException("kexTimeout must be non-negative!");
707 final TimeoutState state = new TimeoutState();
709 tm = new TransportManager();
710 tm.setSoTimeout(connectTimeout);
711 tm.setConnectionMonitors(connectionMonitors);
713 /* Make sure that the runnable below will observe the new value of "tm"
714 * and "state" (the runnable will be executed in a different thread, which
715 * may be already running, that is why we need a memory barrier here).
716 * See also the comment in Channel.java if you
717 * are interested in the details.
719 * OKOK, this is paranoid since adding the runnable to the todo list
720 * of the TimeoutService will ensure that all writes have been flushed
721 * before the Runnable reads anything
722 * (there is a synchronized block in TimeoutService.addTimeoutHandler).
727 /* We could actually synchronize on anything. */
732 TimeoutToken token = null;
736 final Runnable timeoutHandler = new Runnable()
742 if (state.isCancelled)
744 state.timeoutSocketClosed = true;
745 tm.close(new SocketTimeoutException("The connect timeout expired"), false);
750 long timeoutHorizont = System.currentTimeMillis() + kexTimeout;
752 token = TimeoutService.addTimeoutHandler(timeoutHorizont, timeoutHandler);
758 if (precreatedSocket != null) {
759 tm.clientInit(precreatedSocket, softwareversion, cryptoWishList, verifier, dhgexpara,
760 getOrCreateSecureRND());
762 tm.clientInit(hostname, port, softwareversion, cryptoWishList, verifier, dhgexpara, connectTimeout,
763 getOrCreateSecureRND(), proxyData);
766 catch (SocketTimeoutException se)
768 throw (SocketTimeoutException) new SocketTimeoutException(
769 "The connect() operation on the socket timed out.").initCause(se);
772 tm.setTcpNoDelay(tcpNoDelay);
774 /* Wait until first KEX has finished */
776 ConnectionInfo ci = tm.getConnectionInfo(1);
778 /* Now try to cancel the timeout, if needed */
782 TimeoutService.cancelTimeoutHandler(token);
784 /* Were we too late? */
788 if (state.timeoutSocketClosed)
789 throw new IOException("This exception will be replaced by the one below =)");
790 /* Just in case the "cancelTimeoutHandler" invocation came just a little bit
791 * too late but the handler did not enter the semaphore yet - we can
794 state.isCancelled = true;
800 catch (SocketTimeoutException ste)
804 catch (IOException e1)
806 /* This will also invoke any registered connection monitors */
807 close(new Throwable("There was a problem during connect."), false);
811 /* Show a clean exception, not something like "the socket is closed!?!" */
812 if (state.timeoutSocketClosed)
813 throw new SocketTimeoutException("The kexTimeout (" + kexTimeout + " ms) expired.");
816 /* Do not wrap a HTTPProxyException */
817 if (e1 instanceof HTTPProxyException)
820 throw (IOException) new IOException("There was a problem while connecting to " + hostname + ":" + port)
826 * Creates a new {@link LocalPortForwarder}.
827 * A <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive at a local
828 * port via the secure tunnel to another host (which may or may not be
829 * identical to the remote SSH-2 server).
831 * This method must only be called after one has passed successfully the authentication step.
832 * There is no limit on the number of concurrent forwardings.
834 * @param local_port the local port the LocalPortForwarder shall bind to.
835 * @param host_to_connect target address (IP or hostname)
836 * @param port_to_connect target port
837 * @return A {@link LocalPortForwarder} object.
838 * @throws IOException
840 public synchronized LocalPortForwarder createLocalPortForwarder(int local_port, String host_to_connect,
841 int port_to_connect) throws IOException
844 throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
847 throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
849 return new LocalPortForwarder(cm, local_port, host_to_connect, port_to_connect);
853 * Creates a new {@link LocalPortForwarder}.
854 * A <code>LocalPortForwarder</code> forwards TCP/IP connections that arrive at a local
855 * port via the secure tunnel to another host (which may or may not be
856 * identical to the remote SSH-2 server).
858 * This method must only be called after one has passed successfully the authentication step.
859 * There is no limit on the number of concurrent forwardings.
861 * @param addr specifies the InetSocketAddress where the local socket shall be bound to.
862 * @param host_to_connect target address (IP or hostname)
863 * @param port_to_connect target port
864 * @return A {@link LocalPortForwarder} object.
865 * @throws IOException
867 public synchronized LocalPortForwarder createLocalPortForwarder(InetSocketAddress addr, String host_to_connect,
868 int port_to_connect) throws IOException
871 throw new IllegalStateException("Cannot forward ports, you need to establish a connection first.");
874 throw new IllegalStateException("Cannot forward ports, connection is not authenticated.");
876 return new LocalPortForwarder(cm, addr, host_to_connect, port_to_connect);
880 * Creates a new {@link LocalStreamForwarder}.
881 * A <code>LocalStreamForwarder</code> manages an Input/Outputstream pair
882 * that is being forwarded via the secure tunnel into a TCP/IP connection to another host
883 * (which may or may not be identical to the remote SSH-2 server).
885 * @param host_to_connect
886 * @param port_to_connect
887 * @return A {@link LocalStreamForwarder} object.
888 * @throws IOException
890 public synchronized LocalStreamForwarder createLocalStreamForwarder(String host_to_connect, int port_to_connect)
894 throw new IllegalStateException("Cannot forward, you need to establish a connection first.");
897 throw new IllegalStateException("Cannot forward, connection is not authenticated.");
899 return new LocalStreamForwarder(cm, host_to_connect, port_to_connect);
903 * Create a very basic {@link SCPClient} that can be used to copy
904 * files from/to the SSH-2 server.
906 * Works only after one has passed successfully the authentication step.
907 * There is no limit on the number of concurrent SCP clients.
909 * Note: This factory method will probably disappear in the future.
911 * @return A {@link SCPClient} object.
912 * @throws IOException
914 public synchronized SCPClient createSCPClient() throws IOException
917 throw new IllegalStateException("Cannot create SCP client, you need to establish a connection first.");
920 throw new IllegalStateException("Cannot create SCP client, connection is not authenticated.");
922 return new SCPClient(this);
926 * Force an asynchronous key re-exchange (the call does not block). The
927 * latest values set for MAC, Cipher and DH group exchange parameters will
928 * be used. If a key exchange is currently in progress, then this method has
929 * the only effect that the so far specified parameters will be used for the
930 * next (server driven) key exchange.
932 * Note: This implementation will never start a key exchange (other than the initial one)
933 * unless you or the SSH-2 server ask for it.
935 * @throws IOException
936 * In case of any failure behind the scenes.
938 public synchronized void forceKeyExchange() throws IOException
941 throw new IllegalStateException("You need to establish a connection first.");
943 tm.forceKeyExchange(cryptoWishList, dhgexpara, null, null);
947 * Returns the hostname that was passed to the constructor.
949 * @return the hostname
951 public synchronized String getHostname()
957 * Returns the port that was passed to the constructor.
959 * @return the TCP port
961 public synchronized int getPort()
967 * Returns a {@link ConnectionInfo} object containing the details of
968 * the connection. Can be called as soon as the connection has been
969 * established (successfully connected).
971 * @return A {@link ConnectionInfo} object.
972 * @throws IOException
973 * In case of any failure behind the scenes.
975 public synchronized ConnectionInfo getConnectionInfo() throws IOException
978 throw new IllegalStateException(
979 "Cannot get details of connection, you need to establish a connection first.");
980 return tm.getConnectionInfo(1);
984 * After a successful connect, one has to authenticate oneself. This method
985 * can be used to tell which authentication methods are supported by the
986 * server at a certain stage of the authentication process (for the given
989 * Note 1: the username will only be used if no authentication step was done
990 * so far (it will be used to ask the server for a list of possible
991 * authentication methods by sending the initial "none" request). Otherwise,
992 * this method ignores the user name and returns a cached method list
993 * (which is based on the information contained in the last negative server response).
995 * Note 2: the server may return method names that are not supported by this
998 * After a successful authentication, this method must not be called
1002 * A <code>String</code> holding the username.
1004 * @return a (possibly emtpy) array holding authentication method names.
1005 * @throws IOException
1007 public synchronized String[] getRemainingAuthMethods(String user) throws IOException
1010 throw new IllegalArgumentException("user argument may not be NULL!");
1013 throw new IllegalStateException("Connection is not established!");
1016 throw new IllegalStateException("Connection is already authenticated!");
1019 am = new AuthenticationManager(tm);
1022 cm = new ChannelManager(tm);
1024 return am.getRemainingMethods(user);
1028 * Determines if the authentication phase is complete. Can be called at any
1031 * @return <code>true</code> if no further authentication steps are
1034 public synchronized boolean isAuthenticationComplete()
1036 return authenticated;
1040 * Returns true if there was at least one failed authentication request and
1041 * the last failed authentication request was marked with "partial success"
1042 * by the server. This is only needed in the rare case of SSH-2 server setups
1043 * that cannot be satisfied with a single successful authentication request
1044 * (i.e., multiple authentication steps are needed.)
1046 * If you are interested in the details, then have a look at RFC4252.
1048 * @return if the there was a failed authentication step and the last one
1049 * was marked as a "partial success".
1051 public synchronized boolean isAuthenticationPartialSuccess()
1056 return am.getPartialSuccess();
1060 * Checks if a specified authentication method is available. This method is
1061 * actually just a wrapper for {@link #getRemainingAuthMethods(String)
1062 * getRemainingAuthMethods()}.
1065 * A <code>String</code> holding the username.
1067 * An authentication method name (e.g., "publickey", "password",
1068 * "keyboard-interactive") as specified by the SSH-2 standard.
1069 * @return if the specified authentication method is currently available.
1070 * @throws IOException
1072 public synchronized boolean isAuthMethodAvailable(String user, String method) throws IOException
1075 throw new IllegalArgumentException("method argument may not be NULL!");
1077 String methods[] = getRemainingAuthMethods(user);
1079 for (int i = 0; i < methods.length; i++)
1081 if (methods[i].compareTo(method) == 0)
1088 private SecureRandom getOrCreateSecureRND()
1090 if (generator == null)
1091 generator = new SecureRandom();
1097 * Open a new {@link Session} on this connection. Works only after one has passed
1098 * successfully the authentication step. There is no limit on the number of
1099 * concurrent sessions.
1101 * @return A {@link Session} object.
1102 * @throws IOException
1104 public synchronized Session openSession() throws IOException
1107 throw new IllegalStateException("Cannot open session, you need to establish a connection first.");
1110 throw new IllegalStateException("Cannot open session, connection is not authenticated.");
1112 return new Session(cm, getOrCreateSecureRND());
1116 * Send an SSH_MSG_IGNORE packet. This method will generate a random data attribute
1117 * (length between 0 (invlusive) and 16 (exclusive) bytes, contents are random bytes).
1119 * This method must only be called once the connection is established.
1121 * @throws IOException
1123 public synchronized void sendIgnorePacket() throws IOException
1125 SecureRandom rnd = getOrCreateSecureRND();
1127 byte[] data = new byte[rnd.nextInt(16)];
1128 rnd.nextBytes(data);
1130 sendIgnorePacket(data);
1134 * Send an SSH_MSG_IGNORE packet with the given data attribute.
1136 * This method must only be called once the connection is established.
1138 * @throws IOException
1140 public synchronized void sendIgnorePacket(byte[] data) throws IOException
1143 throw new IllegalArgumentException("data argument must not be null.");
1146 throw new IllegalStateException(
1147 "Cannot send SSH_MSG_IGNORE packet, you need to establish a connection first.");
1149 PacketIgnore pi = new PacketIgnore();
1152 tm.sendMessage(pi.getPayload());
1156 * Removes duplicates from a String array, keeps only first occurence
1157 * of each element. Does not destroy order of elements; can handle nulls.
1158 * Uses a very efficient O(N^2) algorithm =)
1160 * @param list a String array.
1161 * @return a cleaned String array.
1163 private String[] removeDuplicates(String[] list)
1165 if ((list == null) || (list.length < 2))
1168 String[] list2 = new String[list.length];
1172 for (int i = 0; i < list.length; i++)
1174 boolean duplicate = false;
1176 String element = list[i];
1178 for (int j = 0; j < count; j++)
1180 if (((element == null) && (list2[j] == null)) || ((element != null) && (element.equals(list2[j]))))
1190 list2[count++] = list[i];
1193 if (count == list2.length)
1196 String[] tmp = new String[count];
1197 System.arraycopy(list2, 0, tmp, 0, count);
1203 * Unless you know what you are doing, you will never need this.
1207 public synchronized void setClient2ServerCiphers(String[] ciphers)
1209 if ((ciphers == null) || (ciphers.length == 0))
1210 throw new IllegalArgumentException();
1211 ciphers = removeDuplicates(ciphers);
1212 BlockCipherFactory.checkCipherList(ciphers);
1213 cryptoWishList.c2s_enc_algos = ciphers;
1217 * Unless you know what you are doing, you will never need this.
1221 public synchronized void setClient2ServerMACs(String[] macs)
1223 if ((macs == null) || (macs.length == 0))
1224 throw new IllegalArgumentException();
1225 macs = removeDuplicates(macs);
1226 MAC.checkMacList(macs);
1227 cryptoWishList.c2s_mac_algos = macs;
1231 * Sets the parameters for the diffie-hellman group exchange. Unless you
1232 * know what you are doing, you will never need this. Default values are
1233 * defined in the {@link DHGexParameters} class.
1235 * @param dgp {@link DHGexParameters}, non null.
1238 public synchronized void setDHGexParameters(DHGexParameters dgp)
1241 throw new IllegalArgumentException();
1247 * Unless you know what you are doing, you will never need this.
1251 public synchronized void setServer2ClientCiphers(String[] ciphers)
1253 if ((ciphers == null) || (ciphers.length == 0))
1254 throw new IllegalArgumentException();
1255 ciphers = removeDuplicates(ciphers);
1256 BlockCipherFactory.checkCipherList(ciphers);
1257 cryptoWishList.s2c_enc_algos = ciphers;
1261 * Unless you know what you are doing, you will never need this.
1265 public synchronized void setServer2ClientMACs(String[] macs)
1267 if ((macs == null) || (macs.length == 0))
1268 throw new IllegalArgumentException();
1270 macs = removeDuplicates(macs);
1271 MAC.checkMacList(macs);
1272 cryptoWishList.s2c_mac_algos = macs;
1276 * Define the set of allowed server host key algorithms to be used for
1277 * the following key exchange operations.
1279 * Unless you know what you are doing, you will never need this.
1281 * @param algos An array of allowed server host key algorithms.
1282 * SSH-2 defines <code>ssh-dss</code> and <code>ssh-rsa</code>.
1283 * The entries of the array must be ordered after preference, i.e.,
1284 * the entry at index 0 is the most preferred one. You must specify
1285 * at least one entry.
1287 public synchronized void setServerHostKeyAlgorithms(String[] algos)
1289 if ((algos == null) || (algos.length == 0))
1290 throw new IllegalArgumentException();
1292 algos = removeDuplicates(algos);
1293 KexManager.checkServerHostkeyAlgorithmsList(algos);
1294 cryptoWishList.serverHostKeyAlgorithms = algos;
1298 * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm) on the underlying socket.
1300 * Can be called at any time. If the connection has not yet been established
1301 * then the passed value will be stored and set after the socket has been set up.
1302 * The default value that will be used is <code>false</code>.
1304 * @param enable the argument passed to the <code>Socket.setTCPNoDelay()</code> method.
1305 * @throws IOException
1307 public synchronized void setTCPNoDelay(boolean enable) throws IOException
1309 tcpNoDelay = enable;
1312 tm.setTcpNoDelay(enable);
1316 * Used to tell the library that the connection shall be established through a proxy server.
1317 * It only makes sense to call this method before calling the {@link #connect() connect()}
1320 * At the moment, only HTTP proxies are supported.
1322 * Note: This method can be called any number of times. The {@link #connect() connect()}
1323 * method will use the value set in the last preceding invocation of this method.
1325 * @see HTTPProxyData
1327 * @param proxyData Connection information about the proxy. If <code>null</code>, then
1328 * no proxy will be used (non surprisingly, this is also the default).
1330 public synchronized void setProxyData(ProxyData proxyData)
1332 this.proxyData = proxyData;
1336 * Request a remote port forwarding.
1337 * If successful, then forwarded connections will be redirected to the given target address.
1338 * You can cancle a requested remote port forwarding by calling
1339 * {@link #cancelRemotePortForwarding(int) cancelRemotePortForwarding()}.
1341 * A call of this method will block until the peer either agreed or disagreed to your request-
1343 * Note 1: this method typically fails if you
1345 * <li>pass a port number for which the used remote user has not enough permissions (i.e., port
1347 * <li>or pass a port number that is already in use on the remote server</li>
1348 * <li>or if remote port forwarding is disabled on the server.</li>
1351 * Note 2: (from the openssh man page): By default, the listening socket on the server will be
1352 * bound to the loopback interface only. This may be overriden by specifying a bind address.
1353 * Specifying a remote bind address will only succeed if the server's <b>GatewayPorts</b> option
1354 * is enabled (see sshd_config(5)).
1356 * @param bindAddress address to bind to on the server:
1358 * <li>"" means that connections are to be accepted on all protocol families
1359 * supported by the SSH implementation</li>
1360 * <li>"0.0.0.0" means to listen on all IPv4 addresses</li>
1361 * <li>"::" means to listen on all IPv6 addresses</li>
1362 * <li>"localhost" means to listen on all protocol families supported by the SSH
1363 * implementation on loopback addresses only, [RFC3330] and RFC3513]</li>
1364 * <li>"127.0.0.1" and "::1" indicate listening on the loopback interfaces for
1365 * IPv4 and IPv6 respectively</li>
1367 * @param bindPort port number to bind on the server (must be > 0)
1368 * @param targetAddress the target address (IP or hostname)
1369 * @param targetPort the target port
1370 * @throws IOException
1372 public synchronized void requestRemotePortForwarding(String bindAddress, int bindPort, String targetAddress,
1373 int targetPort) throws IOException
1376 throw new IllegalStateException("You need to establish a connection first.");
1379 throw new IllegalStateException("The connection is not authenticated.");
1381 if ((bindAddress == null) || (targetAddress == null) || (bindPort <= 0) || (targetPort <= 0))
1382 throw new IllegalArgumentException();
1384 cm.requestGlobalForward(bindAddress, bindPort, targetAddress, targetPort);
1388 * Cancel an earlier requested remote port forwarding.
1389 * Currently active forwardings will not be affected (e.g., disrupted).
1390 * Note that further connection forwarding requests may be received until
1391 * this method has returned.
1393 * @param bindPort the allocated port number on the server
1394 * @throws IOException if the remote side refuses the cancel request or another low
1395 * level error occurs (e.g., the underlying connection is closed)
1397 public synchronized void cancelRemotePortForwarding(int bindPort) throws IOException
1400 throw new IllegalStateException("You need to establish a connection first.");
1403 throw new IllegalStateException("The connection is not authenticated.");
1405 cm.requestCancelGlobalForward(bindPort);
1409 * Provide your own instance of SecureRandom. Can be used, e.g., if you
1410 * want to seed the used SecureRandom generator manually.
1412 * The SecureRandom instance is used during key exchanges, public key authentication,
1413 * x11 cookie generation and the like.
1415 * @param rnd a SecureRandom instance
1417 public synchronized void setSecureRandom(SecureRandom rnd)
1420 throw new IllegalArgumentException();
1422 this.generator = rnd;