123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- import crypto, { isCryptoKey } from './webcrypto.js';
- import invalidKeyInput from '../lib/invalid_key_input.js';
- import { encodeBase64, decodeBase64 } from './base64url.js';
- import formatPEM from '../lib/format_pem.js';
- import { JOSENotSupported } from '../util/errors.js';
- import { types } from './is_key_like.js';
- const genericExport = async (keyType, keyFormat, key) => {
- if (!isCryptoKey(key)) {
- throw new TypeError(invalidKeyInput(key, ...types));
- }
- if (!key.extractable) {
- throw new TypeError('CryptoKey is not extractable');
- }
- if (key.type !== keyType) {
- throw new TypeError(`key is not a ${keyType} key`);
- }
- return formatPEM(encodeBase64(new Uint8Array(await crypto.subtle.exportKey(keyFormat, key))), `${keyType.toUpperCase()} KEY`);
- };
- export const toSPKI = (key) => {
- return genericExport('public', 'spki', key);
- };
- export const toPKCS8 = (key) => {
- return genericExport('private', 'pkcs8', key);
- };
- const findOid = (keyData, oid, from = 0) => {
- if (from === 0) {
- oid.unshift(oid.length);
- oid.unshift(0x06);
- }
- let i = keyData.indexOf(oid[0], from);
- if (i === -1)
- return false;
- const sub = keyData.subarray(i, i + oid.length);
- if (sub.length !== oid.length)
- return false;
- return sub.every((value, index) => value === oid[index]) || findOid(keyData, oid, i + 1);
- };
- const getNamedCurve = (keyData) => {
- switch (true) {
- case findOid(keyData, [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07]):
- return 'P-256';
- case findOid(keyData, [0x2b, 0x81, 0x04, 0x00, 0x22]):
- return 'P-384';
- case findOid(keyData, [0x2b, 0x81, 0x04, 0x00, 0x23]):
- return 'P-521';
- case findOid(keyData, [0x2b, 0x65, 0x6e]):
- return 'X25519';
- case findOid(keyData, [0x2b, 0x65, 0x6f]):
- return 'X448';
- case findOid(keyData, [0x2b, 0x65, 0x70]):
- return 'Ed25519';
- case findOid(keyData, [0x2b, 0x65, 0x71]):
- return 'Ed448';
- default:
- throw new JOSENotSupported('Invalid or unsupported EC Key Curve or OKP Key Sub Type');
- }
- };
- const genericImport = async (replace, keyFormat, pem, alg, options) => {
- var _a;
- let algorithm;
- let keyUsages;
- const keyData = new Uint8Array(atob(pem.replace(replace, ''))
- .split('')
- .map((c) => c.charCodeAt(0)));
- const isPublic = keyFormat === 'spki';
- switch (alg) {
- case 'PS256':
- case 'PS384':
- case 'PS512':
- algorithm = { name: 'RSA-PSS', hash: `SHA-${alg.slice(-3)}` };
- keyUsages = isPublic ? ['verify'] : ['sign'];
- break;
- case 'RS256':
- case 'RS384':
- case 'RS512':
- algorithm = { name: 'RSASSA-PKCS1-v1_5', hash: `SHA-${alg.slice(-3)}` };
- keyUsages = isPublic ? ['verify'] : ['sign'];
- break;
- case 'RSA-OAEP':
- case 'RSA-OAEP-256':
- case 'RSA-OAEP-384':
- case 'RSA-OAEP-512':
- algorithm = {
- name: 'RSA-OAEP',
- hash: `SHA-${parseInt(alg.slice(-3), 10) || 1}`,
- };
- keyUsages = isPublic ? ['encrypt', 'wrapKey'] : ['decrypt', 'unwrapKey'];
- break;
- case 'ES256':
- algorithm = { name: 'ECDSA', namedCurve: 'P-256' };
- keyUsages = isPublic ? ['verify'] : ['sign'];
- break;
- case 'ES384':
- algorithm = { name: 'ECDSA', namedCurve: 'P-384' };
- keyUsages = isPublic ? ['verify'] : ['sign'];
- break;
- case 'ES512':
- algorithm = { name: 'ECDSA', namedCurve: 'P-521' };
- keyUsages = isPublic ? ['verify'] : ['sign'];
- break;
- case 'ECDH-ES':
- case 'ECDH-ES+A128KW':
- case 'ECDH-ES+A192KW':
- case 'ECDH-ES+A256KW': {
- const namedCurve = getNamedCurve(keyData);
- algorithm = namedCurve.startsWith('P-') ? { name: 'ECDH', namedCurve } : { name: namedCurve };
- keyUsages = isPublic ? [] : ['deriveBits'];
- break;
- }
- case 'EdDSA':
- algorithm = { name: getNamedCurve(keyData) };
- keyUsages = isPublic ? ['verify'] : ['sign'];
- break;
- default:
- throw new JOSENotSupported('Invalid or unsupported "alg" (Algorithm) value');
- }
- return crypto.subtle.importKey(keyFormat, keyData, algorithm, (_a = options === null || options === void 0 ? void 0 : options.extractable) !== null && _a !== void 0 ? _a : false, keyUsages);
- };
- export const fromPKCS8 = (pem, alg, options) => {
- return genericImport(/(?:-----(?:BEGIN|END) PRIVATE KEY-----|\s)/g, 'pkcs8', pem, alg, options);
- };
- export const fromSPKI = (pem, alg, options) => {
- return genericImport(/(?:-----(?:BEGIN|END) PUBLIC KEY-----|\s)/g, 'spki', pem, alg, options);
- };
- function getElement(seq) {
- let result = [];
- let next = 0;
- while (next < seq.length) {
- let nextPart = parseElement(seq.subarray(next));
- result.push(nextPart);
- next += nextPart.byteLength;
- }
- return result;
- }
- function parseElement(bytes) {
- let position = 0;
- let tag = bytes[0] & 0x1f;
- position++;
- if (tag === 0x1f) {
- tag = 0;
- while (bytes[position] >= 0x80) {
- tag = tag * 128 + bytes[position] - 0x80;
- position++;
- }
- tag = tag * 128 + bytes[position] - 0x80;
- position++;
- }
- let length = 0;
- if (bytes[position] < 0x80) {
- length = bytes[position];
- position++;
- }
- else if (length === 0x80) {
- length = 0;
- while (bytes[position + length] !== 0 || bytes[position + length + 1] !== 0) {
- if (length > bytes.byteLength) {
- throw new TypeError('invalid indefinite form length');
- }
- length++;
- }
- const byteLength = position + length + 2;
- return {
- byteLength,
- contents: bytes.subarray(position, position + length),
- raw: bytes.subarray(0, byteLength),
- };
- }
- else {
- let numberOfDigits = bytes[position] & 0x7f;
- position++;
- length = 0;
- for (let i = 0; i < numberOfDigits; i++) {
- length = length * 256 + bytes[position];
- position++;
- }
- }
- const byteLength = position + length;
- return {
- byteLength,
- contents: bytes.subarray(position, byteLength),
- raw: bytes.subarray(0, byteLength),
- };
- }
- function spkiFromX509(buf) {
- const tbsCertificate = getElement(getElement(parseElement(buf).contents)[0].contents);
- return encodeBase64(tbsCertificate[tbsCertificate[0].raw[0] === 0xa0 ? 6 : 5].raw);
- }
- function getSPKI(x509) {
- const pem = x509.replace(/(?:-----(?:BEGIN|END) CERTIFICATE-----|\s)/g, '');
- const raw = decodeBase64(pem);
- return formatPEM(spkiFromX509(raw), 'PUBLIC KEY');
- }
- export const fromX509 = (pem, alg, options) => {
- let spki;
- try {
- spki = getSPKI(pem);
- }
- catch (cause) {
- throw new TypeError('Failed to parse the X.509 certificate', { cause });
- }
- return fromSPKI(spki, alg, options);
- };
|