Suppose person A sends a document to person B. Person A doesn’t want Person B to alter the document but can read the document. In this case person A can sign the document with an digital signature and send to person B to make that the document integrity is protected.
Digital Signatures are used widely used in online agreement and contract signing. Its used make sure that the employer doesn’t alter the text/data of the document after employee has agreed to the terms and signed with ink signature. The idea behind digital signature is to avoid document forgery.
In this tutorial I will show how to sign a digital document with an digital signature and also how to verify a document’s digital signature.
What is an Digital Signature?
Digital signature is simply encrypted hash of the document. Person A generates the hash of the document and encrypts the hash with his/her private key. And then sends the document and the respective public key to person B. Now person B can use the public key to decrypt and verify the hash of the document. There is no way person B can change the document data and regenerate the same hash value. The document’s integrity is completely protected from person B and any other people who may have seen the document.
Converting String to Array Buffer
For signing and verifying text/binary data you need to provide it in form of an array buffer to web cryptography api.
Here is the 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;
}
Generating Public and Private Key
For signing(or generating digital signature) data we need private key and to verify digital signature we need public key.
Here is the code to generate public and private key
var promise_key = null;
var private_key_object = null;
var public_key_object = null;
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: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: {name: "SHA-256"}}, false, ["sign", "verify"]);
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");
}
Signing Data
Here is the code to generate digital signature
var encrypted_hash = null;
var encrypt_promise = null;
var signature = null;
function encrypt_data()
{
encrypt_promise = crypto.subtle.sign({name: "RSASSA-PKCS1-v1_5"}, private_key_object, convertStringToArrayBufferView(data));
encrypt_promise.then(
function(result_signature){
signature = result_signature; //signature generated
},
function(e){
console.log(e);
}
);
}
Verifying Digital Signature
Here is the code to verify the digital signature
function decrypt_data()
{
decrypt_promise = crypto.subtle.verify({name: "RSASSA-PKCS1-v1_5"}, public_key_object, signature, convertStringToArrayBufferView(data));
decrypt_promise.then(
function(result){
console.log(result);//true or false
},
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: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: {name: "SHA-256"}}, true, ["sign"]).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;
}
var crypto = window.crypto || window.msCrypto;
var promise_key = null;
var private_key_object = null;
var public_key_object = null;
var data = "QNimate";
var encrypted_hash = null;
var encrypt_promise = null;
var signature = null;
var decrypt_promise = null;
if(crypto.subtle)
{
alert("Cryptography API Supported");
promise_key = crypto.subtle.generateKey({name: "RSASSA-PKCS1-v1_5", modulusLength: 2048, publicExponent: new Uint8Array([1, 0, 1]), hash: {name: "SHA-256"}}, false, ["sign", "verify"]);
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.sign({name: "RSASSA-PKCS1-v1_5"}, private_key_object, convertStringToArrayBufferView(data));
encrypt_promise.then(
function(result_signature){
signature = result_signature; //signature generated
decrypt_data();
},
function(e){
console.log(e);
}
);
}
function decrypt_data()
{
decrypt_promise = crypto.subtle.verify({name: "RSASSA-PKCS1-v1_5"}, public_key_object, signature, convertStringToArrayBufferView(data));
decrypt_promise.then(
function(result){
console.log(result);//true or false
},
function(e){
console.log(e.message);
}
);
}