Charles Engelke's Blog

August 29, 2014

Changes to the Web Cryptography API

Filed under: Uncategorized — Charles Engelke @ 1:32 pm
Tags: , ,

Chrome 37 made it to Stable a few days ago, and now supports the Web Cryptography API without needing to set a special flag. YAY!

But it dropped support for the RSAES-PKCS1-v1_5 algorithm so now the example from the public-key cryptography in the browser post last week doesn’t work any more. BOO!

What happened was that, as part of making the API generally available in Chrome, they updated their code to match the latest editor’s draft of the specification. Which drops RSAES-PKCS1-v1_5. I’m trying to understand why by looking at the change logs and mailing lists, but it seems that there is a vulnerability in that algorithm in certain use cases, so the working group felt it shouldn’t be included. The only public-key encryption and decryption algorithm in the spec now is RSA-OAEP. So I need to rework the example to use that algorithm instead. Which is a very simple set of changes except for one thing: my installation of Chrome (on Ubuntu 14.04) doesn’t support it. The error message when I tried to generate a key said that I needed a newer version of NSS (Network Security Services) to use it.

I tried using apt-get to install a newer version, but I already has the newest version available in the standard repositories. A bit of searching around led me to a discussion on how to watch Netflix on Linux, which included how to get the newest versions of the necessary NSS packages:

Then restart Chrome (you may need to kill all the hidden Chrome processes with pkill chrome for a full restart).

Now my installation of Chrome 37 supports RSA-OAEP, so I can fix the earlier samples. Watch this blog for an announcement when it’s done.

[Update August 31, 2014: Included all three required packages, instead of just the main one.]

August 26, 2014

Digital Signatures in the Browser

Filed under: Uncategorized — Charles Engelke @ 5:31 pm
Tags: ,

Digital signatures are kind of a mirror image of public-key encryption. They use essentially the same algorithms and kinds of key pairs, but for authentication instead of secrecy. Alice can send Bob a document that Bob can be certain was created by Alice, and not changed by anyone else, by signing the document with her privateKey. Bob can verify the signature using her publicKey. It’s the reverse of how you use keys for secrecy.

Because digital signatures are closely related to public-key encryption, the Web Cryptography API offers methods to create and use them. This post will build an example web page and related JavaScript to digitally sign a selected file, and then verify (or reject verification of) a signed file. The structure of the page and code are very similar to the encryption sample build in the last post on using the Web Cryptography API. The sample code is available on Github, and a live demonstration is also available.

About Digital Signatures

People have been signing documents for as long as there has been writing. And people having been forging signatures probably nearly as long. Forging an ink signature on paper requires skill, and there are various ways to analyze the signature to indicate whether it was forged or not. But when we start dealing with digital documents, the problem gets harder. It may be legal to “sign” a digital document, perhaps by appending your name to it, but how do we deal with avoiding forgeries? Or even with people repudiating completely valid documents by claiming they are forgeries? After all, anybody can change the bits in a file and there’s no way to know whether that happened.

Digital signatures apply public-key cryptography to make forgeries, or repudiation by claiming forgery, essentially impossible. Recall that anything signed one of a key pair’s keys can be decrypted only by the other member of the same key pair. Encryption used this for secret messages: Alice can send Bob a message that only Bob can read by encrypting it with Bob’s public key, which everybody knows. But only Bob has the matching private key, so only Bob can read the message. But what if Alice encrypted the message with her private key instead? Then anybody could read the message, because everybody knows her public key. But only Alice could have created the encrypted file, because only Alice has the matching private key. If Alice provides a message in both original and encrypted forms, where the encryption used her private key, she cannot credibly repudiate it because nobody but Alice could have created the encrypted version. You could even say that the encrypted version is a signature of the original message.

Digital signatures work almost like this, but with an optimization. Instead of encrypting the entire message to provide a signature, a message is digitally signed by creating a message digest and then encrypting it with the creator’s private key. A message digest is just a hashed value of the original message, so that any change to the message would change the hashed value, and there’s no effective way to create a second message with the same hashed value. Recipients decrypt the encrypted message digest with the purported signer’s public key, and also compute their own message digest on the original message, and compare the two. If they match, the signature is verified. If not, it is considered invalid.

The Web Page

The demonstration web page is almost exactly the same as the one for public-key encryption and decryption, except that it references a different JavaScript file and labels the buttons sign and verify instead of encrypt and decrypt:

<!DOCTYPE html>
    <title>Digital Signature</title>
    <script src="digitalsign.js"></script>
    <h1>Digital Signature</h1>
    <section id="sign-and-verify">
        <input type="file" id="source-file"/>
        <button id="sign">Sign File</button>
        <button id="verify">Verify Signature</button>
    <section id="results">
        Download results:
        <ul id="download-links">

The basic structure of the JavaScript file is also quite similar to the encryption and decryption example:

document.addEventListener("DOMContentLoaded", function() {
    "use strict";

    if (!window.crypto || !window.crypto.subtle) {
        alert("Your current browser does not support the Web Cryptography API! This page will not work.");

    var keyPair;
    then(function() {
        document.getElementById("sign").addEventListener("click", signTheFile);
        document.getElementById("verify").addEventListener("click", verifyTheFile);
    catch(function(err) {
        alert("Could not create a keyPair or enable buttons: " + err.message + "\n" + err.stack);

The only difference in this high-level JavaScript is the IDs of the buttons and the names of the click handlers being attached.

Creating a Key Pair

There are only three differences between creating a key pair for encryption and creating one for signing:

  1. The name of the algorithm to use must be one that supports signing and verifying.
  2. Keys used for digital signing must have an additional parameter stating which hash function to use for message digests.
  3. The usages array must include sign and verify instead of encrypt and decrypt.

The public-key encryption sample application used the RSAES-PKCS1-v1_5 algorithm, but the table of registered algorithms shows that can only be used for encryption and decryption. The closely related algorithm RSASSA-PKCS1-v1_5 supports signing and verifying, and is already implemented in Chrome and Firefox, so that’s the one we use. As for the hash function to use, any of the SHA-2 family should be fine, such as SHA-256.

With those minor changes, the code is:

function createAndSaveAKeyPair() {
    return window.crypto.subtle.generateKey(
            name: "RSASSA-PKCS1-v1_5",
            modulusLength: 2048,
            publicExponent: new Uint8Array([1, 0, 1]),  // 24 bit representation of 65537
            hash: {name: "SHA-256"}
        true,   // can extract it later if we want
        ["sign", "verify"]).
    then(function (key) {
        keyPair = key;
        return key;


Once again, the high level structure is the same as before, except for performing a sign operation instead of an encrypt one:

function signTheFile() {
    var sourceFile = document.getElementById("source-file").files[0];
    var reader = new FileReader();
    reader.onload = processTheFile;

    function processTheFile() {
        var reader = this;              // Was invoked by the reader object
        var plaintext = reader.result;

        sign(plaintext, keyPair.privateKey).
        then(function(blob) {
            var url = URL.createObjectURL(blob);
                '<li><a href="' + url + '">Signed file</a></li>');
        catch(function(err) {
            alert("Something went wrong signing: " + err.message + "\n" + err.stack);

The sign function, though, is much simpler than the encrypt one was. That’s because the API’s sign function handles creating the message digest for us, instead of our having to perform several additional steps as was needed to create a session key. There’s only one API method needed: window.crypto.subtle.sign. That yields the digital signature to the returned Promise’s then method, which needs to put the signature and original plaintext together into one file. The simple packaging used here is just:


The needed code is:

function sign(plaintext, privateKey) {
    return window.crypto.subtle.sign(
        {name: "RSASSA-PKCS1-v1_5"},

    function packageResults(signature) {
        var length = new Uint16Array([signature.byteLength]);
        return new Blob(
                length,     // Always a 2 byte unsigned integer
                signature,  // "length" bytes long
                plaintext   // Remainder is the original plaintext
            {type: "application/octet-stream"}


To verify a signature we need to extract the signature and plaintext from the combined package, then check that the signature is valid for that plaintext and public key. The new piece here is informing the user of whether the signature is valid or not, done with the alert function. A download link will only be provided if the signature checks out:

function verifyTheFile() {
    var sourceFile = document.getElementById("source-file").files[0];
    var reader = new FileReader();
    reader.onload = processTheFile;

    function processTheFile() {
        var reader = this;              // Invoked by the reader object
        var data = reader.result;
        var signatureLength = new Uint16Array(data, 0, 2)[0];   // First 16 bit integer
        var signature       = new Uint8Array( data, 2, signatureLength);
        var plaintext       = new Uint8Array( data, 2 + signatureLength);

        verify(plaintext, signature, keyPair.publicKey).
        then(function(blob) {
            if (blob === null) {
                alert("Invalid signature!");
            } else {
                alert("Signature is valid.");
                var url = URL.createObjectURL(blob);
                    '<li><a href="' + url + '">Verified file</a></li>');
        catch(function(err) {
            alert("Something went wrong verifying: " + err.message + "\n" + err.stack);

The verify function yields a Blob containing the plaintext if the signature is verified, and null otherwise. Again, only one API function is needed, because the API handles the message digest operation automatically:

function verify(plaintext, signature, publicKey) {
    return window.crypto.subtle.verify(
        {name: "RSASSA-PKCS1-v1_5"},

    function handleVerification(successful) {
        if (successful) {
            return new Blob([plaintext], {type: "application/octet-stream"});
        } else {
            return null;

Summing Up

This post is quite a bit shorter than the example of public-key encryption and decryption. That’s partly because there was less exposition needed now, and partly because signing and verifying with the API are quite a bit simpler. As before, this illustrates how to use the API, but has no practical use because there is no key persistence, export, or import performed. I hope to address those things in a future post.

Blog at