Chapter 11. Encrypted Local Store

Adobe AIR offers several ways to store data locally. You have the legacy option of storing data as a shared object, you can also access the local file system to read and write data, and finally you can write to an embedded SQLite database. When it comes to securing pieces of data like passwords or access keys, your best option is to use the built-in encrypted data store, defined by the EncryptedLocalStore class.

Note

If you need to secure sets of structured data, your best option is to use the embedded SQLite database encryption support covered in Creating an Encrypted Database and Encrypting a Database with a Password.

The data within this store is housed within DPAPI on Windows and within Keychain on Mac OS X using AES-CBC 128-bit encryption.

Once data is written to the encrypted local store, it is accessible only by the application that wrote the data. In addition, the data is stored in a user-specific directory, so each user on the operating system will have a different encrypted local store for the application. For even more restrictive security on data, the encrypted local store also offers an option that ensures that any application attempting to get data from EncryptedLocalStore not only has the correct publisher ID but also has had no changes made to the application directory.

The recipes in this chapter will demonstrate how to store and retrieve data from EncryptedLocalStore.

Storing Data in the Encrypted Local Store

Problem

You would like your application to store a serial number that will be used to verify the user with your remote server for data retrieval. It is critical that this information be private and secure.

Solution

Use the EncryptedLocalStore class, which is the built-in storage solution within AIR for persisting secure information.

Discussion

The EncryptedLocalStore class includes methods to save, retrieve, and remove data from the secure local store. To store data to the encrypted local store, you must first convert it to binary. To convert the data to binary, you can use the ByteArray class. The ByteArray class is located within the flash.utils package and contains methods to read and write data of many different data types to the byte stream.

ActionScript/Flex

To convert a simple piece of String data to a ByteArray, you can use the writeUTFBytes method. The following simple example takes a variable serialNumber and converts it to a ByteArray using the writeUTFBytes method of the ByteArray class. For example:

var serialNumber:String = "0000-1234-7777-9876"
var bytes:ByteArray = new ByteArray();
bytes.writeUTFBytes(serialNumber);

Now that you have the data in the format needed to save to the encrypted local store, you can complete this simple example by including a fourth line of code, which sets the bytes into the encrypted local store:

EncryptedLocalStore.setItem("serialNumber", bytes);

The following full example shows a simple form where a user can enter a serial number and persist it to the encrypted local store:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">

    <mx:Script>
        <![CDATA[

            private function saveToLocalStore():void{
                var bytes:ByteArray = new ByteArray();
                bytes.writeUTFBytes(serialNumberTxt.text);
                EncryptedLocalStore.setItem("serialNumber", bytes);
            }

        ]]>
    </mx:Script>

    <mx:HBox horizontalCenter="0" y="134">
        <mx:Label text="Serial Number"/>
        <mx:TextInput id="serialNumberTxt"/>
        <mx:Button label="Save to the encrypted local store"
                        click="saveToLocalStore()"/>
    </mx:HBox>

</mx:WindowedApplication>

JavaScript

To convert a simple piece of String data to a ByteArray, you can use the writeUTFBytes method. The following simple example takes a variable serialNumber and converts it to a ByteArray using the writeUTFBytes method of the ByteArray class:

var serialNumber = "0000-1234-7777-9876"
var bytes = new air.ByteArray();
bytes.writeUTFBytes(serialNumber);

Now that you have the data in the format needed to save to the encrypted local store, you can complete this simple example by including a fourth line of code, which sets the bytes into the encrypted local store:

air.EncryptedLocalStore.setItem("serialNumber ", bytes);

The following full example shows a simple form where a user can enter a serial number and persist it to the encrypted local store:

<html>
    <head>
    <script src="AIRAliases.js" />
    <script>

          function saveToLocalStore(){
               var bytes = new air.ByteArray();
               bytes.writeUTFBytes(document.theform.serialNumberTxt.value);
               air.EncryptedLocalStore.setItem("serialNumber", bytes);
           }

    </script>
    </head>
    <body>
          <form name="theform">
               Serial Number: <input type="text" size="20" name="serialNumberTxt"/>
          <input type="button" value="Save to the encrypted local store"
                     onclick="saveToLocalStore()"/>
          </form>
    </body>
</html>

Retrieving Data from the Encrypted Local Store

Problem

You need to retrieve secure data you added to the encrypted local store.

Solution

Use the getItem function of the EncryptedLocalStore class to retrieve your data from the local store.

Discussion

As mentioned in Storing Data in the Encrypted Local Store, all data stored within the encrypted local store is stored in binary format as a flash.utils.ByteArray and assigned a String identifier. To retrieve data from the encrypted local store, you need to know the identifier that it was stored with and also be working within the same AIR application that originally stored the data.

ActionScript/Flex

In the following example, which extends Storing Data in the Encrypted Local Store, the getItem method is called with an identifier of serialNumber passed in:

var data:ByteArray = EncryptedLocalStore.getItem("serialNumber");

Now that you have retrieved the data from the encrypted local store, the only thing left to do is to use the correct ByteArrayretrieving data in encrypted local stores function to parse the data from binary back to a human-readable format.

The following example adds one line of code, which uses the readUTFBytes method to parse the data object into a String. This method also requires a single argument that passes the length of the data object.

var data:ByteArray = EncryptedLocalStore.getItem("serialNumber");
var serialNumber:String = data.readUTFBytes(data.length);

Starting with the code from Storing Data in the Encrypted Local Store, the following example adds the functionality to retrieve the data from the encrypted local store and display it within a TextInput control:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">

    <mx:Script>
        <![CDATA[

            private function saveToLocalStore():void{
                var bytes:ByteArray = new ByteArray();
                bytes.writeUTFBytes(serialNumberTxt.text);
                EncryptedLocalStore.setItem("serialNumber", bytes);
            }

            private function retrieveFromLocalStore():void {
                var data:ByteArray = EncryptedLocalStore.getItem("serialNumber");
                serial.text = data.readUTFBytes(data.length);
            }

        ]]>
    </mx:Script>

    <mx:HBox horizontalCenter="0" y="134">
        <mx:Label text="Serial Number"/>
        <mx:TextInput id="serialNumberTxt"/>
        <mx:Button label="Save to the encrypted local store"
                        click="saveToLocalStore()"/>
    </mx:HBox>

    <mx:Button label="Retrieve from the encrypted local store"
                click="retrieveFromLocalStore()" x="263" y="181"/>

    <mx:TextInput id="serial" x="41" y="181" width="214"/>

</mx:WindowedApplication>

JavaScript

In the following example, which extends Storing Data in the Encrypted Local Store, the getItem method is called with an identifier of serialNumber passed in:

var data = air.EncryptedLocalStore.getItem("serialNumber");

Now that you have retrieved the data from the encrypted local store, the only step left to take is to use the correct ByteArray function to parse the data from binary back to a human-readable format.

The following example adds one line of code, which uses the readUTFBytes method to parse the data object into a String. This method also requires a single argument that passes the length of the data object.

var data = air.EncryptedLocalStore.getItem("serialNumber");
var serialNumber = data.readUTFBytes(data.length);

Starting with the code from Storing Data in the Encrypted Local Store, the following example adds the functionality to retrieve the data from the encrypted local store and display it within an Input form field:

<html>
    <head>
    <script src="AIRAliases.js" />
    <script>

          function saveToLocalStore(){
               var bytes = new air.ByteArray();
               bytes.writeUTFBytes(document.theform.serialNumberTxt.value);
               air.EncryptedLocalStore.setItem("serialNumber", bytes);
          }

          function retrieveFromLocalStore(){
               var data = air.EncryptedLocalStore.getItem("serialNumber");
               document.theform.serialNumber.value =
               data.readUTFBytes(data.length);
          }

          </script>
    </head>
    <body>
          <form name="theform">
          Serial Number: <input type="text" size="20" name="serialNumberTxt"/>
          <input type="button" value="Save to the encrypted local store"
                      onclick="saveToLocalStore()"/>
          <br/>
          <input type="text" size="20" name="serialNumber"/>
          <input type="button" value="Retrieve from the encrypted local store"
                      onclick="retrieveFromLocalStore()"/>
          </form>
    </body>
</html>

Removing and Resetting Data in the Encrypted Local Store

Problem

You have data being stored within the encrypted local store that you would like to delete.

Solution

Use either the removeItem method (which will remove the single item) or the reset method (which will clear the entire store) of the EncryptedLocalStore class to remove data from the encrypted local store.

Discussion

Data stored in encrypted local store persists until either the application that created it removes it by identifier or the encrypted local store is reset. Even uninstalling the application will not remove the data that was persisted, so it is up to the developer to remove any data that is no longer necessary.

To remove a single item from the local store, you use the removeItem method. This method accepts a single argument of type String that is the identifier that the data was associated with when persisted.

ActionScript/Flex

The following example removes an item under the identifier serialNumber from the encrypted local store:

EncryptedLocalStore.removeItem("serialNumber");

The second way to remove data from the encrypted local store is to use the reset method. The reset method will remove all data that is stored within the EncryptedLocalStore that is being stored for the accompanying application.

The following example utilizes the reset method to clear all data from the EncryptedLocalStore:

EncryptedLocalStore.reset();

The following full example includes code to save data to the encrypted local store from Storing Data in the Encrypted Local Store and includes code to retrieve data from the encrypted local store from Retrieving Data from the Encrypted Local Store. It also adds the functions to remove data or reset the entire encrypted local store data set associated with the application.

Note that if you attempt to retrieve data with an identifier that has been removed or not yet saved, you will get a runtime error (specifically, error #1009: “Cannot access a property or method of a null object reference”).

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" 
layout="absolute">

    <mx:Script>
        <![CDATA[

            private function saveToLocalStore():void{
                var bytes:ByteArray = new ByteArray();
                bytes.writeUTFBytes(serialNumberTxt.text);
                EncryptedLocalStore.setItem("serialNumber", bytes);
            }

            private function retrieveFromLocalStore():void {
                var data:ByteArray = EncryptedLocalStore.getItem("serialNumber");
                serial.text = data.readUTFBytes(data.length);
            }

            private function removeFromLocalStore():void{
                EncryptedLocalStore.removeItem("serialNumber");
            }

            private function resetLocalStore():void{
                EncryptedLocalStore.reset();
            }

        ]]>
    </mx:Script>

    <mx:HBox horizontalCenter="0" y="134">
        <mx:Label text="Serial Number"/>
        <mx:TextInput id="serialNumberTxt"/>
        <mx:Button label="Save to the encrypted local store"
                        click="saveToLocalStore()"/>
    </mx:HBox>

    <mx:Button label="Retrieve from the encrypted local store"
                click="retrieveFromLocalStore()" x="263" y="181"/>

    <mx:TextInput id="serial" x="41" y="181" width="214"/>

    <mx:Button x="153" y="220" label="Remove from the encrypted local store"
        click="removeFromLocalStore()"/>

    <mx:Button x="164.5" y="250" label="Reset the encrypted local store"
        click="resetLocalStore()"/>

</mx:WindowedApplication>

JavaScript

The following example removes an item under the identifier serialNumber from the encrypted local store:

air.EncryptedLocalStore.removeItem("serialNumber");

The second way to remove data from the encrypted local store is to use the reset method. The reset method will remove all data that is stored within the EncryptedLocalStore that is being stored for the accompanying application.

The following example utilizes the reset method to clear all data from the EncryptedLocalStore:

air.EncryptedLocalStore.reset();

The following full example includes code to save data to the encrypted local store from Storing Data in the Encrypted Local Store and includes code to retrieve data from the encrypted local store from Retrieving Data from the Encrypted Local Store. It also adds the functions to remove data or reset the entire EncryptedLocalStore.

<html>
    <head>
    <script src="AIRAliases.js" />
    <script>

    function saveToLocalStore(){
          var bytes = new air.ByteArray();
          bytes.writeUTFBytes(document.theform.serialNumberTxt.value);
          air.EncryptedLocalStore.setItem("serialNumber", bytes);                  }

    function retrieveFromLocalStore(){
          var data = air.EncryptedLocalStore.getItem("serialNumber");
          document.theform.serialNumber.value = data.readUTFBytes(data.length);    }

    function removeFromLocalStore(){
          air.EncryptedLocalStore.removeItem("serialNumber");
    }

    function resetLocalStore(){
          air.EncryptedLocalStore.reset();
    }

    </script>
    </head>
    <body>
          <form name="theform">
          Serial Number: <input type="text" size="20" name="serialNumberTxt"/>
          <input type="button" value="Save to the encrypted local store"
                      onclick="saveToLocalStore()"/>
          <br/>
          <input type="text" size="20" name="serialNumber"/>
          <input type="button" value="Retrieve from the encrypted local store"
                      onclick="retrieveFromLocalStore()"/>
          <br/>
          <input type="button" value="Remove from the encrypted local store"
                      onclick="removeFromLocalStore()"/>
          <br/>
          <input type="button" value="Reset the encrypted local store"
                      onclick="resetLocalStore()"/>
          </form>
    </body>
</html>

Storing Application-Specific Data in the Encrypted Local Store

Problem

Your client requires more data security than the standard encrypted local store settings enable.

Solution

When adding data to the encrypted local store, set stronglyBound, the optional third argument of the setItem method, to true.

Discussion

Data stored within the encrypted local store is accessible to only the AIR application that originally saved the data because it is bound to the application’s publisher ID. However, for an even higher level of security, you can set the stronglyBound argument of the setItem method to true. Setting this property to true within the setItem method binds the data to the actual bits of the application. This ensures that any application that attempts to get data from the encrypted local store not only has the correct publisher ID but also has had no changes made to its application directory.

Be aware, however, that this extra layer of protection comes with some trade-off. Because no changes can be made to the application directory, you will lose any stronglyBound data stored within the encrypted local store when your application is updated to a newer version. To avoid runtime errors, you must account for this when coding your application with stronglyBound data.

ActionScript/Flex

The following example is identical to the one used within Storing Data in the Encrypted Local Store with the only change being the optional third argument of the setItem method set to true. This alters the data being stored within the encrypted local store to be stronglyBound.

var serialNumber:String = "0000-1234-7777-9876"
var bytes:ByteArray = new ByteArray();
bytes.writeUTFBytes(serialNumber);
EncryptedLocalStore.setItem("serialNumber ", bytes, true);

Here is a full example showing a simple form where a user can enter a serial number and persist it to the encrypted local store where the data is stronglyBound to the application:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" 
layout="absolute">

    <mx:Script>
        <![CDATA[

            private function saveToLocalStore():void{
                var bytes:ByteArray = new ByteArray();
                bytes.writeUTFBytes(serialNumberTxt.text);
                EncryptedLocalStore.setItem("serialNumber", bytes, true);
            }

        ]]>
    </mx:Script>

    <mx:HBox horizontalCenter="0" y="134">
        <mx:Label text="Serial Number"/>
        <mx:TextInput id="serialNumberTxt"/>
        <mx:Button label="Save to the encrypted local store"
                        click="saveToLocalStore()"/>
    </mx:HBox>

</mx:WindowedApplication>

JavaScript

The following example is identical to the one used within Storing Data in the Encrypted Local Store with the only change being the optional third argument of the setItem method set to true. This alters the data being stored within the encrypted local store to be stronglyBound.

var serialNumber = "0000-1234-7777-9876"
var bytes = new air.ByteArray();
bytes.writeUTFBytes(serialNumber);
air.EncryptedLocalStore.setItem("serialNumber", bytes, true);

Here is a full example showing a simple form where a user can enter a serial number and persist it to the encrypted local store where the data is stronglyBound to the application:

<html>
    <head>
    <script src="AIRAliases.js" />
    <script>

          function saveToLocalStore(){
               var bytes = new air.ByteArray();
               bytes.writeUTFBytes(document.theform.serialNumberTxt.value);
               air.EncryptedLocalStore.setItem("serialNumber", bytes, true);
          }

    </script>
    </head>
    <body>
          <form name="theform">
               Serial Number: <input type="text" size="20" name="serialNumberTxt"/>
          <input type="button" value="Save to the encrypted local store"
                     onclick="saveToLocalStore()"/>
          </form>
    </body>
</html>

Safeguarding Files with Encrypted Local Store

Contributed by Ryan Stewart (http://blog.digitalbackcountry.com/)

Problem

You need to protect entire files inside an AIR application.

Solution

Using the EncryptedLocalStore and the File APIs you can put a file in a safe and secure location.

Discussion

Most people associate the encrypted local store with storing usernames and passwords or other bits of text. But thanks to the fact that EncryptedLocalStore uses instances of the ByteArray class, it’s easy to throw all kinds of things in there. This recipe offers two examples for saving data into the encrypted local store and loading it back out.

There are couple of items to keep in mind when working with entire files in EncryptedLocalStore. First, the EncryptedLocalStore is supported only up to 10MB. It can go higher, but you may see performance problems. In addition, the encrypted local store isn’t cleared out when the application is uninstalled. You may have to manually clear it out using the reset method.

In this example, two methods handle the interaction with the EncryptedLocalStore: saveFile and loadFile.

The saveFile function takes a File object (which could be from a drag-and-drop operation or a file open dialog box), and it also contains start a ByteArray that will store the file data. Just like any other File API, there needs to be a stream to put the bytes into the ByteArray. When that is complete, the application grabs the name of the file so that it can reference it later. It then uses EncryptedLocalStore.setItem to add the file data to the encrypted local store.

The next function is the loadFile function. It takes the data out of EncryptedLocalStore and saves it to the hard drive. It also takes a File object so that the application knows where to store the file when it is finished reading it. Just like with any other File API, a ByteArray is created that will contain the file bits. Then the EncryptedLocalStore.getItem method is called to grab the data out of the encrypted local store. When that is complete, a new FileStream is created, and it writes that file data to the file.

If you ever need to clear the encrypted local store, you can just call the EncryptedLocalStore.reset function, and that will wipe away the data. The EncryptedLocalStore APIs are some of the easiest in AIR, but they allow for the ability to save and protect all kinds of data thanks to the ByteArray.

ActionScript/Flex

Here is the completed application in Flex with both the saveFile and loadFile methods:

<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" 
layout="absolute">
    <mx:Script>
          <![CDATA[
               import mx.events.FileEvent;

               public function onSaveClick(event:Event) : void
               {
                   // Clear out everything
                       EncryptedLocalStore.reset();

                   var file : File = new File();
                       file.browseForOpen("Save File in Encrypted Local Store");
                       // Add the event listener for the selection
                       file.addEventListener(Event.SELECT,
                           function onSaveSelect(event:Event) : void
                           {
                               saveFile(event.currentTarget as File);
                           });
               }

               public function saveFile(file:File) : void
               {
                   var stream : FileStream = new FileStream();

                   var filedataArray : ByteArray = new ByteArray();
                       stream.open(file, FileMode.READ);
                       stream.readBytes(filedataArray, 0, file.size);
                       stream.close();
                       // Set the file name so we can pull it out later.
                       fileName = file.name;

                       // Set the item in the Encrypted Local Store
                       EncryptedLocalStore.setItem(fileName,filedataArray);
               }

               public function onLoadClick(event:Event) : void
               {
                   var file : File = File.desktopDirectory.resolvePath(fileName);
                   file.addEventListener(Event.SELECT,
                       function onLoadSelect(event:Event) : void
                       {
                           loadFile(event.currentTarget as File);
                       });
                   file.browseForSave("Load File From Encrypted Local Store");
               }

               public function loadFile(file:File) : void
               {
                   // Create the ByteArray and pull it out of the local store
                   var byteArray : ByteArray = new ByteArray();
                   byteArray = EncryptedLocalStore.getItem(fileName);

                   // Open and write the file using the regular File APIs
                   var stream : FileStream = new FileStream();
                   stream.open(file, FileMode.WRITE)
                   stream.writeBytes(byteArray);
                   stream.close();
               }

               private var fileName : String;

          ]]>
    </mx:Script>
    <mx:Label text="Click the button below to save a file into the 
Encrypted Local Store" />
    <mx:Button id="btnSave" click="onSaveClick(event)" label="Save
 File to Encrypted Local Store" />
    <mx:Label text="Click the button below to save the file from the Encrypted 
Local Store back to your hard drive" />
    <mx:Button id="btnLoad" click="onLoadClick(event)" label="Load File from 
Encrpted Local Store" y="50" />
</mx:WindowedApplication>

JavaScript

Here is the completed application in JavaScript with both the saveFile and loadFile methods:

<html>
    <head>
        <title>Encrypted Local Store File Storage</title>
        <script type="text/javascript" src="AIRAliases.js"></script>
        <script type="text/javascript">

               var fileName;

            function onSaveClick(event) {
                     // Clear out everything
                     air.EncryptedLocalStore.reset();
                     var file = new air.File();
                     file.browseForOpen("Save File in Encrypted Local Store");
                     // Add the event listener for the selection
                     file.addEventListener(air.Event.SELECT,onSaveSelect);
               }

               function onSaveSelect(event) {
                     saveFile(event.currentTarget);
               }

               function saveFile(file) {
                     var stream = new air.FileStream();

                     var filedataArray = new air.ByteArray();
                     stream.open(file, air.FileMode.READ);
                     stream.readBytes(filedataArray, 0, file.size);
                     stream.close();
                     // Set the file name so we can pull it out later.
                     fileName = file.name;
                     // Set the item in the Encrypted Local Store
                     air.EncryptedLocalStore.setItem(fileName,filedataArray);
               }

               function onLoadClick(event) {
                     var file = air.File.desktopDirectory.resolvePath(fileName);
                     file.addEventListener(air.Event.SELECT,onLoadSelect);
                     file.browseForSave("Load File From Encrypted Local Store");
               }

               function onLoadSelect(event) {
                     loadFile(event.currentTarget);
               }

               function loadFile(file) {
                     // Create the ByteArray and pull it out of the local store
                     var byteArray = new air.ByteArray();
                     byteArray = air.EncryptedLocalStore.getItem(fileName);

                     // Open and write the file using the regular File APIs
                     var stream = new air.FileStream();
                     stream.open(file, air.FileMode.WRITE)
                     stream.writeBytes(byteArray);
                     stream.close();
               }

        </script>
    </head>

    <body>
    <p>Click the button below to save a file into the Encrypted Local Store</p>
    <input type="button" value="Save File to Encrypted Local Store" 
onclick="onSaveClick(event)" /><br />
    <p> Click the button below to save the file from the Encrypted Local Store back 
to your hard drive</p>
    <input type="button" value="Load File from Encrpted Local Store" 
onclick="onLoadClick(event)" />
    </body>
</html>
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset