UNPKG

@adp-psych/container-tools

Version:

Tools for using containers for psychology experiments

213 lines (198 loc) 5.07 kB
/* * Copyright (C) 2021 Anthony Di Pietro * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. */ /** * @file Generates a self-signed TLS certificate. * * @module module:generate-selfsigned-certificate * @author Anthony Di Pietro <anthony.dipietro@research.uwa.edu.au> * @copyright © 2021 Anthony Di Pietro * @license AGPL-3.0-or-later */ /* eslint-disable security/detect-non-literal-fs-filename -- Trusted source. */ const fs = require('node:fs/promises'); const path = require('node:path'); const { 'pki': { certificateToPem, createCertificate, privateKeyToPem, 'rsa': {generateKeyPair}, }, } = require('node-forge'); /** * The RSA key size. * * @constant {Number} * @static * @access protected */ const KEY_SIZE = 4096; /** * The certificate subject. * * @constant {Object} * @static * @access protected */ const SUBJECT = [{ 'name': 'commonName', 'value': 'localhost', }]; /** * The certificate extensions. * * @constant {Array.<Object>} * @static * @access protected */ const EXTENSIONS = [ { 'cA': true, 'name': 'basicConstraints', }, { 'dataEncipherment': true, 'digitalSignature': true, 'keyCertSign': true, 'keyEncipherment': true, 'name': 'keyUsage', 'nonRepudiation': true, }, { 'clientAuth': true, 'codeSigning': true, 'emailProtection': true, 'name': 'extKeyUsage', 'serverAuth': true, 'timeStamping': true, }, { 'client': true, 'email': true, 'emailCA': true, 'name': 'nsCertType', 'objCA': true, 'objsign': true, 'server': true, 'sslCA': true, }, { 'altNames': [{ 'ip': '127.0.0.1', 'type': 7, }], 'name': 'subjectAltName', }, { 'name': 'subjectKeyIdentifier', }, ]; /** * Gets the validity dates for a certificate that lasts one year starting now. * * @static * @access protected * @returns {Object} The validity dates. * @example * validityDates(); */ const validityDates = () => { const notBefore = new Date(); const notAfter = new Date(); notAfter.setFullYear(notBefore.getFullYear() + 1); return {notAfter, notBefore}; }; /** * Makes a key pair. * * @static * @access protected * @returns {Object} The key pair. * @example * makeKeyPair(); */ const makeKeyPair = () => generateKeyPair(KEY_SIZE); /** * Makes a certificate. * * @static * @access protected * @param {Object} keyPair - The key pair for the certificate. * @returns {Object} The certificate. * @example * makeCertificate(makeKeyPair()); */ const makeCertificate = (keyPair) => { const {notAfter, notBefore} = validityDates(); const certificate = createCertificate(); certificate.publicKey = keyPair.publicKey; certificate.serialNumber = '01'; certificate.validity.notBefore = notBefore; certificate.validity.notAfter = notAfter; certificate.setSubject(SUBJECT); certificate.setIssuer(SUBJECT); certificate.setExtensions(EXTENSIONS); certificate.sign(keyPair.privateKey); return certificate; }; /** * Writes a private key. * * @static * @access protected * @param {Object} privateKey - The private key. * @param {String} filename - The filename. * @example * writePrivateKey(makeKeyPair().privateKey, 'privkey.pem'); */ const writePrivateKey = async (privateKey, filename) => { await fs.mkdir(path.dirname(filename), {'recursive': true}); await fs.writeFile(filename, privateKeyToPem(privateKey)); }; /** * Writes a certificate. * * @static * @access protected * @param {Object} certificate - The certificate. * @param {String} filename - The filename. * @example * writeCertificate(makeCertificate(makeKeyPair()), 'cert.pem'); */ const writeCertificate = async (certificate, filename) => { await fs.mkdir(path.dirname(filename), {'recursive': true}); await fs.writeFile(filename, certificateToPem(certificate)); }; /** * Generates a self-signed TLS certificate. * * @static * @param {String} keyFilename - The filename of the private key. * @param {String} certificateFilename - The filename of the certificate. * @example * // Generates a self-signed certificate. * generateSelfsignedCertificate('tls/privkey.pem', 'tls/cert.pem'); */ const generateSelfsignedCertificate = async ( keyFilename, certificateFilename, ) => { const keyPair = makeKeyPair(); const certificate = makeCertificate(keyPair); await writePrivateKey(keyPair.privateKey, keyFilename); await writeCertificate(certificate, certificateFilename); }; module.exports = generateSelfsignedCertificate;