Charles Engelke's Blog

August 23, 2014

Public Key Cryptography in the Browser

Filed under: Uncategorized — Charles Engelke @ 11:08 am
Tags: ,

Update August 30, 2014: The Web Cryptography API has dropped support of the RSAES-PKCS1-v1_5 algorithm that was used here originally, so this post has been changed to use RSA-OAEP instead. There’s a new post with more information, including how to update Ubuntu 14.04 so that this algorithm will work in browsers.

It’s been a month since I published posts on how to perform symmetric cryptography in web browsers using the Web Cryptography API. Now we’ll move on to performing asymmetric, or public-key, cryptography in the browser. The goal of this post is to write the simplest possible in-browser code to encrypt and decrypt files using public-key cryptography. The result will not be a useful tool at all, but should be a good first step toward a useful tool.

Even though this is just about the simplest possible example, this is still a very long post! Sorry about that, but it will be clearer this way than spread out over several posts. The final example is available on Github, and a live demonstration page is available, too.

The API

The Web Cryptography API is available through the window.crypto object in the browser. Most of the functionality is in the window.crypto.subtle object. It is still under development but already widely available. Chrome browsers currently turn it off unless the flag Enable Experimental Web Platform Features is set, but Google has announced that it will be on by default starting with Chrome 37, due out in a few weeks. Firefox Nightly builds have it, and it’s expected to move to the Aurora releases on September 2, then step by step to beta and general release. The Opera Developer release now supports it, but doesn’t seem to support public-key algorithms yet and I don’t when it will be complete and move to stable. I haven’t been able to find any information on if and when Safari will support it. Internet Explorer 11 general release supports a prefixed version at window.msCrypto. Unfortunately, that’s based on an earlier version of the API. The functionality is there, but the behavior of the methods is a bit different from what we will use here.

Those are all desktop browsers, but mobile versions of them seem to be working to implement this API, too. They are lagging a bit behind the desktops, but making progress.

Any of the browser versions supporting window.crypto.subtle should eventually be able to run the code in this post. It was developed with Chrome 36 and the web platform flag enabled, and tested in in Firefox Nightly. As of this writing, it does not yet work in Opera Developer.

JavaScript Promises

Promise objects offer a way to perform asynchronous operations without using callbacks. They can’t do anything more than callbacks can, but they can be more convenient to use, especially when you have to chain aynchronous operations together. The API returns a Promise for almost every operation.

A Promise has two important methods: then and catch. They each take a function as a parameter, and each return another Promise. If the operation of the Promise succeeds, the function provided to the then method is executed and passed the result of the operation as its sole parameter. If it fails or has an exception, the function provided to the catch method is run and usually provided with an Error object as its sole parameter.

The result of the then or catch method is another promise, which provides its return value as the parameter to that new Promise’s then handler (or Error object to the catch handler). Finally, instead of separate then and catch methods, you can just pass two functions to the then handler which will execute the first one on success, or the second one on failure.

The code in this post often chains Promises together to force asynchronous operations to happen sequentially. Once you get comfortable with them, you’ll likely find this pretty clear to follow.

About Public-Key Cryptography

Symmetric cryptography, as used in the earlier blog posts, uses a single secret key to encrypt and decrypt data. Which means that any two parties (call them Alice and Bob) that want to communicate securely have to first find a secure way to share that key. And if three or more parties want to communicate securely they either have to accept that everyone in the group can read everything, or create and share separate keys for every pair of people in the group. And a secret shared by a group isn’t likely to remain secret. As Ben Franklin said, three can keep a secret if two of them are dead.

Public key cryptography solves these problems, though it adds complexity to do so. It can be hard to get your head around it, which isn’t surprising considering that symmetric cryptography has been around for millennia but public key cryptography wasn’t invented until the 1970s. The core idea is that each party owns a pair of keys, called the public and private keys, that are related in such a way that anything encrypted with one of those keys can only be decrypted with the other one. The difference between the keys in a key pair is just that one is arbitrarily called public and shared with anyone at all, while the other one is called private and kept under the sole control of the key pair owner.

So even if Alice and Bob have never met or had a secure way to communicate before, they can use public key cryptography to securely share information. Alice can send Bob a secret message by encrypting it with Bob’s public key, and then only Bob can decrypt it because he has sole control of his private key. And if you have a large group, each member would have a separate key pair and anyone could communicate securely with any other specific member using that member’s public key.

It’s important to remember that this provides secure communication with the owner of a key pair, who may or may not be the actual person you think it is. Reliably authenticating the owner of a key pair is a separate problem that public key cryptography can help with, but it isn’t addressed in this post.

The Web Page

The page is just going to allow the user to select a file and then click a button to encrypt or decrypt it. Those operations will use a key pair that is randomly generated when the page is created. That’s right. Once the page is closed, there’s no way to decrypt anything it encrypted because the key pair is gone for good. Persisting, exporting, and importing keys are left out of this example.

The HTML is really simple:

<!DOCTYPE html>
<html>
<head>
    <title>Public-Key Encryption</title>
    <script src="pkcrypto.js"></script>
</head>
<body>
    <h1>Public-Key Encryption</h1>
    <section id="encrypt-and-decrypt">
        <input type="file" id="source-file"/>
        <button id="encrypt">Encrypt File</button>
        <button id="decrypt">Decrypt File</button>
    </section>
    <section id="results">
        Download results:
        <ul id="download-links">
        </ul>
    </section>
</body>
</html>

The skeleton of the JavaScript is a lot like the symmetric cryptography code. The main difference is that the user doesn’t control the key pair; it’s automatically created when the page loads:

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.");
        return;
    }

    var keyPair;
    createAndSaveAKeyPair().
    then(function() {
        // Only enable the cryptographic operation buttons if a key pair can be created
        document.getElementById("encrypt").addEventListener("click", encryptTheFile);
        document.getElementById("decrypt").addEventListener("click", decryptTheFile);
    }).
    catch(function(err) {
        alert("Could not create a keyPair or enable buttons: " + err.message);
    });

    // More code to come
}

The createAndSaveAKeyPair function puts its result in the keyPair variable instead of returning it because it runs asynchronously. It returns before it has created the pair. However, the following then clause does not run until the keyPair has been successfully created and saved.

Creating a Key Pair

We used the API’s generateKey method to create a key for symmetric cryptography in the earlier posts, though any bit sequence of the right length would have worked. With public key cryptography you must use a sophisticated algorithm to create a key pair, because the keys must have necessary mathematical properties that anything encrypted with one key can only be decrypted with the other. It is also important that it must not be feasible to derive one member of the key pair from the other. The generateKey method can do all that. It’s the same method signature as before:

window.crypto.subtle.generateKey(algorithmIdentifier, extractableFlag, keyUsagesList).
then(successHandler).
catch(failureHandler);

So the first step in creating a key pair is to figure out the algorithm that will use it and the parameters to provide for it. The draft has a table of registered algorithms listed with their possible usages. Right now there are only two is only one public key algorithms listed that can be used to encrypt and decrypt: RSAES-PKCS1-v1_5 and RSA-OAEP, both specified in RFC 3447. Both use It uses the RSA algorithm as their its basis.

The algorithmIdentifier object has to include the name of the algorithm and the RsaHashedKeyGenParams modulusLength, publicExponent, and hash. The modulusLength is generally known as the key length, and has to be much larger than an AES key of similar security. The most common choice at this time is 2048 bits for the modulusLength, considered secure enough but not too large to work with easily. The publicExponent is a different story, and picking a good choice requires a pretty deep understanding of the RSA algorithm. But good news: Chrome currently only supports the values 3 and 65537 (2^16 + 1) for this, so we don’t have to think much about it. We’ll use 65537 (0x101). Finally, hash has to identify a supported hash function for the algorithm, so we will use SHA-256.

The following code returns a Promise that generates one key pair and saves it in the variable keyPair. You can just call the function and expect that the value of keyPair will eventually be updated, or you can use the then clause of the returned Promise to run code that should only occur after the value has been updated. Since the function provided to the then method returns the keyPair, that value will be provided as the input parameter to the next then clause in a chain.

function createAndSaveAKeyPair() {
    return window.crypto.subtle.generateKey(
        {
            name: "RSA-OAEP",
            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
        ["encrypt", "decrypt"]).
    then(function(key) {
        keyPair = key;
        return key;
    });
}

If this works, keyPair will be an object with fields of type Key named privateKey and publicKey. We’re going to encrypt a file with the publicKey, and then later decrypt it with the matching privateKey.

Encryption

This should be easy. The earlier series of posts encrypted and decrypted a file, so this should be pretty much the same, right? Unfortunately, wrong. The RSA algorithm can only encrypt data somewhat smaller than the key’s modulusLength, which is only 2048 bits (256 bytes). That’s pretty limiting. And even if it could encrypt more data, we wouldn’t want to do it. RSA is extremely slow. Really, really, slow.

So how can you share a file secretly using public key cryptography? By creating a random symmetric key (say, a 128 bit AES key) and encrypting the file with that key. Then encrypt that key (known as the session key) using public key cryptography with the public key. Give the recipient the encrypted file and the encrypted session key so they can first decrypt the session key with their private key, then decrypt the file with that session key.

Having to use a session key adds more work, but it has a benefit. You can encrypt a file to multiple recipients by using a single session key, and then separately encrypt the session key to each recipient. The resulting secret message is much smaller than it would be if you had to encrypt the actual message with multiple keys.

So the steps we need to follow are:

  1. Create a random session key.
  2. Encrypt the plaintext (original file) with the session key.
  3. Export the session key.
  4. Encrypt the session key with the recipient’s public key.
  5. Package the encrypted session key and encrypted file together for delivery.

These steps don’t have to be done strictly in sequence. For instance, step 2 could be done in parallel with steps 3 and 4. Since all the steps are done with Promises, that’s possible, but to keep this example as simple as possible we’ll just do the steps in order. Here’s the skeleton of the needed code:

function encrypt(plaintext, publicKey) {
    // Returns a Promise that provides its then handler
    // a Blob representing the encrypted data.
    var sessionKey, encryptedFile; // Used in two steps, so saved here for passing

    return window.crypto.subtle.generateKey(
        {name: "AES-CBC", length: 128}, 
        true, 
        ["encrypt", "decrypt"]).
    then(saveSessionKey).     // Need this in a later (not just the next) step
    then(encryptPlaintext).
    then(saveEncryptedFile).  // Need this result in a later step
    then(exportSessionKey).
    then(encryptSessionKey).
    then(packageResults);
}

Note the lack of a catch method. Since this returns a promise, it can defer handling errors to a catch method on the return value or even a later step in a chain. The relatively global variables sessionKey and encryptedFile are holders for intermediate values needed in later (not just immediately following) steps:

  • saveSessionKey saves the session key in the variable sessionKey, and also passes it to the next step.
  • encryptPlaintext takes the session key as a parameter and encrypts plaintext with that session key and a random initialization vector, passing the resulting iv and ciphertext in an array to the next step.
  • saveEncryptedFile gets an array of the iv and ciphertext as a parameter, and saves them in the variable encryptedFile. It doesn’t return anything.
  • exportSessionKey ignores the parameters it is given, and exports the saved sessionKey to an ArrayBuffer, passing the result to the next step.
  • encryptSessionKey will get the exported session key as a parameter, and encrypt it with publicKey, passing the encrypted key result to the next step.
  • packageResults will use the encrypted session key it is passed as a parameter and the saved encryptedFile to produce a Blob holding all the encrypted data, and pass the Blob to the next step (the then method handler of the Promise being returned).

Now that it’s broken down into parts, it isn’t too complicated to build. encryptPlaintext and exportSessionKey are each essentially operations that were shown in earlier posts:

function encryptPlaintext(sessionKey) {
    // The plaintext is in an enclosing scope, called plaintext
    var iv = window.crypto.getRandomValues(new Uint8Array(16));
    return window.crypto.subtle.encrypt({name: "AES-CBC", iv: iv}, sessionKey, plaintext).
    then(function(ciphertext) {
        return [iv, new Uint8Array(ciphertext)];
    });
}

function exportSessionKey() {
    // Exports the sessionKey from the enclosing scope.
    return window.crypto.subtle.exportKey('raw', sessionKey);
}

The functions to save intermediate values in enclosing scopes are both pretty trivial:

function saveSessionKey(key) {
    sessionKey = key;
    return key;
}

function saveEncryptedFile(ivAndCiphertext) {
    encryptedFile = ivAndCiphertext;
}

Which brings us to the two meaty new parts, one dealing with crypto, one wrangling and packaging multiple pieces of data into a Blob. Encrypting the session key turns out to be pretty easy:

function encryptSessionKey(exportedKey) {
    // Encrypts the exportedKey with the publicKey found in the enclosing scope.
    return window.crypto.subtle.encrypt({name: "RSA-OAEP"}, publicKey, exportedKey);
}

Now for the nasty part: packaging this up to provide to the recipient, who will eventually decrypt it all. At a minimum, the package has to contain the encrypted session key, iv, and ciphertext. But it should also have a lot more in it:

  • The symmetric algorithm that was used to create the ciphertext.
  • The public key algorithm used to create the encrypted session key.
  • Some kind of identifier of the public key the session key was encrypted for, so that recipients can know which private key they need to decrypt it.
  • A way to indicate which bytes of the file represent the different pieces of the package.

There are two widely used message formats that address all these issues and more: OpenPGP and Cryptographic Message Syntax (CMS). They’re both pretty complex, so this post won’t cover them. After all, the key pair being used is stored only as a JavaScript variable, so it’s going to go away as soon as the browser is closed and any encrypted messages built with this page will then be forever inaccessible. So keep it as simple as possible: the package format will contain the encrypted session key followed immediately by the iv and ciphertext. Since it’s not clear that the encrypted session key will always be the same size, it will be preceded by a 16 bit integer giving its length:

2-byte-key-length:key-length-byte-encrypted-session-key:16-byte-iv:ciphertext

We end up with the following:

function packageResults(encryptedKey) {
    var length = new Uint16Array([encryptedKey.byteLength]);
    return new Blob(
        [
            length,             // Always a 2 byte unsigned integer
            encryptedKey,       // "length" bytes long
            encryptedFile[0],   // 16 bytes long initialization vector
            encryptedFile[1]    // Remainder is the ciphertext
        ],
        {type: "application/octet-stream"}
    );
}

Note that we get the 16 bit length by creating a one element array of unsigned 16 bit integers.

Creating and Saving the Encrypted Results

The symmetric encryption example in the previous posts got their input file from an element in the page with the id source-file, and put a link to the result at the end of an unordered list with id download-links. The page containing all this code follows the same pattern.

function encryptTheFile() {
    var sourceFile = document.getElementById("source-file").files[0];

    var reader = new FileReader();
    reader.onload = processTheFile;
    reader.readAsArrayBuffer(sourceFile);

    function processTheFile() {
        var reader = this;  // Was invoked by the reader object
        var plaintext = reader.result;
        encrypt(plaintext, keyPair.publicKey). // keyPair defined in enclosing scope
        then(function(blob) {
            var url = URL.createObjectURL(blob);
            document.getElementById("download-links").insertAdjacentHTML(
                'beforeEnd',
                '<li><a href="' + blobUrl + '">Encrypted file</a></li>');
        }).
        catch(function(err) {
            alert("Something went wrong encrypting: " + err.message + "\n" + err.stack);
        });
    }
}

Decryption

Decryption is similar to encryption, though actually a bit easier. Encryption was described more or less bottom-up; decryption will be described top-down. The click handler is identical, except that the processTheFile step is different:

function decryptTheFile() {
    var sourceFile = document.getElementById("source-file").files[0];

    var reader = new FileReader();
    reader.onload = processTheFile;
    reader.readAsArrayBuffer(sourceFile);

    function processTheFile() {
        var reader = this;              // Invoked by the reader object
        var data = reader.result;

        var keyLength       = new Uint16Array(data, 0, 2)[0];   // First 16 bit integer
        var encryptedKey    = new Uint8Array( data, 2,              keyLength);
        var iv              = new Uint8Array( data, 2 + keyLength,  16);
        var ciphertext      = new Uint8Array( data, 2 + keyLength + 16);

        decrypt(ciphertext, iv, encryptedKey, keyPair.privateKey).
        then(function(blob) {
            var url = URL.createObjectURL(blob);
            document.getElementById("download-links").insertAdjacentHTML(
                'beforeEnd',
                '<li><a href="' + url + '">Decrypted file</a></li>');
        }).
        catch(function(err) {
            alert("Something went wrong decrypting: " + err.message + "\n" + err.stack);
        });
    }
}

The first part of processing is to pull out the four parts of the file: the keyLength, needed only to then get the right number of bytes for the encryptedKey, then the initialization vector, and finally the ciphertext itself. These are all passed to the decrypt function, which returns a promise that yields a Blob to the then method handler. That handler creates a URL for the blob and puts it in the page.

The actual decryption takes only three steps:

function decrypt(ciphertext, iv, encryptedSessionKey, privateKey) {
    return decryptKey(encryptedSessionKey, privateKey).
    then(importSessionKey).
    then(decryptCiphertext);
}

And the three steps are themselves pretty simple, given all the background covered so far:

function decryptKey(encryptedKey, privateKey) {
    return window.crypto.subtle.decrypt({name: "RSA-OAEP"}, privateKey, encryptedKey);
}

function importSessionKey(keyBytes) {
    return window.crypto.subtle.importKey(
        "raw",
        keyBytes,
        {name: "AES-CBC", length: 128},
        true,
        ["encrypt", "decrypt"]
    );
}

function decryptCiphertext(sessionKey) {
    return window.crypto.subtle.decrypt({name: "AES-CBC", iv: iv}, sessionKey, ciphertext).
    then(function(plaintext) {
        return new Blob([new Uint8Array(plaintext)], {type: "application/octet-stream"});
    });
}

Summing Up

This was a long post, but it’s as concise as possible. It illustrates how to use public-key cryptography to encrypt and decrypt files with the Web Cryptography API. And it is of no practical use. The key pair used goes away as soon as the page is closed, lost forever. And even if that problem were solved, this page could not interoperate with any other software. More code is needed to import and export key pairs in standard formats used by other systems, and build an encrypted file in a standard format. I hope to deal with those challenges in a future post.

The next post in this series, though, will continue to be completely useless as a practical matter. It will demonstrate digitally signing files and then verifying those digital signatures.

Advertisements

13 Comments

  1. […] 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 […]

    Pingback by Digital Signatures in the Browser | Charles Engelke's Blog — August 26, 2014 @ 5:31 pm

  2. […] 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. […]

    Pingback by Changes to the Web Cryptography API | Charles Engelke's Blog — August 29, 2014 @ 1:32 pm

  3. […] posts here have used the Web Cryptography API to encrypt and decrypt files, and to sign and verify them. But those examples have no practical use because the keys being […]

    Pingback by Saving Cryptographic Keys in the Browser | Charles Engelke's Blog — September 19, 2014 @ 2:39 pm

  4. Tremendous post. It helped me a lot in understanding how webcrypto could actually be implemented in a webapp using Chrome!

    Comment by mpltt — October 15, 2014 @ 10:23 am

  5. Dear Charles, I’ve been rather busy trying to get a secure exchange established between a browserclient and a php backend, using the webcrypto api and leaning heavily on the code you posted here. (For which my unending gratitude) And a PHP server using openssl. I’ve broken down things as much as I can. I made the javascript to generate a keypair, print out the values private and public, encrypt a simple string and print it out as well.

    I’ve copied the values straight to a simple php script. Trying to decode it with the values from the javascipt.

    Encrypting and decrypting works properly in the javascript (as posted below), decrypting in php does not (also posted beneath the javascript). First thinking I made some error and failing to find where to set the proper sha512 hashcode, I replaced the default openssl mod with phpseclib which does allow setting the hash. This narrowed the problem down to an error where the hash is compared. I hope you don’t mind, but my question to you is, have you ever managed to make the webcrypto api communicate succesfully with an other environment?

    Hope you can help me, and if not, still a lot of thanks for the code you posted her.

    Kind regards, Gideon

    // JavaScript Document
    var keyPair;
    var pemPublicKey;
    var pemPrivateKey;

    var _spki;
    var _pkcs8;

    window.crypto.subtle.generateKey({
    name: “RSA-OAEP”,
    modulusLength: 2048,
    publicExponent: new Uint8Array([1, 0, 1]), // 24 bit representation of 65537
    hash: {name: “SHA-512”}
    }, true, [“encrypt”, “decrypt”])
    .then(function(newKeyPair) {
    keyPair = newKeyPair;
    return keyPair;
    })
    .then(function(keyPair) {
    window.crypto.subtle.exportKey(‘spki’, keyPair.publicKey)
    .then(function(spki) {
    _spki = spki;
    var pemPublicKey = convertBinaryToPem(spki, “PUBLIC KEY”);
    document.writeln(pemPublicKey);
    sendToPhp();
    });

    window.crypto.subtle.exportKey(‘pkcs8’, keyPair.privateKey)
    .then(function(pkcs8) {
    _pkcs8 = pkcs8;
    var pemPrivateKey = convertBinaryToPem(pkcs8, “PRIVATE KEY”);
    document.writeln(pemPrivateKey);
    })
    });

    function sendToPhp() {
    window.crypto.subtle.importKey(‘spki’, _spki, {name:”RSA-OAEP”, hash: {name: “SHA-512”}}, false, [“encrypt”])
    .then(function(cryptokey) {
    window.crypto.subtle.encrypt({ name: “RSA-OAEP”}, cryptokey, str2ab(‘mijn geheimpje’) )
    .then(function(encrypted){
    //returns an ArrayBuffer containing the encrypted data
    document.writeln(arrayBufferToBase64String(encrypted));
    receivedFromPhp(arrayBufferToBase64String(encrypted));
    });
    });
    }

    function receivedFromPhp(encrypted) {
    window.crypto.subtle.importKey(‘pkcs8’, _pkcs8, {name:”RSA-OAEP”, hash: {name: “SHA-512”}}, false, [“decrypt”])
    .then(function(cryptokey) {
    window.crypto.subtle.decrypt({ name: “RSA-OAEP”}, cryptokey, base64StringToArrayBuffer(encrypted) )
    .then(function(decrypted){
    //returns an ArrayBuffer containing the encrypted data
    var decryp = ab2str(decrypted);
    debugger;
    });
    });
    }

    function ab2str(buf) {
    return String.fromCharCode.apply(null, new Uint16Array(buf));
    }

    function str2ab(str) {
    var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
    var bufView = new Uint16Array(buf);
    for (var i=0, strLen=str.length; i<strLen; i++) {
    bufView[i] = str.charCodeAt(i);
    }
    return buf;
    }
    function base64StringToArrayBuffer(base64) {
    var binary_string = atob(base64);
    var len = binary_string.length;
    var bytes = new Uint8Array( len );
    for (var i = 0; i < len; i++) {
    bytes[i] = binary_string.charCodeAt(i);
    }
    return bytes.buffer;
    }
    function arrayBufferToBase64String(arrayBuffer) {
    var byteArray = new Uint8Array(arrayBuffer)
    var byteString = '';
    for (var i=0; i<byteArray.byteLength; i++) {
    byteString += String.fromCharCode(byteArray[i]);
    }
    return btoa(byteString);
    }

    function convertBinaryToPem(binaryData, label) {
    var base64Cert = arrayBufferToBase64String(binaryData);

    var pemCert = "—–BEGIN " + label + "—–\r\n";

    var nextIndex = 0;
    var lineLength;
    while (nextIndex < base64Cert.length) {
    if (nextIndex + 64 <= base64Cert.length) {
    pemCert += base64Cert.substr(nextIndex, 64) + "\r\n";
    } else {
    pemCert += base64Cert.substr(nextIndex) + "\r\n";
    }
    nextIndex += 64;
    }

    pemCert += "—–END " + label + "—–\r\n";
    return pemCert;
    }
    ————————-
    loadKey($pemPublicKey); // public key

    $rsa->setEncryptionMode(CRYPT_RSA_ENCRYPTION_OAEP);
    $rsa->setHash(‘sha512’);

    $rsa->loadKey($pemPrivateKey); // private key
    $rsa->setHash(‘sha512’);

    echo $rsa->decrypt($encrMessage);
    ?>

    Comment by Gideon Visbeen — April 15, 2015 @ 3:59 pm

    • Thanks for the comment. Yes, I have been able to get Web Crypto results to work in other environments, specifically OpenSSL command lines. I’ll write up a few examples and post them in the next couple of days. By the way, I’m speaking next week at the O’Reilly Fluent Conference about this, and I will be posting my slides and a lot of examples and other resources at engelke.com/fluent. There’s nothing there yet, but there will be by Wednesday, April 22.

      Comment by Charles Engelke — April 15, 2015 @ 4:10 pm

      • Excellent, I’ll be waiting in anticipation for things to come. And thanks again for the reply Charles, it’s appreciated!

        Comment by Gideon Visbeen — April 15, 2015 @ 4:31 pm

      • Hello again Charles. Well I got things working last night. Though I’m still puzzled about the why of it. Anyways, if I set the hash to SHA-1 on both the javascript and the php sides, the code works. Not quite happy with SHA-1 but at least it’s a starting point. I suspect php is at fault, defaulting to SHA-1 instead of accepting the set hash algorithm.

        Comment by Gideon Visbeen — April 16, 2015 @ 6:49 am

  6. Great! It’s also possible that the OpenSSL on your machine doesn’t support SHA-1 for RSA-OAEP.

    Comment by Charles Engelke — April 16, 2015 @ 8:07 am

  7. Hello Mr.Engelke, I really like your blog which provided for me personally already a lot of usefull information which I coudn’t find elsewhere. But there are still some parts about Web Crypto API I don’t udnerstand.
    AlgorithmIdentifier object is actually the same according to the API? I mean it has the same structure. But How do I know what to include in it when I want to generate keys, encrypt or decrypt for example. Because I noticed that for each function there’s always this AlgorithmIdentifier object, but depends on what I use, there are different attributes in it (not values) like fpr generateKey and encrypt – both have AlgorithmIdentifier but it looks different in both functions. And in some examples I saw that encrypt function uses a vector to get random values. What does it change?
    I’m totally new to cryptography and especially this API and I try to understand it all. So I thought maybe you could give me some inputs to understand it better?
    And thank you in advance!

    Comment by middleendian — June 29, 2015 @ 8:28 am

    • I think that AlgorithmIdentifier type is badly named. It actually contains a name field that, elsewhere in the text, is sometimes called the “algorithm identifier” all by itself. But that’s just the way it is. The tricky part is that the AlgorithmIdentifier argument can be different for different operations. It always contains a name field, but usually also has other fields. How do you know what those should be?

      Look at the “Registration” subsection for the algorithm in question. For example, the RSASSA-PKCS1-v1_5 algorithm is covered in section 20. Section 20.2 is called Registration, and it looks like this:

      Screen grab of Section 20.2

      That tells you that the AlgorithmIdentifier parameter for RSASSA-PKCS1-v1_5 has only a name property for the sign, verify, and exportKey operations, but must also have the fields specified for RsaHashedKeyGenParams for generateKey.

      I talked about this at the last Fluent conference. Slides, a link to the video, and a lot of other resources are available on this page at my website.

      Comment by Charles Engelke — June 29, 2015 @ 10:01 am

      • Oh thank you very much for this short and good explanation and thank you for taking your time to anser me! Now I understand finally how it works with those parameters!

        Comment by middleendian — June 29, 2015 @ 10:26 am

  8. […] just replied to a comment by middleendian’s blog on a post from last August asking about the AlgorithmIdentifier […]

    Pingback by AlgorithmIdentifier in WebCrypto | Charles Engelke's Blog — June 29, 2015 @ 10:17 am


RSS feed for comments on this post.

Create a free website or blog at WordPress.com.

%d bloggers like this: