oidc-spa
Version:
Openidconnect client for Single Page Applications
85 lines • 3.55 kB
JavaScript
const INFO_LABEL = "oidc-spa/tools/asymmetricEncryption";
export async function generateKeys() {
const keyPair = await crypto.subtle.generateKey({
name: "ECDH",
namedCurve: "P-256"
}, true, ["deriveKey", "deriveBits"]);
const publicKeyRaw = await crypto.subtle.exportKey("jwk", keyPair.publicKey);
const privateKeyRaw = await crypto.subtle.exportKey("jwk", keyPair.privateKey);
return {
publicKey: btoa(JSON.stringify(publicKeyRaw)),
privateKey: btoa(JSON.stringify(privateKeyRaw))
};
}
export async function asymmetricEncrypt(params) {
const { publicKey, message } = params;
const importedPublicKey = await crypto.subtle.importKey("jwk", JSON.parse(atob(publicKey)), {
name: "ECDH",
namedCurve: "P-256"
}, false, []);
const ephemeralKeyPair = await crypto.subtle.generateKey({
name: "ECDH",
namedCurve: "P-256"
}, true, ["deriveKey", "deriveBits"]);
const sharedSecret = await crypto.subtle.deriveBits({
name: "ECDH",
public: importedPublicKey
}, ephemeralKeyPair.privateKey, 256);
const salt = crypto.getRandomValues(new Uint8Array(16));
const infoBytes = new TextEncoder().encode(INFO_LABEL);
const hkdfKey = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, ["deriveKey"]);
const derivedKey = await crypto.subtle.deriveKey({
name: "HKDF",
hash: "SHA-256",
salt,
info: infoBytes
}, hkdfKey, { name: "AES-GCM", length: 256 }, false, ["encrypt"]);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encodedMessage = new TextEncoder().encode(message);
const ciphertext = await crypto.subtle.encrypt({
name: "AES-GCM",
iv
}, derivedKey, encodedMessage);
const ephemeralPubKeyRaw = await crypto.subtle.exportKey("jwk", ephemeralKeyPair.publicKey);
const payload = {
ephemeralPubKey: ephemeralPubKeyRaw,
iv: Array.from(iv),
salt: Array.from(salt),
ciphertext: Array.from(new Uint8Array(ciphertext))
};
return {
encryptedMessage: btoa(JSON.stringify(payload))
};
}
export async function asymmetricDecrypt(params) {
const { privateKey, encryptedMessage } = params;
const { ephemeralPubKey, iv, salt, ciphertext } = JSON.parse(atob(encryptedMessage));
const importedPrivateKey = await crypto.subtle.importKey("jwk", JSON.parse(atob(privateKey)), {
name: "ECDH",
namedCurve: "P-256"
}, false, ["deriveKey", "deriveBits"]);
const importedEphemeralPubKey = await crypto.subtle.importKey("jwk", ephemeralPubKey, {
name: "ECDH",
namedCurve: "P-256"
}, false, []);
const sharedSecret = await crypto.subtle.deriveBits({
name: "ECDH",
public: importedEphemeralPubKey
}, importedPrivateKey, 256);
const infoBytes = new TextEncoder().encode(INFO_LABEL);
const hkdfKey = await crypto.subtle.importKey("raw", sharedSecret, "HKDF", false, ["deriveKey"]);
const derivedKey = await crypto.subtle.deriveKey({
name: "HKDF",
hash: "SHA-256",
salt: new Uint8Array(salt),
info: infoBytes
}, hkdfKey, { name: "AES-GCM", length: 256 }, false, ["decrypt"]);
const decryptedBuffer = await crypto.subtle.decrypt({
name: "AES-GCM",
iv: new Uint8Array(iv)
}, derivedKey, new Uint8Array(ciphertext));
return {
message: new TextDecoder().decode(decryptedBuffer)
};
}
//# sourceMappingURL=asymmetricEncryption.js.map