SubtleCrypto: deriveKey() method
The deriveKey()
method of the SubtleCrypto
interface can be used to derive a secret key from a master key.
It takes as arguments some initial key material, the derivation algorithm to use, and the desired properties for the key to derive. It returns a Promise
which will be fulfilled with a CryptoKey
object representing the new key.
It's worth noting that the three key derivation algorithms you can use have quite different characteristics and are appropriate in quite different situations. See Supported algorithms for some more detail on this.
Syntax
deriveKey(algorithm, baseKey, derivedKeyAlgorithm, extractable, keyUsages)
Parameters
algorithm
-
An object defining the derivation algorithm to use.
baseKey
-
A CryptoKey
representing the input to the derivation algorithm. If algorithm
is ECDH, then this will be the ECDH private key. Otherwise it will be the initial key material for the derivation function: for example, for PBKDF2 it might be a password, imported as a CryptoKey
using SubtleCrypto.importKey()
.
derivedKeyAlgorithm
-
An object defining the algorithm the derived key will be used for:
-
A boolean value indicating whether it will be possible to export the key using SubtleCrypto.exportKey()
or SubtleCrypto.wrapKey()
.
keyUsages
-
An Array
indicating what can be done with the derived key. Note that the key usages must be allowed by the algorithm set in derivedKeyAlgorithm
. Possible values of the array are:
-
encrypt
: The key may be used to encrypt
messages. -
decrypt
: The key may be used to decrypt
messages. -
sign
: The key may be used to sign
messages. -
verify
: The key may be used to verify
signatures. -
deriveKey
: The key may be used in deriving a new key
. -
deriveBits
: The key may be used in deriving bits
. -
wrapKey
: The key may be used to wrap a key
. -
unwrapKey
: The key may be used to unwrap a key
.
Return value
A Promise
that fulfills with a CryptoKey
.
Exceptions
The promise is rejected when one of the following exceptions are encountered:
-
InvalidAccessError
DOMException
-
Raised when the master key is not a key for the requested derivation algorithm or if the keyUsages
value of that key doesn't contain deriveKey
.
-
NotSupported
DOMException
-
Raised when trying to use an algorithm that is either unknown or isn't suitable for derivation, or if the algorithm requested for the derived key doesn't define a key length.
-
SyntaxError
DOMException
-
Raised when keyUsages
is empty but the unwrapped key is of type secret
or private
.
Supported algorithms
The three algorithms supported by deriveKey()
have quite different characteristics and are appropriate in different situations.
ECDH
ECDH (Elliptic Curve Diffie-Hellman) is a key-agreement algorithm. It enables two people who each have an ECDH public/private key pair to generate a shared secret: that is, a secret that they — and no one else — share. They can then use this shared secret as a symmetric key to secure their communication, or can use the secret as an input to derive such a key (for example, using the HKDF algorithm).
ECDH is specified in RFC 6090.
HKDF
HKDF is a key derivation function. It's designed to derive key material from some high-entropy input, such as the output of an ECDH key agreement operation.
It's not designed to derive keys from relatively low-entropy inputs such as passwords. For that, use PBKDF2.
HKDF is specified in RFC 5869.
PBKDF2
PBKDF2 is also a key derivation function. It's designed to derive key material from some relatively low-entropy input, such as a password. It derives key material by applying a function such as HMAC to the input password along with some salt, and repeating this process many times. The more times the process is repeated, the more computationally expensive key derivation is: this makes it harder for an attacker to use brute-force to discover the key using a dictionary attack.
PBKDF2 is specified in RFC 2898.
Examples
ECDH
In this example Alice and Bob each generate an ECDH key pair, then exchange public keys. They then use deriveKey()
to derive a shared AES key, that they could use to encrypt messages. See the complete code on GitHub.
function deriveSecretKey(privateKey, publicKey) {
return window.crypto.subtle.deriveKey(
{
name: "ECDH",
public: publicKey,
},
privateKey,
{
name: "AES-GCM",
length: 256,
},
false,
["encrypt", "decrypt"],
);
}
async function agreeSharedSecretKey() {
let alicesKeyPair = await window.crypto.subtle.generateKey(
{
name: "ECDH",
namedCurve: "P-384",
},
false,
["deriveKey"],
);
let bobsKeyPair = await window.crypto.subtle.generateKey(
{
name: "ECDH",
namedCurve: "P-384",
},
false,
["deriveKey"],
);
let alicesSecretKey = await deriveSecretKey(
alicesKeyPair.privateKey,
bobsKeyPair.publicKey,
);
let bobsSecretKey = await deriveSecretKey(
bobsKeyPair.privateKey,
alicesKeyPair.publicKey,
);
let encryptButton = document.querySelector(".ecdh .encrypt-button");
encryptButton.addEventListener("click", () => {
encrypt(alicesSecretKey);
});
let decryptButton = document.querySelector(".ecdh .decrypt-button");
decryptButton.addEventListener("click", () => {
decrypt(bobsSecretKey);
});
}
PBKDF2
In this example we ask the user for a password, then use it to derive an AES key using PBKDF2, then use the AES key to encrypt a message. See the complete code on GitHub.
function getKeyMaterial() {
const password = window.prompt("Enter your password");
const enc = new TextEncoder();
return window.crypto.subtle.importKey(
"raw",
enc.encode(password),
"PBKDF2",
false,
["deriveBits", "deriveKey"],
);
}
async function encrypt(plaintext, salt, iv) {
const keyMaterial = await getKeyMaterial();
const key = await window.crypto.subtle.deriveKey(
{
name: "PBKDF2",
salt,
iterations: 100000,
hash: "SHA-256",
},
keyMaterial,
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"],
);
return window.crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, plaintext);
}
HKDF
In this example, we encrypt a message plainText
given a shared secret secret
, which might itself have been derived using an algorithm such as ECDH. Instead of using the shared secret directly, we use it as key material for the HKDF function, to derive an AES-GCM encryption key, which we then use to encrypt the message. See the complete code on GitHub.
function getKey(keyMaterial, salt) {
return window.crypto.subtle.deriveKey(
{
name: "HKDF",
salt: salt,
info: new Uint8Array("Encryption example"),
hash: "SHA-256",
},
keyMaterial,
{ name: "AES-GCM", length: 256 },
true,
["encrypt", "decrypt"],
);
}
async function encrypt(secret, plainText) {
const message = {
salt: window.crypto.getRandomValues(new Uint8Array(16)),
iv: window.crypto.getRandomValues(new Uint8Array(12)),
};
const key = await getKey(secret, message.salt);
message.ciphertext = await window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: message.iv,
},
key,
plainText,
);
return message;
}
Specifications
Browser compatibility
|
Desktop |
Mobile |
|
Chrome |
Edge |
Firefox |
Internet Explorer |
Opera |
Safari |
WebView Android |
Chrome Android |
Firefox for Android |
Opera Android |
Safari on IOS |
Samsung Internet |
deriveKey |
41 |
7912–79["Not supported: ECDH.", "Not supported: HKDF, PBKDF2."]
|
34 |
No |
28 |
11 |
41 |
41 |
34 |
28 |
11 |
4.0 |
derivedKeyAlgorithm_option_aes |
41 |
79 |
34 |
No |
28 |
No |
41 |
41 |
34 |
28 |
No |
4.0 |
derivedKeyAlgorithm_option_hkdf |
41 |
79 |
119 |
No |
28 |
No |
41 |
41 |
119 |
28 |
No |
4.0 |
derivedKeyAlgorithm_option_hmac |
41 |
79 |
34 |
No |
28 |
No |
41 |
41 |
34 |
28 |
No |
4.0 |
derivedKeyAlgorithm_option_pbkdf2 |
41 |
79 |
NoSee bug 1856679 |
No |
28 |
No |
41 |
41 |
NoSee bug 1856679 |
28 |
No |
4.0 |
See also