MENU
Web Crypto
generateKey()This generates a new key (for symmetric algorithms) or key pair (for public-key algorithms).
const result = crypto.subtle.generateKey(algorithm, extractable, keyUsages);
extractable is a Boolean indicating whether it will be possible to export the key using SubtleCrypto.exportKey() or SubtleCrypto.wrapKey().keyUsages is an Array indicating what can be done with the newly generated key. Possible values for array elements 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.
let keyPair = window.crypto.subtle.generateKey(
{ name: "RSA-OAEP",
modulusLength: 4096,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256" },
true,
["encrypt", "decrypt"]);
encrypt() / decrypt()
This encrypts and decrypts data.
function getMessageEncoding() {
const messageBox = document.querySelector("#msg");
let message = messageBox.value;
let enc = new TextEncoder();
return enc.encode(message);
}
function encryptMessage(publicKey) {
let encoded = getMessageEncoding();
return window.crypto.subtle.encrypt(
{ name: "RSA-OAEP" },
publicKey,
encoded
);
}
function decryptMessage(privateKey, ciphertext) {
return window.crypto.subtle.decrypt(
{ name: "RSA-OAEP" },
privateKey,
ciphertext
);
}
Algorithms:- RSA-OAEP
- AES-CTR
- AES-CBC
- AES-GCM
sign() / verify()
This creates and verifies a digital signature.
const signature = crypto.subtle.sign(algorithm, key, data);
const result = crypto.subtle.verify(algorithm, key, signature, data);
function getMessageEncoding() {
const messageBox = document.querySelector("#msg");
let message = messageBox.value;
let enc = new TextEncoder();
return enc.encode(message);
}
let encoded = getMessageEncoding();
let signature = await window.crypto.subtle.sign("RSASSA-PKCS1-v1_5", privateKey, encoded);
function getMessageEncoding() {
const messageBox = document.querySelector("#msg");
let message = messageBox.value;
let enc = new TextEncoder();
return enc.encode(message);
}
async function verifyMessage(publicKey) {
const signatureValue = document.querySelector("#val");
signatureValue.classList.remove("valid", "invalid");
let encoded = getMessageEncoding();
let result = await window.crypto.subtle.verify(
"RSASSA-PKCS1-v1_5",
publicKey,
signature,
encoded
);
signatureValue.classList.add(result ? "valid" : "invalid");}
Algorithms:- RSASSA-PKCS1-v1_5
- RSA-PSS
- ECDSA
- HMAC
digest()
A digest is a short fixed-length hash derived from some variable-length input. Cryptographic digests should exhibit collision-resistance, meaning that it's hard to come up with two different inputs that have the same digest value.
const digest = crypto.subtle.digest(algorithm, data);
const text = `An obscure body in the S-K System, your majesty. The inhabitants refer to it as the planet Earth.`;
async function digestMessage(message) {
const msgUint8 = new TextEncoder().encode(message);
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
return hashHex;
}
const digestHex = await digestMessage(text);
console.log(digestHex);
Algorithms:- SHA-1
- SHA-256
- SHA-384
- SHA-512
deriveKey() / deriveBits()
This is used to derive a secret key from a master key.
/* 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 alicesKeyPair = await crypto.subtle.generateKey(
{ name: "ECDH",
namedCurve: "P-384" },
false,
["deriveKey"]);
let bobsKeyPair = await 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 alicesSecretKey = await deriveSecretKey(alicesKeyPair.privateKey, bobsKeyPair.publicKey);
// Bob generates the same secret key using his private key and Alice's public key.
let bobsSecretKey = await deriveSecretKey(bobsKeyPair.privateKey, alicesKeyPair.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(alicesSecretKey); });
// Bob can use his copy to decrypt the message.
let decryptButton = document.querySelector(".ecdh .decrypt-button");
decryptButton.addEventListener("click", () => { decrypt(bobsSecretKey); });}
async function deriveSharedSecret(privateKey, publicKey) {
const sharedSecret = await crypto.subtle.deriveBits(
{ name: "ECDH",
namedCurve: "P-384",
public: publicKey },
privateKey,
128
);
const buffer = new Uint8Array(sharedSecret, 0, 5);
const sharedSecretValue = document.querySelector(".ecdh .derived-bits-value");
sharedSecretValue.classList.add("fade-in");
sharedSecretValue.addEventListener("animationend",()=> { sharedSecretValue.classList.remove("fade-in"); });
sharedSecretValue.textContent = `${buffer}...[${sharedSecret.byteLength} bytes total]`;
}
// 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
const generateAlicesKeyPair = window.crypto.subtle.generateKey(
{ name: "ECDH",
namedCurve: "P-384" },
false,
["deriveBits"]);
const generateBobsKeyPair = window.crypto.subtle.generateKey(
{ name: "ECDH",
namedCurve: "P-384" },
false,
["deriveBits"]);
Promise.all([generateAlicesKeyPair, generateBobsKeyPair]).then(values => {
const alicesKeyPair = values[0];
const bobsKeyPair = values[1];
const deriveBitsButton = document.querySelector(".ecdh .derive-bits-button");
deriveBitsButton.addEventListener("click", () => {
// Alice then generates a secret using her private key and Bob's public key. Bob could generate the same secret using his private key and Alice's public key.
deriveSharedSecret(alicesKeyPair.privateKey, bobsKeyPair.publicKey);
});
});
Algorithms:- ECDH
- HKDF
- PBKDF2
wrapKey() / unwrapKey()
Wrapping a key exports the key in an external, portable format, then encrypts the exported key. This helps protect it in untrusted environments, such as inside an otherwise unprotected data store or in transmission over an unprotected network.
let salt;
// 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),
{name: "PBKDF2"},
false,
["deriveBits", "deriveKey"]
);
}
// Given some key material and some random saltderive an AES-KW key using PBKDF2.
function getKey(keyMaterial, salt) {
return window.crypto.subtle.deriveKey(
{ "name": "PBKDF2",
salt: salt,
"iterations": 100000,
"hash": "SHA-256" },
keyMaterial,
{ "name": "AES-KW", "length": 256},
true,
[ "wrapKey", "unwrapKey" ]
);
}
// Wrap the given key.
async function wrapCryptoKey(keyToWrap) { // get the key encryption key
const keyMaterial = await getKeyMaterial();
salt = window.crypto.getRandomValues(new Uint8Array(16));
const wrappingKey = await getKey(keyMaterial, salt);
return window.crypto.subtle.wrapKey("raw", keyToWrap, wrappingKey, "AES-KW");
}
// Generate an encrypt/decrypt secret key, then wrap it.
window.crypto.subtle.generateKey(
{ name: "AES-GCM",
length: 256 },
true,
["encrypt", "decrypt"]
).then((secretKey) => {
return wrapCryptoKey(secretKey);
}).then((wrappedKey) => {
console.log(wrappedKey);
});
/*Salt that is to be used in derivation of the key-wrapping key, alongside the password the user supplies. This must match the salt value that was originally used to derivethe key.*/
const saltBytes = [89,113,135,234,168,204,21,36,55,93,1,132,242,242,192,156];
// The wrapped key itself.
const wrappedKeyBytes = [171,223,14,36,201,233,233,120,164,68,217,192,226,80,224,39,199,235,239,60,212,169,100,23,61,54,244,197,160,80,109,230,207,225,57,197,175,71,80,209];
// Convert an array of byte values to an ArrayBuffer.
function bytesToArrayBuffer(bytes) {
const bytesAsArrayBuffer = new ArrayBuffer(bytes.length);
const bytesUint8 = new Uint8Array(bytesAsArrayBuffer);
bytesUint8.set(bytes);
return bytesAsArrayBuffer;
}
// Get some key material to use as input to the deriveKey method. The key material is a password supplied by the user.
function getKeyMaterial() {
let password = window.prompt("Enter your password");
let enc = new TextEncoder();
return window.crypto.subtle.importKey(
"raw",
enc.encode(password),
{name: "PBKDF2"},
false,
["deriveBits", "deriveKey"]
);
}
// Derive an AES-KW key using PBKDF2.
async function getUnwrappingKey() {
// 1. get the key material (user-supplied password)
const keyMaterial = await getKeyMaterial();
// 2 initialize the salt parameter.
// The salt must match the salt originally used to derive the key.
// In this example it's supplied as a constant "saltBytes".
const saltBuffer = bytesToArrayBuffer(saltBytes);
// 3 derive the key from key material and salt
return window.crypto.subtle.deriveKey(
{ "name": "PBKDF2",
salt: saltBuffer,
"iterations": 100000,
"hash": "SHA-256" },
keyMaterial,
{ "name": "AES-KW", "length": 256},
true,
[ "wrapKey", "unwrapKey" ]
);
}
/*Unwrap an AES secret key from an ArrayBuffer containing the raw bytes. Takes an array containing the bytes, and returns a Promise that will resolve to a CryptoKey representing the secret key.*/
async function unwrapSecretKey(wrappedKey) {
// 1. get the unwrapping key
const unwrappingKey = await getUnwrappingKey();
// 2. initialize the wrapped key
const wrappedKeyBuffer = bytesToArrayBuffer(wrappedKey);
// 3. unwrap the key
return window.crypto.subtle.unwrapKey(
"raw", // import format
wrappedKeyBuffer, // ArrayBuffer representing key to unwrap
unwrappingKey, // CryptoKey representing key encryption key
"AES-KW", // algorithm identifier for key encryption key
"AES-GCM", // algorithm identifier for key to unwrap
true, // extractability of key to unwrap
["encrypt", "decrypt"] // key usages for key to unwrap
);}
Algorithms:- RSA-OAEP
- AES-CTR
- AES-CBC
- AES-GCM
- AES-KW
exportKey() / importKey()
This converts a key to/from an external portable format.
const result = crypto.subtle.exportKey(format, key);
const result = crypto.subtle.importKey(
format,
keyData,
algorithm,
extractable,
keyUsages);
- format is a string describing the data format of the key to import. It can be one of the following:
- raw: Raw format.
- pkcs8: PKCS #8 format.
- spki: SubjectPublicKeyInfo format.
- jwk: JSON Web Key format.
- keyData is an ArrayBuffer, a TypedArray, a DataView, or a JSONWebKey object containing the key in the given format.
- algorithm is a dictionary object defining the type of key to import and providing extra algorithm-specific parameters.
- For RSASSA-PKCS1-v1_5, RSA-PSS, or RSA-OAEP: Pass an RsaHashedImportParams object.
- For ECDSA or ECDH: Pass an EcKeyImportParams object.
- For HMAC: Pass an HmacImportParams object.
- For AES-CTR, AES-CBC, AES-GCM, or AES-KW: Pass the string identifying the algorithm or an object of the form { "name": ALGORITHM }, where ALGORITHM is the name of the algorithm.
- For PBKDF2 : Pass the string PBKDF2.
- For HKDF: Pass the string HKDF.
- extractable is a Boolean indicating whether it will be possible to export the key using SubtleCrypto.exportKey() or SubtleCrypto.wrapKey().
- keyUsages is an Array indicating what can be done with the key. Possible array values 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.
const jwkEcKey = {
"crv": "P-384",
"d": "wouCtU7Nw4E8_7n5C1-xBjB4xqSb_liZhYM6Q8N…",
"ext": true,
"key_ops": ["sign"],
"kty": "EC",
"x": "SzrRXmyI8VWFJg1dPUNbFcc9jZvjZEfH7ulKI1cwu…",
"y": "hHUag3OvDzEr0uUQND4PXHQTXP5IDGdYhJhL-Wy…"
};
// Import a PEM encoded RSA private key, to use for RSA-PSS signing. Takes a string containing the PEM encoded key, and returns a Promise that will resolve to a CryptoKey representing the private key.
function importPrivateKey(jwk) {
return window.crypto.subtle.importKey(
"jwk",
jwk,
{ name: "ECDSA",
namedCurve: "P-384"},
true,
["sign"]
);
}