UNPKG

@smartdcc/dccboxed-keystore

Version:
211 lines 7.75 kB
"use strict"; /* * Created on Thu Aug 04 2022 * * Copyright (c) 2022 Smart DCC Limited * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.prepareRequest = prepareRequest; exports.parseUrl = parseUrl; exports.resolveHeaders = resolveHeaders; exports.search = search; exports.query = query; const fast_xml_parser_1 = require("fast-xml-parser"); const got_1 = __importDefault(require("got")); const content_type_1 = require("content-type"); const crypto_1 = require("crypto"); const certificateMetadata_1 = require("./certificateMetadata"); function prepareRequest(name, body) { const builder = new fast_xml_parser_1.XMLBuilder({ ignoreAttributes: false }); return builder.build({ '?xml': { '@_version': '1.0', '@_encoding': 'utf-8', [name]: body, }, }); } /** * Parses a URL-like string and returns a properly formatted URL string * * @param url_like - String that may be a partial or complete URL * @returns Properly formatted URL string with protocol and port if needed * * If the input string: * - Does not contain a colon: Prepends 'http://' and appends port 8083 * - Does not contain protocol: Prepends 'http://' * - Is already a complete URL: Returns as-is after URL validation */ function parseUrl(url_like) { if (!url_like.includes(':')) { return `http://${url_like}:8083/`; } const fullUrl = url_like.includes('://') ? url_like : `http://${url_like}`; return new URL(fullUrl).toString(); } /** * Resolves a Headers object into a wider object by evaluating any function values * and resolving any promises. * * @param headers - The Headers object containing string values, functions or promises * @param implHeaders - Optional existing headers object to merge with (e.g. got's headers) * @returns Promise resolving to a headers record with all string values */ async function resolveHeaders(headers = {}, implHeaders = {}) { const resolvedHeaders = { ...implHeaders }; for (const [key, value] of Object.entries(headers)) { if (typeof value === 'function') { const result = value(); if (result instanceof Promise) { resolvedHeaders[key] = await result; } else { resolvedHeaders[key] = result; } } else { resolvedHeaders[key] = value; } } return resolvedHeaders; } /** * queries the SMKI certificatesearch service. when entering the * CertificateSubjectName or CertificateSubjectAltName parameters, ensure they * follow the <code>a1-a2-a3-a4-a5-a6-a7-a8</code> format. * * @param sr * @param boxedAddress * @returns */ async function search(sr, boxedAddress, headers) { const result = await (0, got_1.default)(`${parseUrl(boxedAddress)}services/certificatesearch`, { method: 'post', headers: await resolveHeaders(headers, { 'content-type': 'application/xml', }), searchParams: { apikey: 'u3bg9gt38htd0j2' }, body: prepareRequest('CertificateSearchRequest', sr.q), timeout: { lookup: 100, connect: 500, request: 4000, }, throwHttpErrors: false, }); const ct = result.headers['content-type']; if (result.statusCode === 402) { return []; } else if (result.statusCode === 401) { throw new Error('invalid search parameters'); } else if (result.statusCode !== 200 || !ct || (0, content_type_1.parse)(ct).type !== 'application/xml') { throw new Error(`unknown error ${result.statusCode}: ${ct}`); } const parser = new fast_xml_parser_1.XMLParser({ ignoreAttributes: false, parseAttributeValue: false, parseTagValue: false, }); const searchResult = parser.parse(result.body); const resultListMaybe = searchResult?.CertificateSearchResponse?.Result; if (!resultListMaybe) { return []; } const serials = []; let resultList; if (!Array.isArray(resultListMaybe)) { resultList = [resultListMaybe]; } else { resultList = resultListMaybe; } for (const e of resultList) { if (e?.CertificateUsage === sr.CertificateUsage && typeof e?.CertificateSerial === 'string' && e?.CertificateStatus === sr.CertificateStatus) { const srRole = sr; if (typeof srRole.CertificateRole !== 'string' || srRole.CertificateRole === e?.CertificateRole) { serials.push(e?.CertificateSerial); } } } const queryResults = await Promise.all(serials.map((serial) => query(serial, boxedAddress, headers))); return queryResults.filter((qr) => qr !== null); } async function query(serial, boxedAddress, headers) { const result = await (0, got_1.default)(`${parseUrl(boxedAddress)}services/retrievecertificate`, { method: 'post', headers: await resolveHeaders(headers, { 'content-type': 'application/xml', }), searchParams: { apikey: 'u3bg9gt38htd0j2' }, body: prepareRequest('CertificateDataRequest', { CertificateSerial: serial, }), timeout: { lookup: 100, connect: 500, request: 4000, }, throwHttpErrors: false, }); const ct = result.headers['content-type']; if (result.statusCode === 402) { return null; } else if (result.statusCode === 401) { throw new Error('invalid search parameters'); } else if (result.statusCode !== 200 || !ct || (0, content_type_1.parse)(ct).type !== 'application/xml') { throw new Error(`unknown error ${result.statusCode}: ${ct}`); } const parser = new fast_xml_parser_1.XMLParser({ ignoreAttributes: false, parseAttributeValue: false, parseTagValue: false, }); const certificateResult = parser.parse(result.body); const certificateResponse = certificateResult?.CertificateDataResponse?.CertificateResponse; if (typeof certificateResponse?.CertificateBody === 'string') { const x509 = new crypto_1.X509Certificate(`-----BEGIN CERTIFICATE-----\n${certificateResponse?.CertificateBody}\n-----END CERTIFICATE-----`); if (typeof certificateResponse?.CertificateSubjectName === 'string') { /* org cert */ return { meta: (0, certificateMetadata_1.buildOrgCertificateMetadata)(x509), x509, }; } else if (typeof certificateResponse?.CertificateSubjectAltName === 'string') { /* device cert */ return { meta: (0, certificateMetadata_1.buildDeviceCertificateMetadata)(x509), x509, }; } } return null; } //# sourceMappingURL=certificateSearch.js.map