UNPKG

@reclaimprotocol/tls

Version:

TLS 1.2/1.3 for any JavaScript Environment

125 lines (124 loc) 4.32 kB
import * as peculiar from '@peculiar/x509'; import { SubjectAlternativeNameExtension } from '@peculiar/x509'; import { crypto } from "../crypto/index.js"; const AIA_EXT_TYPE = '1.3.6.1.5.5.7.1.1'; export function loadX509FromPem(pem) { let cert; try { cert = new peculiar.X509Certificate(pem); } catch (e) { throw new Error(`Unsupported certificate: ${e}`); } return { internal: cert, isWithinValidity() { const now = new Date(); return now > cert.notBefore && now < cert.notAfter; }, getAIAExtension() { const aiaExt = cert .getExtension(AIA_EXT_TYPE); return aiaExt?.caIssuers?.find(obj => obj.type === 'url')?.value; }, getSubjectField(name) { return cert.subjectName.getField(name); }, getAlternativeDNSNames() { // search for names in SubjectAlternativeNameExtension const ext = cert.extensions .find(e => e.type === '2.5.29.17'); //subjectAltName if (ext instanceof SubjectAlternativeNameExtension) { return ext.names.items .filter(n => n.type === 'dns') .map(n => n.value); } return []; }, isIssuer({ internal: ofCert }) { var i = ofCert.issuer; var s = cert.subject; return i === s; }, getPublicKey() { return { buffer: new Uint8Array(cert.publicKey.rawData), algorithm: cert.publicKey.algorithm.name, }; }, async verifyIssued(otherCert) { const sigAlg = getSigAlgorithm(cert.publicKey, otherCert.internal); const impPublicKey = await crypto .importKey(sigAlg, new Uint8Array(cert.publicKey.rawData), 'public'); const signature = new Uint8Array(otherCert.internal.signature); const verified = await crypto.verify(sigAlg, { publicKey: impPublicKey, signature, data: new Uint8Array(otherCert.internal['tbs']) }); return verified; }, serialiseToPem() { return cert.toString('pem'); }, }; } function getSigAlgorithm(key, { signatureAlgorithm }) { if (!('name' in signatureAlgorithm)) { throw new Error('Missing signature algorithm name'); } const { name, hash } = signatureAlgorithm; const { algorithm: keyAlg } = key; if (keyAlg.name !== name) { throw new Error(`Signature algorithm ${name} does not match` + ` public key algorithm ${keyAlg.name}`); } let hashName; switch (hash.name) { case 'SHA-256': hashName = 'SHA256'; break; case 'SHA-384': hashName = 'SHA384'; break; case 'SHA-512': hashName = 'SHA512'; break; case 'SHA-1': hashName = 'SHA1'; break; default: throw new Error(`Unsupported hash algorithm: ${hash.name}`); } switch (name) { case 'RSASSA-PKCS1-v1_5': case 'RSA-PKCS1-SHA1': return `RSA-PKCS1-${hashName}`; case 'ECDSA': if (hashName === 'SHA512' || hashName === 'SHA1') { throw new Error(`Unsupported hash algorithm for ECDSA: ${hashName}`); } switch (keyAlg.namedCurve) { case 'P-256': return `ECDSA-SECP256R1-${hashName}`; case 'P-384': return `ECDSA-SECP384R1-${hashName}`; default: throw new Error(`Unsupported named curve: ${keyAlg.namedCurve}`); } default: throw new Error(`Unsupported signature algorithm: ${name}`); } } export function loadX509FromDer(der) { // peculiar handles both return loadX509FromPem(der); } export async function defaultFetchCertificateBytes(url) { const res = await fetch(url); if (!res.ok) { throw new Error(`Failed to fetch certificate from ${url}: ${res.statusText}`); } const buffer = await res.arrayBuffer(); return new Uint8Array(buffer); }