Skip to content

Commit 952590a

Browse files
author
Will Bamberg
committed
Add examples for wrapKey
1 parent 9712d74 commit 952590a

File tree

6 files changed

+484
-0
lines changed

6 files changed

+484
-0
lines changed

web-crypto/wrap-key/index.html

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Web Crypto API example</title>
6+
<link rel="stylesheet" href="style.css">
7+
</head>
8+
9+
<body>
10+
<main>
11+
<h1>Web Crypto: wrapKey</h1>
12+
13+
<section class="description">
14+
<p>This page shows the use of the <code>wrapKey()</code> function of the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API">Web Crypto API</a>.</p>
15+
<p>On page load, it generates four keys:</p>
16+
<ul>
17+
<li>An AES-GCM secret key</li>
18+
<li>An RSA-PSS signing key pair</li>
19+
<li>An RSA-OAEP encryption key pair</li>
20+
<li>An ECDSA signing key pair</li>
21+
</ul>
22+
<p>It provides four buttons across the top:</p>
23+
<ul>
24+
<li>Wrap the AES-GCM key in raw format</li>
25+
<li>Wrap the RSA-PSS private key in PKCS #8 format</li>
26+
<li>Wrap the RSA-OAEP public key in SubjectPublicKeyInfo format</li>
27+
<li>Wrap the ECDSA private key in JWK format</li>
28+
</ul>
29+
<p>"Wrapping" a key essentially means exporting it in an encrypted form. So in all four cases, when you click the button you'll be asked for a password, and we will derive a symmetric key from the password, which we will use to encrypt the key after it is exported.</p>
30+
<p>The exported, encrypted key is written to the area below the buttons.</p>
31+
</section>
32+
33+
<section class="examples">
34+
<section class="buttons">
35+
<input class="raw wrap-key-button" type="button" value="Wrap AES-GCM key as raw">
36+
<input class="pkcs8 wrap-key-button" type="button" value="Wrap RSA-PSS key as PKCS #8">
37+
<input class="spki wrap-key-button" type="button" value="Wrap RSA-OAEP key as SPKI">
38+
<input class="jwk wrap-key-button" type="button" value="Wrap ECDSA key as JWK">
39+
</section>
40+
<section class="output">
41+
<pre class="wrapped-key"></pre>
42+
</section>
43+
</section>
44+
45+
</main>
46+
47+
</body>
48+
<script src="raw.js"></script>
49+
<script src="pkcs8.js"></script>
50+
<script src="spki.js"></script>
51+
<script src="jwk.js"></script>
52+
</html>

web-crypto/wrap-key/jwk.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
(() => {
2+
3+
let salt;
4+
let iv;
5+
6+
/*
7+
Get some key material to use as input to the deriveKey method.
8+
The key material is a password supplied by the user.
9+
*/
10+
function getKeyMaterial() {
11+
let password = window.prompt("Enter your password");
12+
let enc = new TextEncoder();
13+
return window.crypto.subtle.importKey(
14+
"raw",
15+
enc.encode(password),
16+
{name: "PBKDF2"},
17+
false,
18+
["deriveBits", "deriveKey"]
19+
);
20+
}
21+
22+
/*
23+
Given some key material and some random salt
24+
derive an AES-GCM key using PBKDF2.
25+
*/
26+
function getKey(keyMaterial, salt) {
27+
return window.crypto.subtle.deriveKey(
28+
{
29+
"name": "PBKDF2",
30+
salt: salt,
31+
"iterations": 100000,
32+
"hash": "SHA-256"
33+
},
34+
keyMaterial,
35+
{ "name": "AES-GCM", "length": 256},
36+
true,
37+
[ "wrapKey", "unwrapKey" ]
38+
);
39+
}
40+
41+
/*
42+
Wrap the given key and write it into the "wrapped-key" space.
43+
*/
44+
async function wrapCryptoKey(keyToWrap) {
45+
// get the key encryption key
46+
const keyMaterial = await getKeyMaterial();
47+
salt = window.crypto.getRandomValues(new Uint8Array(16));
48+
const wrappingKey = await getKey(keyMaterial, salt);
49+
iv = window.crypto.getRandomValues(new Uint8Array(12));
50+
51+
const wrapped = await window.crypto.subtle.wrapKey(
52+
"jwk",
53+
keyToWrap,
54+
wrappingKey,
55+
{
56+
name: "AES-GCM",
57+
iv: iv
58+
}
59+
);
60+
61+
const wrappedKeyBuffer = new Uint8Array(wrapped);
62+
63+
const wrappedKeyOutput = document.querySelector(".wrapped-key");
64+
wrappedKeyOutput.classList.add("fade-in");
65+
wrappedKeyOutput.addEventListener("animationend", () => {
66+
wrappedKeyOutput.classList.remove("fade-in");
67+
});
68+
wrappedKeyOutput.textContent = `[${wrappedKeyBuffer}]`;
69+
}
70+
71+
/*
72+
Generate a sign/verify key pair,
73+
then set up an event listener on the "Wrap" button.
74+
*/
75+
window.crypto.subtle.generateKey(
76+
{
77+
name: "ECDSA",
78+
namedCurve: "P-384"
79+
},
80+
true,
81+
["sign", "verify"]
82+
).then((keyPair) => {
83+
const wrapButton = document.querySelector(".jwk");
84+
wrapButton.addEventListener("click", () => {
85+
wrapCryptoKey(keyPair.privateKey);
86+
});
87+
88+
});
89+
90+
})();

web-crypto/wrap-key/pkcs8.js

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
(() => {
2+
3+
let salt;
4+
let iv;
5+
6+
/*
7+
Get some key material to use as input to the deriveKey method.
8+
The key material is a password supplied by the user.
9+
*/
10+
function getKeyMaterial() {
11+
let password = window.prompt("Enter your password");
12+
let enc = new TextEncoder();
13+
return window.crypto.subtle.importKey(
14+
"raw",
15+
enc.encode(password),
16+
{name: "PBKDF2"},
17+
false,
18+
["deriveBits", "deriveKey"]
19+
);
20+
}
21+
22+
/*
23+
Given some key material and some random salt
24+
derive an AES-GCM key using PBKDF2.
25+
*/
26+
function getKey(keyMaterial, salt) {
27+
return window.crypto.subtle.deriveKey(
28+
{
29+
"name": "PBKDF2",
30+
salt: salt,
31+
"iterations": 100000,
32+
"hash": "SHA-256"
33+
},
34+
keyMaterial,
35+
{ "name": "AES-GCM", "length": 256},
36+
true,
37+
[ "wrapKey", "unwrapKey" ]
38+
);
39+
}
40+
41+
/*
42+
Wrap the given key and write it into the "wrapped-key" space.
43+
*/
44+
async function wrapCryptoKey(keyToWrap) {
45+
// get the key encryption key
46+
const keyMaterial = await getKeyMaterial();
47+
salt = window.crypto.getRandomValues(new Uint8Array(16));
48+
const wrappingKey = await getKey(keyMaterial, salt);
49+
iv = window.crypto.getRandomValues(new Uint8Array(12));
50+
51+
const wrapped = await window.crypto.subtle.wrapKey(
52+
"pkcs8",
53+
keyToWrap,
54+
wrappingKey,
55+
{
56+
name: "AES-GCM",
57+
iv: iv
58+
}
59+
);
60+
61+
const wrappedKeyBuffer = new Uint8Array(wrapped);
62+
63+
const wrappedKeyOutput = document.querySelector(".wrapped-key");
64+
wrappedKeyOutput.classList.add("fade-in");
65+
wrappedKeyOutput.addEventListener("animationend", () => {
66+
wrappedKeyOutput.classList.remove("fade-in");
67+
});
68+
wrappedKeyOutput.textContent = `[${wrappedKeyBuffer}]`;
69+
}
70+
71+
/*
72+
Generate a sign/verify key pair,
73+
then set up an event listener on the "Wrap" button.
74+
*/
75+
window.crypto.subtle.generateKey(
76+
{
77+
name: "RSA-PSS",
78+
// Consider using a 4096-bit key for systems that require long-term security
79+
modulusLength: 2048,
80+
publicExponent: new Uint8Array([1, 0, 1]),
81+
hash: "SHA-256",
82+
},
83+
true,
84+
["sign", "verify"]
85+
).then((keyPair) => {
86+
const wrapButton = document.querySelector(".pkcs8");
87+
wrapButton.addEventListener("click", () => {
88+
wrapCryptoKey(keyPair.privateKey);
89+
});
90+
91+
});
92+
93+
})();

web-crypto/wrap-key/raw.js

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
(() => {
2+
3+
let salt;
4+
5+
/*
6+
Get some key material to use as input to the deriveKey method.
7+
The key material is a password supplied by the user.
8+
*/
9+
function getKeyMaterial() {
10+
let password = window.prompt("Enter your password");
11+
let enc = new TextEncoder();
12+
return window.crypto.subtle.importKey(
13+
"raw",
14+
enc.encode(password),
15+
{name: "PBKDF2"},
16+
false,
17+
["deriveBits", "deriveKey"]
18+
);
19+
}
20+
21+
/*
22+
Given some key material and some random salt
23+
derive an AES-KW key using PBKDF2.
24+
*/
25+
function getKey(keyMaterial, salt) {
26+
return window.crypto.subtle.deriveKey(
27+
{
28+
"name": "PBKDF2",
29+
salt: salt,
30+
"iterations": 100000,
31+
"hash": "SHA-256"
32+
},
33+
keyMaterial,
34+
{ "name": "AES-KW", "length": 256},
35+
true,
36+
[ "wrapKey", "unwrapKey" ]
37+
);
38+
}
39+
40+
/*
41+
Wrap the given key and write it into the "wrapped-key" space.
42+
*/
43+
async function wrapCryptoKey(keyToWrap) {
44+
// get the key encryption key
45+
const keyMaterial = await getKeyMaterial();
46+
salt = window.crypto.getRandomValues(new Uint8Array(16));
47+
const wrappingKey = await getKey(keyMaterial, salt);
48+
49+
const wrapped = await window.crypto.subtle.wrapKey(
50+
"raw",
51+
keyToWrap,
52+
wrappingKey,
53+
"AES-KW"
54+
);
55+
56+
const wrappedKeyBuffer = new Uint8Array(wrapped);
57+
58+
const wrappedKeyOutput = document.querySelector(".wrapped-key");
59+
wrappedKeyOutput.classList.add("fade-in");
60+
wrappedKeyOutput.addEventListener("animationend", () => {
61+
wrappedKeyOutput.classList.remove("fade-in");
62+
});
63+
wrappedKeyOutput.textContent = `[${wrappedKeyBuffer}]`;
64+
}
65+
66+
/*
67+
Generate an encrypt/decrypt secret key,
68+
then set up an event listener on the "Wrap" button.
69+
*/
70+
window.crypto.subtle.generateKey(
71+
{
72+
name: "AES-GCM",
73+
length: 256,
74+
},
75+
true,
76+
["encrypt", "decrypt"]
77+
).then((secretKey) => {
78+
const wrapButton = document.querySelector(".raw");
79+
wrapButton.addEventListener("click", () => {
80+
wrapCryptoKey(secretKey);
81+
});
82+
83+
});
84+
85+
})();

0 commit comments

Comments
 (0)