@denis-kalinin/dev-certs
Version:
Managing certificates on nodejs http server
145 lines • 5.87 kB
JavaScript
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
Object.defineProperty(exports, "__esModule", { value: true });
exports.verifyCertificates = exports.validateCertificateAndKey = exports.isCaInDir = exports.isCaCertificateInstalled = exports.outputMarker = void 0;
const child_process_1 = require("child_process");
const crypto = require("crypto");
const fs = require("fs");
const path = require("path");
const defaults = require("./defaults");
const defaults_1 = require("./defaults");
const office_addin_usage_data_1 = require("office-addin-usage-data");
// On win32 this is a unique hash used with PowerShell command to reliably delineate command output
exports.outputMarker = process.platform === "win32"
? `[${crypto
.createHash("md5")
.update(`${defaults.certificateName}${defaults.caCertificatePath}`)
.digest("hex")}]`
: "";
/* global process, Buffer, __dirname */
function getVerifyCommand(returnInvalidCertificate) {
switch (process.platform) {
case "win32": {
const script = path.resolve(__dirname, "..\\scripts\\verify_ca.ps1");
const defaultCommand = `powershell -ExecutionPolicy Bypass -File "${script}" -CaCertificateName "${defaults.certificateName}" -CaCertificatePath "${defaults.caCertificatePath}" -OutputMarker "${exports.outputMarker}"`;
if (returnInvalidCertificate) {
return defaultCommand + ` -ReturnInvalidCertificate`;
}
return defaultCommand;
}
case "darwin": {
// macOS
const script = path.resolve(__dirname, "../scripts/verify_ca.sh");
return `sh '${script}' '${defaults.certificateName}'`;
}
case "linux": {
const script = path.resolve(__dirname, "../scripts/verify_ca_linux.sh");
return `sh '${script}' '${defaults.certificateName}'`;
}
default:
throw new office_addin_usage_data_1.ExpectedError(`Platform not supported: ${process.platform}`);
}
}
function isCaCertificateInstalled(returnInvalidCertificate = false) {
const command = getVerifyCommand(returnInvalidCertificate);
try {
const output = (0, child_process_1.execSync)(command, { stdio: "pipe" }).toString();
if (process.platform === "win32") {
// Remove any PowerShell output that preceeds invoking the actual certificate check command
return (output
.slice(output.lastIndexOf(exports.outputMarker) + exports.outputMarker.length)
.trim().length !== 0);
}
// script files return empty string if the certificate not found or expired
if (output.length !== 0) {
return true;
}
}
catch (error) {
// Some commands throw errors if the certifcate is not found or expired
}
return false;
}
exports.isCaCertificateInstalled = isCaCertificateInstalled;
function isCaInDir() {
//check CA has a valid key
const caCertPath = path.join(defaults.certificateDirectory, defaults.caCertificateFileName);
const caCertKey = path.join(defaults.certificateDirectory, defaults.caKeyFileName);
try {
validateCertificateAndKey(caCertPath, caCertKey);
return true;
}
catch (err) {
return false;
}
}
exports.isCaInDir = isCaInDir;
function validateCertificateAndKey(certificatePath, keyPath) {
let certificate;
let key;
try {
certificate = fs.readFileSync(certificatePath).toString();
}
catch (err) {
throw new Error(`Unable to read the certificate.\n${err}`);
}
try {
key = fs.readFileSync(keyPath).toString();
}
catch (err) {
throw new Error(`Unable to read the certificate key.\n${err}`);
}
let encrypted;
try {
encrypted = crypto.publicEncrypt(certificate, Buffer.from("test"));
}
catch (err) {
throw new Error(`The certificate is not valid.\n${err}`);
}
try {
crypto.privateDecrypt(key, encrypted);
}
catch (err) {
throw new Error(`The certificate key is not valid.\n${err}`);
}
return { certificate, key };
}
exports.validateCertificateAndKey = validateCertificateAndKey;
function validateIssuer(certificate) {
const caPath = path.join(defaults.certificateDirectory, defaults.caCertificateFileName);
const caCert = new crypto.X509Certificate(fs.readFileSync(caPath));
const now = new Date();
if (now < new Date(caCert.validFrom))
throw new Error("CA certificate is not YET valid.");
if (now > new Date(caCert.validTo))
throw new Error("CA certificate is not ALREADY valid");
const localcert = new crypto.X509Certificate(certificate);
if (now < new Date(localcert.validFrom))
throw new Error(`Cert for ${localcert.subject} is not YET valid.`);
if (now > new Date(localcert.validTo))
throw new Error(`Cert for ${localcert.subject} is not ALREADY valid.`);
const isCertChainOk = localcert.checkIssued(caCert);
if (!isCertChainOk)
throw new Error("Certificate chain is wrong.");
}
function verifyCertificates(filename) {
const localPath = defaults.getLocalPath(filename);
try {
let isCertificateValid = true;
try {
const certKey = validateCertificateAndKey(`${localPath}.crt`, `${localPath}.key`);
validateIssuer(certKey.certificate);
}
catch (err) {
isCertificateValid = false;
}
defaults_1.usageDataObject.reportSuccess("verifyCertificates()");
return isCertificateValid;
}
catch (err) {
defaults_1.usageDataObject.reportException("verifyCertificates()", err);
throw err;
}
}
exports.verifyCertificates = verifyCertificates;
//# sourceMappingURL=verify.js.map