Example: Encrypting data

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

package ianywhere.ultralitej.demo;
import ianywhere.ultralitej.*;
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.4.2 (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 );

        conn.schemaCreateBegin();

        TableSchema table_schema = conn.createTable( "Product" );
        table_schema.createColumn( "prod_no", Domain.INTEGER );
        table_schema.createColumn( "prod_name", Domain.VARCHAR, 32 );
        table_schema.createColumn( "price", Domain.NUMERIC, 9, (short)2 );
        IndexSchema index_schema = table_schema.createPrimaryIndex( "prime_keys" );
        index_schema.addColumn( "prod_no", IndexSchema.ASCENDING );

        conn.schemaCreateComplete();

        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(); ) {
            /* Can't access columns by name because no meta data */
            String prod_no = cursor.getString( 1 /* "prod_no" */ );
            String prod_name = cursor.getString( 2 /* "prod_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)
         */
        public void decrypt( int page_no, byte[] src, byte[] tgt )
            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();
            }
            for( int i = tgt.length; i > 0; ) {
                --i;
                tgt[ i ] = decrypted[ i ];
            }
        }
    
        /** 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();
            }
            for( int i = tgt.length; i > 0; ) {
                --i;
                tgt[ i ] = encrypted[ i ];
            }
        }
    
        /** 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;
        }
    }
}