Database Encryption
  • Encryption should be pluggable/adaptable, but include an example based on AES/MD5 which uses the JCE. The sun reference that should be in every modern JRE. An example of use of the (JCE/JRE) encryption using Blowfish/MD5. This implementation is LGPL. Major advantage of using JCE is the ability for someone to change/swap to which ever encryption algorithm they like or feel is more secure (3DES/AES/IDEA/Twofish etc) - and as JCE is pluggable, they could even use another provider away from the JRE.
  • Passwords/Keys should really be passed as byte[] as Strings can stick around in memory/RAM. You cant write out (clear/null) an object like you can with byte[].
  • Code changes in the IOFactory's getInstance method seem to talk about an adaptable IO - looking at DefaultFileIO you could probably extend this and adapt the read/write methods to be encrypted before writing to the file.
  • Small issue is block size - as encryption is done in blocks, you would need to ensure all read/writes are blocked to match encryption algorithms block size. This becomes tricky with a random file as you have to be careful in making sure the same position is in the same block.

Here is some sample code that encrypts/decrypts using a random file.

package test;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class CryptoFileIO //extends DefaultFileIO 
{
    private static int CRYPTO_BLOCKSIZE = 16; // AES = 16 bytes or 128 bits.
    private static int BLOCKSIZE = 4*16; // ensure total block size is much larger.
    RandomAccessFile _raf = null;
    long _pos = 0;

    Cipher decipher;
    Cipher encipher;

//    public CryptoFileIO(int nbBuffers, String name, Session session,String fileName, boolean canWrite, int bufferSize) throws IOException 
//    {
//        super(nbBuffers, name, session, fileName, canWrite, bufferSize);
//    }

    public CryptoFileIO(String fileName, String access) throws FileNotFoundException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException 
    {
        _raf = new RandomAccessFile(fileName,access);

        String pwd = "password";

        MessageDigest md5 = MessageDigest.getInstance("MD5"); // get the hash algorithm
        byte[] key = md5.digest(pwd.getBytes());// hash the pwd to make a 128bit key
        SecretKeySpec skey = new SecretKeySpec(key,"AES"); // create a key suitable for AES
        IvParameterSpec ivSpec = new IvParameterSpec(md5.digest(key)); // create an init vector (based on the key, hashed again)
        encipher = Cipher.getInstance("AES/CTR/NoPadding"); // load a cipher AES / Segmented Integer Counter
        encipher.init(Cipher.ENCRYPT_MODE, skey,ivSpec);
        decipher = Cipher.getInstance("AES/CTR/NoPadding"); // load a cipher AES / Segmented Integer Counter
        decipher.init(Cipher.DECRYPT_MODE, skey,ivSpec);

    }

    public void seek(long pos) throws IOException 
    {
        _pos = pos;
        _raf.seek(pos);        
    }

    public void close() throws IOException 
    {
        _raf.close();
    }

    public void write(byte[] bytes) throws IOException, IllegalBlockSizeException, BadPaddingException 
    {    
        long pos = _pos;        
        int balength = bytes.length;
        long pos1 = ( pos / BLOCKSIZE ) * BLOCKSIZE;        
        byte[] blockbit = new byte[BLOCKSIZE];

        _raf.seek(pos1);        
        if (_raf.read(blockbit) > 0) // decrypt blockbit
        {            
            blockbit = decipher.doFinal(blockbit);
        }
        _raf.seek(pos1);
        int len1 = BLOCKSIZE - (int)(pos - pos1);
        if (balength < len1)
        {
            len1 = balength;
        }
        System.arraycopy(bytes, 0, blockbit, (int)(pos - pos1), len1);
        blockbit = encipher.doFinal(blockbit);    // encrypt blockbit
        _raf.write(blockbit);
        pos += len1;
        balength -= len1;
        int blocks = balength / BLOCKSIZE;        
        for (int i=0;i<blocks;i++)
        {
            pos1 = ( pos / BLOCKSIZE ) * BLOCKSIZE;
            _raf.seek(pos1);            
            if (_raf.read(blockbit) > 0) // decrypt blockbit
            {            
                blockbit = decipher.doFinal(blockbit);
            }
            _raf.seek(pos1);
            len1 = BLOCKSIZE - (int)(pos - pos1);
            if (balength < len1)
            {
                len1 = balength;
            }
            System.arraycopy(bytes, bytes.length-balength, blockbit, 0, len1);
            blockbit = encipher.doFinal(blockbit);    // encrypt blockbit
            _raf.write(blockbit);
            pos += len1;
            balength -= len1;
        }
        if (balength > 0)
        {
            blockbit = new byte[BLOCKSIZE];
            pos1 = ( pos / BLOCKSIZE ) * BLOCKSIZE;
            _raf.seek(pos1);                
            if (_raf.read(blockbit) > 0) // decrypt blockbit
            {            
                blockbit = decipher.doFinal(blockbit);
            }
            _raf.seek(pos1);
            len1 = BLOCKSIZE - (int)(pos - pos1);
            if (balength < len1)
            {
                len1 = balength;
            }
            System.arraycopy(bytes, bytes.length-balength, blockbit, 0, len1);
            blockbit = encipher.doFinal(blockbit);    // encrypt blockbit
            _raf.write(blockbit);
            pos += len1;
            balength -= len1;
        }        
        _pos +=bytes.length;
    }

    public long read(byte[] bytes) throws IOException, IllegalBlockSizeException, BadPaddingException 
    {        
        int totalread = 0;
        long pos = _pos;        
        int balength = bytes.length;
        long pos1 = ( pos / BLOCKSIZE ) * BLOCKSIZE;        
        byte[] blockbit = new byte[BLOCKSIZE];

        _raf.seek(pos1);        
        totalread+=_raf.read(blockbit);
        blockbit = decipher.doFinal(blockbit);        // decrypt blockbit
        int len1 = BLOCKSIZE - (int)(pos - pos1);
        if (balength < len1)
        {
            len1 = balength;
        }
        System.arraycopy(blockbit, (int)(pos - pos1), bytes, 0, len1);

        pos += len1;
        balength -= len1;
        int blocks = balength / BLOCKSIZE;        
        for (int i=0;i<blocks;i++)
        {
            pos1 = ( pos / BLOCKSIZE ) * BLOCKSIZE;
            _raf.seek(pos1);        
            totalread+=_raf.read(blockbit);
            blockbit = decipher.doFinal(blockbit);        // decrypt blockbit
            len1 = BLOCKSIZE - (int)(pos - pos1);
            if (balength < len1)
            {
                len1 = balength;
            }
            System.arraycopy(blockbit, 0, bytes, bytes.length-balength, len1);

            pos += len1;
            balength -= len1;
        }
        if (balength > 0)
        {
            blockbit = new byte[BLOCKSIZE];
            pos1 = ( pos / BLOCKSIZE ) * BLOCKSIZE;
            _raf.seek(pos1);        
            totalread+=_raf.read(blockbit);
            blockbit = decipher.doFinal(blockbit);        // decrypt blockbit
            len1 = BLOCKSIZE - (int)(pos - pos1);
            if (balength < len1)
            {
                len1 = balength;
            }
            System.arraycopy(blockbit, 0, bytes, bytes.length-balength, len1);

            pos += len1;
            balength -= len1;
        }        
        _pos += Math.min(totalread,bytes.length);        

        return Math.min(totalread,bytes.length); 
    }
}

Info ยป Original Source Forge Feature Request

Variable block size
Olivier SmadjaOlivier Smadja 1200836504|%e %b %Y, %H:%M %Z|agohover

One of the biggest issue is that ODB does not use fixed blocked to write object data and sometimes ODB access directly one part of the object data to do "in place update". I am not sure how encryption would work with that characteristic.

The next file format version of ODB will have fixed size blocks.

Reply  |  Options
Unfold Variable block size by Olivier SmadjaOlivier Smadja, 1200836504|%e %b %Y, %H:%M %Z|agohover
Re: Variable block size
gslendergslender 1200862767|%e %b %Y, %H:%M %Z|agohover

I think there is a way around this problem - you would just treat all read/writes as if they are in blocks.

Example: if you had 4 byte blocks, a read at pos 3 would be in the 1st block. The next read at pos 5 would be in the 2nd. An update of bytes read from pos 3 to pos 5 would 1st re-write 1st block (read all 1-4 bytes, update 3-4 bytes and then write all 1-4 back) and then write the 2nd block (read all 5-8 bytes, update byte 5 and then write all 5-8 back).

I would expect this overhead to be slow and sure its complex, but I don't see a major problem.

Reply  |  Options
Unfold Re: Variable block size by gslendergslender, 1200862767|%e %b %Y, %H:%M %Z|agohover
Add a New Comment
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License