Key Manager

The Key Manager (KM) provides JavaScript applications with key management and cryptographic functionality. It makes this available on the device's public bus via a number of API calls. The Key Manager implements a key store, allowing apps a place to safely keep keys where they are readily available for cryptographic operations.

The Key Manager is designed to do the following:

  • Provide apps and services easy access to cryptographic functions they need to perform local tasks safely, comply with standards, and interact with external services.

  • Protect user secrets (keys, passwords, etc.) in the event of loss or theft. Even if the device is in Developer Mode, possession alone should not provide access to secrets.

  • Protect application secrets (keys, tokens, etc.) from disclosure to the user or other applications.

  • Provide apps and services access, via a flexible and easy-to-use interface, to commonly used encryption algorithms such as AES, DES/3DES, and HMAC, and digest functions such as SHA-1.

Keys are stored encrypted in a database. They have both an app name (i.e., "com.someone.appname") and a key name. The latter is not necessarily unique -- two different apps can both have a key with the same name. However, the Key Manager service obtains the app name from the protocol, making it almost impossible for apps to use the same keys.

An app can use the KM service to store or generate a key while specifying its encryption algorithm type, length, and an option to hide the key and never return it to the application, though it remains available for cryptographic operations.

In this section:


Supported Key Types

In cryptography, a key is a piece of information (a parameter) that determines the functional output of a cryptographic algorithm or cipher. Without a key, the algorithm would have no result. In encryption, a key specifies the particular transformation of plaintext (unencrypted text) into ciphertext (encrypted text), or vice-versa during decryption. Keys are also used in other cryptographic algorithms, such as digital signature schemes and message authentication codes.

Key Manager supports the following key encryption algorithm types:

  • AES -- Advanced Encryption Standard. AES-128, AES-192, and AES-256 are supported.

  • 3DES (3 key outer CBC) -- The size includes the padding.

  • DES is supported but discouraged since it is a weak algorithm.

Note: Imported DES and 3DES keys must have valid DES parity.

  • HMAC -- SHA1 -- Hash-based Message Authentication Code - Secure Hash Algorithm-1 . Any size from 16 bytes up is supported.

  • BLOB -- For storing any type of data, up to 4k bytes.

  • ASCIIBLOB -- An ascii (non-base64 encoded) BLOB. The data must be in valid JSON format with no unescaped JSON formatting characters. This can also be used for storing up to 4k of data.


Supported Block Cipher Modes

A block cipher is a type of symmetric-key encryption algorithm that transforms a fixed-length block of plaintext data into a block of ciphertext data of the same length. This transformation takes place with the aid of a user-provided secret key. Decryption is performed by applying the reverse transformation to the ciphertext block using the same secret key. The fixed length is called the block size, and for many block ciphers, the block size is 64 bits or 128 bits.

AES and DES/3DES are the supported block ciphers. The block size for DES/3DES is 8 and for AES it's 16.

Key Manager supports the following block cipher modes:

  • CBC -- Cipher Block Chaining.
  • ECB -- Electronic CodeBook.
  • CFB -- Cipher FeedBack Mode. CFB is used for turning a block cipher into a stream cipher. Note that the drawback of stream ciphers is that reusing the key allows an attacker to recover the key stream and decrypt anything encrypted using it.

Supported Block Cipher Padding Modes

Because a block cipher works on units of a fixed size, but messages come in a variety of lengths, some modes (mainly CBC) require that the final block be padded before encryption. CBC and ECB require it unless the data is a multiple of the block size. CFB does not.

Key Manager supports the following block cipher padding modes:

  • None -- Data must be a multiple of the block size.
  • PKCS1 -- Public Key Cryptography Standards #1.

Data Formatting

Since JSON objects are composed and formatted with ascii (UTF-8) text, data to and from the Key Manager service needs to be base64 encoded to avoid having it interpreted as JSON formatting instructions. This includes key data, data to be encrypted or decrypted, and IVs (Initialization Vectors). Developers need to do their own bas64 encoding and decoding; code for this is freely available on the Internet.


Methods

  • crypt -- Encrypts or decrypts data.
  • export -- Wraps a key using a specified wrapping key.
  • fetchKey -- Returns key metadata and key data.
  • generate -- Generates a key.
  • import -- Takes a wrapped key and imports it.
  • keyInfo -- Returns key metadata but not the key data.
  • remove -- Deletes a key from the database.
  • store -- Stores a key in the database.

crypt

You can use this API to encrypt or decrypt data. The key encryption algorithm specified (AES, DES/3DES, or HMACSHA1) must be the same as the one used to create the key.

Key data needs to be base64 encoded. Since JSON objects are composed and formatted with ascii (UTF-8) text, data needs to be base64 encoded to avoid having it interpreted as JSON formatting instructions. Code for this is freely available on the Internet.

Syntax

{
    "keyname"   : string,
    "algorithm" : string,
    "pad"       : string,
    "mode"      : string,
    "iv"        : string,
    "decrypt"   : boolean,
    "data"      : string
}

Parameters

Argument Required Type Description
keyname Yes string Key name.
algorithm Yes string Possible values: "AES", "DES", "3DES", or "HMACSHA1".
pad No string Possible values: "PKCS1", or "none". Required for block ciphers.
mode No string Possible values: "CBC", "CFB", "ECB", or "none". This is only appropriate for block ciphers. none is not valid for block ciphers.
iv No string Initialization Vector -- a block of bits that is required to allow a stream cipher or a block cipher to to produce a unique stream independent from other streams produced by the same encryption key without having to go through a re-keying process. Only required for CBC and CFB modes. 16 bytes for AES, 8 bytes for DES/3DES. Should be base64-encoded.
decrypt Yes boolean false to encrypt, true to decrypt.
data Yes string Data to encrypt or decrypt. Should be base64-encoded.

Returns

{
    "returnValue" : boolean,
    "errorText"   : string, |
    "data"        : string
}
Argument Required Type Description
returnValue Yes boolean true (success) or false (failure)
errorText No string Error message returned on failure.
data No string Encrypted data.

Examples

Enyo

enyo.kind({
   name : "enyo.Canon.KeyManager",
   kind : enyo.VFlexBox,
   components : [ 
     { flex : 1,
      kind : "Pane",
      components : [{
         flex : 1,
         kind : "Scroller",
         components : [
         {
            name : "cryptData",
            kind : "PalmService",
            service : "palm://com.palm.keymanager",
            method : "crypt",
            onSuccess : "cryptDataSuccess",
            onFailure : "cryptDataFailure",
            subscribe : true
         },               
         {kind : "Button", name : "cryptDataButton", caption : "Encrypt Data", onclick : "cryptDataClick"}
        ]
      }]
    }],
    cryptDataClick: function() {    
         // Note that "data" has been base64 encoded    
         this.$.cryptData.call({ "keyname":"myKey", "algorithm":"AES", "decrypt":false, 
                                 "data":"SSB3YW50IHRoaXMgZGF0YSB0byBiZSBlbmNyeXB0ZWQ="});
    },
    cryptDataSuccess: function(inSender, inResponse) {
        this.log("Encrypt data success, results=" + enyo.json.stringify(inResponse));
    },          
    // Log errors to the console for debugging
    cryptDataFailure: function(inSender, inError, inRequest) {
        this.log(enyo.json.stringify(inError));
    }                           
});

Mojo

this.controller.serviceRequest("palm://com.palm.keymanager/", {
    method: "crypt",
    parameters: {
            "keyname"   : "myKey",
            "algorithm" : "AES",
            "decrypt"   : false,
            "data"      : B64data
    },
    onSuccess: function(e){ Mojo.Log.info("Crypt success, data="+e.data); },
    onFailure: function(e){ Mojo.Log.info("Crypt failure, err="+e.errorText); }
});

Success

 Encrypt data success, results=
 {
   "data":"cfOqsq443VfOtMBUdpCZY6aTq0LmiV7SodWlvVOEQNr8rFhhkLmMXm8nv1cvzteI",
   "returnValue":true
}

export

Wraps a key using a specified wrapping key. Both keys must belong to the same owner. A key can not wrap itself.

Syntax

{ 
    "keyname"         : string,
    "wrappingkeyname" : string
}

Parameters

Argument Type Description
keyname string Key name.
wrappingkeyname string Wrapping key name.

Returns

{
    "returnValue" : boolean,
    "errorText"   : string, |
    "wrappedkey"  : string
}
Argument Required Type Description
returnValue Yes boolean true (success) or false (failure)
errorText No string Error message returned on failure.
wrappedkey No string Wrapped key returned on success.

Examples

Mojo

 
 this.controller.serviceRequest("palm://com.palm.keymanager/", {
    method: "export",
    parameters: {
        "keyname": "MyKey",
        "wrappingkeyname": "MyWrappingKey"
    },
    onSuccess: function(e) {Mojo.Log.info("KeyManager: export success="+JSON.stringify(e));},
    onFailure: function(e) {Mojo.Log.info("KeyManager: export failure="+JSON.stringify(e));}
});

fetchKey

Returns key metadata and key data. Key data is returned if the key had been originally stored or generated with nohide = true. Otherwise, the call fails and a "key is not exportable" error message is returned.

Syntax

{
    "keyname" : string
}

Parameters

Argument Required Type Description
keyname Yes string Key name.

Returns

{
    "returnValue" : boolean,
    "errorText"   : string, |
    "keyname"     : string,
    "type"        : string,
    "keydata"     : string,
    "nohide"      : boolean,    
    "noexport"    : boolean,
    "backup"      : boolean,
    "cloud"       : boolean,
    "shared"      : boolean,
    "unwrap_only" : boolean
}
Argument Required Type Description
returnValue Yes boolean true (success) or false (failure).
errorText No string Error message returned on failure.
keyname No string Key name. Returned on success.
type No string Key type. One of the following -- "AES", "DES", "3DES", "HMACSHA1", "BLOB", or "ASCIIBLOB". Returned on success.
keydata No string Optional key data string. Returned if nohide is true. Returned on success.
nohide No boolean If true, it means key data can be returned with this call. Returned on success.
backup No boolean Is this key backed up in the cloud with the device backup? Returned on success.
noexport No boolean Key can be exported flag. Returned on success.
cloud No boolean Is this key stored in the cloud and used for backups? Returned on success.
unwrap_only No boolean If true, key can only be used to unwrap wrapped keys and no other operation. Returned on success.
shared No boolean Can other apps access the key flag. Returned on success.

Examples

luna-send

 luna-send -n 1   luna://com.palm.keymanager/fetchKey '{"keyname":"myKey"}'
{"backup":false,"cloud":false,"keydata":"VUV7/1cYn9WRRyxArRXBrg==","keyname":"myKey","noexport":false,"nohide":true,"returnValue":true,"shared":true,"type":"AES","unwrap_only":false}

Enyo

enyo.kind({
   name : "enyo.Canon.KeyManager",
   kind : enyo.VFlexBox,
   components : [ 
     { flex : 1,
      kind : "Pane",
      components : [
         {
            name : "fetchKey",
            kind : "PalmService",
            service : "palm://com.palm.keymanager",
            method : "fetchKey",
            onSuccess : "fetchKeySuccess",
            onFailure : "fetchKeyFailure",
            subscribe : true
         },
         {kind : "Button", name : "fetchKeyButton", caption : "Fetch Key", onclick : "fetchKeyClick"}        
        ]
      }]
    }],
    fetchKeyClick: function() {     
         this.$.fetchKey.call({"keyname":"myKey"});
    },
    fetchKeySuccess: function(inSender, inResponse) {
        this.log("Generate key success, results=" + enyo.json.stringify(inResponse));
    },          
    // Log errors to the console for debugging
    fetchKeyFailure: function(inSender, inError, inRequest) {
        this.log(enyo.json.stringify(inError));
    }   
});

Mojo

this.controller.serviceRequest("palm://com.palm.keymanager/", {
    method: "fetchKey",
    parameters: {
        "keyname" :"MyKey"
    },
    onSuccess: function(e){ var MyKeyB64 = Base64.decode(e.keydata); },
    onFailure: function(e){ Mojo.Log.info("fetchKey Failure, err="+e.errorText); }
});

Success

Generate key success, results=
{
   "backup":false,
   "cloud":false,
   "keydata":"VUV7/1cYn9WRRyxArRXBrg==",
   "keyname":"myKey",
   "noexport":false,
   "nohide":true,
   "returnValue":true,
   "shared":true,
   "type":"AES",
   "unwrap_only":false
}

generate

Generates a key with the name specified. If nohide = true, the key data can be accessed with fetchKey.

Supported types and their size requirements:

  • AES -- 16 (default), 24 or 32 bytes. These sizes correspond to AES 128, 192 or 256 bit.
  • DES/3DES -- 24 bytes.
  • HMACSHA1 -- 16 bytes or up.

Syntax

{
     "keyname": string,
     "size"   : integer,
     "type"   : string,
     "nohide" : boolean
}

Parameters

Argument Required Type Description
keyname Yes string Name of key to generate.
size Yes integer Key size in bytes.
type Yes string Key type. Must be one of the following -- "AES", "DES", "3DES", or "HMACSHA1".
nohide No boolean If false (default), the key data can not be exported with fetchKey.

Returns

{
    "returnValue" : boolean,
    "errorText"   : string
}
Argument Required Type Description
returnValue Yes boolean true (success) or false (failure).
errorText No string Error message returned on failure.

Examples

luna-send

 luna-send -n 1 luna://com.palm.keymanager/generate '{"keyname":"myKey", "type":"AES", "nohide": true, "size": 16}'
{"returnValue":true}

Enyo

enyo.kind({
   name : "enyo.Canon.KeyManager",
   kind : enyo.VFlexBox,
   components : [ 
     { flex : 1,
      kind : "Pane",
      components : [{
         flex : 1,
         kind : "Scroller",
         components : [{
            name : "genKey",
            kind : "PalmService",
            service : "palm://com.palm.keymanager",
            method : "generate",
            onSuccess : "genKeySuccess",
            onFailure : "genKeyFailure",
            subscribe : true
         },
         {kind : "Button", name : "genKeyButton", caption : "Generate Key", onclick : "genKeyClick"}
        ]
      }]
    }],
    genKeyClick: function() {       
         this.$.genKey.call({"keyname":"myKey", "type":"AES", "nohide": true, "size": 16});
    },
    genKeySuccess: function(inSender, inResponse) {
        this.log("Generate key success, results=" + enyo.json.stringify(inResponse));
    },          
    // Log errors to the console for debugging
    genKeyFailure: function(inSender, inError, inRequest) {
        this.log(enyo.json.stringify(inError));
    }
});

Mojo

this.controller.serviceRequest("palm://com.palm.keymanager/", {
    method: "generate",
    parameters: {
        "keyname" : "MyGenKey",
        "size"    : 16,
        "type"    : "HMACSHA1",
        "nohide"  : true
    },
    onSuccess: function(e){ Mojo.Log.info("KeyManager: Generate MyGenKey success, e="+JSON.stringify(e)); },
    onFailure: function(e){ Mojo.Log.info("KeyManager: Generate MyGenKey failure, e="+JSON.stringify(e)); }
});

import

Import takes a wrapped key and imports it. If the wrapping key is not present, the key is not installed and an error is returned. If successful, the unwrapped key's name is returned. A wrapped key can not replace an existing key with the same owner and name.

Syntax

{
    "wrappedkey": string
}

Parameters

Argument Type Description
wrappedkey string Wrapped key.

Returns

{
    "returnValue" : boolean,
    "errorText"   : string, |
    "keyname"     : string
}
Argument Required Type Description
returnValue Yes boolean true (success) or false (failure).
errorText No string Error message returned on failure. Returned on success.
keyname No string Key name.

Examples

this.controller.serviceRequest("palm://com.palm.keymanager/", {
    method: "import",
    parameters: {
        "wrappedkey": "MyWrappedKey"
    },
    onSuccess: function(e){ Mojo.Log.info("Import success, e="+JSON.stringify(e)); },
    onFailure: function(e){ Mojo.Log.info("Import failure, e="+JSON.stringify(e)); }
});

keyInfo

Returns the key's metadata but not the key data.

Syntax

{
    "keyname" : string
}

Parameters

Argument Required Type Description
keyname Yes string Key name

Returns

{
    "returnValue" : boolean,
    "errorText"   : string, |
    "keyname"     : string,
    "type"        : string,
    "nohide"      : boolean,
    "backup"      : boolean,
    "noexport"    : boolean,
    "cloud"       : boolean,
    "unwrap_only" : boolean,
    "shared"      : boolean
}
Argument Required Type Description
returnValue Yes boolean true (success) or false (failure).
errorText No string Error message returned on failure.
keyname No string Key name. Returned on success.
type No string One of the following -- "AES", "DES", "3DES", "HMACSHA1", "BLOB", or "ASCIIBLOB". Returned on success.
nohide No boolean Indicates if key data is accessible via fetchKey (true), or can only be used for operations (false). Returned on success.
backup No boolean Is this key backed up in the cloud with the device backup? Returned on success.
noexport No boolean Key can be exported flag. Returned on success.
cloud No boolean Is this key stored in the cloud and used for backups? Returned on success.
unwrap_only No boolean If true, key can only be used to unwrap wrapped keys and no other operation. Returned on success.
shared No boolean Can other apps access the key flag. Returned on success.

Examples

luna-send

luna-send -n 1 luna://com.palm.keymanager/keyInfo '{"keyname":"myKey"}'
{"backup":false,"cloud":false,"keyname":"unknown","noexport":false,"nohide":true,"returnValue":true,"shared":true,"type":"AES","unwrap_only":false}

Enyo

enyo.kind({
   name : "enyo.Canon.KeyManager",
   kind : enyo.VFlexBox,
   components : [ 
     { flex : 1,
      kind : "Pane",
      components : [{
         flex : 1,
         kind : "Scroller",
         components : [ 
         {
            name : "getKeyInfo",
            kind : "PalmService",
            service : "palm://com.palm.keymanager",
            method : "keyInfo",
            onSuccess : "getKeyinfoSuccess",
            onFailure : "getKeyinfoFailure",
            subscribe : true
         }, 
         {kind : "Button", name : "getKeyinfoButton", caption : "Get Key Info", onclick : "getKeyinfoClick"}
        ]
      }]
    }],
    getKeyinfoClick: function() {       
         this.$.getKeyInfo.call({"keyname":"myKey"});
    },
    getKeyinfoSuccess: function(inSender, inResponse) {
        this.log("Get key info success, results=" + enyo.json.stringify(inResponse));
    },          
    // Log errors to the console for debugging
    getKeyinfoFailure: function(inSender, inError, inRequest) {
        this.log(enyo.json.stringify(inError));
    }           
});

Mojo

this.controller.serviceRequest("palm://com.palm.keymanager/", {
    method: "keyInfo",
    parameters: {
        "keyname" :"MyKey"
    },
    onSuccess: function(e){ Mojo.Log.info("keyInfo Success: type= "+e.type); },
    onFailure: function(e){ Mojo.Log.info("keyInfo Failure err="+JSON.stringify(e)); }
});

Success

Get key info success, results=
{
   "backup":false,
   "cloud":false,
   "keyname":"com.palmdts.enyo.keymanager",
   "noexport":false,
   "nohide":true,
   "returnValue":true,
   "shared":true,
   "type":"AES",
   "unwrap_only":false
}

remove

Given a key name, removes a key from the key store.

Syntax

{
    "keyname" : string
}

Parameters

Argument Required Type Description
keyname Yes string Key name.

Returns

{
    "returnValue" : boolean,
    "errorText"   : string
}
Argument Required Type Description
returnValue Yes boolean true (success) or false (failure).
errorText No string Error message on failure.

Examples

luna-send

 luna-send -n 1 -a com.palmdts.enyo.keymanager luna://com.palm.keymanager/remove '{"keyname":"myStoredKey"}'
{"returnValue":true}

Enyo

enyo.kind({
   name : "enyo.Canon.KeyManager",
   kind : enyo.VFlexBox,
   components : [ 
     { flex : 1,
      kind : "Pane",
      components : [{
         flex : 1,
         kind : "Scroller",
         components : [             
         {
            name : "removeKey",
            kind : "PalmService",
            service : "palm://com.palm.keymanager",
            method : "remove",
            onSuccess : "removeKeySuccess",
            onFailure : "removeKeyFailure",
            subscribe : true
         },            
         {kind : "Button", name : "removeKeyButton", caption : "Remove Key", onclick : "removeKeyClick"}
        ]
      }]
    }],
    removeKeyClick: function() {        
         this.$.removeKey.call({ "keyname":"myStoredKey"});
    },
    removeKeySuccess: function(inSender, inResponse) {
        this.log("Remove key success, results=" + enyo.json.stringify(inResponse));
    },          
    // Log errors to the console for debugging
    removeKeyFailure: function(inSender, inError, inRequest) {
        this.log(enyo.json.stringify(inError));
    }                               
});

Mojo

this.controller.serviceRequest("palm://com.palm.keymanager/", {
    method: "remove",
    parameters: {
        "keyname" : "MyKey4"
    },
    onSuccess: function(e){ Mojo.Log.info("Remove key success"); },
    onFailure: function(e){ Mojo.Log.info("Remove key failure, err="+JSON.stringify(e)); }
});

store

Stores a key in the database given a key name, data, key encryption algorithm type, and other optional parameters.

Key data needs to be base64 encoded unless its type is ASCIIBLOB. Since JSON objects are composed and formatted with ascii (UTF-8) text, data needs to be base64 encoded to avoid having it interpreted as JSON formatting instructions. Code for this is freely available on the Internet.

Type must be one of the supported key types -- AES, DES/3DES, HMACSHA1, BLOB, or ASCIIBLOB. BLOB is not actually a key type, but can be used to safely store data (up to 4096 bytes) of any kind. ASCIIBLOB is the same as BLOB, but for storing ascii, JSON formatted text.

If nohide is set to false (default), the key data can not be exported with fetchKey. BLOB types should not have nohide set, as that makes them useless.

The backup flag (keys backed up in the cloud with the device backup) is false by default.

The cloud flag indicates a key which is stored in the cloud and is a special key for backups. If true, The type should be ASCIIBLOB and the keydata contains the backup-specific part of the URL for fetching the key.

Syntax

{
    "keyname" : string,
    "keydata" : string,
    "type"    : string,
    "nohide"  : boolean,
    "backup"  : boolean,
    "cloud"   : boolean
}

Parameters

Argument Required Type Description
keyname Yes string Key name.
keydata Yes string Key data. Key data needs to be base64 encoded unless its type is ASCIIBLOB.
type Yes string Key algroithm encryption type. Must be one of the following -- AES, DES, 3DES, HMACSHA1, BLOB, or ASCIIBLOB. DES/3DES keys must be properly formed with DES parity bits and weak key checking already done.
nohide No boolean If false (default), the key data can not be exported with fetchKey.
backup No boolean If true, this key is backed up in the cloud with the device backup. This field is false by default.
cloud No boolean Indicates a key which is stored in the cloud and is a special key for backups. The type should be ASCIIBLOB and the keydata contains the backup-specific part of the URL for fetching the key.

Returns

{ 
    "returnValue" : boolean,
    "errorText"   : string
}
Argument Required Type Description
returnValue Yes boolean true (success) or false (failure).
errorText No string Error message returned on failure.

Examples

Enyo

enyo.kind({
   name : "enyo.Canon.KeyManager",
   kind : enyo.VFlexBox,
   components : [ 
     { flex : 1,
      kind : "Pane",
      components : [{
         flex : 1,
         kind : "Scroller",
         components : [ {
            name : "storeKey",
            kind : "PalmService",
            service : "palm://com.palm.keymanager",
            method : "store",
            onSuccess : "storeKeySuccess",
            onFailure : "storeKeyFailure",
            subscribe : true
         }, 
         {kind : "Button", name : "storeKeyButton", caption : "Store Key", onclick : "storeKeyClick"}
        ]
      }]
    }],
    storeKeyClick: function() { 
         // Note that keydata has been base64 encoded    
         this.$.storeKey.call({"keyname":"myStoredKey", "keydata":"dmdyZmY0NWU4OTBnZGVzMg==", "type":"AES", 
                               "nohide": "true", "backup":true, "cloud":true});
    },
    storeKeySuccess: function(inSender, inResponse) {
        this.log("Store key success, results=" + enyo.json.stringify(inResponse));
    },          
    // Log errors to the console for debugging
    storeKeyFailure: function(inSender, inError, inRequest) {
        this.log(enyo.json.stringify(inError));
    }                   
});

Mojo

//  Note that Base64 encoding functionality is something YOU need to provide. 
// "KeyDataB64" is 16 bytes.

KeyDataB64 = Base64.encode(KeyData);
this.controller.serviceRequest("palm://com.palm.keymanager/", {
    method: "store",
    parameters: {
        "keyname" : "MyKey",
        "keydata" : KeyDataB64,
        "type"    : "AES",
        "nohide"  : true
    },
    onSuccess: function(e){ Mojo.Log.info("Store key success"); },
    onFailure: function(e){ Mojo.Log.info("Store key failure, err="+JSON.stringify(e)); }
});