Encrypted Local Store in AIR
One of the questions I encountered regularly during the AIR Bus Tour was about encryption using AIR. There’s an obvious sensitivity towards having an application remember certain details (like passwords) about the user, and then storing that data in the clear in a text file. During AIR Beta 1, I suggested that folks use one of the various ActionScript encryption libraries. I’m happy to report that AIR Beta 2 includes an “encrypted local store” for developers to use.
The class EncryptedLocalStore, as one might imagine, is what’s used to gain access to this functionality. It has only a few methods on it, and all of them are static. If you want to put something in the store, you call EncryptedLocalStore.setItem(). To get something out of the store you call EncryptedLocalStore.getItem(). And to remove something entirely from the store, you call EncryptedLocalStore.removeItem(). Pretty sensible, right? Let’s take a closer look at the details.
I should note that as per the documentation, each application has it’s own encrypted local store, and that store can only be accessed from the “application” security sandbox. AIR uses DPAPI on Windows and KeyChain on Mac OS. Data is encrypted to the local store using AES-CBC 128-bit. Also a warning that this feature is still changing a little and data stored using AIR Beta 2 may not be retrievable using later versions. This will stabilize for AIR 1.0.
Everything put in the store, and retrieved from the store, as a ByteArray, which I found a little confusing at first. To better clarify how to use this, I took the common case of remembering a user’s login information. There are two parts to my approach. The first part is in storing the information. This means getting the information out of the input field, writing it to a ByteArray, and then putting the data in the encrypted store.
function doSignIn()
{
var data = null;
var email = null;
var password = null;
if( document.getElementById( 'remember' ).checked )
{
email = document.getElementById( 'email' ).value;
password = document.getElementById( 'password' ).value;
data = new air.ByteArray();
data.writeUTFBytes( email );
air.EncryptedLocalStore.setItem( 'email', data );
data = new air.ByteArray();
data.writeUTFBytes( password );
air.EncryptedLocalStore.setItem( 'password', data );
} else {
removeUser();
}
document.getElementById( 'login' ).style.visibility = 'hidden';
}
When calling EncryptedLocalStore.setItem(), you need to have the data already in a ByteArray. When storing a String, this can be most easily accomplished by creating a new ByteArray instance, and then using ByteArray.writeUTFBytes(), which takes a String argument. The other part of EncryptedLocalStore.setItem() that is important is the name of the item you’re setting. This acts as a label for reference later when you want to get, change, or remove the data, so don’t forget it.
The “UTF” part is important in both reading and writing string data, so don’t try and shortcut it to ByteArray.writeMultiBytes().
When you choose to store the users login credentials is really up to you. You might choose to store the data when they hit the “Remember me” button. You might choose to store the data when they hit the “Sign In” button. Or you might even wait to store the data, until a successful login has been made. Your call, of course, but I generally like to wait for a successful login, that way I’m storing data that will actually help the user in the future.
Next up comes getting the data back out of the encrypted local store. In the case of “Remember me” functionality, this will most commonly be done when the AIR application starts. You get the data using the item name you used to store it. And as you might expect, the ByteArray.readUTFBytes() will get the String back out of the ByteArray.
function rememberUser()
{
var email = air.EncryptedLocalStore.getItem( 'email' );
var pass = air.EncryptedLocalStore.getItem( 'password' );
if( email != null )
{
document.getElementById( 'email' ).value = email.readUTFBytes( email.bytesAvailable );
document.getElementById( 'password' ).value = pass.readUTFBytes( pass.bytesAvailable );
document.getElementById( 'remember' ).checked = true;
} else {
document.getElementById( 'email' ).value = '';
document.getElementById( 'password' ).value = '';
document.getElementById( 'remember' ).checked = false;
}
}
It occurred to me that I could take the email address and password, and concatenate them with a comma (or the likes). Then I’d only have to store one piece of data. Geting it back out would still only be one call too, but then I’d have to String.split() the data back apart. In the end, I decided to simply store each piece of data separately in case I ever needed just the one value.
Finally comes the task of removing the data in the situation that the user no longer wants you to store it. The API makes that task easy enough by calling EncryptedLocalStore.removeItem() and specifying the name of item it is you want to remove.
function removeUser()
{
air.EncryptedLocalStore.removeItem( 'email' );
air.EncryptedLocalStore.removeItem( 'password' );
}
The harder part is deciding when to actually remove the data. Do you clear the values right when the user turns of the “Remember me”? Maybe you check the button, and then remove the values when the application closes? I chose to remove the information as soon as the button was clicked, but I’d be interested in hearing your thoughts on the matter, so drop me a comment. Example code is attached to this post, and includes one very ugly and rudimentary login screen.