10

I want to use Web Cryptography API on an already existing project. To encrypt and decrypt something I have to use an CryptoKey, but when I save to CryptoKey into localStorage it only saves the String (CryptoKey) instead of the object.

Is it possible to serialize / convert an CryptoKey in an simple type (string)?

My decrypting method is

function decryptDataWithAES(keyName)
{
    var decrypt_promise; 
    var aesKey = localStorage.getItem(keyName + 'key')
    var item = localStorage.getItem(keyName)
    var invokeVektor = localStorage.getItem(keyName + 'vector')
    console.log("aesKey", aesKey )

    crypto.subtle.decrypt({ name: "AES-CBC", iv: invokeVektor }, aesKey, item).then(function (result) {
        decrypted_data = new Uint8Array(result); decrypted_data = new Uint8Array(result);

        decrypt_promise = convertArrayBufferViewtoString(decrypted_data);
        console.log('decryptDataWithAES ' + decrypt_promise);
        return decrypt_promise; 
    },
        function(e){
            console.log(e.message);
        }
    );
}

The error message is of course:

Failed to execute 'decrypt' on 'SubtleCrypto': parameter 2 is not of type 'CryptoKey'. 2localStorageHandler.js:39 CryptoPromise[object CryptoKey]

If I decrypt without using localStorage there is no problem with encrypting the data.

fracz
  • 18,175
  • 16
  • 93
  • 143

2 Answers2

6

Consider using crypto.subtle.exportKey() and crypto.subtle.importKey() before saving it in localStorage, so your decryption code would be like this:

function decryptDataWithAES(keyName)
{
    var decrypt_promise; 

    // read raw value of aesKey
    var aesKey_RAW = localStorage.getItem(keyName + 'key')
    var importPromise = crypto.subtle.importKey('raw', aesKey_RAW, 'AES-CBC', true, ['encrypt','decrypt']);

    importPromise.then(function(aesKey){

    var item = localStorage.getItem(keyName)
    var invokeVektor = localStorage.getItem(keyName + 'vector')

        console.log("aesKey", aesKey )

        crypto.subtle.decrypt({ name: "AES-CBC", iv: invokeVektor }, aesKey, item).then(function (result) {
            decrypted_data = new Uint8Array(result); decrypted_data = new Uint8Array(result);

            decrypt_promise = convertArrayBufferViewtoString(decrypted_data);
            console.log('decryptDataWithAES ' + decrypt_promise);
            return decrypt_promise; 
        },
            function(e){
                console.log(e.message);
            }
        );

    }, function(e){ console.log(e.message) } );
}

To save your key in raw format in localStorage:

function saveKeyInLocalStorage(keyName, aesKey){
   var exportPromise = crypto.subtle.exportKey('raw',aesKey);
   exportPromise.then(function(aesKey_RAW){ 
        localStorage.setItem(keyName + 'key' , aesKey_RAW);
        console.log("saved.");
   });
}

Note that both exportKey() and importKey() methods returns a promise.

Shadi Shaaban
  • 1,505
  • 1
  • 7
  • 16
  • 1
    I have still a problems with exporting the raw key. I call the saveKeyInLocalStorage function `cryptoTestObject = crypto.subtle.generateKey(...) .then(function (key) { saveKeyInLocalStorage(keyName, key); ` and receive then in the exportPromise the error message in Chrome:`DOMException: key is not extractable (InvalidAccessError)`and in Firefox `A parameter or an operation is not supported by the underlying object (InvalidAccessError)` – AntonDerProgrammierer Jun 28 '16 at 13:09
  • I do not think you can store the keys in `localStorage`, you must use IndexedDB. – dendog Mar 26 '18 at 14:52
2

Some of the keys cannot be exported in RAW format. But so far it seems the JWK (json web key) format is supported everywhere, so you can use it.

// to store the key:
window.crypto.subtle.exportKey("jwk", key)
.then(e=>localStorage.setItem("webkey",JSON.stringify(e)));

Similarly you can importKey() back, this varies depending on your key algo. For syntax, see https://github.com/diafygi/webcrypto-examples/

Tomas M
  • 5,535
  • 5
  • 24
  • 31