Avg. Rating 5.0

Problem

You need to encrypt a database based on a user’s password. In addition, you need to allow the user to change his password and update the encryption accordingly.

Solution

Use the user’s password as the basis for the encryption key, and use the reencrypt() method of the SQLConnection class to change the encryption key for a database.

Detailed explanation

Although creating a random key may work for many situations, in some cases it is preferable to base the key on user input. This is ideal in situations where you are downloading password-protected data from an online service, for example, as it allows the same password that secures the data online to secure the data in the AIR application.

Because the open() and openAsync() methods of the SQLConnection class expect a 16-byte binary key, you will need to create a MD5 hash of the user's password by utilizing the AS3Crypto library. This creates the needed 16-byte ByteArray that can then be passed into the open() or openAsync() method.

When the user changes his password, the reencrypt() method of the SQLConnection class allows the application to change the encryption key for a specific database, as long as the database connection has already been opened with the old encryption key. Once this method has been called, a transaction-like process is executed. If the process is interrupted before completion, the database retains the old encryption key. If the process is completed successfully, a SQLEvent.REENCRYPT event is dispatched. If it fails, a SQLError.ERROR event is dispatched.

Warning

Remember that a database that is not encrypted cannot be encrypted; its data must be imported into a new encrypted database.

In this example, the user must specify a password to connect to the database. If the database does not exist, a new database is created using the password hash as the encryption key. While the database is connected, the user can enter a new password and click the Change Password button. This triggers the reencrypt() method, which changes the encryption key for the database.

You will need to include the AS3Crypto library for this project, because it will be used to generate the random encryption key. Download the as3crypto.swc file from http://code.google.com/p/as3crypto/ and add it to your project's build path.

To create the encryption key, the MD5 class from the AS3Crypto library is used along with the password entered by the user. The password, which is passed into the function as a string, is converted into a ByteArray by using the writeUTFBytes() method. Next, the MD5 class is instantiated, and the hash() method is called on the ByteArray. The returned value is used as the encryption key for the database:

private function createEncryptionKey(password:String):ByteArray {

    var ba:ByteArray = new ByteArray();

    ba.writeUTFBytes(password);

    var md5:MD5 = new MD5();

    var output:ByteArray = md5.hash(ba);

    return output;

}

To allow the changing of the password, the reencrypt() method of the SQLConnection class is used. This method must be called while the SQLConnection is open:

connection.addEventListener(SQLEvent.REENCRYPT, handleDatabaseReencrypt);

connection.reencrypt( createEncryptionKey(newPassword.text) );

The completed example, shown here, integrates all this functionality into a single AIR application:

<?xml version="1.0" encoding="utf-8"?>

<s:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml"

                       xmlns:s= "library://ns.adobe.com/flex/spark"

                       xmlns:mx="library://ns.adobe.com/flex/halo"

                       horizontalAlign="left">

    <s:layout><s:HorizontalLayout/></s:layout>

    <fx:Script>

        <![CDATA[

            import flash.utils.ByteArray;

            import mx.collections.ArrayCollection;

            import com.hurlant.crypto.hash.MD5;


            public static const ENCRYPTED_DB_FILE:String = "encrypted.db";


            [Bindable]

            private var results:ArrayCollection = new ArrayCollection();


            private var connection:SQLConnection;

            private var dbFile:File;


            private function createEncryptionKey(password:String):ByteArray {

                var ba:ByteArray = new ByteArray();

                ba.writeUTFBytes(password);

                var md5:MD5 = new MD5();

                var output:ByteArray = md5.hash(ba);

                results.addItem("[ACTION]: Hash Key Created " +

                                output.toString());

                return output;

            }


            private function handleConnectClick(event:MouseEvent):void {

                results.addItem("[ACTION]: Attempting Database Connection");

                dbFile = File.applicationStorageDirectory.resolvePath(

                         ENCRYPTED_DB_FILE );

                connection = new SQLConnection();

                connection.addEventListener(SQLEvent.OPEN,

                                            handleDatabaseOpen);

                connection.addEventListener(SQLErrorEvent.ERROR,

                                            handleDatabaseError);

                connection.openAsync(dbFile,SQLMode.CREATE,null,false,1024,

                                     createEncryptionKey(password.text));

            }


            private function handleDisconnectClick(event:MouseEvent):void {

                connection.close();

                disconnectButton.enabled = false;

                password.enabled = true;

                connectButton.enabled = true;

                newPassword.enabled = false;

                reencryptButton.enabled = false;

            }


            private function handleReencryptClick(event:MouseEvent):void {

                connection.addEventListener(SQLEvent.REENCRYPT,

                                            handleDatabaseReencrypt);

                connection.reencrypt(createEncryptionKey(newPassword.text));

            }


            private function handleDatabaseOpen(event:SQLEvent):void {

                results.addItem("[ACTION]: Database Connection Successful");

                disconnectButton.enabled = true;

                newPassword.enabled = true;

                reencryptButton.enabled = true;

                password.enabled = false;

                connectButton.enabled = false;

            }


            private function handleDatabaseReencrypt(event:SQLEvent):void {

                connection.removeEventListener(SQLEvent.REENCRYPT,

                                               handleDatabaseReencrypt);

                results.addItem("[ACTION]: Database Reencrypted");

            }


            private function handleDatabaseError(event:SQLErrorEvent):void {

                results.addItem("[ERROR]: Database Error " +

                                event.error.detailArguments.toString() );

            }


        ]]>

    </fx:Script>


    <mx:Label text="Encryption By Password" fontWeight="bold" fontSize="18" />


    <mx:Label text="Connect To Database" fontWeight="bold"/>

    <s:HGroup>

        <s:Label text="Password" />

        <s:TextInput id="password" />

        <s:Button id="connectButton" label="Connect"

                  click="handleConnectClick(event)" />

        <s:Button id="disconnectButton" label="Disconnect"

                  click="handleDisconnectClick(event)" enabled="false" />

    </s:HGroup>


    <s:Label text="Change Password / ReEncrypt" fontWeight="bold"/>

    <s:HGroup>

        <s:Label text="New Password" />

        <s:TextInput id="newPassword" enabled="false" />

        <s:Button id="reencryptButton" label="Change Password"

                  click="handleReencryptClick(event)" enabled="false" />

    </s:HGroup>

</s:WindowedApplication>

This recipe was originally contributed by Marco Casario as part of O'Reilly's Flex 4 Cookbook.


+
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.

Report abuse

Related recipes