UNPKG

@peculiar/x509

Version:

@peculiar/x509 is an easy to use TypeScript/Javascript library based on @peculiar/asn1-schema that makes generating X.509 Certificates and Certificate Requests as well as validating certificate chains easy

1,355 lines (1,340 loc) 142 kB
/*! * MIT License * * Copyright (c) Peculiar Ventures. All rights reserved. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ 'use strict'; var asn1Schema = require('@peculiar/asn1-schema'); var asn1X509 = require('@peculiar/asn1-x509'); var pvtsutils = require('pvtsutils'); var tslib = require('tslib'); var asn1Cms = require('@peculiar/asn1-cms'); var asn1Ecc = require('@peculiar/asn1-ecc'); var asn1Rsa = require('@peculiar/asn1-rsa'); var tsyringe = require('tsyringe'); var asnPkcs9 = require('@peculiar/asn1-pkcs9'); var asn1Csr = require('@peculiar/asn1-csr'); function _interopNamespaceDefault(e) { var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var asn1X509__namespace = /*#__PURE__*/_interopNamespaceDefault(asn1X509); var asn1Cms__namespace = /*#__PURE__*/_interopNamespaceDefault(asn1Cms); var asn1Ecc__namespace = /*#__PURE__*/_interopNamespaceDefault(asn1Ecc); var asn1Rsa__namespace = /*#__PURE__*/_interopNamespaceDefault(asn1Rsa); var asnPkcs9__namespace = /*#__PURE__*/_interopNamespaceDefault(asnPkcs9); const diAlgorithm = "crypto.algorithm"; class AlgorithmProvider { getAlgorithms() { return tsyringe.container.resolveAll(diAlgorithm); } toAsnAlgorithm(alg) { const algCopy = { ...alg }; if (algCopy.hash && typeof algCopy.hash === "string") { algCopy.hash = { name: algCopy.hash }; } for (const algorithm of this.getAlgorithms()) { const res = algorithm.toAsnAlgorithm(algCopy); if (res) { return res; } } if (/^[0-9.]+$/.test(alg.name)) { const res = new asn1X509.AlgorithmIdentifier({ algorithm: alg.name }); if ("parameters" in alg) { const unknown = alg; res.parameters = unknown.parameters; } return res; } throw new Error("Cannot convert WebCrypto algorithm to ASN.1 algorithm"); } toWebAlgorithm(alg) { for (const algorithm of this.getAlgorithms()) { const res = algorithm.toWebAlgorithm(alg); if (res) { return res; } } const unknown = { name: alg.algorithm, parameters: alg.parameters, }; return unknown; } } const diAlgorithmProvider = "crypto.algorithmProvider"; tsyringe.container.registerSingleton(diAlgorithmProvider, AlgorithmProvider); var EcAlgorithm_1; const idVersionOne = "1.3.36.3.3.2.8.1.1"; const idBrainpoolP160r1 = `${idVersionOne}.1`; const idBrainpoolP160t1 = `${idVersionOne}.2`; const idBrainpoolP192r1 = `${idVersionOne}.3`; const idBrainpoolP192t1 = `${idVersionOne}.4`; const idBrainpoolP224r1 = `${idVersionOne}.5`; const idBrainpoolP224t1 = `${idVersionOne}.6`; const idBrainpoolP256r1 = `${idVersionOne}.7`; const idBrainpoolP256t1 = `${idVersionOne}.8`; const idBrainpoolP320r1 = `${idVersionOne}.9`; const idBrainpoolP320t1 = `${idVersionOne}.10`; const idBrainpoolP384r1 = `${idVersionOne}.11`; const idBrainpoolP384t1 = `${idVersionOne}.12`; const idBrainpoolP512r1 = `${idVersionOne}.13`; const idBrainpoolP512t1 = `${idVersionOne}.14`; const brainpoolP160r1 = "brainpoolP160r1"; const brainpoolP160t1 = "brainpoolP160t1"; const brainpoolP192r1 = "brainpoolP192r1"; const brainpoolP192t1 = "brainpoolP192t1"; const brainpoolP224r1 = "brainpoolP224r1"; const brainpoolP224t1 = "brainpoolP224t1"; const brainpoolP256r1 = "brainpoolP256r1"; const brainpoolP256t1 = "brainpoolP256t1"; const brainpoolP320r1 = "brainpoolP320r1"; const brainpoolP320t1 = "brainpoolP320t1"; const brainpoolP384r1 = "brainpoolP384r1"; const brainpoolP384t1 = "brainpoolP384t1"; const brainpoolP512r1 = "brainpoolP512r1"; const brainpoolP512t1 = "brainpoolP512t1"; const ECDSA = "ECDSA"; exports.EcAlgorithm = EcAlgorithm_1 = class EcAlgorithm { toAsnAlgorithm(alg) { switch (alg.name.toLowerCase()) { case ECDSA.toLowerCase(): if ("hash" in alg) { const hash = typeof alg.hash === "string" ? alg.hash : alg.hash.name; switch (hash.toLowerCase()) { case "sha-1": return asn1Ecc__namespace.ecdsaWithSHA1; case "sha-256": return asn1Ecc__namespace.ecdsaWithSHA256; case "sha-384": return asn1Ecc__namespace.ecdsaWithSHA384; case "sha-512": return asn1Ecc__namespace.ecdsaWithSHA512; } } else if ("namedCurve" in alg) { let parameters = ""; switch (alg.namedCurve) { case "P-256": parameters = asn1Ecc__namespace.id_secp256r1; break; case "K-256": parameters = EcAlgorithm_1.SECP256K1; break; case "P-384": parameters = asn1Ecc__namespace.id_secp384r1; break; case "P-521": parameters = asn1Ecc__namespace.id_secp521r1; break; case brainpoolP160r1: parameters = idBrainpoolP160r1; break; case brainpoolP160t1: parameters = idBrainpoolP160t1; break; case brainpoolP192r1: parameters = idBrainpoolP192r1; break; case brainpoolP192t1: parameters = idBrainpoolP192t1; break; case brainpoolP224r1: parameters = idBrainpoolP224r1; break; case brainpoolP224t1: parameters = idBrainpoolP224t1; break; case brainpoolP256r1: parameters = idBrainpoolP256r1; break; case brainpoolP256t1: parameters = idBrainpoolP256t1; break; case brainpoolP320r1: parameters = idBrainpoolP320r1; break; case brainpoolP320t1: parameters = idBrainpoolP320t1; break; case brainpoolP384r1: parameters = idBrainpoolP384r1; break; case brainpoolP384t1: parameters = idBrainpoolP384t1; break; case brainpoolP512r1: parameters = idBrainpoolP512r1; break; case brainpoolP512t1: parameters = idBrainpoolP512t1; break; } if (parameters) { return new asn1X509.AlgorithmIdentifier({ algorithm: asn1Ecc__namespace.id_ecPublicKey, parameters: asn1Schema.AsnConvert.serialize(new asn1Ecc__namespace.ECParameters({ namedCurve: parameters })), }); } } } return null; } toWebAlgorithm(alg) { switch (alg.algorithm) { case asn1Ecc__namespace.id_ecdsaWithSHA1: return { name: ECDSA, hash: { name: "SHA-1" }, }; case asn1Ecc__namespace.id_ecdsaWithSHA256: return { name: ECDSA, hash: { name: "SHA-256" }, }; case asn1Ecc__namespace.id_ecdsaWithSHA384: return { name: ECDSA, hash: { name: "SHA-384" }, }; case asn1Ecc__namespace.id_ecdsaWithSHA512: return { name: ECDSA, hash: { name: "SHA-512" }, }; case asn1Ecc__namespace.id_ecPublicKey: { if (!alg.parameters) { throw new TypeError("Cannot get required parameters from EC algorithm"); } const parameters = asn1Schema.AsnConvert.parse(alg.parameters, asn1Ecc__namespace.ECParameters); switch (parameters.namedCurve) { case asn1Ecc__namespace.id_secp256r1: return { name: ECDSA, namedCurve: "P-256", }; case EcAlgorithm_1.SECP256K1: return { name: ECDSA, namedCurve: "K-256", }; case asn1Ecc__namespace.id_secp384r1: return { name: ECDSA, namedCurve: "P-384", }; case asn1Ecc__namespace.id_secp521r1: return { name: ECDSA, namedCurve: "P-521", }; case idBrainpoolP160r1: return { name: ECDSA, namedCurve: brainpoolP160r1, }; case idBrainpoolP160t1: return { name: ECDSA, namedCurve: brainpoolP160t1, }; case idBrainpoolP192r1: return { name: ECDSA, namedCurve: brainpoolP192r1, }; case idBrainpoolP192t1: return { name: ECDSA, namedCurve: brainpoolP192t1, }; case idBrainpoolP224r1: return { name: ECDSA, namedCurve: brainpoolP224r1, }; case idBrainpoolP224t1: return { name: ECDSA, namedCurve: brainpoolP224t1, }; case idBrainpoolP256r1: return { name: ECDSA, namedCurve: brainpoolP256r1, }; case idBrainpoolP256t1: return { name: ECDSA, namedCurve: brainpoolP256t1, }; case idBrainpoolP320r1: return { name: ECDSA, namedCurve: brainpoolP320r1, }; case idBrainpoolP320t1: return { name: ECDSA, namedCurve: brainpoolP320t1, }; case idBrainpoolP384r1: return { name: ECDSA, namedCurve: brainpoolP384r1, }; case idBrainpoolP384t1: return { name: ECDSA, namedCurve: brainpoolP384t1, }; case idBrainpoolP512r1: return { name: ECDSA, namedCurve: brainpoolP512r1, }; case idBrainpoolP512t1: return { name: ECDSA, namedCurve: brainpoolP512t1, }; } } } return null; } }; exports.EcAlgorithm.SECP256K1 = "1.3.132.0.10"; exports.EcAlgorithm = EcAlgorithm_1 = tslib.__decorate([ tsyringe.injectable() ], exports.EcAlgorithm); tsyringe.container.registerSingleton(diAlgorithm, exports.EcAlgorithm); const NAME = Symbol("name"); const VALUE = Symbol("value"); class TextObject { constructor(name, items = {}, value = "") { this[NAME] = name; this[VALUE] = value; for (const key in items) { this[key] = items[key]; } } } TextObject.NAME = NAME; TextObject.VALUE = VALUE; class DefaultAlgorithmSerializer { static toTextObject(alg) { const obj = new TextObject("Algorithm Identifier", {}, OidSerializer.toString(alg.algorithm)); if (alg.parameters) { switch (alg.algorithm) { case asn1Ecc__namespace.id_ecPublicKey: { const ecAlg = new exports.EcAlgorithm().toWebAlgorithm(alg); if (ecAlg && "namedCurve" in ecAlg) { obj["Named Curve"] = ecAlg.namedCurve; } else { obj["Parameters"] = alg.parameters; } break; } default: obj["Parameters"] = alg.parameters; } } return obj; } } class OidSerializer { static toString(oid) { const name = this.items[oid]; if (name) { return name; } return oid; } } OidSerializer.items = { [asn1Rsa__namespace.id_sha1]: "sha1", [asn1Rsa__namespace.id_sha224]: "sha224", [asn1Rsa__namespace.id_sha256]: "sha256", [asn1Rsa__namespace.id_sha384]: "sha384", [asn1Rsa__namespace.id_sha512]: "sha512", [asn1Rsa__namespace.id_rsaEncryption]: "rsaEncryption", [asn1Rsa__namespace.id_sha1WithRSAEncryption]: "sha1WithRSAEncryption", [asn1Rsa__namespace.id_sha224WithRSAEncryption]: "sha224WithRSAEncryption", [asn1Rsa__namespace.id_sha256WithRSAEncryption]: "sha256WithRSAEncryption", [asn1Rsa__namespace.id_sha384WithRSAEncryption]: "sha384WithRSAEncryption", [asn1Rsa__namespace.id_sha512WithRSAEncryption]: "sha512WithRSAEncryption", [asn1Ecc__namespace.id_ecPublicKey]: "ecPublicKey", [asn1Ecc__namespace.id_ecdsaWithSHA1]: "ecdsaWithSHA1", [asn1Ecc__namespace.id_ecdsaWithSHA224]: "ecdsaWithSHA224", [asn1Ecc__namespace.id_ecdsaWithSHA256]: "ecdsaWithSHA256", [asn1Ecc__namespace.id_ecdsaWithSHA384]: "ecdsaWithSHA384", [asn1Ecc__namespace.id_ecdsaWithSHA512]: "ecdsaWithSHA512", [asn1X509__namespace.id_kp_serverAuth]: "TLS WWW server authentication", [asn1X509__namespace.id_kp_clientAuth]: "TLS WWW client authentication", [asn1X509__namespace.id_kp_codeSigning]: "Code Signing", [asn1X509__namespace.id_kp_emailProtection]: "E-mail Protection", [asn1X509__namespace.id_kp_timeStamping]: "Time Stamping", [asn1X509__namespace.id_kp_OCSPSigning]: "OCSP Signing", [asn1Cms__namespace.id_signedData]: "Signed Data", }; class TextConverter { static serialize(obj) { return this.serializeObj(obj).join("\n"); } static pad(deep = 0) { return "".padStart(2 * deep, " "); } static serializeObj(obj, deep = 0) { const res = []; let pad = this.pad(deep++); let value = ""; const objValue = obj[TextObject.VALUE]; if (objValue) { value = ` ${objValue}`; } res.push(`${pad}${obj[TextObject.NAME]}:${value}`); pad = this.pad(deep); for (const key in obj) { if (typeof key === "symbol") { continue; } const value = obj[key]; const keyValue = key ? `${key}: ` : ""; if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { res.push(`${pad}${keyValue}${value}`); } else if (value instanceof Date) { res.push(`${pad}${keyValue}${value.toUTCString()}`); } else if (Array.isArray(value)) { for (const obj of value) { obj[TextObject.NAME] = key; res.push(...this.serializeObj(obj, deep)); } } else if (value instanceof TextObject) { value[TextObject.NAME] = key; res.push(...this.serializeObj(value, deep)); } else if (pvtsutils.BufferSourceConverter.isBufferSource(value)) { if (key) { res.push(`${pad}${keyValue}`); res.push(...this.serializeBufferSource(value, deep + 1)); } else { res.push(...this.serializeBufferSource(value, deep)); } } else if ("toTextObject" in value) { const obj = value.toTextObject(); obj[TextObject.NAME] = key; res.push(...this.serializeObj(obj, deep)); } else { throw new TypeError("Cannot serialize data in text format. Unsupported type."); } } return res; } static serializeBufferSource(buffer, deep = 0) { const pad = this.pad(deep); const view = pvtsutils.BufferSourceConverter.toUint8Array(buffer); const res = []; for (let i = 0; i < view.length;) { const row = []; for (let j = 0; j < 16 && i < view.length; j++) { if (j === 8) { row.push(""); } const hex = view[i++].toString(16).padStart(2, "0"); row.push(hex); } res.push(`${pad}${row.join(" ")}`); } return res; } static serializeAlgorithm(alg) { return this.algorithmSerializer.toTextObject(alg); } } TextConverter.oidSerializer = OidSerializer; TextConverter.algorithmSerializer = DefaultAlgorithmSerializer; var _AsnData_rawData; class AsnData { get rawData() { if (!tslib.__classPrivateFieldGet(this, _AsnData_rawData, "f")) { tslib.__classPrivateFieldSet(this, _AsnData_rawData, asn1Schema.AsnConvert.serialize(this.asn), "f"); } return tslib.__classPrivateFieldGet(this, _AsnData_rawData, "f"); } constructor(...args) { _AsnData_rawData.set(this, void 0); if (pvtsutils.BufferSourceConverter.isBufferSource(args[0])) { this.asn = asn1Schema.AsnConvert.parse(args[0], args[1]); tslib.__classPrivateFieldSet(this, _AsnData_rawData, pvtsutils.BufferSourceConverter.toArrayBuffer(args[0]), "f"); this.onInit(this.asn); } else { this.asn = args[0]; this.onInit(this.asn); } } equal(data) { if (data instanceof AsnData) { return pvtsutils.isEqual(data.rawData, this.rawData); } return false; } toString(format = "text") { switch (format) { case "asn": return asn1Schema.AsnConvert.toString(this.rawData); case "text": return TextConverter.serialize(this.toTextObject()); case "hex": return pvtsutils.Convert.ToHex(this.rawData); case "base64": return pvtsutils.Convert.ToBase64(this.rawData); case "base64url": return pvtsutils.Convert.ToBase64Url(this.rawData); default: throw TypeError("Argument 'format' is unsupported value"); } } getTextName() { const constructor = this.constructor; return constructor.NAME; } toTextObject() { const obj = this.toTextObjectEmpty(); obj[""] = this.rawData; return obj; } toTextObjectEmpty(value) { return new TextObject(this.getTextName(), {}, value); } } _AsnData_rawData = new WeakMap(); AsnData.NAME = "ASN"; class Extension extends AsnData { constructor(...args) { let raw; if (pvtsutils.BufferSourceConverter.isBufferSource(args[0])) { raw = pvtsutils.BufferSourceConverter.toArrayBuffer(args[0]); } else { raw = asn1Schema.AsnConvert.serialize(new asn1X509.Extension({ extnID: args[0], critical: args[1], extnValue: new asn1Schema.OctetString(pvtsutils.BufferSourceConverter.toArrayBuffer(args[2])), })); } super(raw, asn1X509.Extension); } onInit(asn) { this.type = asn.extnID; this.critical = asn.critical; this.value = asn.extnValue.buffer; } toTextObject() { const obj = this.toTextObjectWithoutValue(); obj[""] = this.value; return obj; } toTextObjectWithoutValue() { const obj = this.toTextObjectEmpty(this.critical ? "critical" : undefined); if (obj[TextObject.NAME] === Extension.NAME) { obj[TextObject.NAME] = OidSerializer.toString(this.type); } return obj; } } var _a; class CryptoProvider { static isCryptoKeyPair(data) { return data && data.privateKey && data.publicKey; } static isCryptoKey(data) { return data && data.usages && data.type && data.algorithm && data.extractable !== undefined; } constructor() { this.items = new Map(); this[_a] = "CryptoProvider"; if (typeof self !== "undefined" && typeof crypto !== "undefined") { this.set(CryptoProvider.DEFAULT, crypto); } else if (typeof global !== "undefined" && global.crypto && global.crypto.subtle) { this.set(CryptoProvider.DEFAULT, global.crypto); } } clear() { this.items.clear(); } delete(key) { return this.items.delete(key); } forEach(callbackfn, thisArg) { return this.items.forEach(callbackfn, thisArg); } has(key) { return this.items.has(key); } get size() { return this.items.size; } entries() { return this.items.entries(); } keys() { return this.items.keys(); } values() { return this.items.values(); } [Symbol.iterator]() { return this.items[Symbol.iterator](); } get(key = CryptoProvider.DEFAULT) { const crypto = this.items.get(key.toLowerCase()); if (!crypto) { throw new Error(`Cannot get Crypto by name '${key}'`); } return crypto; } set(key, value) { if (typeof key === "string") { if (!value) { throw new TypeError("Argument 'value' is required"); } this.items.set(key.toLowerCase(), value); } else { this.items.set(CryptoProvider.DEFAULT, key); } return this; } } _a = Symbol.toStringTag; CryptoProvider.DEFAULT = "default"; const cryptoProvider = new CryptoProvider(); const OID_REGEX = /^[0-2](?:\.[1-9][0-9]*)+$/; function isOID(id) { return new RegExp(OID_REGEX).test(id); } class NameIdentifier { constructor(names = {}) { this.items = {}; for (const id in names) { this.register(id, names[id]); } } get(idOrName) { return this.items[idOrName] || null; } findId(idOrName) { if (!isOID(idOrName)) { return this.get(idOrName); } return idOrName; } register(id, name) { this.items[id] = name; this.items[name] = id; } } const names = new NameIdentifier(); names.register("CN", "2.5.4.3"); names.register("L", "2.5.4.7"); names.register("ST", "2.5.4.8"); names.register("O", "2.5.4.10"); names.register("OU", "2.5.4.11"); names.register("C", "2.5.4.6"); names.register("DC", "0.9.2342.19200300.100.1.25"); names.register("E", "1.2.840.113549.1.9.1"); names.register("G", "2.5.4.42"); names.register("I", "2.5.4.43"); names.register("SN", "2.5.4.4"); names.register("T", "2.5.4.12"); function replaceUnknownCharacter(text, char) { return `\\${pvtsutils.Convert.ToHex(pvtsutils.Convert.FromUtf8String(char)).toUpperCase()}`; } function escape(data) { return data .replace(/([,+"\\<>;])/g, "\\$1") .replace(/^([ #])/, "\\$1") .replace(/([ ]$)/, "\\$1") .replace(/([\r\n\t])/, replaceUnknownCharacter); } class Name { static isASCII(text) { for (let i = 0; i < text.length; i++) { const code = text.charCodeAt(i); if (code > 0xFF) { return false; } } return true; } static isPrintableString(text) { return /^[A-Za-z0-9 '()+,-./:=?]*$/g.test(text); } constructor(data, extraNames = {}) { this.extraNames = new NameIdentifier(); this.asn = new asn1X509.Name(); for (const key in extraNames) { if (Object.prototype.hasOwnProperty.call(extraNames, key)) { const value = extraNames[key]; this.extraNames.register(key, value); } } if (typeof data === "string") { this.asn = this.fromString(data); } else if (data instanceof asn1X509.Name) { this.asn = data; } else if (pvtsutils.BufferSourceConverter.isBufferSource(data)) { this.asn = asn1Schema.AsnConvert.parse(data, asn1X509.Name); } else { this.asn = this.fromJSON(data); } } getField(idOrName) { const id = this.extraNames.findId(idOrName) || names.findId(idOrName); const res = []; for (const name of this.asn) { for (const rdn of name) { if (rdn.type === id) { res.push(rdn.value.toString()); } } } return res; } getName(idOrName) { return this.extraNames.get(idOrName) || names.get(idOrName); } toString() { return this.asn.map((rdn) => rdn.map((o) => { const type = this.getName(o.type) || o.type; const value = o.value.anyValue ? `#${pvtsutils.Convert.ToHex(o.value.anyValue)}` : escape(o.value.toString()); return `${type}=${value}`; }) .join("+")) .join(", "); } toJSON() { var _a; const json = []; for (const rdn of this.asn) { const jsonItem = {}; for (const attr of rdn) { const type = this.getName(attr.type) || attr.type; (_a = jsonItem[type]) !== null && _a !== void 0 ? _a : (jsonItem[type] = []); jsonItem[type].push(attr.value.anyValue ? `#${pvtsutils.Convert.ToHex(attr.value.anyValue)}` : attr.value.toString()); } json.push(jsonItem); } return json; } fromString(data) { const asn = new asn1X509.Name(); const regex = /(\d\.[\d.]*\d|[A-Za-z]+)=((?:"")|(?:".*?[^\\]")|(?:[^,+"\\](?=[,+]|$))|(?:[^,+].*?(?:[^\\][,+]))|(?:))([,+])?/g; let matches = null; let level = ","; while (matches = regex.exec(`${data},`)) { let [, type, value] = matches; const lastChar = value[value.length - 1]; if (lastChar === "," || lastChar === "+") { value = value.slice(0, value.length - 1); matches[3] = lastChar; } const next = matches[3]; type = this.getTypeOid(type); const attr = this.createAttribute(type, value); if (level === "+") { asn[asn.length - 1].push(attr); } else { asn.push(new asn1X509.RelativeDistinguishedName([attr])); } level = next; } return asn; } fromJSON(data) { const asn = new asn1X509.Name(); for (const item of data) { const asnRdn = new asn1X509.RelativeDistinguishedName(); for (const type in item) { const typeId = this.getTypeOid(type); const values = item[type]; for (const value of values) { const asnAttr = this.createAttribute(typeId, value); asnRdn.push(asnAttr); } } asn.push(asnRdn); } return asn; } getTypeOid(type) { if (!/[\d.]+/.test(type)) { type = this.getName(type) || ""; } if (!type) { throw new Error(`Cannot get OID for name type '${type}'`); } return type; } createAttribute(type, value) { const attr = new asn1X509.AttributeTypeAndValue({ type }); if (typeof value === "object") { for (const key in value) { switch (key) { case "ia5String": attr.value.ia5String = value[key]; break; case "utf8String": attr.value.utf8String = value[key]; break; case "universalString": attr.value.universalString = value[key]; break; case "bmpString": attr.value.bmpString = value[key]; break; case "printableString": attr.value.printableString = value[key]; break; } } } else if (value[0] === "#") { attr.value.anyValue = pvtsutils.Convert.FromHex(value.slice(1)); } else { const processedValue = this.processStringValue(value); if (type === this.getName("E") || type === this.getName("DC")) { attr.value.ia5String = processedValue; } else { if (Name.isPrintableString(processedValue)) { attr.value.printableString = processedValue; } else { attr.value.utf8String = processedValue; } } } return attr; } processStringValue(value) { const quotedMatches = /"(.*?[^\\])?"/.exec(value); if (quotedMatches) { value = quotedMatches[1]; } return value .replace(/\\0a/ig, "\n") .replace(/\\0d/ig, "\r") .replace(/\\0g/ig, "\t") .replace(/\\(.)/g, "$1"); } toArrayBuffer() { return asn1Schema.AsnConvert.serialize(this.asn); } async getThumbprint(arg1, arg2) { let crypto; let algorithm = "SHA-1"; if (arg1) { if (typeof arg1 === "object" && "subtle" in arg1) { crypto = arg1; } else { algorithm = arg1; crypto = arg2; } } crypto !== null && crypto !== void 0 ? crypto : (crypto = cryptoProvider.get()); return await crypto.subtle.digest(algorithm, this.toArrayBuffer()); } } const ERR_GN_CONSTRUCTOR = "Cannot initialize GeneralName from ASN.1 data."; const ERR_GN_STRING_FORMAT = `${ERR_GN_CONSTRUCTOR} Unsupported string format in use.`; const ERR_GUID = `${ERR_GN_CONSTRUCTOR} Value doesn't match to GUID regular expression.`; const GUID_REGEX = /^([0-9a-f]{8})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{4})-?([0-9a-f]{12})$/i; const id_GUID = "1.3.6.1.4.1.311.25.1"; const id_UPN = "1.3.6.1.4.1.311.20.2.3"; const DNS = "dns"; const DN = "dn"; const EMAIL = "email"; const IP = "ip"; const URL = "url"; const GUID = "guid"; const UPN = "upn"; const REGISTERED_ID = "id"; class GeneralName extends AsnData { constructor(...args) { let name; if (args.length === 2) { switch (args[0]) { case DN: { const derName = new Name(args[1]).toArrayBuffer(); const asnName = asn1Schema.AsnConvert.parse(derName, asn1X509__namespace.Name); name = new asn1X509__namespace.GeneralName({ directoryName: asnName }); break; } case DNS: name = new asn1X509__namespace.GeneralName({ dNSName: args[1] }); break; case EMAIL: name = new asn1X509__namespace.GeneralName({ rfc822Name: args[1] }); break; case GUID: { const matches = new RegExp(GUID_REGEX, "i").exec(args[1]); if (!matches) { throw new Error("Cannot parse GUID value. Value doesn't match to regular expression"); } const hex = matches .slice(1) .map((o, i) => { if (i < 3) { return pvtsutils.Convert.ToHex(new Uint8Array(pvtsutils.Convert.FromHex(o)).reverse()); } return o; }) .join(""); name = new asn1X509__namespace.GeneralName({ otherName: new asn1X509__namespace.OtherName({ typeId: id_GUID, value: asn1Schema.AsnConvert.serialize(new asn1Schema.OctetString(pvtsutils.Convert.FromHex(hex))), }), }); break; } case IP: name = new asn1X509__namespace.GeneralName({ iPAddress: args[1] }); break; case REGISTERED_ID: name = new asn1X509__namespace.GeneralName({ registeredID: args[1] }); break; case UPN: { name = new asn1X509__namespace.GeneralName({ otherName: new asn1X509__namespace.OtherName({ typeId: id_UPN, value: asn1Schema.AsnConvert.serialize(asn1Schema.AsnUtf8StringConverter.toASN(args[1])), }), }); break; } case URL: name = new asn1X509__namespace.GeneralName({ uniformResourceIdentifier: args[1] }); break; default: throw new Error("Cannot create GeneralName. Unsupported type of the name"); } } else if (pvtsutils.BufferSourceConverter.isBufferSource(args[0])) { name = asn1Schema.AsnConvert.parse(args[0], asn1X509__namespace.GeneralName); } else { name = args[0]; } super(name); } onInit(asn) { if (asn.dNSName != undefined) { this.type = DNS; this.value = asn.dNSName; } else if (asn.rfc822Name != undefined) { this.type = EMAIL; this.value = asn.rfc822Name; } else if (asn.iPAddress != undefined) { this.type = IP; this.value = asn.iPAddress; } else if (asn.uniformResourceIdentifier != undefined) { this.type = URL; this.value = asn.uniformResourceIdentifier; } else if (asn.registeredID != undefined) { this.type = REGISTERED_ID; this.value = asn.registeredID; } else if (asn.directoryName != undefined) { this.type = DN; this.value = new Name(asn.directoryName).toString(); } else if (asn.otherName != undefined) { if (asn.otherName.typeId === id_GUID) { this.type = GUID; const guid = asn1Schema.AsnConvert.parse(asn.otherName.value, asn1Schema.OctetString); const matches = new RegExp(GUID_REGEX, "i").exec(pvtsutils.Convert.ToHex(guid)); if (!matches) { throw new Error(ERR_GUID); } this.value = matches .slice(1) .map((o, i) => { if (i < 3) { return pvtsutils.Convert.ToHex(new Uint8Array(pvtsutils.Convert.FromHex(o)).reverse()); } return o; }) .join("-"); } else if (asn.otherName.typeId === id_UPN) { this.type = UPN; this.value = asn1Schema.AsnConvert.parse(asn.otherName.value, asn1X509__namespace.DirectoryString).toString(); } else { throw new Error(ERR_GN_STRING_FORMAT); } } else { throw new Error(ERR_GN_STRING_FORMAT); } } toJSON() { return { type: this.type, value: this.value, }; } toTextObject() { let type; switch (this.type) { case DN: case DNS: case GUID: case IP: case REGISTERED_ID: case UPN: case URL: type = this.type.toUpperCase(); break; case EMAIL: type = "Email"; break; default: throw new Error("Unsupported GeneralName type"); } let value = this.value; if (this.type === REGISTERED_ID) { value = OidSerializer.toString(value); } return new TextObject(type, undefined, value); } } class GeneralNames extends AsnData { constructor(params) { let names; if (params instanceof asn1X509__namespace.GeneralNames) { names = params; } else if (Array.isArray(params)) { const items = []; for (const name of params) { if (name instanceof asn1X509__namespace.GeneralName) { items.push(name); } else { const asnName = asn1Schema.AsnConvert.parse(new GeneralName(name.type, name.value).rawData, asn1X509__namespace.GeneralName); items.push(asnName); } } names = new asn1X509__namespace.GeneralNames(items); } else if (pvtsutils.BufferSourceConverter.isBufferSource(params)) { names = asn1Schema.AsnConvert.parse(params, asn1X509__namespace.GeneralNames); } else { throw new Error("Cannot initialize GeneralNames. Incorrect incoming arguments"); } super(names); } onInit(asn) { const items = []; for (const asnName of asn) { let name = null; try { name = new GeneralName(asnName); } catch { continue; } items.push(name); } this.items = items; } toJSON() { return this.items.map((o) => o.toJSON()); } toTextObject() { const res = super.toTextObjectEmpty(); for (const name of this.items) { const nameObj = name.toTextObject(); let field = res[nameObj[TextObject.NAME]]; if (!Array.isArray(field)) { field = []; res[nameObj[TextObject.NAME]] = field; } field.push(nameObj); } return res; } } GeneralNames.NAME = "GeneralNames"; const rPaddingTag = "-{5}"; const rEolChars = "\\n"; const rNameTag = `[^${rEolChars}]+`; const rBeginTag = `${rPaddingTag}BEGIN (${rNameTag}(?=${rPaddingTag}))${rPaddingTag}`; const rEndTag = `${rPaddingTag}END \\1${rPaddingTag}`; const rEolGroup = "\\n"; const rHeaderKey = `[^:${rEolChars}]+`; const rHeaderValue = `(?:[^${rEolChars}]+${rEolGroup}(?: +[^${rEolChars}]+${rEolGroup})*)`; const rBase64Chars = "[a-zA-Z0-9=+/]+"; const rBase64 = `(?:${rBase64Chars}${rEolGroup})+`; const rPem = `${rBeginTag}${rEolGroup}(?:((?:${rHeaderKey}: ${rHeaderValue})+))?${rEolGroup}?(${rBase64})${rEndTag}`; const rEolPattern = new RegExp(`[${rEolChars}]+`, "g"); class PemConverter { static isPem(data) { return typeof data === "string" && new RegExp(rPem, "g").test(data.replace(/\r/g, "")); } static decodeWithHeaders(pem) { pem = pem.replace(/\r/g, ""); const pattern = new RegExp(rPem, "g"); const res = []; let matches = null; while (matches = pattern.exec(pem)) { const base64 = matches[3] .replace(rEolPattern, ""); const pemStruct = { type: matches[1], headers: [], rawData: pvtsutils.Convert.FromBase64(base64), }; const headersString = matches[2]; if (headersString) { const headers = headersString.split(new RegExp(rEolGroup, "g")); let lastHeader = null; for (const header of headers) { const [key, value] = header.split(/:(.*)/); if (value === undefined) { if (!lastHeader) { throw new Error("Cannot parse PEM string. Incorrect header value"); } lastHeader.value += key.trim(); } else { if (lastHeader) { pemStruct.headers.push(lastHeader); } lastHeader = { key, value: value.trim(), }; } } if (lastHeader) { pemStruct.headers.push(lastHeader); } } res.push(pemStruct); } return res; } static decode(pem) { const blocks = this.decodeWithHeaders(pem); return blocks.map((o) => o.rawData); } static decodeFirst(pem) { const items = this.decode(pem); if (!items.length) { throw new RangeError("PEM string doesn't contain any objects"); } return items[0]; } static encode(rawData, tag) { if (Array.isArray(rawData)) { const raws = new Array(); if (tag) { rawData.forEach((element) => { if (!pvtsutils.BufferSourceConverter.isBufferSource(element)) { throw new TypeError("Cannot encode array of BufferSource in PEM format. Not all items of the array are BufferSource"); } raws.push(this.encodeStruct({ type: tag, rawData: pvtsutils.BufferSourceConverter.toArrayBuffer(element), })); }); } else { rawData.forEach((element) => { if (!("type" in element)) { throw new TypeError("Cannot encode array of PemStruct in PEM format. Not all items of the array are PemStrut"); } raws.push(this.encodeStruct(element)); }); } return raws.join("\n"); } else { if (!tag) { throw new Error("Required argument 'tag' is missed"); } return this.encodeStruct({ type: tag, rawData: pvtsutils.BufferSourceConverter.toArrayBuffer(rawData), }); } } static encodeStruct(pem) { var _a; const upperCaseType = pem.type.toLocaleUpperCase(); const res = []; res.push(`-----BEGIN ${upperCaseType}-----`); if ((_a = pem.headers) === null || _a === void 0 ? void 0 : _a.length) { for (const header of pem.headers) { res.push(`${header.key}: ${header.value}`); } res.push(""); } const base64 = pvtsutils.Convert.ToBase64(pem.rawData); for (let i = 0; i < base64.length; i += 64) { res.push(base64.substring(i, i + 64)); } res.push(`-----END ${upperCaseType}-----`); return res.join("\n"); } } PemConverter.CertificateTag = "CERTIFICATE"; PemConverter.CrlTag = "CRL"; PemConverter.CertificateRequestTag = "CERTIFICATE REQUEST"; PemConverter.PublicKeyTag = "PUBLIC KEY"; PemConverter.PrivateKeyTag = "PRIVATE KEY"; class PemData extends AsnData { static isAsnEncoded(data) { return pvtsutils.BufferSourceConverter.isBufferSource(data) || typeof data === "string"; } static toArrayBuffer(raw) { if (typeof raw === "string") { if (PemConverter.isPem(raw)) { return PemConverter.decode(raw)[0]; } else if (pvtsutils.Convert.isHex(raw)) { return pvtsutils.Convert.FromHex(raw); } else if (pvtsutils.Convert.isBase64(raw)) { return pvtsutils.Convert.FromBase64(raw); } else if (pvtsutils.Convert.isBase64Url(raw)) { return pvtsutils.Convert.FromBase64Url(raw); } else { throw new TypeError("Unsupported format of 'raw' argument. Must be one of DER, PEM, HEX, Base64, or Base4Url"); } } else { const buffer = pvtsutils.BufferSourceConverter.toUint8Array(raw); if (buffer.length > 0 && buffer[0] === 0x30) { return pvtsutils.BufferSourceConverter.toArrayBuffer(raw); } const stringRaw = pvtsutils.Convert.ToBinary(raw); if (PemConverter.isPem(stringRaw)) { return PemConverter.decode(stringRaw)[0]; } else if (pvtsutils.Convert.isHex(stringRaw)) { return pvtsutils.Convert.FromHex(stringRaw); } else if (pvtsutils.Convert.isBase64(stringRaw)) { return pvtsutils.Convert.FromBase64(stringRaw); } else if (pvtsutils.Convert.isBase64Url(stringRaw)) { return pvtsutils.Convert.FromBase64Url(stringRaw); } throw new TypeError("Unsupported format of 'raw' argument. Must be one of DER, PEM, HEX, Base64, or Base4Url"); } } constructor(...args) { if (PemData.isAsnEncoded(args[0])) { super(PemData.toArrayBuffer(args[0]), args[1]); } else { super(args[0]); } } toString(format = "pem") { switch (format) { case "pem": return PemConverter.encode(this.rawData, this.tag); default: return super.toString(format); } } } class PublicKey extends PemData { static async create(data, crypto = cryptoProvider.get()) { if (data instanceof PublicKey) { return data; } else if (CryptoProvider.isCryptoKey(data)) { if (data.type !== "public") { throw new TypeError("Public key is required"); } const spki = await crypto.subtle.exportKey("spki", data); return new PublicKey(spki); } else if (data.publicKey) { return data.publicKey; } else if (pvtsutils.BufferSourceConverter.isBufferSource(data)) { return new PublicKey(data); } else { throw new TypeError("Unsupported PublicKeyType"); } } constructor(param) { if (PemData.isAsnEncoded(param)) { super(param, asn1X509.SubjectPublicKeyInfo); } else { super(param); } this.tag = PemConverter.PublicKeyTag; } async export(arg1, arg2, arg3) { let crypto; let keyUsages = ["verify"]; let algorithm = { hash: "SHA-256", ...this.algorithm, }; if (arg2) { algorithm = arg1; keyUsages = arg2; crypto = arg3; } else { crypto = arg1; } crypto !== null && crypto !== void 0 ? crypto : (crypto = cryptoProvider.get());