Skip to content

Commit e0ef705

Browse files
author
Will Bamberg
committed
Add examples for exportKey
1 parent 7032b53 commit e0ef705

File tree

6 files changed

+307
-0
lines changed

6 files changed

+307
-0
lines changed

web-crypto/export-key/index.html

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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: exportKey</h1>
12+
13+
<section class="description">
14+
<p>This page shows the use of the <code>exportKey()</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>Export the AES-GCM key in raw format</li>
25+
<li>Export the RSA-PSS private key in PKCS #8 format</li>
26+
<li>Export the RSA-OAEP public key in SubjectPublicKeyInfo format</li>
27+
<li>Export the ECDSA private key in JWK format</li>
28+
</ul>
29+
<p>The exported key is written to the area below the buttons. PKCS #8 and SubjectPublicKeyInfo formats are further encoded as PEM objects.</p>
30+
</section>
31+
32+
<section class="examples">
33+
<section class="buttons">
34+
<input class="raw export-key-button" type="button" value="Export AES-GCM key as raw">
35+
<input class="pkcs8 export-key-button" type="button" value="Export RSA-PSS key as PKCS #8">
36+
<input class="spki export-key-button" type="button" value="Export RSA-OAEP key as SPKI">
37+
<input class="jwk export-key-button" type="button" value="Export ECDSA key as JWK">
38+
</section>
39+
<section class="output">
40+
<pre class="exported-key"></pre>
41+
</section>
42+
</section>
43+
44+
</main>
45+
46+
</body>
47+
<script src="raw.js"></script>
48+
<script src="pkcs8.js"></script>
49+
<script src="spki.js"></script>
50+
<script src="jwk.js"></script>
51+
</html>

web-crypto/export-key/jwk.js

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
(() => {
2+
3+
/*
4+
Export the given key and write it into the "exported-key" space.
5+
*/
6+
async function exportCryptoKey(key) {
7+
const exported = await window.crypto.subtle.exportKey(
8+
"jwk",
9+
key
10+
);
11+
const exportKeyOutput = document.querySelector(".exported-key");
12+
exportKeyOutput.classList.add("fade-in");
13+
exportKeyOutput.addEventListener("animationend", () => {
14+
exportKeyOutput.classList.remove("fade-in");
15+
});
16+
exportKeyOutput.textContent = JSON.stringify(exported, null, " ");
17+
}
18+
19+
/*
20+
Generate a sign/verify key pair,
21+
then set up an event listener on the "Export" button.
22+
*/
23+
window.crypto.subtle.generateKey(
24+
{
25+
name: "ECDSA",
26+
namedCurve: "P-384"
27+
},
28+
true,
29+
["sign", "verify"]
30+
).then((keyPair) => {
31+
const exportButton = document.querySelector(".jwk");
32+
exportButton.addEventListener("click", () => {
33+
exportCryptoKey(keyPair.privateKey);
34+
});
35+
36+
});
37+
38+
})();

web-crypto/export-key/pkcs8.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
(() => {
2+
3+
/*
4+
Convert an ArrayBuffer into a string
5+
from https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
6+
*/
7+
function ab2str(buf) {
8+
return String.fromCharCode.apply(null, new Uint8Array(buf));
9+
}
10+
11+
/*
12+
Export the given key and write it into the "exported-key" space.
13+
*/
14+
async function exportCryptoKey(key) {
15+
const exported = await window.crypto.subtle.exportKey(
16+
"pkcs8",
17+
key
18+
);
19+
const exportedAsString = ab2str(exported);
20+
const exportedAsBase64 = window.btoa(exportedAsString);
21+
const pemExported = `-----BEGIN PRIVATE KEY-----\n${exportedAsBase64}\n-----END PRIVATE KEY-----`;
22+
23+
const exportKeyOutput = document.querySelector(".exported-key");
24+
exportKeyOutput.classList.add("fade-in");
25+
exportKeyOutput.addEventListener("animationend", () => {
26+
exportKeyOutput.classList.remove("fade-in");
27+
});
28+
exportKeyOutput.textContent = pemExported;
29+
}
30+
31+
/*
32+
Generate a sign/verify key pair,
33+
then set up an event listener on the "Export" button.
34+
*/
35+
window.crypto.subtle.generateKey(
36+
{
37+
name: "RSA-PSS",
38+
// Consider using a 4096-bit key for systems that require long-term security
39+
modulusLength: 2048,
40+
publicExponent: new Uint8Array([1, 0, 1]),
41+
hash: "SHA-256",
42+
},
43+
true,
44+
["sign", "verify"]
45+
).then((keyPair) => {
46+
const exportButton = document.querySelector(".pkcs8");
47+
exportButton.addEventListener("click", () => {
48+
exportCryptoKey(keyPair.privateKey);
49+
});
50+
51+
});
52+
53+
})();

web-crypto/export-key/raw.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
(() => {
2+
3+
/*
4+
Export the given key and write it into the "exported-key" space.
5+
*/
6+
async function exportCryptoKey(key) {
7+
const exported = await window.crypto.subtle.exportKey(
8+
"raw",
9+
key
10+
);
11+
const exportedKeyBuffer = new Uint8Array(exported);
12+
13+
const exportKeyOutput = document.querySelector(".exported-key");
14+
exportKeyOutput.classList.add("fade-in");
15+
exportKeyOutput.addEventListener("animationend", () => {
16+
exportKeyOutput.classList.remove("fade-in");
17+
});
18+
exportKeyOutput.textContent = `[${exportedKeyBuffer}]`;
19+
}
20+
21+
/*
22+
Generate an encrypt/decrypt secret key,
23+
then set up an event listener on the "Export" button.
24+
*/
25+
window.crypto.subtle.generateKey(
26+
{
27+
name: "AES-GCM",
28+
length: 256,
29+
},
30+
true,
31+
["encrypt", "decrypt"]
32+
).then((key) => {
33+
const exportButton = document.querySelector(".raw");
34+
exportButton.addEventListener("click", () => {
35+
exportCryptoKey(key);
36+
});
37+
38+
});
39+
40+
})();

web-crypto/export-key/spki.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
(() => {
2+
3+
/*
4+
Convert an ArrayBuffer into a string
5+
from https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
6+
*/
7+
function ab2str(buf) {
8+
return String.fromCharCode.apply(null, new Uint8Array(buf));
9+
}
10+
11+
/*
12+
Export the given key and write it into the "exported-key" space.
13+
*/
14+
async function exportCryptoKey(key) {
15+
const exported = await window.crypto.subtle.exportKey(
16+
"spki",
17+
key
18+
);
19+
const exportedAsString = ab2str(exported);
20+
const exportedAsBase64 = window.btoa(exportedAsString);
21+
const pemExported = `-----BEGIN PUBLIC KEY-----\n${exportedAsBase64}\n-----END PUBLIC KEY-----`;
22+
23+
const exportKeyOutput = document.querySelector(".exported-key");
24+
exportKeyOutput.classList.add("fade-in");
25+
exportKeyOutput.addEventListener("animationend", () => {
26+
exportKeyOutput.classList.remove("fade-in");
27+
});
28+
exportKeyOutput.textContent = pemExported;
29+
}
30+
31+
/*
32+
Generate an encrypt/decrypt key pair,
33+
then set up an event listener on the "Export" button.
34+
*/
35+
window.crypto.subtle.generateKey(
36+
{
37+
name: "RSA-OAEP",
38+
// Consider using a 4096-bit key for systems that require long-term security
39+
modulusLength: 2048,
40+
publicExponent: new Uint8Array([1, 0, 1]),
41+
hash: "SHA-256",
42+
},
43+
true,
44+
["encrypt", "decrypt"]
45+
).then((keyPair) => {
46+
const exportButton = document.querySelector(".spki");
47+
exportButton.addEventListener("click", () => {
48+
exportCryptoKey(keyPair.publicKey);
49+
});
50+
51+
});
52+
53+
})();

web-crypto/export-key/style.css

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/* General setup */
2+
3+
* {
4+
box-sizing: border-box;
5+
}
6+
7+
html,body {
8+
font-family: sans-serif;
9+
line-height: 1.2rem;
10+
}
11+
12+
/* Layout and styles */
13+
14+
h1 {
15+
color: green;
16+
margin-left: .5rem;
17+
}
18+
19+
.description, .examples {
20+
margin: 0 .5rem;
21+
}
22+
23+
.description > p {
24+
margin-top: 0;
25+
}
26+
27+
.output {
28+
box-shadow: inset 0 0 3px 1px black;
29+
height: 100%;
30+
}
31+
32+
.exported-key {
33+
padding: 1em;
34+
white-space: pre-wrap;
35+
word-break: break-all;
36+
}
37+
38+
/* Whole page grid */
39+
main {
40+
display: grid;
41+
grid-template-columns: 45rem 1fr;
42+
grid-template-rows: 4rem 1fr;
43+
}
44+
45+
h1 {
46+
grid-column: 1/2;
47+
grid-row: 1;
48+
}
49+
50+
.examples {
51+
grid-column: 1;
52+
grid-row: 2;
53+
}
54+
55+
.description {
56+
grid-column: 2;
57+
grid-row: 2;
58+
}
59+
60+
/* Animate output display */
61+
.fade-in {
62+
animation: fadein .5s;
63+
}
64+
65+
@keyframes fadein {
66+
from {
67+
opacity: 0;
68+
}
69+
to {
70+
opacity: 1;
71+
}
72+
}

0 commit comments

Comments
 (0)