SSL Sockets Android and server using a certificate

Print Friendly

This article describes how to create a secure socket from an android device to a server using a certificate. It explains how to create a key that Android can use, write a secure server socket, a client socket and send information back and forth between them.

First we can start by downloading the apk file for android and the jar file for your computer (server) along with the correct secure certificate and another certificate to test that other certificates will not work. Download link: http://ece301-examples.googlecode.com/files/ssl_sockets.zip

First I will go through the steps so you can see a working example before looking at the code.

The easiest way to test this:

1. First, make sure that you are using Wifi and that your laptop and your android device are both connected to the same Wifi.
2. From a terminal, run sslsocket.jar with the port number and the whole path of the certificate. In my case:

java -jar sslsocket.jar 9998 /home/juan/testserverkeys

3. Install the APK file in your android device. The certificate is inside the assets folder. When the screen comes up, you need to specify the IP address of your computer, the port number (I used 9998) and a message you like to send over. Then press the SEND button. Look at the image at the start of this article. Should look like that

In your server side you should see the following

Now if you use the wrong certificate:

java -jar sslsocket.jar 9998 /home/juan/testserverWrongKey

You will get an I/O exception in your android application. Try it!

And your server as well will give you an IO exception:

The rest of this article is available for $.99 cents. It has all the source code available to create the apk and jar file and it explains how to create the android SSL certificate as well as the server side certificate.

Create a key using keytool

This all was done in Ubuntu!

Using shell, create a key using keytool:

keytool -genkey -keystore testserverkeys -keyalg rsa -alias testkey

In my case, I generate a key called testserverkeys using RSA and with an alias testkey. It will ask you for password, name, etc.
Here is my shell command output

My kkeystorepass = key12345 and key password = 12345key

Create an Android key

There are many ways, but the easiest is to download portecle to do it. Its GUI based and is so easy. You can download it here:
http://sourceforge.net/projects/portecle/files/portecle/1.7/portecle-1.7.zip/download
Extract it anywhere you like and run it:

java -jar portecle.jar

On the GUI you will do:
File >> open key store file >> look for it with all files >> enter password: key12345

The password being key12345.
Now that we have the certificate open ( or key – whatever you want to call it), we have to convert it to BKS. Why?
Android only supports BKS.
Tools >> change keystore type >> BKS >> enter password: 12345key
Save as testkey (any name you like)
Now you have created a BKS key that android can work with.

Server code

Create a new Java project, not an android project, and create a class SSLSocketMain.java. Here we put the server code:

package com.ssl.server;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ServerSocketFactory;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocket;
import javax.net.ssl.SSLSocket;

public class SSLSocketMain {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		if (args.length != 2){
			System.out.println("Wrong number of arugments");
			System.out.println("Usage: sslsocket.jar <port> <path to key>");
			System.exit(-1);
		}
		int socket = Integer.parseInt(args[0]);
		String keystore = args[1];
		char keystorepass[] = "key12345".toCharArray();
		char keypassword[] = "12345key".toCharArray();
		SSLServerSocket serverSocket = null;
		SSLSocket client;

		try {
			KeyStore ks = KeyStore.getInstance("JKS");
			ks.load(new FileInputStream(keystore),keystorepass);
			KeyManagerFactory kmf = 
				KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
			kmf.init(ks, keypassword);

			SSLContext sslcontext = 
				SSLContext.getInstance("TLS");

			sslcontext.init(kmf.getKeyManagers(), null, null);

			ServerSocketFactory ssf = 
				sslcontext.getServerSocketFactory();


			serverSocket = (SSLServerSocket) 
			ssf.createServerSocket(socket);
			//serverSocket.setNeedClientAuth(true);

			System.out.println("Starting server...");
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("Could not listen on port "+socket);
			System.exit(-1);
		} catch (KeyStoreException e) {
			System.out.println("Could not get key store");
			System.exit(-1);
		} catch (NoSuchAlgorithmException e) {
			System.out.println("There is no algorithm in ks.load");
			e.printStackTrace();
			System.exit(-1);
		} catch (CertificateException e) {
			e.printStackTrace();
			System.exit(-1);
		} catch (UnrecoverableKeyException e) {
			System.out.println("kmf.init() no key");
			System.exit(-1);
		} catch (KeyManagementException e) {
			System.out.println("sslcontext.init keymanagementexception");
			System.exit(-1);
		}
			try {
				client = (SSLSocket) serverSocket.accept();
				System.out.println("client connected");
				BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
				BufferedWriter out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
				String message;
				message = in.readLine();
				System.out.println("Client's message: "+message);
				System.out.println("Responding same message: "+message);
				out.write(message);
				out.flush();
				out.close();
				in.close();
				client.close();
				serverSocket.close();
			} catch (IOException e) {
				System.out.println("Accept failed on "+socket);
				e.printStackTrace();
				System.exit(-1);
			}
	}

}

This means:
1. You create a Keystore which reads the path of the key created previously.
2. Create an SSLConext, in this case using TLS and finally a SocketFactory will create your socket which
happens to be an SSLSocket using an SSLServerSocket.
3. BufferedWriter and BufferedReader are just to write messages back and forth between server and client.
Important: after an out.write you must create a newline by “\n”.

Client code

In the manifest, put the internet permission:

uses-permission android:name="android.permission.INTERNET"

Main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical" android:layout_width="fill_parent"
	android:layout_height="fill_parent">
	<TextView android:layout_width="fill_parent"
		android:layout_height="wrap_content" android:text="ENTER YOUR IP ADDRESS" />
	<EditText android:layout_width="fill_parent"
		android:layout_height="wrap_content" android:id="@+id/ip_address" />
	<TextView android:layout_width="fill_parent"
		android:layout_height="wrap_content" android:text="ENTER YOUR PORT NUMBER" />
	<EditText android:layout_width="fill_parent"
		android:layout_height="wrap_content" android:id="@+id/port" />
	<TextView android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:text="ENTER THE MESSAGE YOU LIKE TO SEND TO YOUR SERVER:" />
	<EditText android:layout_width="fill_parent"
		android:layout_height="wrap_content" android:id="@+id/editext" />
	<Button android:layout_width="fill_parent"
		android:layout_height="wrap_content" android:id="@+id/send_button"
		android:text="SEND" />
	<TextView android:layout_width="fill_parent"
		android:layout_height="wrap_content" android:id="@+id/server_response" />
</LinearLayout>

Make an android project. Inside the res folder make another folder called raw. In here you will copy the key that is BKS that you made earlier for android.
The code goes like this:

package com.ssl.client;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.security.cert.Certificate;

import org.apache.http.conn.ssl.SSLSocketFactory;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class SSLClientActivity extends Activity {
	/** Called when the activity is first created. */

	private EditText mText;
	private Button mSend;
	private TextView mResponse;
	private EditText mIPaddress;
	private EditText mPort;

	// port to use
	private String ip_address;
	private int port = 9998;
	private SSLSocket socket = null;
	private BufferedWriter out = null;
	private BufferedReader in = null;
	private final String TAG = "TAG";
	private char keystorepass[] = "key12345".toCharArray();
	private char keypassword[] = "12345key".toCharArray();

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		mText = (EditText) findViewById(R.id.editext);
		mIPaddress = (EditText) findViewById(R.id.ip_address);
		mPort = (EditText) findViewById(R.id.port);
		mSend = (Button) findViewById(R.id.send_button);
		mResponse = (TextView) findViewById(R.id.server_response);

		mSend.setClickable(true);
		mSend.setOnClickListener(new View.OnClickListener() {

			@Override
			public void onClick(View v) {
				if (mIPaddress.getText().toString().equals(null) || mPort.getText().toString().equals(null)){
					Toast.makeText(v.getContext(), "Please enter an IP address or Port number", Toast.LENGTH_LONG).show();
				}
				else{
					String temp = mText.getText().toString();
					if (temp == null){
						temp = "No text was entered";
					}

					Log.i(TAG,"makes it to here");

					port = Integer.parseInt(mPort.getText().toString());
					ip_address = mIPaddress.getText().toString();
					
					try{

						KeyStore ks = KeyStore.getInstance("BKS");
						InputStream keyin = v.getResources().openRawResource(R.raw.androidtestserverkeys);
						ks.load(keyin,keystorepass);
						SSLSocketFactory socketFactory = new SSLSocketFactory(ks);
						socketFactory.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
						socket = (SSLSocket) 
						socketFactory.createSocket(new Socket(ip_address,port), ip_address, port, false);
						socket.startHandshake();

						printServerCertificate(socket);
						printSocketInfo(socket);

						out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
						in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
						chat(temp);
					} catch (UnknownHostException e) {
						Toast.makeText(v.getContext(), "Unknown host", Toast.LENGTH_SHORT).show();
						Log.i(TAG,"Unknown host");
						//System.exit(1);
					} catch  (IOException e) {
						Toast.makeText(v.getContext(), "No I/O", Toast.LENGTH_SHORT).show();
						Log.i(TAG,"No I/O");
						e.printStackTrace();
						//System.exit(1);
					} catch (KeyStoreException e) {
						Toast.makeText(v.getContext(), "Keystore ks error", Toast.LENGTH_SHORT).show();
						Log.i(TAG,"Keystore ks error");
						//System.exit(-1);
					} catch (NoSuchAlgorithmException e) {
						Toast.makeText(v.getContext(), "No such algorithm for ks.load", Toast.LENGTH_SHORT).show();
						Log.i(TAG,"No such algorithm for ks.load");
						e.printStackTrace();
						//System.exit(-1);
					} catch (CertificateException e) {
						Toast.makeText(v.getContext(), "certificate missing", Toast.LENGTH_SHORT).show();
						Log.i(TAG,"certificate missing");
						e.printStackTrace();
						//System.exit(-1);
					} catch (UnrecoverableKeyException e) {
						Toast.makeText(v.getContext(), "UnrecoverableKeyException", Toast.LENGTH_SHORT).show();
						Log.i(TAG,"unrecoverableKeyException");
						e.printStackTrace();
						//System.exit(-1);
					} catch (KeyManagementException e) {
						Toast.makeText(v.getContext(), "KeyManagementException", Toast.LENGTH_SHORT).show();
						Log.i(TAG,"key management exception");
						e.printStackTrace();
						//System.exit(-1);
					}
				}

			}
		});

	}

	private void printServerCertificate(SSLSocket socket) {
		try {
			Certificate[] serverCerts =
				socket.getSession().getPeerCertificates();
			for (int i = 0; i < serverCerts.length; i++) {
				Certificate myCert = serverCerts[i];
				Log.i(TAG,"====Certificate:" + (i+1) + "====");
				Log.i(TAG,"-Public Key-\n" + myCert.getPublicKey());
				Log.i(TAG,"-Certificate Type-\n " + myCert.getType());

				System.out.println();
			}
		} catch (SSLPeerUnverifiedException e) {
			Log.i(TAG,"Could not verify peer");
			e.printStackTrace();
			System.exit(-1);
		}
	}
	private void printSocketInfo(SSLSocket s) {
		Log.i(TAG,"Socket class: "+s.getClass());
		Log.i(TAG,"   Remote address = "
				+s.getInetAddress().toString());
		Log.i(TAG,"   Remote port = "+s.getPort());
		Log.i(TAG,"   Local socket address = "
				+s.getLocalSocketAddress().toString());
		Log.i(TAG,"   Local address = "
				+s.getLocalAddress().toString());
		Log.i(TAG,"   Local port = "+s.getLocalPort());
		Log.i(TAG,"   Need client authentication = "
				+s.getNeedClientAuth());
		SSLSession ss = s.getSession();
		Log.i(TAG,"   Cipher suite = "+ss.getCipherSuite());
		Log.i(TAG,"   Protocol = "+ss.getProtocol());
	}

	public void chat(String temp){
		String message = temp;
		String line = "";
		// send id of the device to match with the image
		try {
			out.write(message+"\n");
			out.flush();
		} catch (IOException e2) {
			Log.i(TAG,"Read failed");
			System.exit(1);
		}
		// receive a ready command from the server
		try {
			line = in.readLine();
			mResponse.setText("SERVER SAID: "+line);
			//Log.i(TAG,line);
		} catch (IOException e1) {
			Log.i(TAG,"Read failed");
			System.exit(1);
		}
	}
}

Here is the trick that is not in other websites. I am using SSLSocketFactory from
org.apache.http.conn.ssl.SSLSocketFactory. This is what allows me to ALLOW_ALL_HOSTNAME_VERIFIER.
Else, we would need a real certificate from a certificate authority to even test our application before we create them.

You do not need the functions printSocketInfo and printServerCertificate. But it is nice to know about the certificate.
Try this: If you change the password in the client or server code, you will see that there will be no connection.
Run the server first and you should see it running.
Happy Coding!

Comments are closed.