diafygi / webcrypto-examples

Web Cryptography API Examples Demo: https://diafygi.github.io/webcrypto-examples/
GNU General Public License v2.0
1.64k stars 194 forks source link

"jwk" gives ArrayBuffer object, not JSON compatible #25

Open fproulx-pbox opened 8 years ago

fproulx-pbox commented 8 years ago

Hi, All the functions that produce "jwk" produce ArrayBuffer, which is clearly different (a bigger) than the "raw" key, but it's always ArrayBuffer object and not something I appear to be able to convert to JSON (as the JSON Web Key spec suggests) like using JSON.stringify. (https://www.w3.org/TR/WebCryptoAPI/#SubtleCrypto-method-wrapKey)

How can get I convert it to the right format?

fproulx-pbox commented 8 years ago

Correction - it appears to work with exportKey, but not with wrapKey

bsanchezb commented 5 years ago

Hi! Any ideas about it so far? I have the same issue. wrapKey() with param 'jwt' produces arrayBuffer object instead of json.

heri16 commented 5 years ago

Anyone has a fix for this?

themikefuller commented 5 years ago

The wrapKey function produces an ArrayBuffer, not a jwk. Wrapping a key is really just encrypting it. When you specify jwk during the wrapping process, you are specifying that it should be in jwk format WHEN it is encrypted with the wrapping key. If you specified "raw", it would be encrypting (wrapping) the raw bits (byteArray) of the key you are wrapping, instead of (surprise) a byte array of the JWK.

Regardless, your output from a wrapKey function will always be an arrayBuffer, as that is what the Web Crypto library produces as encrypted output. The output of the unwrapKey function produces a cryptokey, made from the wrapped key.

You can export the wrapped key as a jwk, but ONLY after you unwrap (unencrypt) it. The ArrayBuffer you receive when wrapping the key can be converted to base64 or hex string for storing elsewhere (encrypted). Consider appending it to a base64 (or hex) encoded string of the iv.

Here is an example of wrapping / unwrapping:

// Secret key that you want to wrap var secretKey = await crypto.subtle.generateKey({ "name":"AES-GCM", "length":256 },true,['encrypt','decrypt']);

// Key used to wrap (encrypt) the secret key var wrappingKey = await crypto.subtle.generateKey({ "name":"AES-GCM", "length":256 },true,['wrapKey','unwrapKey','encrypt','decrypt']);

// An initialization vector for encrypting the key. var iv = crypto.getRandomValues(new Uint8Array(12));

// An arrayBuffer of the encrypted data. var wrapped = await crypto.subtle.wrapKey('jwk', secretKey, wrappingKey, { "name":"AES-GCM", "iv": iv });

// A cryptokey, generated from the unencrypted data, matching the original secretKey var unwrapped = await crypto.subtle.unwrapKey('jwk', wrapped, wrappingKey, { "name": "AES-GCM", "iv": iv }, { "name":"AES-GCM" }, true, ['encrypt','decrypt']);

// The decrypted (unwrapped) secret key, exported as JWK. var exported = await crypto.subtle.exportKey('jwk',unwrapped);

In summary, a jwk does not hold ENCRYPTED data or encrypted keys. It holds an ENCODED version of the raw data used to reproduce the key. Wrapping a key is really just a wrapper function for encrypting a key (with another key), and the Web Crypto library outputs the encrypted data as an ArrayBuffer.

EDIT:

The reason you specify "raw" or "jwk" during the wrapping process is that SOME KEYS cannot be exported as raw data (ECDH private keys for instance), so they must be exported to JWK format FIRST, then encoded and encrypted (wrapped). the wrapKey function is really just doing this work for you.