In this tutorial I will show you code example of how to generate public key of symmetric encryption and encrypt & decrypt data using the public key.
What is Symmetric Key Cryptography?
Symmetric-key encryption are algorithms for cryptography that use the same cryptographic keys(or public keys) for both encryption of plaintext and decryption of ciphertext(encrypted unreadable text).
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
{
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.
{
var str = "";
for (var iii = 0; iii < buffer.byteLength; iii++)
{
str += String.fromCharCode(buffer[iii]);
}
return str;
}
Generating Public Key
Here is the code to generate key for encryption and decryption
var key_object = null;
var promise_key = null;
if(crypto.subtle)
{
alert("Cryptography API Supported");
//Parameters:
//1. Symmetric 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 key. (http://www.w3.org/TR/WebCryptoAPI/#cryptokey-interface-types)
promise_key = crypto.subtle.generateKey({name: "AES-CBC", length: 128}, false, ["encrypt", "decrypt"]);
promise_key.then(function(key){
key_object = key;
});
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 key
var encrypted_data = null;
var encrypt_promise = null;
//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: "AES-CBC", iv: vector}, 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 encrypt array buffer using the same key which was used for encryption
{
decrypt_promise = crypto.subtle.decrypt({name: "AES-CBC", iv: vector}, 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 key to the server. And also import key from server and use it in browser.
You can use crypto.subtle.exportKey
to convert the key into many different exportable formats and then export it.
Similarly you can use crypto.subtle.importKey
to convert the exported key into an key object for use.
Here is an sample code showing how to import and export the generated key
var string_key = null;
crypto.subtle.exportKey("jwk", key_object).then(function(result){
json_key = result;
string_key = JSON.stringify(json_key);
crypto.subtle.importKey("jwk", JSON.parse(string_key), {name: "AES-CBC", iv: vector}, true, ["encrypt", "decrypt"]).then(function(e){
console.log(e);
}, function(e){
console.log(e);
});
}, function(e){
console.log(e.message);
});
Here we first converted the key to JWK format and then to an string. You can send the string_key
to server which is the string representation of the key.
Complete Code
Here is complete code together for testing or to play with
{
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 crypto = window.crypto || window.msCrypto;
var key_object = null;
var promise_key = null;
var data = "QNimate";
var encrypted_data = null;
var encrypt_promise = null;
var vector = crypto.getRandomValues(new Uint8Array(16));
var decrypt_promise = null;
var decrypted_data = null;
if(crypto.subtle)
{
alert("Cryptography API Supported");
promise_key = crypto.subtle.generateKey({name: "AES-CBC", length: 128}, false, ["encrypt", "decrypt"]);
promise_key.then(function(key){
key_object = key;
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: "AES-CBC", iv: vector}, 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: "AES-CBC", iv: vector}, 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);
}
);
}