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 0000000..8c1f296 Binary files /dev/null and b/logo.png differ 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();