Using SSL For Client-Server Communication

This topic applies to Java version only

With the default settings db4o client-server communication is not encrypted and thus can potentially be a dangerous security hole. This can be fixed with a new pluggable socket client/server implementation. Let's look at a simple example - we will use SSL protocol to protect our communication channel.

Basically the task is to create a NativeSocketFactory implementation that will create sockets able to communicate through an encrypted channel. In Java these would be SSLServerSocket and SSLSocket.

SecureSocketFactory.java
01/* Copyright (C) 2007 db4objects Inc. http://www.db4o.com */ 02package com.db4odoc.ssl; 03 04import java.io.IOException; 05import java.net.ServerSocket; 06import java.net.Socket; 07 08import javax.net.ssl.SSLContext; 09 10import com.db4o.config.NativeSocketFactory; 11 12public class SecureSocketFactory implements NativeSocketFactory { 13 14 private SSLContext _context; 15 16 public SecureSocketFactory(SSLContext context) { 17 _context = context; 18 } 19 20 public ServerSocket createServerSocket(int port) throws IOException { 21 System.out.println("SERVER on " + port); 22 return _context.getServerSocketFactory().createServerSocket(port); 23 } 24 25 public Socket createSocket(String hostName, int port) throws IOException { 26 System.out.println("CLIENT on " + port); 27 return _context.getSocketFactory().createSocket(hostName, port); 28 } 29 30 public Object deepClone(Object context) { 31 return this; 32 } 33 34}

In order for this class to work correctly we need to provide a correctly initialized SSLContext. For those who are not familiar with Java SSL implementation it is recommended to get acquainted with Java documentation for SSLContext, TrustManager, KeyStore and KeyManagerFactory APIs.

For encryption purposes we will need to create a new keystore. This can be done with the following command:

keytool -genkey -keystore SSLcert -storepass password

This command creates a file SSLcert, which contains a keystore protected by "password" password. For a test keystore you can provide any answers to the keytool questions and leave the key password the same as the keystore password. For easy access copy the SSLcert file into your projects directory.

Now we are ready to create a SecureSocketFactory

SSLSocketsExample.java: createSecureSocketFactory
01private static SecureSocketFactory createSecureSocketFactory() throws Exception{ 02 SSLContext sc; 03 04 //Create a trust manager that does not validate certificate chains 05 TrustManager[] trustAllCerts = createTrustManager(); 06 07 // Install the all-trusting trust manager 08 sc = SSLContext.getInstance("SSLv3"); 09 KeyStore ks = KeyStore.getInstance(KEYSTORE_ID); 10 ks.load(new FileInputStream(KEYSTORE_PATH), null); 11 KeyManagerFactory kmf = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() ); 12 kmf.init( ks, KEYSTORE_PASSWORD.toCharArray()); 13 14 sc.init(kmf.getKeyManagers(), trustAllCerts, new java.security.SecureRandom()); 15 return new SecureSocketFactory(sc); 16 }
SSLSocketsExample.java: createTrustManager
01private static TrustManager[] createTrustManager(){ 02 return new TrustManager[]{ 03 new X509TrustManager() { 04 public java.security.cert.X509Certificate[] getAcceptedIssuers() { 05 return null; 06 } 07 public void checkClientTrusted( 08 java.security.cert.X509Certificate[] certs, String authType) { 09 } 10 public void checkServerTrusted( 11 java.security.cert.X509Certificate[] certs, String authType) { 12 } 13 } 14 }; 15 }

Starting a server and opening client connections with the new socket factory is as simple as usual:

SSLSocketsExample.java: main
01public static void main(String[] args) throws Exception { 02 03 // Create a SecureSocketFactory for the SSL context 04 socketFactory = createSecureSocketFactory(); 05 06 Configuration config = Db4o.newConfiguration(); 07 ObjectServer db4oServer = Db4o.openServer(config, FILE, PORT, 08 socketFactory); 09 db4oServer.grantAccess(USER, PASSWORD); 10 try { 11 storeObjectsRemotely(HOST, PORT, USER, PASSWORD); 12 queryRemoteServer(HOST, PORT, USER, PASSWORD); 13 } finally { 14 db4oServer.close(); 15 } 16 }
SSLSocketsExample.java: storeObjectsRemotely
1private static void storeObjectsRemotely(String host, int port,String user,String password) throws IOException { 2 Configuration config = Db4o.newConfiguration(); 3 ObjectContainer client=Db4o.openClient(config, "localhost",port,user,password, socketFactory); 4 Pilot pilot = new Pilot("Fernando Alonso", 89); 5 client.set(pilot); 6 client.close(); 7 }
SSLSocketsExample.java: queryRemoteServer
1private static void queryRemoteServer(String host, int port,String user,String password) throws IOException { 2 Configuration config = Db4o.newConfiguration(); 3 ObjectContainer client=Db4o.openClient(config, "localhost",port,user,password, socketFactory); 4 listResult(client.get(new Pilot(null, 0))); 5 client.close(); 6 }