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();