package org.javaconfessions.baseconverter;
public class BaseConverterUtil {
private static final String baseDigits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
public static String toBase62( int decimalNumber ) {
return fromDecimalToOtherBase( 62, decimalNumber );
}
public static String toBase36( int decimalNumber ) {
return fromDecimalToOtherBase( 36, decimalNumber );
}
public static String toBase16( int decimalNumber ) {
return fromDecimalToOtherBase( 16, decimalNumber );
}
public static String toBase8( int decimalNumber ) {
return fromDecimalToOtherBase( 8, decimalNumber );
}
public static String toBase2( int decimalNumber ) {
return fromDecimalToOtherBase( 2, decimalNumber );
}
public static int fromBase62( String base62Number ) {
return fromOtherBaseToDecimal( 62, base62Number );
}
public static int fromBase36( String base36Number ) {
return fromOtherBaseToDecimal( 36, base36Number );
}
public static int fromBase16( String base16Number ) {
return fromOtherBaseToDecimal( 16, base16Number );
}
public static int fromBase8( String base8Number ) {
return fromOtherBaseToDecimal( 8, base8Number );
}
public static int fromBase2( String base2Number ) {
return fromOtherBaseToDecimal( 2, base2Number );
}
private static String fromDecimalToOtherBase ( int base, int decimalNumber ) {
String tempVal = decimalNumber == 0 ? "0" : "";
int mod = 0;
while( decimalNumber != 0 ) {
mod = decimalNumber % base;
tempVal = baseDigits.substring( mod, mod + 1 ) + tempVal;
decimalNumber = decimalNumber / base;
}
return tempVal;
}
private static int fromOtherBaseToDecimal( int base, String number ) {
int iterator = number.length();
int returnValue = 0;
int multiplier = 1;
while( iterator > 0 ) {
returnValue = returnValue + ( baseDigits.indexOf( number.substring( iterator - 1, iterator ) ) * multiplier );
multiplier = multiplier * base;
--iterator;
}
return returnValue;
}
}
Sunday, September 28, 2008
Convert Between Base 10, Base 62, Base 36, Base 16, Base 8, Base 2 in Java
Saturday, September 27, 2008
Part Three - SSH Channels
We need to understand the SSH channel process in order to continue, so what are channels? All terminal sessions, forwarded connections, etc. are channels. Either side may open a channel and multiple channels are multiplexed into a single connection. Channels are flow-controlled, that is no data may be sent to a channel until a message is received to indicate that window space is available.
Channels can be whatever you want them to be, there are many channel types already defined within the SSH protocol specification, such as the 'session' or 'tcpip-forward' channels. These are the mechanism that provide the transport for your session and port forwarding data.
When you want to open a channel you send a request to the SSH server, the server will then either respond to that request with a confirmation or failure message. Your channel is identified by a name, for instance lets say we want to create a channel to echo information back to the client, we would call the channel "echo@3sp.com";. You should always name your channel using the name@domain syntax.
J2SSH provides a mechanism for developing custom channels, the com.sshtools.j2ssh.connection package contains a number of classes to help you. First the Channel class is the abstract base for all channels. Lets take a look at its abstract methods, the first method is self explainitory, it returns the name/type of the channel.
public String getChannelType() {
return "echo@3sp.com";
}
The next two methods provide the settings for the flow control. The values returned by the following mehtods set the boundary for the window space. The channel will never provide more window space than the maximum and will increase window space automatically (back up to the maximum) when the minimum is reached.
protected int getMinimumWindowSpace() {
return 1024;
}
protected int getMaximumWindowSpace() {
return 65535;
}
The maximum packet size setting is the maximum amount of data that the remote side can send in one single packet. This value should not exceed the maximum window space.
protected int getMaximumPacketSize() {
return 32768;
} When a request is made to open a channel, the client can send some data with the open request so that the server may process the request based on additional information. Your channel should return this information (if any) in the getChannelOpenData method, if there is no data simply return null.
public byte[] getChannelOpenData() {
return null;
} When the server confirms that the channel is open it can also provide data in response to the channel open information. It does so by returning this information in the getChannelConfirmationData method. Again if no data is required to be sent to the remote side simply return null.
public byte[] getChannelConfirmationData() {
return null;
} Once the channel has been opened, the channel mechanism calls the channels onChannelOpen method. You should not perform any expensive processing in this method since it will lock up the protocol and you will not be able to send data.
protected void onChannelOpen() throws java.io.IOException {
/** Your channel is open so do stuff if you want **/
} Once the channel is open you can send data using the channels sendChannelData method, this type of data is received by the channel with the following method.
protected void onChannelData(SshMsgChannelData msg) throws java.io.IOException {
// Channel data has arrived, process it!
} There is also an extended data channel which can be used, the extended data has a type field which can be used to identify different application defined data types. This is received with the onChannelExtData method and sent with sendChannelExtData.
protected void onChannelExtData(SshMsgChannelExtendedData msg) throws java.io.IOException {
/**@todo Implement this com.sshtools.j2ssh.connection.Channel abstract method*/
}
There is also a request mechanism seperate to the channel data which can be used. This provides named based requests. For example in our echo channel we could have a request to turn echo on and off. Requests are recieved with the onChannelRequet method and sent with sendChannelRequest.
protected void onChannelRequest(String requestname, boolean wantreply, byte[] requestdata)
throws java.io.IOException {
/**@todo Implement this com.sshtools.j2ssh.connection.Channel abstract method*/
}
When you no longer wish to send data you can set the local side to EOF by using setLocalEOF. When the remote server sends EOF the onChannelEOF method is called.
protected void onChannelEOF() throws java.io.IOException {
/**@todo Implement this com.sshtools.j2ssh.connection.Channel abstract method*/
} Finally, to close the channel you can use the close method. When the channel is closed by either side the onChannelClose method is called.
protected void onChannelClose() throws java.io.IOException {
/**@todo Implement this com.sshtools.j2ssh.connection.Channel abstract method*/
} Ok so now we know the basic structure how do we implement our channel and use it from either side of the connection? Heres our server side channel implelentation. This simply returns any data sent to it if echo is on.
public class EchoChannel extends Channel {
boolean echo = true;
public EchoChannel() {
}
public String getChannelType() {
return "echo@3sp.com";;
}
protected void onChannelRequest(String requestname, boolean wantreply, byte[]
requestdata) throws java.io.IOException {
if(requestname.equals("echo-off@3sp.com";)) {
echo = false;
if(wantreply)
connection.sendChannelRequestSuccess(this);
return;
} else if(requestname.equals("echo-on@3sp.com";)) {
echo = true;
if(wantreply)
connection.sendChannelRequestSuccess(this);
return;
}
if(wantreply)
connection.sendChannelRequestFailure(this);
}
protected void onChannelExtData(SshMsgChannelExtendedData msg) throws java.io.IOException {
}
protected void onChannelData(SshMsgChannelData msg) throws java.io.IOException {
if(echo)
sendChannelData(msg.getChannelData());
}
protected int getMaximumPacketSize() {
return 32768;
}
protected void onChannelEOF() throws java.io.IOException {
}
protected void onChannelClose() throws java.io.IOException {
}
public byte[] getChannelOpenData() {
return null;
}
protected int getMinimumWindowSpace() {
return 1024;
}
protected int getMaximumWindowSpace() {
return 65535;
}
protected void onChannelOpen() throws java.io.IOException {
}
public byte[] getChannelConfirmationData() {
return null;
}
} Now we need to configure the server to accept the channel and create an instance of our EchoChannel when a request is made. To do this we will need to create a ChannelFactory that can create the channel, this is a simple interface
public interface ChannelFactory {
public Channel createChannel(String channelType, byte[] requestData)
throws InvalidChannelException;
}
So lets create an EchoChannelFactory implementation
public class EchoChannelFactory implements ChannelFactory {
public Channel createChannel(String channelType, byte[] requestData)
throws InvalidChannelException {
if(channelType.equals("echo@3sp.com";)) {
return new EchoChannel();
}
throw new InvalidChannelException("Only echo channels allowed by this factory");
}
} So now were ready to configure the server. In my previous articles you will remember we implemented the configureServices method of the SshServer? To allow this channel to be opened, we simply add the following line to the configureServices implementation
connection.addChannelFactory("echo@3sp.com";, new EchoChannelFactory()); Your server should now be configured to support your channel. So lets look at how to invoke the channel from the client.
First we need to create a client side implementation of the channel, but we need it to be simpler with a set of IOStreams perhaps??? Well the hard work is already done for you, take a look at the IOChannel class. This provides an InputStream and OutputStream for the channels data. Heres the implementation, it has less methods to implement since the channel data is now handled by the parent class.
public class EchoChannelClient extends IOChannel {
public EchoChannelClient() {
}
public String getChannelType() {
return "echo@3sp.com";;
}
protected void onChannelRequest(String requestname,
boolean wantreply,
byte[] data) throws java.io.IOException {
if(wantreply)
connection.sendChannelRequestFailure(this);
}
protected int getMaximumPacketSize() {
return 32768;
}
public byte[] getChannelOpenData() {
return null;
}
protected int getMinimumWindowSpace() {
return 1024;
}
protected void onChannelOpen() throws java.io.IOException {
}
protected int getMaximumWindowSpace() {
return 65535;
}
public byte[] getChannelConfirmationData() {
return null;
}
} We need to add a method for turning the echo on and off.
public void setEcho(boolean echo) throws java.io.IOException {
if(echo)
connection.sendChannelRequest(this, "echo-on@3sp.com";, false, null);
else
connection.sendChannelRequest(this, "echo-off@3sp.com";, false, null);
} Now were ready to go since any data we write to the channels outputstream, which we obtain by using getOutputStream will be returned to the InputStream if echo is on.
To use the channel using an SshClient instance simply
EchoChannelClient echo = new EchoChannelClient();
if(ssh.openChannel(echo)) {
// Channel is open
echo.getOutputStream().write("hello world!".getBytes());
// Read it back from the inputstream
byte[] buf = new byte[32];
echo.getInputStream().read(buf);
}
So of course your requirements are probably much more complex, if you want the server to handle IOStreams you can use the IOChannel instead of the Channel, in the end its up to you....... with a little imagination ;-)
Part Two - Building an SSH server to provide your applications services.
In the previous part of this article I showed you how to secure your client-application server by using J2SSH to create an embeded tunnel to an SSH server installed on the same host as your server. Whilst the tunnel is secure it does not provide access to the strong public key authentication that SSH provides. In this article we take no prisoners, were going to implement an SSH server and transfer your existing application server functionality into the SSH server itself so that your application can take advantage of public key authentication and other SSH services such as SFTP.
Starting the basic framework server
First things first, even before you think about how to get your existing application functionality into the SSH server, you need to get the server itself running and accepting each connection and authenticating the user. Make sure you have the source version of J2SSH since we're going copy the com.sshtools.daemon.SshDaemon source code to create your server.
If you examine this class you will see that it has a main method and so we're going to use this class to configure, start and stop your server.Lets not worry about what the SshDaemon is doing right now, lets just make sure we can start and stop the server. You may have noticed that the main method allows a couple of command line arguments -start and -stop. No guessing for what these do! Before we start the server there are a couple of configuration items we need to attend to. First of all the server needs a public key to identify itself to clients. Generate a key pair using the J2SSH command line utility ssh-keygen.
¢ ssh-keygen.bat -t dsa -b 1024 server_host_key
When prompted to enter a passprase simply press return and confirm that you do not want to encrypt the private key, server host keys cannot be encrypted since there is no where to store the passphrase!
Copy the host key into the J2SSH conf directory, whilst you're in there open up the server configuration file server.xml and I'll take you over some of the other options. Make sure that there is a
Next make sure that the element is set to the port you wish your server to bind, if you have another SSH server running you might want to change this to a non standard port for now.
Finally the element is the local port which the server listens for commands, there is only one command currently implemented and that is stop.
You should now be able to start the server by executing the SshDaemon class, you will need to set the sshtools.home System property to point to the root installation directory of J2SSH. This will be used to find the XMl configuration files. Once you have the server running (remember to pass in the -start option on the command line) it should accept connections, but if you connect and try to authenticate it will fail because there is no authentication provider installed. So that's our next step.
Creating an authentication providerThe authentication provider allows you to define how users are authenticated when they connect to the SSH server. Your application may already have a mechanism for this, perhaps by searching a database table, or an encrypted password file. In any case you will want to transfer this process to the authentication provider, since I cannot help you on that front, I will show you how to implement a basic mechanism and explain the methods you are required to provide. To get things moving perhaps you should just implement this with me to see how the process fits together.
The abstract NativeAuthenticationProvider is where we start, we are going to extend this class to implement an authentication provider that examines the user name and logs them into the system. For simplicity we will hard code this in this example.
class MyAuthenticationProvider extends NativeAuthenticationProvider {
}
Now lets look at the methods we need to implement, first there is getHomeDirectory. This should return the home directory of the user whose username is passed as a parameter into the method. This will be used for public key configuration and also servers as the default directory for the SFTP server that the framework will provide.
public String getHomeDirectory(String username)
throws IOException {
return "/home/users/martianx";
}
There are two logonUser methods that we need to implement next, the first provides a username and password which should be checked. If the account exists and the password is correct you should perform any processing that would log the user into the system and return true to indicate that the logon was a success. Optionally you can throw a password change exception if the users password has expired (more about that later).
public boolean logonUser(String username,
String password)
throws PasswordChangeException,
IOException {
return username.equals("martianx")
&& password.equals("foo"));
}
The second logonUser method only provides the username. This is called when a user log's into the system using public key authentication. When the method is called the server has already verified the identity of the user through their public key so you should automatically log the user in without restriction.
public boolean logonUser(String username) throws IOException {
return true; // This is for public key authentication and will only
// be called once a key has been verified
} The logoff User method simply should log the user out of the system, performing any necersary processing that your application server requires. In our example we will do nothing
public void logoffUser() throws IOException {
// do nothing yet
}
Finally the changePassword method is called if you had previously had thrown a PasswordChangeException from the logonUser method. This method should change the password and return its success (or failure). Our example will return false since we do not support password change yet.
public boolean changePassword(String username,
String oldpassword,
String newpassword) {
return false; // We dont support this yet
}
So that's the authentication provider implemented, you should have a good idea about how to go about integrating your servers authentication mechanism now? But first we need to have a look at this provider in action, so the next step is to configure the server to load the provider at startup.
The platform configuration file
The providers are configured in a platform configuration file. The actual name of the file should be set to your application's name, but an example is provided in the J2SSH distribution and its called platform.xml. Lets copy the file and create myplatform.xml and configure it for our new authentication provider.
In the file locate the element and enter the FQN of the authentication implementation we just created.
com.myplatform.MyAuthenticationProvider Save the file and before we start the server we need to make sure that the server will load the provider by setting a VM parameter, use
-Dsshtools.platform=myplatform.xml Now start the server and try to connect using a standard SSH client and the authentication information we set in our example. The connection will still fail but you should get prompted for autehntication information and see an error message, for example using Putty as a client will probably show a pty refused message. This is actually a success since the pty is only requested once authentication has succeeded.
The next step is to remove this error message and were going to acheive that by creating a NativeProcessProvider.
Creating a NavtiveProcessProvider
The NativeProcessProvider allows the server to provide shell access and other session based services such as command execution. But you don't want a terminal I hear you say? Well that's fine but since your using the SSH framework your going to have to provide one, and you never know what you might be able to do in the future with a little imagination. For example one of the things I like to do is to provide terminal access to user accounts for self serve account maintenance, allowing them to login using a standard SSH client and change their password account details. It's always a good idea to allow some form of access into your application server that any standard client can access. What if your out of the office and really need to log in to change something, but don't have your laptop, connect using the SSHTerm applet and your connected again!
So what we are going to implement today is a basic terminal that outputs the message "This server does not provide shell access" and then disconnects. Let's start by declaring our class which extends NativeProcessProvider
public class UnsupportedShellProcess extends NativeProcessProvider {
private static String message = "This server does not provide shell access";
}
An instance of this class will be created for each session that is created, the process is required to provide the standard set of IO streams stdin, stdout and stderr. We will simply use a dynamic buffer class that J2SSH (which is similar to Piped streams) that will allow us to output something from the other end of the stream which will be read by the connected client.
import com.sshtools.j2ssh.io.DynamicBuffer;
DynamicBuffer stdin = new DynamicBuffer();
DynamicBuffer stderr = new DynamicBuffer();
DynamicBuffer stdout = new DynamicBuffer();
public InputStream getInputStream() throws IOException {
return stdin.getInputStream();
}
public OutputStream getOutputStream() throws IOException {
return stdout.getOutputStream();
}
public InputStream getStderrInputStream() {
return stderr.getInputStream();
}
When a command or shell is executed, the framework first requests that your provider create the process by calling the createProcess method, this method should initialize your provider with command and environment provided BUT NOT start the command. The framework will then start it sepeartley with a call to start. Since were writing a basic implementation we dont need to setup the process so were ust going to return true to indicate that the command is reading to start.
public boolean createProcess(String command,
Map environment)
throws IOException {
// Of course this could be used to provide a wealth of features
// according to the executed command and the environment
return true;
}
Now when the process is started we output our message
public void start() throws IOException {
stdin.getOutputStream().write(message.getBytes());
}
Next the process is required to provide a default terminal provider, this is used in place of a command when the user requests to start a shell and so will be the command argument when createProcess is called, were not going to read this so just supply any string.
public String getDefaultTerminalProvider() {
return "UnsupportShell";
}
The client may request a pseudo terminal, again this is for a more advanced implementation than we need so in order to make sure that no client bombs out because we don't allocate one, we implement these methods to show we support and have allocated a terminal.
public boolean supportsPseudoTerminal(String term) {
return true;
}
public boolean allocatePseudoTerminal(String term,
int cols,
int rows,
int width,
int height,
String modes) {
return true;
}
The kill method should end the process, we will simply close all the buffers
public void kill() {
try {
stdin.close();
}
catch (IOException ex) {
}
try {
stdout.close();
}
catch (Exception ex1) {
}
try {
stderr.close();
}
catch (Exception ex2) {
}
}
We need to provide the server with an evaluation on the process to determine whether its still active. We will return active whilst the whole message has not been read
public boolean stillActive() {
try {
return stdin.getInputStream().available() > 0;
}
catch (IOException ex) {
return false;
}
}
Finally this method should wait for the process to complete and then return the exit code. We are already planning on deprecating this method to provide a callback method but since its still here we need to implemenet it even if it is rather a crude implementation. We will simple wait for the message to be read and then return.
public int waitForExitCode() {
try {
while (stdin.getInputStream().available() > 0) {
try {
Thread.sleep(1000); // Crude but necersary
}
catch (InterruptedException ex) {
}
}
}
catch (IOException ex1) {
}
return 0;
}
As you can see quite an easy implementation, when a connection is made that requests a shell or to execute a command the command will simply write out the message and once the message has been read the process will close.
To configure this in the platform configuration file you should set the
"This server does not provide shell access" Now you should have a working SSH server which also provides SFTP access to your users. In the final part of this article I will show you how to implement an SSH channel and configure your client and server so that they directly talk to each other through SSH.
Part 1 - Securing your client-server application with J2SSH
Using J2SSH is a great way to secure and enhance your insecure client-server application (or improve an SSL based one). Now it certainly would be a great idea at this point to spend some time evaluating the different methods of securing an applications communication, I could waste pages explaining about SSL or teaching you how to write your proprietary encryption process, but that's not why were here is it? And it's certainly not why I am here! You're here because you know SSH, you use it everyday to access every other computer your work on, or at least you should! You're here because SSH provides strong encryption, integrity checking and public key authentication of both the client and the server; the thing is you want your client-server application to provide that too, so that's what were going to do today.
This tutorial assumes you have a working knowledge of SSH clients and servers. Most importantly you should understand the concept of SSH port forwarding. In this series of articles we will discuss two ways to secure your client-server application
1. Secure existing applications by channelling the TCPIP data through a secure tunnel to an SSH server installed on the same machine as your application server
You may have already used standard SSH port forwarding to setup a secure tunnel to transport your connection, but maintaining both the SSH client and SSH server in addition to your own client and server is not a realistic proposition.
The quickest way to secure your application with J2SSH is to use this very process, but instead of maintaining an SSH client, you can use J2SSH directly within your client code.
2. Build an SSH server to provide your applications services.
J2SSH comes with a set of daemon classes that enable the creation of a custom SSH server in no time! The SSH server itself is already preconfigured to accept connections so the main framework is already available. Your responsibility firstly will be to develop an authentication provider to plug into your existing applications authentication mechanism, and secondly to provide a channel implementation that replaces your applications socket communication. There are of course a few more details but it could not be much simpler!
Securing existing applications by channelling TCPIP data through a secure tunnel
This method essentially forwards your client-server communication through a secure tunnel just as if you had setup standard port forwarding between your client host and the server. The difference is that J2SSH does not require the installation of an SSH client, instead it allows your client application to connect to the SSH server directly and instruct the server where to forward the data to thus removing the configuration nightmare of using port forwarding.
Setup an SSH server on the same host as your application server
First thing we need to do is to install an SSH server on the same host as your application server. That's up to you I'm afraid, we cannot help you out there. Once you have the server installed create an account on the host for your client application, J2SSH will need to authenticate to the SSH server, so it's a good idea to have an account for the application, and since we don't want to be putting passwords into the code we will also want a private key without a passphrase.
Configure your applications account with the public key and test the connection; you should be able to connect without any prompting. Finally, setup your application server to listen on the local loopback address. This will ensure that clients cannot connect directly.
Replace your existing client socket connection with an SSH connection using J2SSH
Now that we have a server installed, a private key for your application to authenticate with and your application server running, we can now turn to the client code changes. We need to replace your existing connection code with the following process
- Create a connection to the SSH server
- Authenticate using the client applications public key
- Create a tunnel to your application server
- Start your own communication using the tunnel's IO streams
Creating the connection is straight forward, you will obviously need to take into account integrating this code into your own client but the connection procedure is as follows
import com.sshtools.j2ssh.SshClient;
public SshClient createConnection() {// Make a client connection SshClient}
ssh = new SshClient();
// Connect to the host
ssh.connect("your-applications-host");
If you run the above code you will notice that the user is prompted in the console to verify the host key of the server. Its probably going to be a good idea to override this default behaviour since not everyone will see the console output. There are a couple of methods available, you could use a different HostKeyVerification implementation, for example the DialogHostKeyVerification will prompt the user using a dialog, and there is also IgnoreHostKeyVerification which simply ignores the process. For now we will use this, since were sure that the server is correct, however when you deploy your client to production environments its wise to replace this with either the dialog verification or your own custom implementation.
Lets import the new verification process
import com.sshtools.j2ssh.transport.IgnoreHostKeyVerification; and change the connect call
ssh.connect("your-applications-host", new IgnoreHostKeyVerification()); Now we can connect to the SSH server, and the next step is to authenticate using our newly generated private key. First we need to open the private key file, this can be done with the following code.
import com.sshtools.j2ssh.transport.authentication.SshPrivateKeyFile;
....
// Open up the private key file
SshPrivateKeyFile file =
SshPrivateKeyFile.parse(new File("filename"));
This provides us with a copy of the file in memory, since we don't have a passphrase on the key we can simply add the key to a new instance of the PublicKeyAuthenticationClient which we are going to use to authenticate with.
import com.sshtools.j2ssh.authentication.PublicKeyAuthenticationClient;
.....
PublicKeyAuthenticationClient pk = new PublicKeyAuthenticationClient();
Pk.setUsername("username");
Pk.setKey(file.toPrivateKey(null)); Now that the key is loaded into the authentication instance, we can authenticate.
if(ssh.autenticate(pk)== AuthenticationProtocolState.COMPLETED) {// Authentication Complete}
Now that we have successfully authenticated, were ready to create the secure tunnel for your application. To do this we are going to create a local forwarding channel, local forwarding is initiated by the client where by remote forwarding is initiated by the server. This forwarding channel will provide a set of IO streams for your client application to use in place of the Sockets IO streams.
import com.sshtools.j2ssh.forwarding.ForwardingIOChannel;
.....
ForwardingIOChannel channel = new ForwardingIOChannel(ForwardingIOChannel.LOCAL_FORWARDING_CHANNEL,
"127.0.0.1",
10022,
"myhost",
10022);
We now have a channel ready so lets waste no more time and open it
if(ssh.openChannel(channel)) {// Channel is opened and can be used}
channel.getOutputStream("START".getBytes());
....
Once the channel is open, you can use its IO streams in place of your Socket, your application should not require any changes.
The advantages and disadvantages of using a secure tunnel
The main advantage of this method is that it's a quick fix for an insecure application. By forwarding the traffic through an embedded SSH tunnel your application's data is now secure. However, your server has no access to the strong authentication that the SSH server provides and you still need to maintain a separate SSH server and authentication details for your client application. Of course these can be minimized by embedding a private key into your client application but that in itself a potential security flaw. What you need is to have your server support SSH connections directly.
In the next part of this series, I will show you how to build your own SSH server using the J2SSH daemon classes. Be prepared though as its not something you're going to be able to plug-in to your existing server, instead we will build an SSH server capable of supporting your applications proprietary protocol.Friday, September 5, 2008
Convert Between Base 10 and Base 62 in PL/SQL
However, if I were to translate a sequential decimal ( base 10 ) number to base 62, I now have 62 x 62 = 3,844 possible values ( 0 - zz ). Now obviously for base 62, the value will be case sensitive. If you can't guarantee case sensitivity, then you'll have to use base 36.
Convert from Base 10 to Base 62 in PL/SQL
CREATE OR REPLACE FUNCTION f_to_base62( a_number_to_convert INTEGER ) RETURN VARCHAR2 ISConvert from Base 62 to Base 10 in PL/SQL
v_modulo INTEGER;
v_temp_int INTEGER := a_number_to_convert;
v_temp_val VARCHAR2(256);
v_temp_char VARCHAR2(1);
c_base62_digits CONSTANT VARCHAR2(62) := '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
BEGIN
IF ( a_number_to_convert = 0 ) THEN
v_temp_val := '0';
END IF;
WHILE ( v_temp_int <> 0 ) LOOP
v_modulo := v_temp_int MOD 62;
v_temp_char := SUBSTR( c_base62_digits, v_modulo + 1, 1 );
v_temp_val := v_temp_char || v_temp_val;
v_temp_int := floor(v_temp_int / 62);
END LOOP;
RETURN v_temp_val;
END;
CREATE OR REPLACE FUNCTION f_from_base62( a_value_to_convert VARCHAR2 ) RETURN INTEGER IS
v_iterator INTEGER;
v_length INTEGER;
v_temp_char VARCHAR2(1);
v_temp_int INTEGER;
v_return_value INTEGER := 0;
v_multiplier INTEGER := 1;
v_temp_convert_val VARCHAR2(256) := a_value_to_convert;
c_base62_digits CONSTANT VARCHAR2(62) := '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
BEGIN
v_length := length( v_temp_convert_val );
v_iterator := v_length;
WHILE ( v_iterator > 0 ) LOOP
v_temp_char := SUBSTR( v_temp_convert_val, v_iterator, 1 );
v_temp_int := INSTR( c_base62_digits, v_temp_char ) - 1;
v_return_value := v_return_value + ( v_temp_int * v_multiplier );
v_multiplier := v_multiplier * 62;
v_iterator := v_iterator - 1;
END LOOP;
RETURN v_return_value;
END;
Wednesday, September 3, 2008
Getting Started with J2SSH
The first thing you will probably want to do is to connect to an SSH server using J2SSH. This is a fairly
straightforward procedure using the SshClient class. This class provides access for connecting, authenticating
and starting a session channel, which enables you to execute commands or start the users shell.
import com.sshtools.j2ssh.SshClient;
First of all prepare your application, in this section we will guide you through the basics so for now just a simple
try/catch inside the static main method.
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
public class SshExample() {
// A buffered reader so we can request information from the user
private static BufferedReader reader =
new BufferedReader(new InputStreamReader(System.in));
public static void main(String args[]) {
try {
// Further code will be added here
} catch(Exception e) {
e.printStackTrace();
}
}
}
The next few sections will guide you through making the initial connection, authenticating the user and executing
a command or starting the users shell for a simple console based SSH application.
Making the initial connection
To create an SshClient instance import the class into your implementation class file and use the following code
to connect to an SSH server on the standard port 22.
SshClient ssh
= new SshClient();
System.out.print("Host to connect: ");
String hostname = reader.readLine();
ssh.connect(hostname);
When the client connects to the server, the server supplies its public key for the client to verify. You will see
that calling the connect method prompts the user within the console to verify the key:
The host firestar is currently unknown to the system
The host key fingerprint is: 1028: 69 54 9c 49 e5 92 59 40 5 66 c5 2e 9d 86 af ed
Do you want to allow this host key? [Yes|No|Always]:
In the default implementation of the connect method, J2SSH reads the $HOME/.ssh/known_hosts file to determines to which hosts connections
may be allowed. This is provided by the class ConsoleKnownHostsKeyVerification and the
default behavior can be emulated by the following code:
import com.sshtools.j2ssh.transport.ConsoleKnownHostsKeyVerification;
ssh.connect("firestar", new ConsoleKnownHostsKeyVerification());
When the connect method returns, the protocol has been negotiated and key exchange has taken place, leaving the connection ready for authenticating the user.
Authenticating the user
Once the connection has been completed the user is required to provide a set of credentials for authentication.
All client side authentication methods are implemented using the abstract class:
import com.sshtools.j2ssh.authentication.SshAuthenticationClient. To perform authentication, the SshClient class provides the following method:
public int authenticate(SshAuthenticationClient auth); There are currently five authentication methods implemented by J2SSH, 'password', 'publickey', 'keyboard-interactive' and 'hostbased'. With an extra agent authentication method that performs public key authentication using the J2SSH key agent.
Password Authentication
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.
J2SSH implements the 'password' authentication method with the following class:
import com.sshtools.j2ssh.authentication.PasswordAuthenticationClient Using the password authentication method is straight forward; create an instance of the PasswordAuthentication
class, set the username and password and pass to the SshClient to complete the authentication.
/**
* Create a PasswordAuthenticationClient instance, set the properties
* and pass to the SessionClient to authenticate
*/
PasswordAuthenticationClient pwd = new PasswordAuthenticationClient();
System.out.print("Username: ");
String username = reader.readLine();
auth.setUsername(username);
System.out.print("Password: ");
String password = reader.readLine();
auth.setPassword(password);
int result = ssh.authenticate(pwd);
The Authentication Result
When the authentication method completes it returns the result of the authentication. This integer value can be any
of the following three values defined in the class:
import com.sshtools.j2ssh.authentication.AuthenticationProtocolState;
..
..
if(result==AuthenticationProtocolState.FAILED)
System.out.println("The authentication failed");
if(result==AuthenticationProtocolState.PARTIAL)
System.out.println("The authentication succeeded but another"
+ "authentication is required");
if(result==AuthenticationProtocolState.COMPLETE)
System.out.println("The authentication is complete");
Retrieving the available authentication Methods
It is possible at any time after the connection has been established and before authentication has been completed to request a list of authentication methods that can be used. The getAvailableAuthMethods method returns a list of authentication method names.
public List getAvailableAuthMethods(String username); It should be noted that the SSH specification allows the server to return authentication methods that are not valid
for the user.
Prompting the User for Authentication Details?
Each SshAuthenticationClient implementation can optionally be set a prompt interface which allows the user to
be prompted for the information once the authenticate method has been invoked.
public interface SshAuthenticationPrompt {
public boolean showPrompt(SshAuthenticationClient instance)
throws AuthenticationProtocolException;
}
} The showPrompt method is called if the authentication instance is not ready to authenticate. The methods is called and the developer should verify the instance of the SshAuthenticationClient to make sure that it is compatible with the prompt (for example you cannot perform public key authentication
with a password prompt!). The user should then be duly prompted for the information and the instance set with the user's information. Once
complete, the prompt returns true to indicate that the user successfully entered correct information.
There are several prompts provided in the J2SSH common packages that provide useful Swing based dialogs to
prompt the user.
import com.sshtools.common.authentication.PasswordAuthenticationDialog;
/***
* Create a PasswordAuthenticationDialog instance and call the
* showAuthenticationMethod so the user can graphically
* enter their username and password
*/
PasswordAuthenticationClient pwd =
new PasswordAuthenticationClient();
PasswordAuthenticationDialog dialog =
new PasswordAuthenticationDialog(parent);
pwd.setAuthenticationPrompt(dialog);
int result;
result = ssh.authenticate(pwd);
Using a session channel to execute a command or start the users shell
Once the user is authenticated you will probably want to do something such as execute a command or start the users shell. The SSH protocol provides multiplexed channels over a single connection and the session channel is one of the channels defined by the SSH protocol. The session channel allows the client to execute a single command on the remote host and to communicate with the process by sending and receiving data. The J2SSH SessionChannelClient implements this channel and we will use this to execute a basic "ls" command.
The session channel provides an inputstream and outputstream for reading/writing, but before we can do this we need to setup the channel for our command. First we open the channel itself by calling the SshClient method:
SessionChannelClient session = ssh.openSessionChannel();
Now that we have a session instance we need to configure it for our command, there are several options that can be set before we invoke one of the methods that will start the session.
Setting Environment Variables
The SSH protocol provides a method to set an environment variable for the processes environment, the protocol also leaves the actual implementation of this down the server implementation, and in our experience most servers do not allow for this for security reasons. However the method is available since its defined in the protocol specification, so your free to try to use it, but beware the variable may not be set!
public boolean setEnvironmentVariable(String name, String value); Requesting 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. If the command you are executing is expecting a terminal (such as a shell command) you can request that a pseudo terminal be attached to the process by calling the requestPseudoTerminal method.
public boolean requestPseudoTerminal(String term, int cols, int rows, int width, int height, String modes); Invoking a command
After the above operations have been performed you can then request that the session either start the user's shell, execute a specific command or start an SSH subsystem (such as SFTP). You should not invoke a subsystem unless you are able to read/write the subsystem protocol, there are many additional utilities within J2SSH that provide for the available subsystems.
To start the users default shell use:
public boolean startShell(); Or to execute a specific command use:
public boolean executeCommand(String command); An important note to remember is that this does not execute a shell command. You cannot for instance issue the command executeCommand("dir")" on the Windows Operating system as this is a shell command, instead use "cmd.exe /C dir". This method executes a binary executable and so should be used to execute any program other than the users shell.
Handling Session Data
Once the session has been configured and a command or shell has been started, you can begin to
transfer data to and from the remote computer using the sessions IO streams. These streams provide you with a
standardized interface for reading and writing the data.
The Session Channel's OutputStream
The format of writing data varies according to how you configured the session, for example if you executed the
users shell then the data should be written as if the user had entered the commands interactively.
/** Writing to the session OutputStream */
OutputStream out = session.getOutputStream();
String cmd = "ls\n";
out.write(cmd.getBytes());
The Session Channel's InputStream
/**
* Reading from the session InputStream
*/
InputStream in = session.getInputStream();
byte buffer[] = new byte[255];
int read;
while((read = in.read(buffer)) > 0) {
String out = new String(buffer, 0, read);
System.out.println(out);
}
Reading from stderr
The session also provides the stderr data provided by the remote session. Again an InputStream is provided.
public InputStream session.getStderrInputStream();
Closing the Session
The session can be closed using the following method:
public void close(); Disconnecting
The connection can be terminated by either side. To terminate the connection call the SshClient method:
public void disconnect(); A word on executing multiple commands
So we can now execute a single command on the remote server, but what's that I hear you say? I want to execute more than one command? Well if you cast your mind back I told you that the SSH protocol provides multiplexed channels over a single connection, so executing another command is as simple as executing the first, just create a new instance of the SessionChannelClient for every command you want to execute. You can execute them simultaneously or one after another, but always create a new session (since the session is closed when the command finishes and the protocol does not allow for re-using of a session to execute another command).
There is a drawback to this in that the process environment is not passed on from one session to another, so you cannot for example execute a command to change directory and then another to execute a script in that directory, since the change directory is lost when the session closes and the new command starts back in the default working directory. Of course you could always put the cd command into the script? Or use the shell to execute both commands. This subject is certainly a bit more advanced so I will leave it for another day and pencil in a new article to discuss all the alternatives for executing multiple commands.
This concludes our getting started tutorial, you should now have a basic working knowledge of how to connect, authenticate and execute commands using J2SSH.