Movatterモバイル変換


[0]ホーム

URL:


  1. Web
  2. Web APIs
  3. SubtleCrypto
  4. deriveKey()

SubtleCrypto: deriveKey() method

Baseline Widely available *

This feature is well established and works across many devices and browser versions. It’s been available across browsers since ⁨January 2020⁩.

* Some parts of this feature may have varying levels of support.

Secure context: This feature is available only insecure contexts (HTTPS), in some or allsupporting browsers.

Note: This feature is available inWeb Workers.

ThederiveKey() method of theSubtleCrypto 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 aPromise which will be fulfilled with aCryptoKey object representing the new key.

It's worth noting that the supported key derivation algorithms have quite different characteristics and are appropriate in quite different situations.SeeSupported algorithms for some more detail on this.

Syntax

js
deriveKey(algorithm, baseKey, derivedKeyType, extractable, keyUsages)

Parameters

algorithm

An object defining thederivation algorithm to use.

baseKey

ACryptoKey representing the input to the derivation algorithm.Ifalgorithm is ECDH or X25519, then this will be the ECDH or X25519 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 aCryptoKey usingSubtleCrypto.importKey().

derivedKeyType

An object defining the algorithm the derived key will be used for:

extractable

A boolean value indicating whether it will be possible to export the key usingSubtleCrypto.exportKey() orSubtleCrypto.wrapKey().

keyUsages

AnArray indicating what can be done with the derived key.Note that the key usages must be allowed by the algorithm set inderivedKeyAlgorithm.Possible values of the array are:

  • encrypt: The key may be used toencrypt messages.
  • decrypt: The key may be used todecrypt messages.
  • sign: The key may be used tosign messages.
  • verify: The key may be used toverify signatures.
  • deriveKey: The key may be used in deriving a new key.
  • deriveBits: The key may be used inderiving bits.
  • wrapKey: The key may be used towrap a key.
  • unwrapKey: The key may be used tounwrap a key.

Return value

APromise that fulfills with aCryptoKey.

Exceptions

The promise is rejected when one of the following exceptions are encountered:

InvalidAccessErrorDOMException

Raised when the master key is not a key for the requested derivation algorithm or if thekeyUsages value of that key doesn't containderiveKey.

NotSupportedDOMException

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.

SyntaxErrorDOMException

Raised whenkeyUsages is empty but the unwrapped key is of typesecret orprivate.

Supported algorithms

The algorithms supported byderiveKey() have quite different characteristics and are appropriate in different situations.

Key derivation algorithms

HKDF

HKDF is akey 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'snot designed to derive keys from relatively low-entropy inputs such as passwords.For that, use PBKDF2.

HKDF is specified inRFC 5869.

PBKDF2

PBKDF2 is also akey 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 inRFC 2898.

Key agreement algorithms

ECDH

ECDH (Elliptic Curve Diffie–Hellman) is akey-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 inRFC 6090.

X25519

X25519 is a key agreement algorithm like ECDH, but built on theCurve25519 elliptic curve, which is part of the Edwards-Curve Digital Signature Algorithm (EdDSA) family of algorithms defined inRFC 8032.

The Curve25519 algorithms are widely used in cryptography, and are considered to be some of the most efficient/fast available.By comparison with the NIST (National Institute of Standards and Technology) curve key exchange algorithms used with ECDH, Curve25519 is simpler to implement, and its non-governmental origin means that the decisions behind its design choices are transparent and open.

X25519 is specified inRFC 7748.

Examples

Note:You cantry the working examples on GitHub.

ECDH: derive shared secret key

In this example Alice and Bob each generate an ECDH key pair, then exchange public keys.They then usederiveKey() to derive a shared AES key, that they could use to encrypt messages.See the complete code on GitHub.

js
/*Derive an AES key, given:- our ECDH private key- their ECDH public key*/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() {  // Generate 2 ECDH key pairs: one for Alice and one for Bob  // In more normal usage, they would generate their key pairs  // separately and exchange public keys securely  let aliceKeyPair = await window.crypto.subtle.generateKey(    {      name: "ECDH",      namedCurve: "P-384",    },    false,    ["deriveKey"],  );  let bobKeyPair = await window.crypto.subtle.generateKey(    {      name: "ECDH",      namedCurve: "P-384",    },    false,    ["deriveKey"],  );  // Alice then generates a secret key using her private key and Bob's public key.  let aliceSecretKey = await deriveSecretKey(    aliceKeyPair.privateKey,    bobKeyPair.publicKey,  );  // Bob generates the same secret key using his private key and Alice's public key.  let bobSecretKey = await deriveSecretKey(    bobKeyPair.privateKey,    aliceKeyPair.publicKey,  );  // Alice can then use her copy of the secret key to encrypt a message to Bob.  let encryptButton = document.querySelector(".ecdh .encrypt-button");  encryptButton.addEventListener("click", () => {    encrypt(aliceSecretKey);  });  // Bob can use his copy to decrypt the message.  let decryptButton = document.querySelector(".ecdh .decrypt-button");  decryptButton.addEventListener("click", () => {    decrypt(bobSecretKey);  });}

X25519: derive shared secret key

In this example Alice and Bob each generate an X25519 key pair, then exchange public keys.They then each usederiveKey() to derive a shared AES key from their own private key and each other's public key.They can use this shared key to encrypt and decrypt messages they exchange.

HTML

First we define an HTML<input> that you will use to enter the plaintext message that "Alice" will send, and a button that you can click to start the encryption process.

html
<label for="message">Plaintext message from Alice (Enter):</label><input  type="text"   name="message"  size="50"  value="The lion roars near dawn" /><input type="button" value="Encrypt" />

This is followed by another two elements for displaying the ciphertext after Alice has encrypted the plaintext with her copy of the secret key, and for displaying the text after Bob has decrypted it with his copy of the secret key.

html
<div>  <label for="encrypted">Encrypted (Alice)</label>  <input    type="text"       name="encrypted"    size="30"    value=""    readonly />  <label for="results">Decrypted (Bob)</label>  <input    type="text"       name="decrypted"    size="50"    value=""    readonly /></div>
<pre></pre>
input {  display: block;  margin: 5px 0px;}#results {  margin-top: 20px;}#log {  height: 150px;  width: 90%;  white-space: pre-wrap; /* wrap pre blocks */  overflow-wrap: break-word; /* break on words */  overflow-y: auto;  padding: 0.5rem;  border: 1px solid black;  margin-top: 20px;}

JavaScript

const logElement = document.querySelector("#log");function log(text) {  logElement.innerText = `${logElement.innerText}${text}\n`;  logElement.scrollTop = logElement.scrollHeight;}

The code below shows how we usederiveKey().We pass in the remote party's X25519 public key, the local party's X25519 private key, and specify that the derived key should be an AES-GCM key.We also set the derived key to be non-extractable, and suitable for encryption and decryption.

We use this function further down in the code to create shared keys for Bob and Alice.

js
/*Derive an AES-GCM key, given:- our X25519 private key- their X25519 public key*/function deriveSecretKey(privateKey, publicKey) {  return window.crypto.subtle.deriveKey(    {      name: "X25519",      public: publicKey,    },    privateKey,    {      name: "AES-GCM",      length: 256,    },    false,    ["encrypt", "decrypt"],  );}

Next we define the functions that Alice will use toUTF-8 encode and then encrypt her plaintext message, and that Bob will use to decrypt and then decode the message.They both take as arguments the shared AES key, aninitialization vector, and the text to be encrypted or decrypted.

The same initialization vector must be used for encryption and decryption, but it does not need to be secret, so usually it is sent alongside the encrypted message.In this case, though, since we're not actually sending a message, we just make it directly available.

js
async function encryptMessage(key, initializationVector, message) {  try {    const encoder = new TextEncoder();    encodedMessage = encoder.encode(message);    // iv will be needed for decryption    return await window.crypto.subtle.encrypt(      { name: "AES-GCM", iv: initializationVector },      key,      encodedMessage,    );  } catch (e) {    console.log(e);    return `Encoding error`;  }}async function decryptMessage(key, initializationVector, ciphertext) {  try {    const decryptedText = await window.crypto.subtle.decrypt(      // The iv value must be the same as that used for encryption      { name: "AES-GCM", iv: initializationVector },      key,      ciphertext,    );    const utf8Decoder = new TextDecoder();    return utf8Decoder.decode(decryptedText);  } catch (e) {    console.log(e);    return "Decryption error";  }}

TheagreeSharedSecretKey() function below is called on loading to generate pairs and shared keys for Alice and Bob.It also adds a click handler for the "Encrypt" button that will trigger encryption and then decryption of the text defined in the first<input>.Note that all the code is inside atry...catch handler, to ensure that we can log the case where key generation fails because the X25519 algorithm is not supported.

js
async function agreeSharedSecretKey() {  try {    // Generate 2 X25519 key pairs: one for Alice and one for Bob    // In more normal usage, they would generate their key pairs    // separately and exchange public keys securely    const aliceKeyPair = await window.crypto.subtle.generateKey(      {        name: "X25519",      },      false,      ["deriveKey"],    );    log(      `Created Alice's key pair: (algorithm: ${JSON.stringify(        aliceKeyPair.privateKey.algorithm,      )}, usages: ${aliceKeyPair.privateKey.usages})`,    );    const bobKeyPair = await window.crypto.subtle.generateKey(      {        name: "X25519",      },      false,      ["deriveKey"],    );    log(      `Created Bob's key pair: (algorithm: ${JSON.stringify(        bobKeyPair.privateKey.algorithm,      )}, usages: ${bobKeyPair.privateKey.usages})`,    );    // Alice then generates a secret key using her private key and Bob's public key.    const aliceSecretKey = await deriveSecretKey(      aliceKeyPair.privateKey,      bobKeyPair.publicKey,    );    log(      `aliceSecretKey: ${aliceSecretKey.type} (algorithm: ${JSON.stringify(        aliceSecretKey.algorithm,      )}, usages: ${aliceSecretKey.usages}), `,    );    // Bob generates the same secret key using his private key and Alice's public key.    const bobSecretKey = await deriveSecretKey(      bobKeyPair.privateKey,      aliceKeyPair.publicKey,    );    log(      `bobSecretKey: ${bobSecretKey.type} (algorithm: ${JSON.stringify(        bobSecretKey.algorithm,      )}, usages: ${bobSecretKey.usages}), \n`,    );    // Get access for the encrypt button and the three inputs    const encryptButton = document.querySelector("#encrypt-button");    const messageInput = document.querySelector("#message");    const encryptedInput = document.querySelector("#encrypted");    const decryptedInput = document.querySelector("#decrypted");    encryptButton.addEventListener("click", async () => {      log(`Plaintext: ${messageInput.value}`);      // Define the initialization vector used when encrypting and decrypting.      // This must be regenerated for every message!      const initializationVector = window.crypto.getRandomValues(        new Uint8Array(8),      );      // Alice can use her copy of the shared key to encrypt the message.      const encryptedMessage = await encryptMessage(        aliceSecretKey,        initializationVector,        messageInput.value,      );      // We then display part of the encrypted buffer and log the encrypted message      let buffer = new Uint8Array(encryptedMessage, 0, 5);      encryptedInput.value = `${buffer}...[${encryptedMessage.byteLength} bytes total]`;      log(        `encryptedMessage: ${buffer}...[${encryptedMessage.byteLength} bytes total]`,      );      // Bob uses his shared secret key to decrypt the message.      const decryptedCiphertext = await decryptMessage(        bobSecretKey,        initializationVector,        encryptedMessage,      );      decryptedInput.value = decryptedCiphertext;      log(`decryptedCiphertext: ${decryptedCiphertext}\n`);    });  } catch (e) {    log(e);  }}// Finally we call the method to set the example running.agreeSharedSecretKey();

Result

Press the Encrypt button to encrypt the text in the top<input> element, displaying the encrypted ciphertext and decrypted ciphertext in the following two elements.The log area at the bottom provides information about the keys that are generated by the code.

PBKDF2: derive AES key from password

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.

js
/*Get some key material to use as input to the deriveKey method.The key material is a password supplied by the user.*/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: derive AES key from shared secret

In this example, we encrypt a messageplainText given a shared secretsecret, 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.

js
/*  Given some key material and some random salt,  derive an AES-GCM key using HKDF.  */function getKey(keyMaterial, salt) {  return window.crypto.subtle.deriveKey(    {      name: "HKDF",      salt,      info: new TextEncoder().encode("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

Specification
Web Cryptography Level 2
# SubtleCrypto-method-deriveKey

Browser compatibility

See also

Help improve MDN

Learn how to contribute

This page was last modified on byMDN contributors.


[8]ページ先頭

©2009-2025 Movatter.jp