Example: Encrypting data

This example demonstrates a technique for encrypting data in a database. In this scenario, decrypting the data incurs a performance penalty.

 To run the Encrypted.java example
  1. Change to the following directory: samples-dir\UltraLiteJ.

    For information about the default location of samples-dir, see Samples directory.

  2. Run the following command (the command is case sensitive):

    rundemo Encrypted
Note

This example is for Java SE. For a complete BlackBerry encryption sample, see samples-dir\UltraLiteJ\BlackBerryEncryption.



// *****************************************************
// Copyright (c) 2006-2010 iAnywhere Solutions, Inc.
// Portions copyright (c) 2006-2010 Sybase, Inc.
// All rights reserved. All unpublished rights reserved.
// *****************************************************
// This sample code is provided AS IS, without warranty or liability
// of any kind.
//
// You may use, reproduce, modify and distribute this sample code
// without limitation, on the condition that you retain the foregoing
// copyright notice and disclaimer as to the original iAnywhere code.
//
// *********************************************************************
package com.ianywhere.ultralitej.demo;

import com.ianywhere.ultralitej12.*;

import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.*;

/**
 * Encrypted -- sample program to demonstrate encryption of database.
 *
 * This sample requires either the Sun JDK 1.5 (or later) or a freeware
 * version of the Java encryption classes (JCE).
 */
public class Encrypted
{
    /** Create the database.
     * @return connection for a new database
     */
    private static Connection createDatabase()
    	throws ULjException
    {
	ConfigPersistent config = DatabaseManager.createConfigurationFile( "Encrypt.ulj" );
	config.setEncryption( new Encryptor() );
	Connection conn = DatabaseManager.createDatabase( config );

	PreparedStatement ps = conn.prepareStatement(
		"CREATE TABLE Product" +
		"( prod_no INT NOT NULL PRIMARY KEY" +
		", prod_name VARCHAR(32)" +
		", price NUMERIC(9,2)" +
		")"
		);
	ps.execute();
	ps.close();

	return conn;
    }

    /** Add a product row.
     * @param ri PreparedStatement for the Product table
     * @param prod_no product number
     * @param prod_name product name
     * @param price selling price
     */
    private static void addProduct( PreparedStatement ri, int prod_no, String prod_name, String price )
    	throws ULjException
    {
	ri.set( "prod_no", prod_no );
	ri.set( "prod_name", prod_name );
	ri.set( "price", price );
	ri.execute();
    }

    /** Populate the database.
     * @param conn connection to database
     */
    private static void populate( Connection conn )
    	throws ULjException
    {
	PreparedStatement ri = conn.prepareStatement(
		"INSERT INTO Product( prod_no, prod_name, price )"
		+ " VALUES( :prod_no, :prod_name, :price )"
	    );
	addProduct( ri, 2001, "blue screw", ".03" );
	addProduct( ri, 2002, "red screw", ".09" );
	addProduct( ri, 2004, "hammer", "23.99" );
	addProduct( ri, 2005, "vise", "39.99" );
	ri.close();
	conn.commit();
    }

    /** Display contents of Product table.
     * @param conn connection to database
     */
    private static void displayProducts( Connection conn )
    	throws ULjException
    {
	PreparedStatement stmt = conn.prepareStatement(
		"SELECT prod_no, prod_name, price FROM Product"
		+ " ORDER BY prod_no"
	    );
	ResultSet cursor = stmt.executeQuery();
	for( ; cursor.next(); ) {
	    String prod_no = cursor.getString( 1 /* "prod_no" */ );
	    String prod_name = cursor.getString( "prod_name" ); //access by name
	    String price = cursor.getString( 3 /* "price" */ );
	    Demo.display( prod_no + " " + prod_name + " " + price );
	}
	cursor.close();
	stmt.close();
    }

    /** mainline for program.
     * @param args command-line arguments (not used)
     */
    public static void main
	( String[] args )
    {
	try {
	    Connection conn = createDatabase();
	    populate( conn );
	    displayProducts( conn );
	    conn.release();
	} catch( ULjException exc ) {
	    Demo.displayException( exc );
	}
    }

    /** Class to implement encryption/decryption of the database.
     */
    static class Encryptor
    	implements EncryptionControl
    {
	private SecretKeySpec _key = null;
	private Cipher _cipher = null;
	
	/** Encrypt a page stored in the Database.
	 * @param page_no the number of the page being encrypted
	 * @param src the encrypted source page which was read from the Database
	 * @param tgt the unencrypted page (filled in by method)
	 * @param num_bytes the number of bytes to be decrypted
	 */
	public void decrypt( int page_no, byte[] src, byte[] tgt, int num_bytes )
	    throws ULjException
	{
	    byte[] decrypted = null;
	    try {
		_cipher.init( Cipher.DECRYPT_MODE, _key );
		decrypted = _cipher.doFinal( src );
	    } catch( Exception e ) {
		Demo.display( "Error: decrypting" );
		throw new EncryptionError();
	    }
	    System.arraycopy( decrypted, 0, tgt, 0, num_bytes );
	}
    
	/** Encrypt a page stored in the Database.
	 * @param page_no the number of the page being encrypted
	 * @param src the unencrypted source
	 * @param tgt the encrypted target page which will be written to the Database (filled in by method)
	 */
	public void encrypt( int page_no, byte[] src, byte[] tgt )
	    throws ULjException
	{
	    byte[] encrypted = null;
	    try {
		_cipher.init( Cipher.ENCRYPT_MODE, _key );
		encrypted = _cipher.doFinal( src );
	    } catch( Exception e ) {
		Demo.display( "Error: encrypting" );
		throw new EncryptionError();
	    }
	    System.arraycopy( encrypted, 0, tgt, 0, tgt.length );
	}
    
	/** Initialize the encryption control with a password.
	 * @param password the password
	 */
	public void initialize( String password )
	    throws ULjException
	{
	    try {
		byte[] bytes = password.getBytes( "UTF8" );
		MessageDigest md = MessageDigest.getInstance( "SHA" );
		bytes = md.digest( bytes );
		byte[] key_bytes = new byte[16];
		for( int i = key_bytes.length; i > 0; ) {
		    --i;
		    key_bytes[ i ] = bytes[ i ];
		}
		_key = new SecretKeySpec( key_bytes, "AES" );
		_cipher = Cipher.getInstance( "AES/ECB/NoPadding" );
	    } catch( Exception e ) {
		Demo.display( "Error: initializing encryption" );
		throw new EncryptionError();
	    }
	}
    }

    /** Error class for encryption errors.
     */
    static class EncryptionError
    	extends ULjException
    {
	/** Constructor.
	 */
	EncryptionError()
	{
	    super( "Encryption Error" );
	}

	/**
	 * Get the error code, associated with this exception.
	 * @return the error code (from the list at the top of this class) associated
	 *  with this exception
	 */
	public int getErrorCode()
	{
	    return ULjException.SQLE_ERROR;
	}

	/** Get exception causing this exception, if it exists.
	 * @return null, if there exists no causing exception; otherwise, the exception causing this exception
	 */
	public ULjException getCausingException()
	{
	    return null;
	}

	/** Get offset of error within a SQL string.
	 * @return (-1) when there is no SQL string associated with the error message; otherwise,
	 * the (base 0) offset within that string where the error occurred.
	 */
	public int getSqlOffset()
	{
	    return -1;
	}
    }
}