Pre-Requisite
J2SSH Maverick API
Target Host running SSH Server
Apache WebServer
Resources
OpenSSH – Free open source SSH server http://www.openssh.com/
HTTP Proxy Server - Opensource HTTP Server http://httpd.apache.org/
What It Does
This tutorial demonstrates how simple it is to connect to an Ssh Server that is sitting behind a HTTP Proxy Server. A proxy server acts as an intermediary between client and server helping users on a private network get information when they need it but in addition performing secure functions as network address translation, mapping internal network addresses to a single "safe" IP address all in an attempt to keep an internal network safe. Because Proxy Servers operate at the Application layer of the OSI model, proxy servers can do a lot more than just service client requests, all in attempt to minimise illegal traffic. To delve any further into this topic is beyond the scope of this article, this article uses Apache HTTP Server as its Proxy Server further information on the HTTP Apache Server can be found in the Resource section of this article.
In this example the HTTP request will proxy data sent by our Ssh Client to its intended target, the Ssh Server, which is placed behind the our Proxy Server.
How It Works
Setting Up Apache
Apache must already be up and running as a Web Server. If it is not then it is simply a matter of installing and running the application, please refer to the Apache website as directed from the Resource section of this article. In order for Apache Web Server to become a fully working forwarding proxy server we need to include a number of libraries and directives into the start-up process of the application.
Firstly locate the httpd.conf configuration file. This should be located within the default Apache GroupApache2conf directory. The following libraries need to be loaded,
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so
These can be found in Section 1 of the configuration file as commented lines. Simply remove the commenting character # to active these.
NOTE: Section 1 simply refers to the default logical break-up of the configuration content. There should be comments identifying each section.
Next we need to include a number of additional directives to inform Apache we want it to behave as a Forwarding HTTP Proxy Server. To achieve this the following is added to Section 2 of the configuration file.
#
# Forward HTTP Proxy
#
ProxyRequests On
AllowCONNECT 22
ProxyVia On
<Proxy *>
Order deny,allow
Deny from all
Allow from all
</Proxy>
This is an example of basic Forwarding Proxy set-up as you may have gathered, Allow from all, allows all machines access to the proxy server. The AllowCONNECT directive specifies a list of port numbers to which the proxy CONNECT method may connect. By default, only the default https port (443) and the default snews port (563) are enabled. AllowCONNECT directive overrides this default and allow connections to the listed ports.
Once this configuration file has been saved we simply need to restart Apache and it will be running as a forwarding proxy server. Now we are ready to begin working on our client source.
Establishing A Session
The first thing that we carry out in our Ssh Client code HHTPProxyConnect::connectionSetup is establishing a SSH session with the host.
con = SshConnector.getInstance();
ssh = connectionSetup(con);
All necessary SSH communication protocols such as key verification and authentication are maintained within this method. The next few sections delve further into this method.
Retrieve Credentials
The first stage in establishing an Ssh connection is collating all the required passswords and usernames.
System.out.print("Ssh Server Hostname: ");
String hostname = reader.readLine();
int idx = hostname.indexOf(':');
int port = 22;
if(idx > -1) {
port = Integer.parseInt(hostname.substring(idx+1));
hostname = hostname.substring(0, idx);
}
System.out.print("Ssh Server Username [Enter for " + System.getProperty("user.name") + "]: ");
String username = reader.readLine();
if(username==null || username.trim().equals(""))
username = System.getProperty("user.name");
We gather the necessary details for a standard Ssh connection, the hostname of the Ssh Server and the authenticated username that will connect. Becasue we are connecting through a proxy server we need these credentials also.
System.out.print("HTTP Proxy Server Hostname: ");
String proxyHostname = reader.readLine();
idx = proxyHostname.indexOf(':');
int proxyPort = 80;
if(idx > -1) {
proxyPort = Integer.parseInt(hostname.substring(idx+1));
proxyHostname = hostname.substring(0, idx);
}
System.out.print("Proxy Server Username[Enter if not required]: ");
String proxyUsername = reader.readLine();
System.out.print("Proxy Server Password[Enter if not required]: ");
String proxyPassword = reader.readLine();
Get SshConnector
The SshConnector is a singleton instance that establishes a connection with our SSH server, determines which SSH protocol versions are supported and creates an initialised connection ready for authentication. Both SSH1 and SSH2 protocol support is included. The default behaviour for the SshConnector is to select an SSH2 connection where ever possible, this is because SSH1 has known vulnerabilities and should only be used when no SSH2 connection is available. We reiterate this by informing the SshConnector instance we only want force this again with the following line,
con.setSupportedVersions(SshConnector.SSH2);
Host Key Verification
When the client connects to the server, the server supplies its public key for the client to verify. It is good practice to check the supplied key to verify the identity of the server and with Maverick this is possible using the HostKeyVerification interface. Most SSH implementations verify the key against a list of known hosts stored in a special file $HOME/.ssh/known_hosts. Maverick provides an abstract implementation called AbstractKnownHostsKeyVerification to handle the reading of keys from the known_hosts file should you want to prompt the user within your own user interface. For the purposes of this example we implement our own version of this interface that simply prints a finger print of the received key.
A fingerprint is a short value computed from a key. It's analogous to a checksum, verifying that a string of data is unaltered -- in our case, a key.
The code snippet belows has been extracted from our HostKeyVerification implementation called PasswordHostKey.
public boolean verifyHost(String hostname, SshPublicKey key) throws SshException {
System.out.println("The connected host's key (" + key.getAlgorithm() + ") is");
System.out.println(key.getFingerprint());
return true;
}
Back wihtin the
connectionSetup we create an instance of this class and supply it to our
SshConnector instance.
con.getContext(SshConnector.SSH2).setHostKeyVerification(hkv);
Set Preferred Public Key
Next we need a way to create our hash exchange (session key) and verify the signature of the Ssh server for this we inform the SshConnector instance to utilise the Public Key SSH-DSS algorithm.
((Ssh2Context)con.getContext(SshConnector.SSH2)).setPreferredPublicKey(Ssh2Context.PUBLIC_KEY_SSHDSS);
Configuring Message Encryption
Maverick has two methods for this purpose, one to set the Server cipher Ssh2Context::setPreferredCipherSC(String name) and the other to set the client cipher Ssh2Context::setPreferredCipherCS(String name) and a selection of standard ciphers as part of Ssh2Context ready to use:
public static final java.lang.String CIPHER_BLOWFISH_CBC
public static final java.lang.String CIPHER_TRIPLEDES_CBC
In addition to the above we also have the following as part of the com.sshtools.cipher package which are loaded automatically by the context as long as the Maverick-all.jar is available from the classpath and if supported by your Ssh server can be used:
com.sshtools.cipher.AES128Cbc
com.sshtools.cipher.AES128Ctr
com.sshtools.cipher.AES128Cbc
com.sshtools.cipher.AES128Ctr
com.sshtools.cipher.AES192Cbc
com.sshtools.cipher.AES192Ctr
com.sshtools.cipher.AES256Cbc
com.sshtools.cipher.AES256Ctr
com.sshtools.cipher.AESEngine
com.sshtools.cipher.CAST128Cbc
com.sshtools.cipher.CAST5Engine
com.sshtools.cipher.CtrBlockCipher
com.sshtools.cipher.Twofish128Cbc
com.sshtools.cipher.Twofish192Cbc
com.sshtools.cipher.Twofish256Cbc
com.sshtools.cipher.TwofishEngine
For this example we have omitted defining any message algorithm simply because by default the client and Maverick Ssh server utilise the AES128Cbc cipher.
Connect to Host
Now we are ready to connect to our Ssh Server. Under normal direct client server connections we utilise the con.connect(new SocketTransport(hostname, 22), username) method. Maverick has been designed to operate over a transport interface called SshTransport which at its simplist requires just a pair of IO streams. This enables Maverick to operate over any type of current or future communication medium supported by the Java runtime. W supply the hostname of the computer we wish to connect to and the port number to the SocketTransport constructor, which in our case is the default SSH port number 22. The second parameter identifies the username for the connection. However for our connection we are connecting through our HTTP Proxy Server and for this we must use the HttpProxyTransport class. This class provides an SshTransport implementation that can route the connection through our HTTP proxy. To connect the transport we simply call the HttpProxyTransport::connectViaProxy(String, int, String, int, String, String, String), username) method.
ssh = (Ssh2Client)con.connect(HttpProxyTransport.connectViaProxy(hostname, 22,
proxyHostname, proxyPort, proxyUsername, proxyPassword, "J2SSH Maverick"), username);
As you can see this implementation requires a few more additional parameters in particular the HTTP Proxy credentials and a User Agent value, "J2SSH Maverick" that identifies the connecting application to the HTTP Server. This can be left as an empty string if need be as it does not affect the workings of class.
When this is executed the SshConnector will try and establish a connection to the SshServer. Once a connection has been established the method returns an instance of the SshClient interface. This interface provides a protocol independent contract for an SSH client allowing applications to support both versions of the SSH protocol without the need for seperate client libraries. When the client connects to the server, the server supplies its public key for the client to verify. Since we informed the SshConnector to use our PasswordHostKey implementation all we should see is the host key finger print.

Authenticate User
We now have a connection to the SSH server the next task is to authenticate the Ssh user we identified earlier in our connect method, HttpProxyTransport.connectViaProxy(..., username). We will not be able to perform any other operation until the user has been authenticated. Password authentication is ideal for first time users as it requires no additional configuration within the SSH client or server. The user simply supplies his username and password to the client which is then transmitted over the encrypted connection to the server. The server then checks that the given password is acceptable to the native password-authentication mechanism of the host operating system and returns the result to the client.
Ssh2PasswordAuthentication passwordAuthentication = new Ssh2PasswordAuthentication();
System.out.print("Password: ");
passwordAuthentication.setPassword(reader.readLine());
if(ssh.authenticate(passwordAuthentication)!=SshAuthentication.COMPLETE) {
System.out.println("Authentication failed!");
System.exit(0);
}
If authentication fails, !=SshAuthentication.COMPLETE then we end immediately. If however we succeed then we are finally able to start using our Ssh2Client object. This instance is returned to the main calling class.
And that's all there is to it!
The purpose of this article, to establish an Ssh Connection through a HTTP Proxy Server, has been accomplish. The rest of the article returns back to the main calling method which proceeds to use the configured Ssh2Client instance to establish a Terminal Session with the remote server.
Requesting a Pseudo Terminal
After a session has been established between our SSH client and server our next task is to configure a pseudo terminal. A pseudo terminal is a device that imitates a terminal. Rather than being connected to an actual terminal, a pseudo-terminal or pty is connected to a process. This will allow us to gain visual representation of the remotely opened shell.
if(ssh.isAuthenticated()) {
final ForwardingClient fwd = new ForwardingClient(ssh);
final SshSession session = ssh.openSessionChannel();
session.requestPseudoTerminal("vt100", 80, 24, 0, 0);
As you can see the above block of code and the remaining is all maintained within a secure 'if' block, only successfully Authenticated connections are allowed to continue. A psuedoTerminal can only be retrieved through an SshClient session object and as above we open a new session instance through our successful connection 'SshClient' object, calling the 'openSessionChannel' method.
Opening a Shell
We now must start up a shell to interact with the remote shell on the SSH Server.
session.startShell();
Displaying Status Information
Maverick allows us to query various details regarding our Ssh session here we simply demonstrate this by requesting status information on firstly the SshClient, whether it is connected to the server still. Secondly whether the authentication on the client succeeded or not, next if the connection to the SSH server is still alive and finally we display a selection of information detailing the algorithms utilised in our active session.
System.out.println("SshClient connected["+ssh.isConnected()+"]");
System.out.println("SshClient authenticated["+ssh.isAuthenticated()+"]");
System.out.println("SshSession closed["+session.isClosed()+"]");
System.out.println("Cipher[client->server]:"
+((Ssh2Context)con.getContext(SshConnector.SSH2)).getPreferredCipherCS());
System.out.println("Cipher[cleint<-server]:"+
((Ssh2Context)con.getContext(SshConnector.SSH2)).getPreferredCipherSC());
System.out.println("Public Key Algorithm:"+
((Ssh2Context)con.getContext(SshConnector.SSH2)).getPreferredPublicKey());
System.out.println("Key Exchange Algorithm:"+
((Ssh2Context)con.getContext(SshConnector.SSH2)).getPreferredKeyExchange());
Handling Output Session Data
Our session has been configured all we intend to do is establish IO interaction with our shell. Firstly we establish a means of retrieving the shells response to our requests,
final InputStream in = session.getInputStream();
Terminal terminal = new Terminal(in);
terminal.start();
We retrieve the sessions InputStream and pass that to the Terminal class, If you take a look at the Terminal class you can see this is a simple thread that iterates through the InputStream we supplied, simply printing out the characters received from the remote shell.
Handling Input Session Data
Now that we can see what the shell is returning we need someway of replying to and writing commands to it. This is achieved through the current session’s OutputStream.
while((read = System.in.read()) > -1) {
session.getOutputStream().write(read);
}
We simply loop around writing the content of the System read buffer and writing it out to the session’s output stream.
Execute The Code
Once this code is executed the session is established and we are able to execute commands on the remote machine, instead of a direct Ssh connection to the host our secure channel is directed through our Apache HTTP Proxy Server.
Communication
Even with a HTTP proxy server in place to help a company's security we are still able to connect to our company's internal Ssh server resource. But with the transmissions going over Ssh we do not put the integrity of the corporate network at risk and nor does the inclusion of proxying systems interfere with our accessing of company resources. In affect a secure Ssh channel works hand in hand with security systems as proxy servers. The communication is secured right across the route from client through to server with the added security of a Proxy Server in between.
How To Run This Code
This attached zip file contains several source files. Unzip these into a new Java project and build.
© 2005 3SP Ltd, All Rights Reserved