/** * @file utility.ts * @author tngan * @desc Library for some common functions (e.g. de/inflation, en/decoding) */ import * as fs from 'fs'; import { pki, util, asn1 } from 'node-forge'; import { inflate, deflate } from 'deflate-js'; import { isString } from 'lodash'; import { DOMParser } from 'xmldom'; const BASE64_STR = 'base64'; /** * @desc Encode string with base64 format * @param {string} message plain-text message * @return {string} base64 encoded string */ function base64Encode(message: string) { return new Buffer(message).toString(BASE64_STR); } /** * @desc Decode string from base64 format * @param {string} base64Message encoded string * @param {boolean} isBytes determine the return value type (True: bytes False: string) * @return {bytes/string} decoded bytes/string depends on isBytes, default is {string} */ export function base64Decode(base64Message: string, isBytes?: boolean): string | Buffer { const bytes = new Buffer(base64Message, BASE64_STR); return Boolean(isBytes) ? bytes : bytes.toString(); } /** * @desc Compress the string * @param {string} message * @return {string} compressed string */ function deflateString(message: string): string { return deflate(Array.prototype.map.call(message, char => char.charCodeAt(0))); } /** * @desc Decompress the compressed string * @param {string} compressedString * @return {string} decompressed string */ export function inflateString(compressedString: string): string { return inflate(Array.prototype.map.call(new Buffer(compressedString, BASE64_STR).toString('binary'), char => char.charCodeAt(0))) .map(byte => String.fromCharCode(byte)) .join(''); } /** * @desc Abstract the normalizeCerString and normalizePemString * @param {buffer} File stream or string * @param {string} String for header and tail * @return {string} A formatted certificate string */ function _normalizeCerString(bin: string | Buffer, format: string) { return bin.toString().replace(/\n/g, '').replace(/\r/g, '').replace(`-----BEGIN ${format}-----`, '').replace(`-----END ${format}-----`, '').replace(/ /g, ''); } /** * @desc Parse the .cer to string format without line break, header and footer * @param {string} certString declares the certificate contents * @return {string} certificiate in string format */ function normalizeCerString(certString: string | Buffer) { return _normalizeCerString(certString, 'CERTIFICATE'); } /** * @desc Normalize the string in .pem format without line break, header and footer * @param {string} pemString * @return {string} private key in string format */ function normalizePemString(pemString: string | Buffer) { return _normalizeCerString(pemString.toString(), 'RSA PRIVATE KEY'); } /** * @desc Return the complete URL * @param {object} req HTTP request * @return {string} URL */ function getFullURL(req) { return `${req.protocol}://${req.get('host')}${req.originalUrl}`; } /** * @desc Parse input string, return default value if it is undefined * @param {string/boolean} * @return {boolean} */ function parseString(str, defaultValue = '') { return str || defaultValue; } /** * @desc Override the object by another object (rtl) * @param {object} default object * @param {object} object applied to the default object * @return {object} result object */ function applyDefault(obj1, obj2) { return Object.assign({}, obj1, obj2); } /** * @desc Get public key in pem format from the certificate included in the metadata * @param {string} x509 certificate * @return {string} public key fetched from the certificate */ function getPublicKeyPemFromCertificate(x509Certificate: string) { const certDerBytes = util.decode64(x509Certificate); const obj = asn1.fromDer(certDerBytes); const cert = pki.certificateFromAsn1(obj); return pki.publicKeyToPem(cert.publicKey); } /** * @desc Read private key from pem-formatted string * @param {string | Buffer} keyString pem-formattted string * @param {string} protected passphrase of the key * @return {string} string in pem format * If passphrase is used to protect the .pem content (recommend) */ export function readPrivateKey(keyString: string | Buffer, passphrase: string, isOutputString?: boolean) { return isString(passphrase) ? this.convertToString(pki.privateKeyToPem(pki.decryptRsaPrivateKey(String(keyString), passphrase)), isOutputString) : keyString; } /** * @desc Inline syntax sugar */ function convertToString(input, isOutputString) { return Boolean(isOutputString) ? String(input) : input; } /** * @desc Check if the input is an array with non-zero size */ export function isNonEmptyArray(a) { return Array.isArray(a) && a.length > 0; } const utility = { base64Encode, base64Decode, deflateString, inflateString, normalizeCerString, normalizePemString, getFullURL, parseString, applyDefault, getPublicKeyPemFromCertificate, readPrivateKey, convertToString, isNonEmptyArray, }; export default utility;