From dd52e4e8bed80132859fcbfd355077039f40ecc1 Mon Sep 17 00:00:00 2001 From: zboris12 Date: Mon, 14 Nov 2022 22:20:19 +0900 Subject: [PATCH] Working on LTV, Completeness is 90%. --- .env.sample | 2 + README.md | 2 + closure.js | 180 +++++++++++++++++++++++++++++++++++ closure/zb-externs.js | 9 ++ lib/zgacertsutil.js | 6 +- lib/zgaindex.js | 18 +++- lib/zgapdfcryptor.js | 82 ++++++++-------- lib/zgapdfsigner.js | 213 +++++++++++++++++++++++++----------------- logo.png | Bin 0 -> 17612 bytes package.json | 4 +- test4node.js | 201 +++++++++++++++++++++------------------ 11 files changed, 494 insertions(+), 223 deletions(-) create mode 100644 .env.sample create mode 100644 closure.js create mode 100644 logo.png diff --git a/.env.sample b/.env.sample new file mode 100644 index 0000000..78c18d0 --- /dev/null +++ b/.env.sample @@ -0,0 +1,2 @@ +java="C:\java8\jre\bin\java.exe" +closure="C:\closure-compiler\closure-compiler-v20220104.jar" diff --git a/README.md b/README.md index 9976d78..ec1b16b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +
+ # ZgaPdfSigner A javascript tool to sign a pdf or set protection of a pdf in web browser. And it also can be used in Google Apps Script and nodejs. diff --git a/closure.js b/closure.js new file mode 100644 index 0000000..a30d8ca --- /dev/null +++ b/closure.js @@ -0,0 +1,180 @@ +const m_fs = require("fs"); +const m_path = require("path"); +const m_cp = require("child_process"); + +/** @const {string} */ +const m_libpath = "lib/"; +/** @const {string} */ +const m_distpath = "dist/_"; +/** @const {Array} */ +const m_targets = ["zgacertsutil.js", "zgapdfcryptor.js", "zgapdfsigner.js", "zgaindex.js"]; +/** @const {Array} */ +const m_dists = []; + +/** + * @param {string} raw + * @return {Uint8Array} + */ +function rawToU8arr(raw){ + /** @type {Uint8Array} */ + var arr = new Uint8Array(raw.length); + for(var i=0; i= tgtbuf.length){ + return false; + }else if(tgtbuf[idx] != cprbuf[i]){ + return false; + } + } + return true; +} + +/** + * @param {string} js + */ +function fixjs(js){ + /** @type {string} */ + var jspath = m_path.join(__dirname, m_libpath + js); + /** @type {Buffer} */ + var jsbuf = m_fs.readFileSync(jspath); + for(var i=0; i} + */ +function loadEnv(envfil){ + /** @type {Object} */ + var retobj = {}; + /** @type {string} */ + var envpath = m_path.join(__dirname, envfil); + /** @type {Array} */ + var envs = m_fs.readFileSync(envpath, "utf8").split("\n"); + envs.forEach(function(/** @type {string} */a_env){ + a_env = a_env.trimStart(); + if(a_env.charAt(0) != "#"){ + var a_idx = a_env.indexOf("="); + if(a_idx > 0){ + retobj[a_env.substring(0, a_idx)] = a_env.substring(a_idx + 1); + } + } + }); + if(m_debug){ + console.log("Environment:"); + console.log(retobj); + } + return retobj; +} + +function main(){ + if(process.argv.indexOf("-debug") > 0){ + m_debug = true; + } + + /** @type {Object} */ + var env = loadEnv(".env"); + + /** @type {boolean} */ + var flg = true; + if(!env.java){ + console.error("Can't find java's execution path in .env file."); + flg = false; + } + if(!env.closure){ + console.error("Can't find closure complier's path in .env file."); + flg = false; + } + if(!flg){ + return; + } + + m_targets.forEach(fixjs); + + /** @type {Array} */ + var cmd = [env.java]; + cmd.push("-jar " + env.closure); + cmd.push("--charset UTF-8"); + cmd.push("--compilation_level SIMPLE_OPTIMIZATIONS"); + cmd.push("--warning_level VERBOSE"); + cmd.push("--externs closure/google-ext.js"); + cmd.push("--externs closure/forge-ext.js"); + cmd.push("--externs closure/pdflib-ext.js"); + cmd.push("--externs closure/zb-externs.js"); + m_dists.forEach(function(a_js){ + cmd.push("--js " + a_js); + }); + cmd.push("--js_output_file dist/zgapdfsigner.min.js"); + if(m_debug){ + console.log(cmd.join(" ")); + } + + console.log("Excuting google closure compiler...\n"); + m_cp.exec(cmd.join(" "), function(a_err, a_stdout, a_stderr){ + const a_rex = new RegExp("^" + m_distpath, "g"); + // if(a_err){ + // console.log(a_err); + // } + if(a_stdout){ + console.log(a_stdout.replaceAll(a_rex, m_libpath)); + } + if(a_stderr){ + console.log(a_stderr.replaceAll(a_rex, m_libpath)); + } + m_dists.forEach(deltmpjs); + console.log("Done"); + }); +} + +main(); diff --git a/closure/zb-externs.js b/closure/zb-externs.js index 7bd435c..73df217 100644 --- a/closure/zb-externs.js +++ b/closure/zb-externs.js @@ -164,6 +164,11 @@ Zga.PdfCryptor = function(encopt){}; * @return {Promise} */ Zga.PdfCryptor.prototype.encryptPdf = function(pdf, ref){}; +/** + * @param {number} num + * @param {PDFLib.PDFObject} val + */ +Zga.PdfCryptor.prototype.encryptObject = function(num, val){}; /** * @constructor * @param {Array=} certs @@ -198,6 +203,10 @@ Zga.CertsChain.prototype.prepareDSSInf = function(crlOnly){}; * @param {TsaServiceInfo} inf */ Zga.TsaFetcher = function(inf){}; +/** @type {string} */ +Zga.TsaFetcher.prototype.url; +/** @type {number} */ +Zga.TsaFetcher.prototype.len; /** * @param {string=} data * @return {Promise} diff --git a/lib/zgacertsutil.js b/lib/zgacertsutil.js index 4ad3be1..e392fe4 100644 --- a/lib/zgacertsutil.js +++ b/lib/zgacertsutil.js @@ -1,3 +1,5 @@ +'use strict'; + /** * @param {Object} z */ @@ -877,9 +879,9 @@ z.TsaFetcher = class{ this.asnc = forge.asn1.Class; /** @private @lends {forge.asn1.Type} */ this.asnt = forge.asn1.Type; - /** @private @type {string} */ + /** @public @type {string} */ this.url = inf.url; - /** @private @type {number} */ + /** @public @type {number} */ this.len = inf.len ? inf.len : 0; /** @private @type {Object|undefined} */ this.headers = inf.headers; diff --git a/lib/zgaindex.js b/lib/zgaindex.js index 7934726..04703df 100644 --- a/lib/zgaindex.js +++ b/lib/zgaindex.js @@ -1,3 +1,5 @@ +'use strict'; + /** * @return {Object} */ @@ -59,11 +61,17 @@ function genZga(){ return z; } +//Only for nodejs Start// if(typeof exports === "object" && typeof module !== "undefined"){ module.exports = genZga(); -}else if(!globalThis.Zga){ - globalThis.Zga = genZga(); - supplyZgaCertsChain(globalThis.Zga); - supplyZgaCryptor(globalThis.Zga); - supplyZgaSigner(globalThis.Zga); +}else{ +//Only for nodejs End// + if(!globalThis.Zga){ + globalThis.Zga = genZga(); + supplyZgaCertsChain(globalThis.Zga); + supplyZgaCryptor(globalThis.Zga); + supplyZgaSigner(globalThis.Zga); + } +//Only for nodejs Start// } +//Only for nodejs End// diff --git a/lib/zgapdfcryptor.js b/lib/zgapdfcryptor.js index cb66f32..8ddf9b5 100644 --- a/lib/zgapdfcryptor.js +++ b/lib/zgapdfcryptor.js @@ -1,3 +1,5 @@ +'use strict'; + // This module was migrated from [TCPDF](http://www.tcpdf.org) /** * @param {Object} z @@ -428,6 +430,8 @@ z.PdfCryptor = class{ * @return {Promise} */ async encryptPdf(pdf, ref){ + /** @const {z.PdfCryptor} */ + const _this = this; /** @type {PDFLib.PDFDocument} */ var pdfdoc = await z.loadPdf(pdf); if(pdfdoc === pdf && !ref){ @@ -437,45 +441,10 @@ z.PdfCryptor = class{ /** @type {PDFLib.PDFContext} */ var pdfcont = pdfdoc.context; /** @type {PDFLib.PDFObject} */ - var trobj = this.prepareEncrypt(pdfcont); + var trobj = _this.prepareEncrypt(pdfcont); - /** - * @param {number} a_num - * @param {PDFLib.PDFObject} a_val - */ - var func = function(a_num, a_val){ - if(a_val instanceof PDFLib.PDFContentStream){ - /** @type {Uint8Array} */ - var a_dat = a_val.contentsCache.access(); - if(a_dat){ - a_val.contentsCache.value = this.encryptU8arr(a_num, a_dat); - } - }else if(a_val instanceof PDFLib.PDFStream){ - if(a_val.contents){ - a_val.contents = this.encryptU8arr(a_num, a_val.contents); - } - }else if(a_val instanceof PDFLib.PDFHexString){ - if(a_val.value){ - a_val.value = this.encryptHexstr(a_num, a_val.value); - } - }else if(a_val instanceof PDFLib.PDFString){ - if(a_val.value){ - a_val.value = z.Crypto._escape(this._encrypt_data(a_num, a_val.value)); - } - } - if(a_val.dict instanceof Map){ - /** @type {Iterator} */ - var a_es = a_val.dict.entries(); - /** @type {IIterableResult} */ - var a_res = a_es.next(); - while(!a_res.done){ - func(a_num, a_res.value[1]); - a_res = a_es.next(); - } - } - }.bind(this); pdfcont.enumerateIndirectObjects().forEach(function(/** @type {PdfObjEntry} */a_arr){ - func(a_arr[0].objectNumber, a_arr[1]); + _this.encryptObject(a_arr[0].objectNumber, a_arr[1]); }); if(ref){ @@ -488,6 +457,43 @@ z.PdfCryptor = class{ return pdfdoc; } + /** + * @public + * @param {number} num + * @param {PDFLib.PDFObject} val + */ + encryptObject(num, val){ + if(val instanceof PDFLib.PDFContentStream){ + /** @type {Uint8Array} */ + var dat = val.contentsCache.access(); + if(dat){ + val.contentsCache.value = this.encryptU8arr(num, dat); + } + }else if(val instanceof PDFLib.PDFStream){ + if(val.contents){ + val.contents = this.encryptU8arr(num, val.contents); + } + }else if(val instanceof PDFLib.PDFHexString){ + if(val.value){ + val.value = this.encryptHexstr(num, val.value); + } + }else if(val instanceof PDFLib.PDFString){ + if(val.value){ + val.value = z.Crypto._escape(this._encrypt_data(num, val.value)); + } + } + if(val.dict instanceof Map){ + /** @type {Iterator} */ + var es = val.dict.entries(); + /** @type {IIterableResult} */ + var res = es.next(); + while(!res.done){ + this.encryptObject(num, res.value[1]); + res = es.next(); + } + } + } + /** * Prepare for encryption and create the object for saving in trailer. * @@ -955,6 +961,8 @@ z.PdfCryptor = class{ } +//Only for nodejs Start// if(typeof exports === "object" && typeof module !== "undefined"){ module.exports = supplyZgaCryptor; } +//Only for nodejs End// diff --git a/lib/zgapdfsigner.js b/lib/zgapdfsigner.js index bee9cdb..d88b464 100644 --- a/lib/zgapdfsigner.js +++ b/lib/zgapdfsigner.js @@ -1,3 +1,5 @@ +'use strict'; + /** * @param {Object} z */ @@ -215,6 +217,8 @@ z.PdfSigner = class{ constructor(signopt){ /** @public @type {Zga.TsaFetcher} */ this.tsaFetcher = null; + /** @public @type {Zga.PdfCryptor} */ + this.cyptr = null; /** @private @const {string} */ this.DEFAULT_BYTE_RANGE_PLACEHOLDER = "**********"; @@ -226,8 +230,6 @@ z.PdfSigner = class{ this.privateKey = null; /** @type {Zga.CertsChain} */ this.cchain = null; - /** @private @type {?TsaServiceInfo} */ - this.tsainf = null; /** @private @type {string} */ this.signature = ""; /** @private @type {number} */ @@ -250,35 +252,38 @@ z.PdfSigner = class{ if(!(globalThis.forge || forge)){ throw new Error("node-forge is not imported."); } + /** @type {?TsaServiceInfo} */ + var tsainf = null; if(signopt.signdate){ if(typeof signopt.signdate == "string"){ - this.tsainf = { + tsainf = { url: signopt.signdate, }; }else if(signopt.signdate.url){ - this.tsainf = /** @type {TsaServiceInfo} */(Object.assign({}, signopt.signdate)); + tsainf = /** @type {TsaServiceInfo} */(Object.assign({}, signopt.signdate)); } } - if(this.tsainf){ + if(tsainf){ if(!z.urlFetch){ throw new Error("Because of the CORS security restrictions, signing with TSA is not supported in web browser."); } - if(z.TSAURLS[this.tsainf.url]){ - Object.assign(this.tsainf, z.TSAURLS[this.tsainf.url]); - }else if(!(new RegExp("^https?://")).test(this.tsainf.url)){ - throw new Error("Unknown tsa data. " + JSON.stringify(this.tsainf)); + if(z.TSAURLS[tsainf.url]){ + Object.assign(tsainf, z.TSAURLS[tsainf.url]); + }else if(!(new RegExp("^https?://")).test(tsainf.url)){ + throw new Error("Unknown tsa data. " + JSON.stringify(tsainf)); } - if(!this.tsainf.len){ - this.tsainf.len = 16000; + if(!tsainf.len){ + tsainf.len = 16000; } + this.tsaFetcher = new z.TsaFetcher(/** @type {TsaServiceInfo} */(tsainf)); } if(signopt.ltv && !z.urlFetch){ throw new Error("Because of the CORS security restrictions, signing with LTV is not supported in web browser."); } - if(signopt.permission == 1 && signopt.ltv){ - z.log("To enable LTV we need to append informations after signing, this will destroy the signature if full DocMDP protection is set. (Sign with permission = 1)"); - throw new Error("When set full DocMDP protection, LTV can't be enabled."); - } + // if(signopt.permission == 1 && signopt.ltv){ + // z.log("To enable LTV we need to append informations after signing, this will destroy the signature if full DocMDP protection is set. (Sign with permission = 1)"); + // throw new Error("When set full DocMDP protection, LTV can't be enabled."); + // } } /** @@ -302,7 +307,7 @@ z.PdfSigner = class{ if(Array.isArray(pdf)){ _this.oriU8pdf = new Uint8Array(pdf); }else{ - _this.oriU8pdf = PDFLib.toUint8Array(/** @type {(ArrayBuffer|Uint8Array|string)} */(pdf)); + _this.oriU8pdf = PDFLib.toUint8Array(/** @type {ArrayBuffer|Uint8Array|string} */(pdf)); } pdfdoc = await PDFLib.PDFDocument.load(_this.oriU8pdf); } @@ -338,7 +343,7 @@ z.PdfSigner = class{ } } z.fixCertAttributes(cert); - }else if(_this.tsainf){ + }else if(_this.tsaFetcher){ z.log("No certificate is specified, so only add a document timestamp.") }else{ throw new Error("Nothing to do because no certificate nor tsa is specified."); @@ -346,9 +351,24 @@ z.PdfSigner = class{ /** @type {boolean} *///append mode or not var apmode = _this.addSignHolder(pdfdoc); - await pdfdoc.flush(); z.log("A signature holder has been added to the pdf."); + if(_this.opt.permission == 1 && (_this.opt.ltv == 1 || _this.opt.ltv == 2)){ + if(!_this.cchain){ + // Query a timestamp from tsa with dummy string to obtain the certificates. + await _this.queryTsa("dummy"); + } + /** @type {PDFLib.PDFDocument} */ + var dmydoc = await _this.addDss(pdfdoc); + if(dmydoc){ + z.log("In order to enable LTV, DSS informations has been added to the pdf."); + } + // Clear ltv + _this.opt.ltv = 0; + }else{ + await pdfdoc.flush(); + } + if(apmode){ if(_this.oriU8pdf){ z.log("The pdf has been signed already, so we add a new signature to it."); @@ -381,8 +401,8 @@ z.PdfSigner = class{ } } /** @type {Zga.PdfCryptor} */ - var cypt = new z.PdfCryptor(cypopt); - await cypt.encryptPdf(pdfdoc, encref); + _this.cyptr = new z.PdfCryptor(cypopt); + await _this.cyptr.encryptPdf(pdfdoc, encref); z.log("Pdf data has been encrypted."); } } @@ -408,22 +428,11 @@ z.PdfSigner = class{ throw new Error("Failed to sign the pdf."); } - if(_this.opt.ltv == 1 || _this.opt.ltv == 2){ - cchain = _this.cchain ? _this.cchain : _this.tsaFetcher.getCertsChain(); - if(cchain.isSelfSignedCert()){ - z.log("No need to enable LTV because the certificate is a self signed one."); - }else{ - /** @type {boolean} */ - var crlOnly = (_this.opt.ltv == 2); - _this.oriU8pdf = ret; - pdfdoc = await PDFLib.PDFDocument.load(_this.oriU8pdf, {ignoreEncryption: true}); - ret = await _this.applyLTV(pdfdoc, cchain, crlOnly); - if(ret){ - z.log("LTV has been enabled."); - }else{ - throw new Error("Failed to enable the LTV."); - } - } + pdfdoc = await _this.addDss(ret); + if(pdfdoc){ + await _this.findChangedObjects(pdfdoc, true); + ret = _this.appendIncrement(pdfdoc); + z.log("LTV has been enabled."); } return ret; @@ -465,6 +474,9 @@ z.PdfSigner = class{ /** @type {PDFLib.PDFObject} */ var a_obj = oriPdfdoc.context.lookup(a_ele[0]); if(!(a_obj && _this.isamePdfObject(a_ele[1], a_obj))){ + if(_this.cyptr){ + _this.cyptr.encryptObject(a_ele[0].objectNumber, a_ele[1]); + } _this.apobjs.push(a_ele); } } @@ -720,7 +732,7 @@ z.PdfSigner = class{ /** @type {Date} */ var signdate = new Date(); - if(_this.opt.signdate instanceof Date && !_this.tsainf){ + if(_this.opt.signdate instanceof Date && !_this.tsaFetcher){ signdate = _this.opt.signdate; } @@ -731,7 +743,7 @@ z.PdfSigner = class{ bytrng.push(PDFLib.PDFName.of(_this.DEFAULT_BYTE_RANGE_PLACEHOLDER)); bytrng.push(PDFLib.PDFName.of(_this.DEFAULT_BYTE_RANGE_PLACEHOLDER)); - _this.siglen = /** @type {number} */(_this.tsainf ? _this.tsainf.len : 3322); + _this.siglen = /** @type {number} */(_this.tsaFetcher ? _this.tsaFetcher.len : 3322); _this.sigContents = PDFLib.PDFHexString.of("0".repeat(_this.siglen)); /** @type {Object} */ @@ -803,7 +815,11 @@ z.PdfSigner = class{ var ans = page.node.Annots(); if(!ans){ ans = new PDFLib.PDFArray(pdfcont); - page.node.set(PDFLib.PDFName.Annots, pdfcont.register(ans)); + // if(docMdp){ + page.node.set(PDFLib.PDFName.Annots, ans); + // }else{ + // page.node.set(PDFLib.PDFName.Annots, pdfcont.register(ans)); + // } } ans.push(widgetDictRef); @@ -922,7 +938,7 @@ z.PdfSigner = class{ if(_this.cchain){ /** @type {Date} */ var signdate = new Date(); - if(_this.opt.signdate instanceof Date && !_this.tsainf){ + if(_this.opt.signdate instanceof Date && !_this.tsaFetcher){ signdate = _this.opt.signdate; } @@ -959,7 +975,7 @@ z.PdfSigner = class{ // Sign in detached mode. p7.sign({"detached": true}); - if(_this.tsainf){ + if(_this.tsaFetcher){ /** @type {forge.asn1} */ var tsatoken = await _this.queryTsa(p7.signers[0].signature, true); p7.signerInfos[0].value.push(tsatoken); @@ -1001,7 +1017,6 @@ z.PdfSigner = class{ async queryTsa(data, forP7){ /** @const {z.PdfSigner} */ const _this = this; - _this.tsaFetcher = new z.TsaFetcher(/** @type {TsaServiceInfo} */(_this.tsainf)); /** @type {?string} */ var err = await _this.tsaFetcher.queryTsa(data); if(err){ @@ -1009,42 +1024,44 @@ z.PdfSigner = class{ }else{ /** @type {forge.asn1} */ var asn1 = _this.tsaFetcher.getToken(forP7); - z.log("Timestamp from " + _this.tsainf.url + " has been obtained."); + z.log("Timestamp from " + _this.tsaFetcher.url + " has been obtained."); return asn1; } } /** - * @public - * @param {PDFLib.PDFDocument} pdfdoc - * @param {Zga.CertsChain} chain - * @param {boolean=} crlOnly - * @return {Promise} + * @private + * @param {PDFLib.PDFDocument|Uint8Array} pdf + * @return {Promise} */ - async applyLTV(pdfdoc, chain, crlOnly){ + async addDss(pdf){ /** @const {z.PdfSigner} */ const _this = this; + if(_this.opt.ltv != 1 && _this.opt.ltv != 2){ + return null; + } + /** @type {Zga.CertsChain} */ + var cchain = _this.cchain ? _this.cchain : _this.tsaFetcher.getCertsChain(); + if(cchain.isSelfSignedCert()){ + z.log("No need to enable LTV because the certificate is a self signed one."); + return null; + } + /** @type {boolean} */ + var crlOnly = (_this.opt.ltv == 2); /** @type {?DSSInfo} */ - var dssinf = await chain.prepareDSSInf(crlOnly); + var dssinf = await cchain.prepareDSSInf(crlOnly); if(!dssinf){ return null; } - if(!_this.addDss(pdfdoc, _this.signature, dssinf)){ - return null; - } - await pdfdoc.flush(); - await _this.findChangedObjects(pdfdoc, true); - return _this.appendIncrement(pdfdoc); - } - /** - * @private - * @param {PDFLib.PDFDocument} pdfdoc - * @param {string} sig signature - * @param {DSSInfo=} dssinf - * @return {boolean} - */ - addDss(pdfdoc, sig, dssinf){ + /** @type {PDFLib.PDFDocument} */ + var pdfdoc = null; + if(pdf.addPage){ + pdfdoc = /** @type {PDFLib.PDFDocument} */(pdf); + }else{ + _this.oriU8pdf = /** @type {Uint8Array} */(pdf); + pdfdoc = await PDFLib.PDFDocument.load(_this.oriU8pdf, {ignoreEncryption: true}); + } /** @type {PDFLib.PDFContext} */ var pdfcont = pdfdoc.context; /** @type {Array} */ @@ -1073,7 +1090,7 @@ z.PdfSigner = class{ if(!(ocspRefs || crlRefs)){ // Nothing to do. - return false; + return null; } if(dssinf && dssinf.certs && dssinf.certs.length > 0){ @@ -1087,11 +1104,14 @@ z.PdfSigner = class{ }); } - /** @type {forge.md.digest} */ - var md = forge.md.sha1.create(); - md.update(sig); /** @type {string} */ - var sighex = md.digest().toHex().toUpperCase(); + var sighex = ""; + if(_this.signature){ + /** @type {forge.md.digest} */ + var md = forge.md.sha1.create(); + md.update(_this.signature); + sighex = md.digest().toHex().toUpperCase(); + } var dss = /** @type {PDFLib.PDFDict} */(pdfdoc.catalog.lookupMaybe(PDFLib.PDFName.of("DSS"), PDFLib.PDFDict)); /** @type {PDFLib.PDFArray} */ @@ -1103,9 +1123,12 @@ z.PdfSigner = class{ /** @type {PDFLib.PDFDict} */ var vri = null; /** @type {Object} */ - var vriObj = { - TU: PDFLib.PDFString.fromDate(new Date()), - }; + var vriObj = null; + if(sighex){ + vriObj = { + TU: PDFLib.PDFString.fromDate(new Date()), + }; + } /** @type {PDFLib.PDFArray} */ var sigcertsarr = null; /** @type {PDFLib.PDFArray} */ @@ -1122,47 +1145,63 @@ z.PdfSigner = class{ pdfdoc.catalog.set(PDFLib.PDFName.of("DSS"), pdfcont.register(dss)); } if(certRefs){ - sigcertsarr = new PDFLib.PDFArray(pdfcont); - vriObj["Cert"] = sigcertsarr; + if(vriObj){ + sigcertsarr = new PDFLib.PDFArray(pdfcont); + vriObj["Cert"] = sigcertsarr; + } if(!certsarr){ certsarr = new PDFLib.PDFArray(pdfcont); dss.set(PDFLib.PDFName.of("Certs"), pdfcont.register(certsarr)); } certRefs.forEach(function(/** @type {PDFLib.PDFRef} */a_ref){ - sigcertsarr.push(a_ref); + if(sigcertsarr){ + sigcertsarr.push(a_ref); + } certsarr.push(a_ref); }); } if(ocspRefs){ - sigocpsarr = new PDFLib.PDFArray(pdfcont); - vriObj["OCSP"] = sigocpsarr; + if(vriObj){ + sigocpsarr = new PDFLib.PDFArray(pdfcont); + vriObj["OCSP"] = sigocpsarr; + } if(!ocpsarr){ ocpsarr = new PDFLib.PDFArray(pdfcont); dss.set(PDFLib.PDFName.of("OCSPs"), pdfcont.register(ocpsarr)); } ocspRefs.forEach(function(/** @type {PDFLib.PDFRef} */a_ref){ - sigocpsarr.push(a_ref); + if(sigocpsarr){ + sigocpsarr.push(a_ref); + } ocpsarr.push(a_ref); }); } if(crlRefs){ - sigcrlsarr = new PDFLib.PDFArray(pdfcont); - vriObj["CRL"] = sigcrlsarr; + if(vriObj){ + sigcrlsarr = new PDFLib.PDFArray(pdfcont); + vriObj["CRL"] = sigcrlsarr; + } if(!crlsarr){ crlsarr = new PDFLib.PDFArray(pdfcont); dss.set(PDFLib.PDFName.of("CRLs"), pdfcont.register(crlsarr)); } crlRefs.forEach(function(/** @type {PDFLib.PDFRef} */a_ref){ - sigcrlsarr.push(a_ref); + if(sigcrlsarr){ + sigcrlsarr.push(a_ref); + } crlsarr.push(a_ref); }); } - if(!vri){ - vri = /** @type {PDFLib.PDFDict} */(pdfcont.obj({})); - dss.set(PDFLib.PDFName.of("VRI"), pdfcont.register(vri)); + if(sighex && vriObj){ + if(!vri){ + vri = /** @type {PDFLib.PDFDict} */(pdfcont.obj({})); + dss.set(PDFLib.PDFName.of("VRI"), pdfcont.register(vri)); + } + vri.set(PDFLib.PDFName.of(sighex), pdfcont.register(pdfcont.obj(vriObj))); } - vri.set(PDFLib.PDFName.of(sighex), pdfcont.register(pdfcont.obj(vriObj))); - return true; + await pdfdoc.flush(); + + return pdfdoc; } }; diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..8c1f296ff9520f5c660bce974bc41ec005fdb6c3 GIT binary patch literal 17612 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 C7VYl< literal 0 HcmV?d00001 diff --git a/package.json b/package.json index 116f098..a610319 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "zgapdfsigner", "version": "2.3.0", + "author": "zboris12", "description": "A javascript tool to sign a pdf or set protection to a pdf in web browser, Google Apps Script and nodejs.", "homepage": "https://github.com/zboris12/zgapdfsigner", "private": false, @@ -26,7 +27,8 @@ "TSA" ], "scripts": { - "testsign": "node test4node.js" + "build": "node closure.js", + "test": "node test4node.js" }, "dependencies": { "pdf-lib": "1.17.1", diff --git a/test4node.js b/test4node.js index 3e7b5d3..a5ba99c 100644 --- a/test4node.js +++ b/test4node.js @@ -4,6 +4,111 @@ const Zga = require("./lib/zganode.js"); const workpath = "test/"; +/** + * @param {string} pdfPath + * @param {string} pfxPath + * @param {string} ps + * @param {number} perm + * @param {string=} imgPath + * @return {Promise} output path + */ +async function sign_protect(pdfPath, pfxPath, ps, perm, imgPath){ + /** @type {Buffer} */ + var pdf = m_fs.readFileSync(pdfPath); + /** @type {Buffer} */ + var pfx = m_fs.readFileSync(pfxPath); + /** @type {Buffer} */ + var img = null; + /** @type {string} */ + var imgType = ""; + + if(perm == 1){ + console.log("\nTest signing pdf with full protection. (permission 1 and password encryption)"); + }else{ + console.log("\nTest signing pdf with permission "+perm); + } + + if(imgPath){ + img = m_fs.readFileSync(imgPath); + imgType = m_path.extname(imgPath).slice(1); + } + /** @type {SignOption} */ + var sopt = { + p12cert: pfx, + pwd: ps, + permission: perm, + signdate: "1", + reason: "I have a test reason "+perm+".", + location: "I am on the earth "+perm+".", + contact: "zga"+perm+"@zga.com", + ltv: 1, + debug: true, + }; + if(img){ + sopt.drawinf = { + area: { + x: 25, // left + y: 150, // top + w: 60, + h: 60, + }, + imgData: img, + imgType: imgType, + }; + } + + /** @type {EncryptOption} */ + var eopt = undefined; + if(perm == 1){ + eopt = { + mode: Zga.Crypto.Mode.AES_256, + permissions: ["copy", "copy-extract", "print-high"], + userpwd: "123", + }; + } + + /** @type {Zga.PdfSigner} */ + var ser = new Zga.PdfSigner(sopt); + /** @type {Uint8Array} */ + var u8dat = await ser.sign(pdf, eopt); + if(u8dat){ + /** @type {string} */ + var outPath = m_path.join(__dirname, workpath+"test_perm"+perm+".pdf"); + m_fs.writeFileSync(outPath, u8dat); + console.log("Output file: " + outPath); + } + return outPath; +} + +/** + * @param {string} pdfPath + * @return {Promise} output path + */ +async function addtsa(pdfPath){ + console.log("\nTest signing pdf by a timestamp."); + + /** @type {Buffer} */ + var pdf = m_fs.readFileSync(pdfPath); + /** @type {SignOption} */ + var sopt = { + signdate: "2", + reason: "I have a test reason tsa.", + location: "I am on the earth tsa.", + contact: "zgatsa@zga.com", + ltv: 1, + debug: true, + }; + /** @type {Zga.PdfSigner} */ + var ser = new Zga.PdfSigner(sopt); + /** @type {Uint8Array} */ + var u8dat = await ser.sign(pdf); + /** @type {string} */ + var outPath = m_path.join(__dirname, workpath+"test_tsa.pdf"); + m_fs.writeFileSync(outPath, u8dat); + console.log("Output file: " + outPath); + return outPath; +} + async function main(){ /** @type {string} */ var pdfPath = m_path.join(__dirname, workpath+"_test.pdf"); @@ -26,101 +131,15 @@ async function main(){ pfxPath = ""; } - /** @type {Buffer} */ - var pdf = m_fs.readFileSync(pdfPath); - /** @type {Buffer} */ - var pfx = null; if(pfxPath){ - pfx = m_fs.readFileSync(pfxPath); - } - /** @type {Buffer} */ - var img = null; - /** @type {string} */ - var imgType = ""; - if(imgPath){ - img = m_fs.readFileSync(imgPath); - imgType = m_path.extname(imgPath).slice(1); + await sign_protect(pdfPath, pfxPath, ps, 1, imgPath); + pdfPath = await sign_protect(pdfPath, pfxPath, ps, 2, imgPath); + await addtsa(pdfPath); + }else{ + await addtsa(pdfPath); } - /** @type {SignOption} */ - var sopt = null; - if(pdf){ - sopt = { - p12cert: pfx, - pwd: ps, - // permission: pfx ? 2 : 0, - signdate: "1", - reason: "I have a test reason.", - location: "I am on the earth.", - contact: "zga@zga.com", - ltv: 1, - debug: true, - }; - if(img){ - sopt.drawinf = { - area: { - x: 25, // left - y: 150, // top - w: 60, - h: 60, - }, -// pageidx: 2, - imgData: img, - imgType: imgType, - }; - } - } - - /** @type {EncryptOption} */ - var eopt = undefined; - eopt = { - mode: Zga.Crypto.Mode.AES_256, - permissions: ["copy", "copy-extract", "print-high"], - userpwd: "123", - }; - // eopt.pubkeys = []; - - /** @type {Uint8Array} */ - var u8dat = null; - if(sopt){ - /** @type {Zga.PdfSigner} */ - var ser = new Zga.PdfSigner(sopt); - u8dat = await ser.sign(pdf, eopt); - } - - if(u8dat){ - /** @type {string} */ - var outPath = m_path.join(__dirname, workpath+"test_signed.pdf"); - m_fs.writeFileSync(outPath, u8dat); - console.log("Output file: " + outPath); - } console.log("Done"); } -async function main2(){ - /** @type {string} */ - var pdfPath = m_path.join(__dirname, workpath+"test_signed.pdf"); - /** @type {Buffer} */ - var pdf = m_fs.readFileSync(pdfPath); - /** @type {SignOption} */ - var sopt = { - signdate: "2", - reason: "I have a test reason.", - location: "I am on the earth.", - contact: "zga@zga.com", - ltv: 1, - debug: true, - }; - /** @type {Zga.PdfSigner} */ - var ser = new Zga.PdfSigner(sopt); - /** @type {Uint8Array} */ - var u8dat = await ser.sign(pdf); - /** @type {string} */ - var outPath = m_path.join(__dirname, workpath+"test_signed_tsa.pdf"); - m_fs.writeFileSync(outPath, u8dat); - console.log("Output file: " + outPath); - return; -} - main(); -// main2();