From 2dc6164119749ea193fd4898b92b4c26fcb31d58 Mon Sep 17 00:00:00 2001 From: Timur Ercan Date: Wed, 1 Mar 2023 18:28:05 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20invalid=20pades=20signature=20fi?= =?UTF-8?q?x=20node-sign=20pdf=20locally?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/signing/node-signpdf/dist/signpdf.js | 6 +- packages/signing/signpdf.js | 193 ------------------ 2 files changed, 3 insertions(+), 196 deletions(-) delete mode 100644 packages/signing/signpdf.js diff --git a/packages/signing/node-signpdf/dist/signpdf.js b/packages/signing/node-signpdf/dist/signpdf.js index 936275e1e..9e06b6028 100644 --- a/packages/signing/node-signpdf/dist/signpdf.js +++ b/packages/signing/node-signpdf/dist/signpdf.js @@ -149,15 +149,15 @@ class SignPdf { authenticatedAttributes: [{ type: _nodeForge.default.pki.oids.contentType, value: _nodeForge.default.pki.oids.data - }, { - type: _nodeForge.default.pki.oids.messageDigest // value will be auto-populated at signing time - }, { type: _nodeForge.default.pki.oids.signingTime, // value can also be auto-populated at signing time // We may also support passing this as an option to sign(). // Would be useful to match the creation time of the document for example. value: new Date() + }, { + type: _nodeForge.default.pki.oids.messageDigest // value will be auto-populated at signing time + }] }); // Sign in detached mode. diff --git a/packages/signing/signpdf.js b/packages/signing/signpdf.js deleted file mode 100644 index 9e06b6028..000000000 --- a/packages/signing/signpdf.js +++ /dev/null @@ -1,193 +0,0 @@ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); -var _exportNames = { - SignPdf: true, - SignPdfError: true -}; -exports.SignPdf = void 0; -Object.defineProperty(exports, "SignPdfError", { - enumerable: true, - get: function () { - return _SignPdfError.default; - } -}); -exports.default = void 0; - -var _nodeForge = _interopRequireDefault(require("node-forge")); - -var _SignPdfError = _interopRequireDefault(require("./SignPdfError")); - -var _helpers = require("./helpers"); - -Object.keys(_helpers).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; - if (key in exports && exports[key] === _helpers[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function () { - return _helpers[key]; - } - }); -}); - -var _const = require("./helpers/const"); - -Object.keys(_const).forEach(function (key) { - if (key === "default" || key === "__esModule") return; - if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; - if (key in exports && exports[key] === _const[key]) return; - Object.defineProperty(exports, key, { - enumerable: true, - get: function () { - return _const[key]; - } - }); -}); - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } - -class SignPdf { - constructor() { - this.byteRangePlaceholder = _const.DEFAULT_BYTE_RANGE_PLACEHOLDER; - this.lastSignature = null; - } - - sign(pdfBuffer, p12Buffer, additionalOptions = {}) { - const options = { - asn1StrictParsing: false, - passphrase: '', - ...additionalOptions - }; - - if (!(pdfBuffer instanceof Buffer)) { - throw new _SignPdfError.default('PDF expected as Buffer.', _SignPdfError.default.TYPE_INPUT); - } - - if (!(p12Buffer instanceof Buffer)) { - throw new _SignPdfError.default('p12 certificate expected as Buffer.', _SignPdfError.default.TYPE_INPUT); - } - - let pdf = (0, _helpers.removeTrailingNewLine)(pdfBuffer); // Find the ByteRange placeholder. - - const { - byteRangePlaceholder - } = (0, _helpers.findByteRange)(pdf); - - if (!byteRangePlaceholder) { - throw new _SignPdfError.default(`Could not find empty ByteRange placeholder: ${byteRangePlaceholder}`, _SignPdfError.default.TYPE_PARSE); - } - - const byteRangePos = pdf.indexOf(byteRangePlaceholder); // Calculate the actual ByteRange that needs to replace the placeholder. - - const byteRangeEnd = byteRangePos + byteRangePlaceholder.length; - const contentsTagPos = pdf.indexOf('/Contents ', byteRangeEnd); - const placeholderPos = pdf.indexOf('<', contentsTagPos); - const placeholderEnd = pdf.indexOf('>', placeholderPos); - const placeholderLengthWithBrackets = placeholderEnd + 1 - placeholderPos; - const placeholderLength = placeholderLengthWithBrackets - 2; - const byteRange = [0, 0, 0, 0]; - byteRange[1] = placeholderPos; - byteRange[2] = byteRange[1] + placeholderLengthWithBrackets; - byteRange[3] = pdf.length - byteRange[2]; - let actualByteRange = `/ByteRange [${byteRange.join(' ')}]`; - actualByteRange += ' '.repeat(byteRangePlaceholder.length - actualByteRange.length); // Replace the /ByteRange placeholder with the actual ByteRange - - pdf = Buffer.concat([pdf.slice(0, byteRangePos), Buffer.from(actualByteRange), pdf.slice(byteRangeEnd)]); // Remove the placeholder signature - - pdf = Buffer.concat([pdf.slice(0, byteRange[1]), pdf.slice(byteRange[2], byteRange[2] + byteRange[3])]); // Convert Buffer P12 to a forge implementation. - - const forgeCert = _nodeForge.default.util.createBuffer(p12Buffer.toString('binary')); - - const p12Asn1 = _nodeForge.default.asn1.fromDer(forgeCert); - - const p12 = _nodeForge.default.pkcs12.pkcs12FromAsn1(p12Asn1, options.asn1StrictParsing, options.passphrase); // Extract safe bags by type. - // We will need all the certificates and the private key. - - - const certBags = p12.getBags({ - bagType: _nodeForge.default.pki.oids.certBag - })[_nodeForge.default.pki.oids.certBag]; - - const keyBags = p12.getBags({ - bagType: _nodeForge.default.pki.oids.pkcs8ShroudedKeyBag - })[_nodeForge.default.pki.oids.pkcs8ShroudedKeyBag]; - - const privateKey = keyBags[0].key; // Here comes the actual PKCS#7 signing. - - const p7 = _nodeForge.default.pkcs7.createSignedData(); // Start off by setting the content. - - - p7.content = _nodeForge.default.util.createBuffer(pdf.toString('binary')); // Then add all the certificates (-cacerts & -clcerts) - // Keep track of the last found client certificate. - // This will be the public key that will be bundled in the signature. - - let certificate; - Object.keys(certBags).forEach(i => { - const { - publicKey - } = certBags[i].cert; - p7.addCertificate(certBags[i].cert); // Try to find the certificate that matches the private key. - - if (privateKey.n.compareTo(publicKey.n) === 0 && privateKey.e.compareTo(publicKey.e) === 0) { - certificate = certBags[i].cert; - } - }); - - if (typeof certificate === 'undefined') { - throw new _SignPdfError.default('Failed to find a certificate that matches the private key.', _SignPdfError.default.TYPE_INPUT); - } // Add a sha256 signer. That's what Adobe.PPKLite adbe.pkcs7.detached expects. - - - p7.addSigner({ - key: privateKey, - certificate, - digestAlgorithm: _nodeForge.default.pki.oids.sha256, - authenticatedAttributes: [{ - type: _nodeForge.default.pki.oids.contentType, - value: _nodeForge.default.pki.oids.data - }, { - type: _nodeForge.default.pki.oids.signingTime, - // value can also be auto-populated at signing time - // We may also support passing this as an option to sign(). - // Would be useful to match the creation time of the document for example. - value: new Date() - }, { - type: _nodeForge.default.pki.oids.messageDigest // value will be auto-populated at signing time - - }] - }); // Sign in detached mode. - - p7.sign({ - detached: true - }); // Check if the PDF has a good enough placeholder to fit the signature. - - const raw = _nodeForge.default.asn1.toDer(p7.toAsn1()).getBytes(); // placeholderLength represents the length of the HEXified symbols but we're - // checking the actual lengths. - - - if (raw.length * 2 > placeholderLength) { - throw new _SignPdfError.default(`Signature exceeds placeholder length: ${raw.length * 2} > ${placeholderLength}`, _SignPdfError.default.TYPE_INPUT); - } - - let signature = Buffer.from(raw, 'binary').toString('hex'); // Store the HEXified signature. At least useful in tests. - - this.lastSignature = signature; // Pad the signature with zeroes so the it is the same length as the placeholder - - signature += Buffer.from(String.fromCharCode(0).repeat(placeholderLength / 2 - raw.length)).toString('hex'); // Place it in the document. - - pdf = Buffer.concat([pdf.slice(0, byteRange[1]), Buffer.from(`<${signature}>`), pdf.slice(byteRange[1])]); // Magic. Done. - - return pdf; - } - -} - -exports.SignPdf = SignPdf; - -var _default = new SignPdf(); - -exports.default = _default; \ No newline at end of file