resedit-cli
Version:
Command-line tool for editing Windows Resource data in executable binaries
295 lines (294 loc) • 9.91 kB
JavaScript
import { createRequire } from 'module';
import { CertificateSelectMode } from '../definitions/DefinitionData.js';
const require = createRequire(import.meta.url);
const forge = require('node-forge');
export function makeChainList(list) {
const map = new Map();
list.forEach((d) => {
let c;
if ('cert' in d && d.cert) {
c = d.cert;
}
else if ('issuer' in d) {
c = d;
}
else {
return;
}
let me = map.get(c.subject.hash);
if (!me) {
me = {
bag: d,
subject: c.subject,
children: [],
};
map.set(me.subject.hash, me);
}
else {
if (!me.bag) {
me.bag = d;
}
}
if (c.subject.hash !== c.issuer.hash) {
let parent = map.get(c.issuer.hash);
if (!parent) {
parent = {
subject: c.issuer,
children: [me],
};
map.set(parent.subject.hash, parent);
}
else {
parent.children.push(me);
}
me.parent = parent;
}
});
const chainRoots = [];
for (const temp of map.values()) {
if (!temp.bag) {
continue;
}
if (temp.parent) {
if (temp.parent.bag) {
delete temp.parent;
continue;
}
delete temp.parent;
}
chainRoots.push(temp);
}
return chainRoots;
}
export function filterAndSortCertListByChain(list, certSelect) {
const concatChainData = (prev, d) => {
return prev
.concat(d.bag)
.concat(d.children.reduce(concatChainData, []));
};
let sortedCertLists = makeChainList(list).map((d) => concatChainData([], d).reverse());
switch (certSelect) {
case CertificateSelectMode.NoRoot:
sortedCertLists = sortedCertLists.map((a) => {
return a.filter((elem) => {
return 'cert' in elem
? elem.cert.subject.hash !== elem.cert.issuer.hash
: elem.subject.hash !==
elem.issuer.hash;
});
});
break;
case CertificateSelectMode.All:
break;
case CertificateSelectMode.Leaf:
default:
sortedCertLists = sortedCertLists.map((a) => a.slice(0, 1));
break;
}
return sortedCertLists.reduce((prev, list) => prev.concat(list), []);
}
function splitCertsFromPem(pemData) {
const ret = [];
let inSection = false;
let tempText = '';
pemData.split(/\r\n|\n/g).forEach((line) => {
if (/^-----BEGIN CERTIFICATE-----$/.test(line)) {
inSection = true;
tempText = line + '\n';
}
else if (/^-----END CERTIFICATE-----$/.test(line)) {
if (inSection) {
inSection = false;
ret.push(tempText + line + '\n');
}
}
else {
if (inSection) {
tempText += line + '\n';
}
}
});
return ret;
}
export function getCertificatesFromPem(pemData, certSelect) {
const pems = splitCertsFromPem(pemData);
if (pems.length === 0) {
throw new Error('No certificates in PEM data');
}
const sortedList = filterAndSortCertListByChain(pems.map((onePemData) => {
return forge.pki.certificateFromPem(onePemData);
}), certSelect);
return sortedList.map((cert) => {
const asn1 = forge.pki.certificateToAsn1(cert);
return Buffer.from(forge.asn1.toDer(asn1).getBytes(), 'binary');
});
}
export function verifyDERCertificates(bin, certSelect) {
const asn1 = forge.asn1.fromDer(forge.util.createBuffer(bin.toString('binary')));
try {
forge.pki.certificateFromAsn1(asn1);
return bin;
}
catch (_a) { }
try {
const signedData = forge.pkcs7.messageFromAsn1(asn1);
if (!('certificates' in signedData)) {
throw new Error();
}
const certificates = signedData.certificates;
let asn1Cert;
switch (certSelect) {
case CertificateSelectMode.NoRoot:
signedData.certificates = certificates.filter((cert) => {
return cert.issuer.hash !== cert.subject.hash;
});
asn1Cert = signedData.toAsn1();
break;
case CertificateSelectMode.All:
asn1Cert = asn1;
break;
case CertificateSelectMode.Leaf:
default:
asn1Cert = forge.pki.certificateToAsn1(certificates[0]);
break;
}
return Buffer.from(forge.asn1.toDer(asn1Cert).getBytes(), 'binary');
}
catch (_b) {
throw new Error('Not supported certificate data');
}
}
function getPrivateKeyAlgorithmTypeFromBase64(data) {
const bin = Buffer.from(data, 'base64');
const asn1 = forge.asn1.fromDer(forge.util.createBuffer(bin.toString('binary')));
if (asn1.type !== forge.asn1.Type.SEQUENCE || asn1.value.length !== 3) {
return null;
}
const privateKeyAlgorithm = asn1.value[1];
if (privateKeyAlgorithm.type !== forge.asn1.Type.SEQUENCE ||
privateKeyAlgorithm.value.length !== 2) {
return null;
}
const algorithm = privateKeyAlgorithm.value[0];
if (algorithm.type !== forge.asn1.Type.OID) {
return null;
}
const oid = forge.asn1.derToOid(forge.util.createBuffer(algorithm.value));
if (oid === '1.2.840.113549.1.1.1') {
return true;
}
else if (oid === '1.2.840.10040.4.1') {
return false;
}
return null;
}
export function pickPrivateKeyFromPem(pemData) {
const ret = [];
let inSection = false;
let isRSA = null;
let tempText = '';
let tempTextHeader = '';
pemData.split(/\r\n|\n/g).forEach((line) => {
if (/^-----BEGIN RSA PRIVATE KEY-----$/.test(line)) {
if (!inSection) {
inSection = true;
isRSA = true;
tempText = line + '\n';
}
}
else if (/^-----END RSA PRIVATE KEY-----$/.test(line)) {
if (inSection) {
inSection = false;
if (isRSA) {
ret.push([true, tempText + line + '\n']);
}
}
}
else if (/^-----BEGIN DSA PRIVATE KEY-----$/.test(line)) {
if (!inSection) {
inSection = true;
isRSA = false;
tempText = line + '\n';
}
}
else if (/^-----END DSA PRIVATE KEY-----$/.test(line)) {
if (inSection) {
inSection = false;
if (isRSA === false) {
ret.push([false, tempText + line + '\n']);
}
}
}
else if (/^-----BEGIN PRIVATE KEY-----$/.test(line)) {
if (!inSection) {
inSection = true;
isRSA = null;
tempTextHeader = line;
tempText = '';
}
}
else if (/^-----END PRIVATE KEY-----$/.test(line)) {
if (inSection) {
inSection = false;
if (isRSA === null) {
isRSA = getPrivateKeyAlgorithmTypeFromBase64(tempText);
if (isRSA !== null) {
ret.push([
isRSA,
tempTextHeader + '\n' + tempText + line + '\n',
]);
}
}
}
}
else {
if (inSection) {
tempText += line + '\n';
}
}
});
return ret;
}
export function pickKeysFromP12File(p12Bin, certSelect, password) {
var _a;
const asn1 = forge.asn1.fromDer(forge.util.createBuffer(p12Bin.toString('binary')));
const p12 = forge.pkcs12.pkcs12FromAsn1(asn1, password);
const pkeyBag = p12.getBags({
bagType: forge.pki.oids.pkcs8ShroudedKeyBag,
});
const pkeyData = (_a = pkeyBag[forge.pki.oids.pkcs8ShroudedKeyBag]) === null || _a === void 0 ? void 0 : _a[0];
if (!(pkeyData === null || pkeyData === void 0 ? void 0 : pkeyData.key)) {
throw new Error('No private key is found for p12 data');
}
const privatePem = forge.pki
.privateKeyToPem(pkeyData.key)
.replace(/\r\n/g, '\n');
const certBag = p12.getBags({ bagType: forge.pki.oids.certBag });
const certList = certBag[forge.pki.oids.certBag];
const certsResult = [];
if (certList) {
const sortedCertList = filterAndSortCertListByChain(certList, certSelect);
sortedCertList.forEach((certData) => {
let asn1;
if (certData.cert) {
asn1 = forge.pki.certificateToAsn1(certData.cert);
}
else {
asn1 = certData.asn1;
}
if (asn1) {
const certBin = forge.asn1.toDer(asn1);
certsResult.push(Buffer.from(certBin.getBytes(), 'binary'));
}
});
}
if (certsResult.length === 0) {
throw new Error('No certificates are found for p12 data.');
}
return {
certs: certsResult,
privatePem,
isRSA: true,
password,
};
}