@citrineos/util
Version:
The OCPP util module which supplies helpful utilities like cache and queue connectors, etc.
330 lines • 13.6 kB
JavaScript
;
// 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