I am trying to apply security to a simple chat application with Java SSLSockets.
I have created a self-signed CA and with it I signed two certificates (all used RSA keys), one for the server, and one for a client. After that, I imported the certificates to a keystore for the server and to another for the client.
CA :
openssl genrsa -out ca.key 1024 -rsa
openssl req -new -key ca.key -out ca.csr
openssl x509 -req -days 365 -in ca.csr -out ca.crt -signkey ca.key
SERVER CERTIFICATE:
openssl genrsa -out server.key 1024 -rsa
openssl req -new -key server.key -out server.csr
openssl ca -in server.csr -cert ca.crt -keyfile ca.key -out server.crt
CLIENT CERTIFICATE :
openssl genrsa -out client.key 1024 -rsa
openssl req -new -key client.key -out client.csr
openssl ca -in client.csr -cert ca.crt -keyfile ca.key -out client.crt
KEYSTORES:
keytool -import -keystore serverkeystore -file ca.crt -alias theCARoot
keytool -import -keystore serverkeystore -file server.crt -alias servercrt
keytool -import -keystore serverkeystore -file client.crt -alias clientcrt
keytool -import -keystore clientkeystore -file ca.crt -alias theCARoot
keytool -import -keystore clientkeystore -file server.crt -alias servercrt
keytool -import -keystore clientkeystore -file client.crt -alias clientcrt
I want to use a specific cipher, but apparently none of the supported ciphers works.
My code for the client :
import java.net.*;
import java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
import javax.net.ssl.*;
public class ChatClient implements Runnable
{
private SSLSocket socket = null;
private Thread thread = null;
private DataInputStream console = null;
private DataOutputStream streamOut = null;
private ChatClientThread client = null;
final String[] enabledCipherSuites = {"TLS_RSA_WITH_AES_256_CBC_SHA256"};
final char[] passphrase = "123456".toCharArray();
public ChatClient(String serverName, int serverPort)
{
System.out.println("Establishing connection to server...");
try
{
SSLSocketFactory factory = null;
SSLContext ctx = SSLContext.getInstance("TLS");
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore ks= KeyStore.getInstance("JKS");
ks.load(new FileInputStream("clientkeystore"), passphrase);
kmf.init(ks, passphrase);
KeyStore serverKey = KeyStore.getInstance("JKS");
serverKey.load(new FileInputStream("serverkeystore"),passphrase);
TrustManagerFactory trustManager = TrustManagerFactory.getInstance("SunX509");
trustManager.init(serverKey);
ctx.init(kmf.getKeyManagers(), trustManager.getTrustManagers(), null);
factory = ctx.getSocketFactory();
socket = (SSLSocket)factory.createSocket(serverName, serverPort);
socket.setEnabledCipherSuites(enabledCipherSuites);
start();
}
catch(UnknownHostException uhe)
{
// Host unkwnown
System.out.println("Error establishing connection - host unknown: " + uhe.getMessage());
}
catch(IOException ioexception)
{
// Other error establishing connection
System.out.println("Error establishing connection - unexpected exception: " + ioexception.getMessage());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
}
public void run()
{
while (thread != null)
{
try
{
// Sends message from console to server
streamOut.writeUTF(console.readLine());
streamOut.flush();
}
catch(IOException ioexception)
{
System.out.println("Error sending string to server: " + ioexception.getMessage());
stop();
}
}
}
public void handle(String msg)
{
// Receives message from server
if (msg.equals(".quit"))
{
// Leaving, quit command
System.out.println("Exiting...Please press RETURN to exit ...");
stop();
}
else
// else, writes message received from server to console
System.out.println(msg);
}
// Inits new client thread
public void start() throws IOException
{
console = new DataInputStream(System.in);
streamOut = new DataOutputStream(socket.getOutputStream());
if (thread == null)
{
client = new ChatClientThread(this, socket);
thread = new Thread(this);
thread.start();
}
}
// Stops client thread
public void stop()
{
if (thread != null)
{
thread.stop();
thread = null;
}
try
{
if (console != null) console.close();
if (streamOut != null) streamOut.close();
if (socket != null) socket.close();
}
catch(IOException ioe)
{
System.out.println("Error closing thread..."); }
client.close();
client.stop();
}
public static void main(String args[])
{
ChatClient client = null;
if (args.length != 2)
// Displays correct usage syntax on stdout
System.out.println("Usage: java ChatClient host port");
else
// Calls new client
client = new ChatClient(args[0], Integer.parseInt(args[1]));
}
}
class ChatClientThread extends Thread
{
private SSLSocket socket = null;
private ChatClient client = null;
private DataInputStream streamIn = null;
public ChatClientThread(ChatClient _client, SSLSocket _socket)
{
client = _client;
socket = _socket;
open();
start();
}
public void open()
{
try
{
streamIn = new DataInputStream(socket.getInputStream());
}
catch(IOException ioe)
{
System.out.println("Error getting input stream: " + ioe);
client.stop();
}
}
public void close()
{
try
{
if (streamIn != null) streamIn.close();
}
catch(IOException ioe)
{
System.out.println("Error closing input stream: " + ioe);
}
}
public void run()
{
while (true)
{ try
{
client.handle(streamIn.readUTF());
}
catch(IOException ioe)
{
System.out.println("Listening error: " + ioe.getMessage());
client.stop();
}
}
}
}
And for the server :
import java.net.*;
import java.io.*;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.Arrays;
import javax.net.ServerSocketFactory;
import javax.net.ssl.*;
public class ChatServer implements Runnable
{
private ChatServerThread clients[] = new ChatServerThread[20];
private SSLServerSocket server_socket = null;
private Thread thread = null;
private int clientCount = 0;
final String[] enabledCipherSuites = {"TLS_RSA_WITH_AES_256_CBC_SHA256"};
final char[] passphrase = "123456".toCharArray();
public ChatServer(int port)
{
try
{
// Binds to port and starts server
System.out.println("Binding to port " + port);
SSLContext ctx = SSLContext.getInstance("TLS");;
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream("serverkeystore"), passphrase);
kmf.init(ks, passphrase);
KeyStore serverKey = KeyStore.getInstance("JKS");
serverKey.load(new FileInputStream("clientkeystore"),passphrase);
TrustManagerFactory trustManager = TrustManagerFactory.getInstance("SunX509");
trustManager.init(serverKey);
ctx.init(kmf.getKeyManagers(), trustManager.getTrustManagers(), null);
SSLServerSocketFactory ssf = ctx.getServerSocketFactory();
server_socket = (SSLServerSocket) ssf.createServerSocket(port);
server_socket.setEnabledCipherSuites(enabledCipherSuites);
server_socket.setNeedClientAuth(true);
System.out.println("Server started: " + server_socket);
start();
}
catch(IOException ioexception)
{
// Error binding to port
System.out.println("Binding error (port=" + port + "): " + ioexception.getMessage());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
}
public void run()
{
while (thread != null)
{
try
{
// Adds new thread for new client
System.out.println("Waiting for a client ...");
addThread((SSLSocket)server_socket.accept());
}
catch(IOException ioexception)
{
System.out.println("Accept error: " + ioexception); stop();
}
}
}
public void start()
{
if (thread == null)
{
// Starts new thread for client
thread = new Thread(this);
thread.start();
}
}
public void stop()
{
if (thread != null)
{
// Stops running thread for client
thread.stop();
thread = null;
}
}
private int findClient(int ID)
{
// Returns client from id
for (int i = 0; i < clientCount; i++)
if (clients[i].getID() == ID)
return i;
return -1;
}
public synchronized void handle(int ID, String input)
{
if (input.equals(".quit"))
{
int leaving_id = findClient(ID);
// Client exits
clients[leaving_id].send(".quit");
// Notify remaing users
for (int i = 0; i < clientCount; i++)
if (i!=leaving_id)
clients[i].send("Client " +ID + " exits..");
remove(ID);
}
else
// Brodcast message for every other client online
for (int i = 0; i < clientCount; i++)
clients[i].send(ID + ": " + input);
}
public synchronized void remove(int ID)
{
int pos = findClient(ID);
if (pos >= 0)
{
// Removes thread for exiting client
ChatServerThread toTerminate =
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…