UNPKG

@citrineos/util

Version:

The OCPP util module which supplies helpful utilities like cache and queue connectors, etc.

330 lines 13.6 kB
"use strict"; // Copyright Contributors to the CitrineOS Project // // SPDX-License-Identifier: Apache 2.0 var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.dateTimeFormat = void 0; exports.getValidityTimeString = getValidityTimeString; exports.createPemBlock = createPemBlock; exports.parseCertificateChainPem = parseCertificateChainPem; exports.extractCertificateArrayFromEncodedString = extractCertificateArrayFromEncodedString; exports.extractEncodedContentFromCSR = extractEncodedContentFromCSR; exports.generateCertificate = generateCertificate; exports.createSignedCertificateFromCSR = createSignedCertificateFromCSR; exports.sendOCSPRequest = sendOCSPRequest; exports.parseCSRForVerification = parseCSRForVerification; exports.generateCSR = generateCSR; const pkijs = __importStar(require("pkijs")); const asn1js = __importStar(require("asn1js")); const jsrsasign_1 = __importDefault(require("jsrsasign")); var KJUR = jsrsasign_1.default.KJUR; var X509 = jsrsasign_1.default.X509; const pkijs_1 = require("pkijs"); const pvutils_1 = require("pvutils"); const asn1js_1 = require("asn1js"); const moment_1 = __importDefault(require("moment")); var KEYUTIL = jsrsasign_1.default.KEYUTIL; exports.dateTimeFormat = 'YYMMDDHHmmssZ'; function getValidityTimeString(time) { return time.utc().format('YYMMDDHHmmss').concat('Z'); } function createPemBlock(type, content) { return `-----BEGIN ${type}-----\n${content}\n-----END ${type}-----\n`; } /* * Parse the certificate chain and extract certificates * @param pem - certificate chain pem containing multiple certificate blocks * @return array of certificate pem blocks */ function parseCertificateChainPem(pem) { var _a; const certs = []; (_a = pem .match(/-----BEGIN CERTIFICATE-----[\s\S]+?-----END CERTIFICATE-----/g)) === null || _a === void 0 ? void 0 : _a.forEach((certPem) => certs.push(certPem)); return certs; } /** * Decode the pem and extract certificates * @param pem - base64 encoded certificate chain string without header and footer * @return array of pkijs.CertificateSetItem */ function extractCertificateArrayFromEncodedString(pem) { try { const cmsSignedBuffer = Buffer.from(pem, 'base64'); const asn1 = asn1js.fromBER(cmsSignedBuffer); const cmsContent = new pkijs.ContentInfo({ schema: asn1.result }); const cmsSigned = new pkijs.SignedData({ schema: cmsContent.content }); if (cmsSigned.certificates && cmsSigned.certificates.length > 0) { return cmsSigned.certificates; } else { return []; } } catch (e) { throw new Error(`Failed to extract certificate ${pem} due to ${e}`); } } /** * extracts the base64-encoded content from a pem encoded csr * @param csrPem * @private * @return {string} The parsed CSR or the original CSR if it cannot be parsed */ function extractEncodedContentFromCSR(csrPem) { return csrPem .replace(/-----BEGIN CERTIFICATE REQUEST-----/, '') .replace(/-----END CERTIFICATE REQUEST-----/, '') .replace(/\n/g, ''); } /** * Generate certificate and its private key * * @param certificateEntity - the certificate * @param logger - the logger * @param issuerKeyPem - the issuer private key * @param issuerCertPem - the issuer certificate * * @return generated certificate pem and its private key pem */ function generateCertificate(certificateEntity, logger, issuerKeyPem, issuerCertPem) { // Generate a key pair let keyPair; logger.debug(`Private key signAlgorithm: ${certificateEntity.signatureAlgorithm}`); if (certificateEntity.signatureAlgorithm === "SHA256withRSA" /* SignatureAlgorithmEnumType.RSA */) { keyPair = jsrsasign_1.default.KEYUTIL.generateKeypair('RSA', certificateEntity.keyLength ? certificateEntity.keyLength : 2048); } else { keyPair = jsrsasign_1.default.KEYUTIL.generateKeypair('EC', 'secp256r1'); } const privateKeyPem = jsrsasign_1.default.KEYUTIL.getPEM(keyPair.prvKeyObj, 'PKCS8PRV'); const publicKeyPem = jsrsasign_1.default.KEYUTIL.getPEM(keyPair.pubKeyObj); logger.debug(`Created publicKeyPem: ${publicKeyPem}`); let issuerCertObj; if (issuerCertPem) { issuerCertObj = new X509(); issuerCertObj.readCertPEM(issuerCertPem); } // Prepare certificate attributes let subjectNotAfter = certificateEntity.validBefore ? (0, moment_1.default)(certificateEntity.validBefore) : (0, moment_1.default)().add(1, 'year'); const subjectString = `/CN=${certificateEntity.commonName}/O=${certificateEntity.organizationName}/C=${certificateEntity.countryName}`; let issuerParam = { str: subjectString }; if (issuerCertObj) { const issuerNotAfter = (0, moment_1.default)(issuerCertObj.getNotAfter(), exports.dateTimeFormat); if (subjectNotAfter.isAfter(issuerNotAfter)) { subjectNotAfter = issuerNotAfter; } issuerParam = { str: issuerCertObj.getSubjectString() }; } // Prepare certificate extensions const keyUsages = ['digitalSignature', 'keyCertSign', 'crlSign']; if (!certificateEntity.isCA) { keyUsages.push('keyEncipherment'); } let basicConstraints = { extname: 'basicConstraints', critical: true, cA: certificateEntity.isCA, }; if (certificateEntity.pathLen) { basicConstraints = { extname: 'basicConstraints', cA: certificateEntity.isCA, pathLen: certificateEntity.pathLen, }; } const extensions = [ basicConstraints, { extname: 'keyUsage', critical: true, names: keyUsages }, { extname: 'subjectKeyIdentifier', kid: publicKeyPem }, ]; if (issuerCertObj) { extensions.push({ extname: 'authorityKeyIdentifier', kid: issuerCertPem, isscert: issuerCertPem, }); } // Prepare certificate sign parameters const signAlgorithm = certificateEntity.signatureAlgorithm === "SHA256withRSA" /* SignatureAlgorithmEnumType.RSA */ ? "SHA256withRSA" /* SignatureAlgorithmEnumType.RSA */ : "SHA256withECDSA" /* SignatureAlgorithmEnumType.ECDSA */; logger.debug(`Certificate SignAlgorithm: ${signAlgorithm}`); const caKey = issuerKeyPem ? issuerKeyPem : privateKeyPem; // Generate certificate const certificate = new KJUR.asn1.x509.Certificate({ version: 3, serial: { int: (0, moment_1.default)().valueOf() }, notbefore: getValidityTimeString((0, moment_1.default)()), notafter: getValidityTimeString(subjectNotAfter), issuer: issuerParam, subject: { str: subjectString }, sbjpubkey: keyPair.pubKeyObj, ext: extensions, sigalg: signAlgorithm, cakey: caKey, }); return [certificate.getPEM(), privateKeyPem]; } /** * Create a signed certificate for the provided CSR using the issuer certificate, and its private key. * * @param csrPem - The CSR that need to be signed. * @param issuerCertPem - The issuer certificate. * @param issuerPrivateKeyPem - The issuer private key. * @return {KJUR.asn1.x509.Certificate} The signed certificate. */ function createSignedCertificateFromCSR(csrPem, issuerCertPem, issuerPrivateKeyPem) { const csrObj = jsrsasign_1.default.KJUR.asn1.csr.CSRUtil.getParam(csrPem); const issuerCertObj = new X509(); issuerCertObj.readCertPEM(issuerCertPem); let subjectNotAfter = (0, moment_1.default)().add(1, 'year'); const issuerNotAfter = (0, moment_1.default)(issuerCertObj.getNotAfter(), exports.dateTimeFormat); if (subjectNotAfter.isAfter(issuerNotAfter)) { subjectNotAfter = issuerNotAfter; } let extensions; if (csrObj.extreq) { extensions = csrObj.extreq; } else { extensions = [ { extname: 'basicConstraints', cA: false }, { extname: 'keyUsage', critical: true, names: ['digitalSignature', 'keyEncipherment'], }, ]; } extensions.push({ extname: 'subjectKeyIdentifier', kid: csrObj.sbjpubkey }); extensions.push({ extname: 'authorityKeyIdentifier', kid: issuerCertPem, isscert: issuerCertPem, }); return new KJUR.asn1.x509.Certificate({ version: 3, serial: { int: (0, moment_1.default)().valueOf() }, issuer: { str: issuerCertObj.getSubjectString() }, subject: { str: csrObj.subject.str }, notbefore: getValidityTimeString((0, moment_1.default)()), notafter: getValidityTimeString(subjectNotAfter), sbjpubkey: csrObj.sbjpubkey, ext: extensions, sigalg: csrObj.sigalg, cakey: issuerPrivateKeyPem, }); } function sendOCSPRequest(ocspRequest, responderURL) { return __awaiter(this, void 0, void 0, function* () { const response = yield fetch(responderURL, { method: 'POST', headers: { 'Content-Type': 'application/ocsp-request', Accept: 'application/ocsp-response', }, body: ocspRequest.getEncodedHex(), }); if (!response.ok) { throw new Error(`Failed to fetch OCSP response from ${responderURL}: ${response.status} with error: ${yield response.text()}`); } return yield response.text(); }); } function parseCSRForVerification(csrPem) { const certificateBuffer = (0, pvutils_1.stringToArrayBuffer)((0, pvutils_1.fromBase64)(extractEncodedContentFromCSR(csrPem))); const asn1 = (0, asn1js_1.fromBER)(certificateBuffer); return new pkijs_1.CertificationRequest({ schema: asn1.result }); } function generateCSR(certificate) { let keyPair; if (certificate.signatureAlgorithm === "SHA256withRSA" /* SignatureAlgorithmEnumType.RSA */) { keyPair = KEYUTIL.generateKeypair('RSA', certificate.keyLength ? certificate.keyLength : 2048); } else { keyPair = KEYUTIL.generateKeypair('EC', 'secp256r1'); } const privateKeyPem = jsrsasign_1.default.KEYUTIL.getPEM(keyPair.prvKeyObj, 'PKCS8PRV'); const publicKeyPem = jsrsasign_1.default.KEYUTIL.getPEM(keyPair.pubKeyObj); let basicConstraintParam; if (certificate.pathLen) { basicConstraintParam = { cA: certificate.isCA, pathLen: certificate.pathLen, }; } else { basicConstraintParam = { cA: certificate.isCA }; } const csr = new KJUR.asn1.csr.CertificationRequest({ subject: { str: `/CN=${certificate.commonName}/O=${certificate.organizationName}/C=${certificate.countryName}`, }, sbjpubkey: publicKeyPem, extreq: [ { extname: 'basicConstraints', array: [basicConstraintParam] }, { extname: 'keyUsage', array: [ { names: ['digitalSignature', 'keyEncipherment', 'keyCertSign', 'crlSign'], }, ], }, ], sigalg: certificate.signatureAlgorithm ? certificate.signatureAlgorithm : "SHA256withECDSA" /* SignatureAlgorithmEnumType.ECDSA */, sbjprvkey: privateKeyPem, }); return [csr.getPEM(), privateKeyPem]; } //# sourceMappingURL=CertificateUtil.js.map