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
{
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 and Private Key
Here is the code to generate public and private keys
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 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 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 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
{
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);
}
);
}