QNimate

  • CoursesVideos
  • WP PremiumPlugins
  • DemosLab
  • Home
  • QIdea
  • QTrack
Home Carbon Ads Asymmetric Encryption using Web Cryptography API

Asymmetric Encryption using Web Cryptography API

This post is a part 3 of Web Cryptography API Tutorial post series.
⎘ Next post in the series is Digital Signature using Web Cryptography API.
⎗ Previous post in the series is Symmetric Encryption using Web Cryptography API.

In this tutorial I will show you code example of how to generate keys of asymmetric encryption and encrypt & decrypt data using the generated keys.

What is Asymmetric Encryption?

Asymmetric encryption consists of two keys i.e., public and private keys. Data encrypted with public key can only be decrypted with private key and vice versa.

Asymmetric encryption and decryption is slow therefore they asymmetric encryption is always used to exchange symmetric key.

Asymmetric encryption is also used for signing and verifying digital signatures.

Converting String to Array Buffer and Array Buffer to String

To encrypt text or binary data you first need to convert it to an array buffer type so that Web Cryptography API can encrypt it.

Here is code to convert a string to an array buffer

function convertStringToArrayBufferView(str)
{
    var bytes = new Uint8Array(str.length);
    for (var iii = 0; iii < str.length; iii++)
    {
        bytes[iii] = str.charCodeAt(iii);
    }

    return bytes;
}

After decrypting, Web Cryptography API returns original string as an array buffer. So we need to convert the array buffer to an string.

function convertArrayBufferViewtoString(buffer)
{
    var str = "";
    for (var iii = 0; iii < buffer.byteLength; iii++)
    {
        str += String.fromCharCode(buffer[iii]);
    }

    return str;
}

Generating Public and Private Key

Here is the code to generate public and private keys

var private_key_object = null;
var public_key_object = null;

var promise_key = null;

var crypto = window.crypto || window.msCrypto;

if(crypto.subtle)
{
    alert("Cryptography API Supported");
   
    //Parameters:
    //1. Asymmetric Encryption algorithm name and its requirements
    //2. Boolean indicating extractable. which indicates whether or not the raw keying material may be exported by the application (http://www.w3.org/TR/WebCryptoAPI/#dfn-CryptoKey-slot-extractable)
    //3. Usage of the keys. (http://www.w3.org/TR/WebCryptoAPI/#cryptokey-interface-types)
    promise_key = crypto.subtle.generateKey({name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: {name: "SHA-256"}}, false, ["encrypt", "decrypt"]);

    promise_key.then(function(key){
        private_key_object = key.privateKey;
        public_key_object = key.publicKey;
    });

    promise_key.catch = function(e){
        console.log(e.message);
    }
   
}
else
{
    alert("Cryptography API not Supported");
}

Encrypting String

Here is the code to encrypt an JavaScript string using the retrieved private key

var encrypted_data = null;
var encrypt_promise = null;

var data = "QNimate";

//iv: Is initialization vector. It must be 16 bytes
var vector = crypto.getRandomValues(new Uint8Array(16));

function encrypt_data()
{
    encrypt_promise = crypto.subtle.encrypt({name: "RSA-OAEP", iv: vector}, public_key_object, convertStringToArrayBufferView(data));

    encrypt_promise.then(
        function(result){
            encrypted_data = new Uint8Array(result);
        },
        function(e){
            console.log(e.message);
        }
    );
}

Decrypting String

Here is the code to decrypt an JavaScript string using the retrieved public key

var decrypt_promise = null;
var decrypted_data = null;

function decrypt_data()
{
    decrypt_promise = crypto.subtle.decrypt({name: "RSA-OAEP", iv: vector}, private_key_object, encrypted_data);

    decrypt_promise.then(
        function(result){
            decrypted_data = new Uint8Array(result);
            console.log(convertArrayBufferViewtoString(decrypted_data));
        },
        function(e){
            console.log(e.message);
        }
    );
}

Importing and Exporting Keys

Sometimes you may need to send the keys to the server. And also import keys from server and use it in browser.

You can use crypto.subtle.exportKey to convert the keys into many different exportable formats and then export them.

Similarly you can use crypto.subtle.importKey to convert the exported keys into an key objects for use.

Here is an sample code showing how to import and export the generated private key

var json_private_key = null;
var string_private_key = null;

crypto.subtle.exportKey("jwk", private_key_object).then(function(result){
   
    json_private_key = result;
    string_private_key = JSON.stringify(json_private_key);

    crypto.subtle.importKey("jwk", JSON.parse(string_private_key), {name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: {name: "SHA-256"}}, true, ["decrypt"]).then(function(e){
        console.log(e);
    }, function(e){
        console.log(e);
    });

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

Here we first converted the private key to JWK format and then to an string. You can send the string_private_key to server which is the string representation of the private key.

Complete Code

Here is complete code together for testing or to play with

function convertStringToArrayBufferView(str)
{
    var bytes = new Uint8Array(str.length);
    for (var iii = 0; iii < str.length; iii++)
    {
        bytes[iii] = str.charCodeAt(iii);
    }

    return bytes;
}  

function convertArrayBufferViewtoString(buffer)
{
    var str = "";
    for (var iii = 0; iii < buffer.byteLength; iii++)
    {
        str += String.fromCharCode(buffer[iii]);
    }

    return str;
}

var private_key_object = null;
var public_key_object = null;

var promise_key = null;

var encrypted_data = null;
var encrypt_promise = null;

var data = "QNimate";

var decrypt_promise = null;
var decrypted_data = null;

var vector = crypto.getRandomValues(new Uint8Array(16));

var crypto = window.crypto || window.msCrypto;

if(crypto.subtle)
{
    alert("Cryptography API Supported");

    promise_key = crypto.subtle.generateKey({name: "RSA-OAEP", modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), hash: {name: "SHA-256"}}, false, ["encrypt", "decrypt"]);

    promise_key.then(function(key){
        private_key_object = key.privateKey;
        public_key_object = key.publicKey;

        encrypt_data();
    });

    promise_key.catch = function(e){
        console.log(e.message);
    }
   
}
else
{
    alert("Cryptography API not Supported");
}

function encrypt_data()
{
    encrypt_promise = crypto.subtle.encrypt({name: "RSA-OAEP", iv: vector}, public_key_object, convertStringToArrayBufferView(data));

    encrypt_promise.then(
        function(result){
            encrypted_data = new Uint8Array(result);


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

function decrypt_data()
{
    decrypt_promise = crypto.subtle.decrypt({name: "RSA-OAEP", iv: vector}, private_key_object, encrypted_data);

    decrypt_promise.then(
        function(result){
            decrypted_data = new Uint8Array(result);
            console.log(convertArrayBufferViewtoString(decrypted_data));
        },
        function(e){
            console.log(e.message);
        }
    );
}
Feb 10, 2015Narayan Prusty
Symmetric Encryption using Web Cryptography APIDigital Signature using Web Cryptography API
Comments: 4
  1. Claudio
    6 years ago

    Make that 1 million and 1 :-)

    ReplyCancel
    • Claudio
      6 years ago

      And this seems to be the unsatisfying answer:

      http://stackoverflow.com/questions/20048214/access-keys-from-browser-store-using-webcrypto-api

      ReplyCancel
  2. Oliver
    7 years ago

    Great article! But how do you securely store the private key in the browser? Local storage?

    ReplyCancel
    • Alex
      6 years ago

      That is the 1 million dollar question

      ReplyCancel

Leave a Reply to Alex Cancel reply

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

Narayan Prusty

I am a software engineer specialising in Blockchain, DevOps and Go/JavaScript. This is my personal blog where I write about things that I learn and feel interesting to share.

8 years ago 4 Comments Web Security
Share this
0
GooglePlus
0
Facebook
0
Twitter
0
Linkedin
  • What is Asymmetric Encryption?
  • Converting String to Array Buffer and Array Buffer to String
  • Generating Public and Private Key
  • Encrypting String
  • Decrypting String
  • Importing and Exporting Keys
  • Complete Code
Related Articles
  • Symmetric Encryption using Web Cryptography API
  • Passphrase Based Encryption using Web Cryptography API
  • Digital Signature using Web Cryptography API
  • Hashing using Web Cryptography API
  • Website Hacking Methods and their Prevention
Our Sponsor
My Books

2014 - 2015 © QNimate
All tutorials MIT license