Changed folder structure and working on LTV.
parent
8b008ccc34
commit
3f0dd0af9f
|
@ -5,12 +5,12 @@ doskey csr=%csr% $*
|
|||
set externs=--externs closure\google-ext.js --externs closure\forge-ext.js --externs closure\pdflib-ext.js --externs closure\zb-externs.js
|
||||
|
||||
rem main
|
||||
set src=.
|
||||
set jss=--js %src%\zgapdfcryptor.js --js %src%\zgapdfsigner.js
|
||||
set src=lib
|
||||
set jss=--js %src%\zgacertsutil.js --js %src%\zgapdfcryptor.js --js %src%\zgapdfsigner.js --js %src%\zgaindex.js
|
||||
echo $
|
||||
set chkj=%%externs%% --checks_only %%jss%%
|
||||
echo chkj=csr %chkj%
|
||||
doskey chkj=%csr% %chkj%
|
||||
set csrj=%%externs%% %%jss%% --js_output_file %src%\dist\zgapdfsigner.min.js
|
||||
set csrj=%%externs%% %%jss%% --js_output_file dist\zgapdfsigner.min.js
|
||||
echo csrj=csr %csrj%
|
||||
doskey csrj=%csr% %csrj%
|
||||
|
|
|
@ -67,6 +67,13 @@ forge.util.hexToBytes = function(hex){};
|
|||
* @return {string}
|
||||
*/
|
||||
forge.util.decodeUtf8 = function(value){};
|
||||
forge.util.binary = {};
|
||||
forge.util.binary.hex = {};
|
||||
/**
|
||||
* @param {string} str
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
forge.util.binary.hex.decode = function(str){};
|
||||
|
||||
/** @constructor */
|
||||
forge.asn1 = function(){};
|
||||
|
@ -119,6 +126,12 @@ forge.asn1.Type.OID;
|
|||
forge.asn1.Type.NULL;
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.OCTETSTRING;
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.PRINTABLESTRING;
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.ENUMERATED;
|
||||
/** @type {number} */
|
||||
forge.asn1.Type.BITSTRING;
|
||||
forge.asn1.Class = {};
|
||||
/** @type {number} */
|
||||
forge.asn1.Class.UNIVERSAL;
|
||||
|
@ -133,8 +146,21 @@ forge.asn1.Class.CONTEXT_SPECIFIC;
|
|||
* @return {forge.asn1}
|
||||
*/
|
||||
forge.asn1.create = function(tagClass, type, constructed, value, options){};
|
||||
/** @type {Array<forge.asn1>} */
|
||||
/**
|
||||
* @param {forge.asn1} obj
|
||||
* @param {Object<string, *>} v
|
||||
* @param {Object<string, *>} capture
|
||||
* @param {Array<string>} errors
|
||||
*/
|
||||
forge.asn1.validate = function(obj, v, capture, errors){};
|
||||
/** @type {Array<forge.asn1>|string} */
|
||||
forge.asn1.prototype.value;
|
||||
/** @type {number} */
|
||||
forge.asn1.prototype.tagClass;
|
||||
/** @type {number} */
|
||||
forge.asn1.prototype.type;
|
||||
/** @type {boolean} */
|
||||
forge.asn1.prototype.constructed;
|
||||
|
||||
/** @constructor */
|
||||
const forge_BigInteger = function(){};
|
||||
|
@ -149,6 +175,20 @@ const forge_cert = function(){};
|
|||
forge_cert.prototype.publicKey;
|
||||
/** @type {forge_cert_issuer} */
|
||||
forge_cert.prototype.issuer;
|
||||
/** @type {string} */
|
||||
forge_cert.prototype.serialNumber;
|
||||
/** @type {forge_cert_issuer} */
|
||||
forge_cert.prototype.subject;
|
||||
/**
|
||||
* @param {forge_cert} parent
|
||||
* @return {boolean}
|
||||
*/
|
||||
forge_cert.prototype.isIssuer = function(parent){};
|
||||
/**
|
||||
* @param {string|forge_cert_extension} nm
|
||||
* @return {forge_cert_extension}
|
||||
*/
|
||||
forge_cert.prototype.getExtension = function(nm){};
|
||||
/** @constructor */
|
||||
const forge_key = function(){};
|
||||
/** @type {forge_BigInteger} */
|
||||
|
@ -159,6 +199,23 @@ forge_key.prototype.e;
|
|||
const forge_cert_issuer = function(){};
|
||||
/** @type {Array<forge_cert_attr>} */
|
||||
forge_cert_issuer.prototype.attributes;
|
||||
/**
|
||||
* @param {string} sn
|
||||
* @return {forge.asn1}
|
||||
*/
|
||||
forge_cert_issuer.prototype.getField = function(sn){};
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* id: (string|undefined),
|
||||
* name: (string|undefined),
|
||||
* critical: (boolean|undefined),
|
||||
* value: (string|undefined),
|
||||
* cA: (boolean|undefined),
|
||||
* }}
|
||||
*/
|
||||
var forge_cert_extension;
|
||||
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
|
@ -263,9 +320,6 @@ var P12Bag;
|
|||
*/
|
||||
forge.pkcs12.prototype.getBags = function(filter){};
|
||||
|
||||
forge.oids = {};
|
||||
/** @type {string} */
|
||||
forge.oids.sha256;
|
||||
forge.pki = {};
|
||||
forge.pki.oids = {};
|
||||
/** @type {string} */
|
||||
|
@ -273,6 +327,8 @@ forge.pki.oids.certBag;
|
|||
/** @type {string} */
|
||||
forge.pki.oids.pkcs8ShroudedKeyBag;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.sha1;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.sha256;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.contentType;
|
||||
|
@ -282,12 +338,35 @@ forge.pki.oids.data;
|
|||
forge.pki.oids.messageDigest;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.signingTime;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.rsaEncryption;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.sha256WithRSAEncryption;
|
||||
/** @type {string} */
|
||||
forge.pki.oids.commonName;
|
||||
/** @lends {forge.pki.oids} */
|
||||
forge.oids = forge.pki.oids;
|
||||
/**
|
||||
* @param {forge.asn1} obj
|
||||
* @param {boolean=} computeHash
|
||||
* @return {forge_cert}
|
||||
*/
|
||||
forge.pki.certificateFromAsn1 = function(obj, computeHash){};
|
||||
/**
|
||||
* @param {forge_cert} cert
|
||||
* @return {forge.asn1}
|
||||
*/
|
||||
forge.pki.certificateToAsn1 = function(cert){};
|
||||
/**
|
||||
* @param {forge_cert_issuer} obj
|
||||
* @return {forge.asn1}
|
||||
*/
|
||||
forge.pki.distinguishedNameToAsn1 = function(obj){};
|
||||
/**
|
||||
* @param {forge_key} key
|
||||
* @return {forge.asn1}
|
||||
*/
|
||||
forge.pki.publicKeyToRSAPublicKey = function(key){};
|
||||
|
||||
forge.md = {};
|
||||
/** @constructor */
|
||||
|
|
|
@ -108,7 +108,10 @@ PDFLib.PDFAcroForm.prototype.addField = function(field){};
|
|||
/** @type {PDFLib.PDFDict} */
|
||||
PDFLib.PDFAcroForm.prototype.dict;
|
||||
|
||||
/** @constructor */
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFDict}
|
||||
*/
|
||||
PDFLib.PDFCatalog = function(){};
|
||||
/**
|
||||
* @param {PDFLib.PDFName} name
|
||||
|
@ -196,6 +199,7 @@ PDFLib.PDFContext.prototype.enumerateIndirectObjects = function(){};
|
|||
* @typedef
|
||||
* {{
|
||||
* Root: PDFLib.PDFRef,
|
||||
* Info: PDFLib.PDFRef,
|
||||
* ID: (PDFLib.PDFArray|undefined),
|
||||
* }}
|
||||
*/
|
||||
|
@ -221,6 +225,12 @@ PDFLib.PDFContext.prototype.nextRef = function(){};
|
|||
* @return {PDFLib.PDFObject}
|
||||
*/
|
||||
PDFLib.PDFContext.prototype.obj = function(literal){};
|
||||
/**
|
||||
* @param {string|Uint8Array} contents
|
||||
* @param {PDFLib.PDFDict=} dict
|
||||
* @return {PDFLib.PDFRawStream}
|
||||
*/
|
||||
PDFLib.PDFContext.prototype.flateStream = function(contents, dict){};
|
||||
/**
|
||||
* @param {PDFLib.PDFRef} ref
|
||||
* @return {PDFLib.PDFObject}
|
||||
|
@ -425,6 +435,11 @@ PDFLib.PDFContentStream = function(){};
|
|||
* @return {PDFLib.PDFContentStream}
|
||||
*/
|
||||
PDFLib.PDFContentStream.of = function(dict, operators, encode){};
|
||||
/**
|
||||
* @constructor
|
||||
* @extends {PDFLib.PDFStream}
|
||||
*/
|
||||
PDFLib.PDFRawStream = function(){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
|
|
|
@ -34,12 +34,16 @@ var SignAreaInfo;
|
|||
var SignDrawInfo;
|
||||
/**
|
||||
* In the case of adding a document timestamp, the p12cert and pwd must be omitted. But meanwhile the tsa must be provided.
|
||||
*
|
||||
*
|
||||
* permission: (DocMDP) The modification permissions granted for this document. Valid values are:
|
||||
* 1 : No changes to the document are permitted; any change to the document invalidates the signature.
|
||||
* 2 : Permitted changes are filling in forms, instantiating page templates, and signing; other changes invalidate the signature.
|
||||
* 3 : Permitted changes are the same as for 2, as well as annotation creation, deletion, and modification; other changes invalidate the signature.
|
||||
*
|
||||
* ltv: Type of Long-Term Validation. Valid values are:
|
||||
* 1 : auto; Try using ocsp only to enable the LTV first; If can't, try using crl to enable the LTV.
|
||||
* 2 : crl only; Only try using crl to enable the LTV.
|
||||
*
|
||||
* @typedef
|
||||
* {{
|
||||
* p12cert: (Array<number>|Uint8Array|ArrayBuffer|string|undefined),
|
||||
|
@ -51,6 +55,7 @@ var SignDrawInfo;
|
|||
* signdate: (Date|TsaServiceInfo|string|undefined),
|
||||
* signame: (string|undefined),
|
||||
* drawinf: (SignDrawInfo|undefined),
|
||||
* ltv: (number|undefined),
|
||||
* debug: (boolean|undefined),
|
||||
* }}
|
||||
*/
|
||||
|
@ -110,6 +115,23 @@ var CFType;
|
|||
* }}
|
||||
*/
|
||||
var RC4LastInfo;
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* certs: (Array<forge_cert>|undefined),
|
||||
* ocsps: (Array<Uint8Array>|undefined),
|
||||
* crls: (Array<Uint8Array>|undefined),
|
||||
* }}
|
||||
*/
|
||||
var DSSInfo;
|
||||
/**
|
||||
* @typedef
|
||||
* {{
|
||||
* resp: (Uint8Array|undefined),
|
||||
* cchainIdx: (number|undefined),
|
||||
* }}
|
||||
*/
|
||||
var OcspData;
|
||||
|
||||
var Zga = {};
|
||||
/**
|
||||
|
@ -142,6 +164,55 @@ Zga.PdfCryptor = function(encopt){};
|
|||
* @return {Promise<PDFLib.PDFDocument>}
|
||||
*/
|
||||
Zga.PdfCryptor.prototype.encryptPdf = function(pdf, ref){};
|
||||
/**
|
||||
* @constructor
|
||||
* @param {Array<forge_cert|forge.asn1|string>=} certs
|
||||
*/
|
||||
Zga.CertsChain = function(certs){};
|
||||
/**
|
||||
* @return {forge_cert}
|
||||
*/
|
||||
Zga.CertsChain.prototype.getSignCert = function(){};
|
||||
/**
|
||||
* @public
|
||||
* @return {boolean}
|
||||
*/
|
||||
Zga.CertsChain.prototype.isSelfSignedCert = function(){};
|
||||
/**
|
||||
* @return {Array<forge_cert>}
|
||||
*/
|
||||
Zga.CertsChain.prototype.getAllCerts = function(){};
|
||||
/**
|
||||
* @public
|
||||
* @param {forge_cert} cert
|
||||
* @return {Promise<boolean>}
|
||||
*/
|
||||
Zga.CertsChain.prototype.buildChain = function(cert){};
|
||||
/**
|
||||
* @param {boolean=} crlOnly
|
||||
* @return {Promise<DSSInfo>}
|
||||
*/
|
||||
Zga.CertsChain.prototype.prepareDSSInf = function(crlOnly){};
|
||||
/**
|
||||
* @constructor
|
||||
* @param {TsaServiceInfo} inf
|
||||
*/
|
||||
Zga.TsaFetcher = function(inf){};
|
||||
/**
|
||||
* @param {string=} data
|
||||
* @return {Promise<string>}
|
||||
*/
|
||||
Zga.TsaFetcher.prototype.queryTsa = function(data){};
|
||||
/**
|
||||
* @param {boolean=} forP7
|
||||
* @return {forge.asn1}
|
||||
*/
|
||||
Zga.TsaFetcher.prototype.getToken = function(forP7){};
|
||||
/**
|
||||
* @return {Zga.CertsChain}
|
||||
*/
|
||||
Zga.TsaFetcher.prototype.getCertsChain = function(){};
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @param {SignOption} signopt
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,69 @@
|
|||
/**
|
||||
* @return {Object<string, *>}
|
||||
*/
|
||||
function genZga(){
|
||||
/** @const {Object<string, *>} */
|
||||
const z = {};
|
||||
|
||||
/**
|
||||
* @param {string} msg
|
||||
*/
|
||||
z.log = function(msg){
|
||||
if(z.debug){
|
||||
console.log(msg);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} uarr
|
||||
* @return {string}
|
||||
*/
|
||||
z.u8arrToRaw = function(uarr){
|
||||
/** @type {Array<string>} */
|
||||
var arr = [];
|
||||
for(var i=0; i<uarr.length; i++){
|
||||
arr.push(String.fromCharCode(uarr[i]));
|
||||
}
|
||||
return arr.join("");
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} raw
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
z.rawToU8arr = function(raw){
|
||||
/** @type {Uint8Array} */
|
||||
var arr = new Uint8Array(raw.length);
|
||||
for(var i=0; i<raw.length; i++){
|
||||
arr[i] = raw.charCodeAt(i);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
// Google Apps Script
|
||||
if(globalThis.UrlFetchApp){
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {UrlFetchParams} params
|
||||
* @return {Promise<Uint8Array>}
|
||||
*/
|
||||
z.urlFetch = function(url, params){
|
||||
return new Promise(function(resolve){
|
||||
/** @type {GBlob} */
|
||||
var tblob = UrlFetchApp.fetch(url, params).getBlob();
|
||||
resolve(new Uint8Array(tblob.getBytes()));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
const m_urlparser = require("url");
|
||||
const m_h = {
|
||||
"http:": require("http"),
|
||||
"https:": require("https"),
|
||||
};
|
||||
const z = require("./zgaindex.js");
|
||||
z.forge = require("node-forge");
|
||||
z.PDFLib = require("pdf-lib");
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {UrlFetchParams} params
|
||||
* @return {Promise<Uint8Array>}
|
||||
*/
|
||||
z.urlFetch = function(url, params){
|
||||
return new Promise(function(resolve, reject){
|
||||
/** @type {URL} */
|
||||
var opts = m_urlparser.parse(url);
|
||||
var http = m_h[opts.protocol];
|
||||
/** @type {string|Buffer} */
|
||||
var dat = null;
|
||||
var encoding = undefined;
|
||||
opts.method = "GET";
|
||||
if(params){
|
||||
if(params.payload instanceof Buffer){
|
||||
dat = params.payload;
|
||||
}else if(params.payload instanceof Uint8Array){
|
||||
dat = Buffer.from(params.payload.buffer);
|
||||
}else if(params.payload instanceof ArrayBuffer){
|
||||
dat = Buffer.from(params.payload);
|
||||
}else{
|
||||
dat = params.payload;
|
||||
encoding = "binary";
|
||||
}
|
||||
if(params.headers){
|
||||
opts.headers = params.headers;
|
||||
}
|
||||
if(params.method){
|
||||
opts.method = params.method;
|
||||
}
|
||||
if(params.validateHttpsCertificates === false){
|
||||
opts.rejectUnauthorized = false;
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {http.ClientRequest} */
|
||||
var hreq = http.request(opts, function(/** @type {http.IncomingMessage} */a_res){
|
||||
if(a_res.statusCode !== 200){
|
||||
var a_err = new Error("Failed to request url. " + url + "\n Status Code: " + a_res.statusCode);
|
||||
a_res.resume();
|
||||
throw a_err;
|
||||
}
|
||||
/** @type {Array<Buffer>} */
|
||||
var a_bufs = [];
|
||||
var a_bufs_len = 0;
|
||||
a_res.on("data", function(/** @type {Buffer} */b_chunk){
|
||||
a_bufs.push(b_chunk);
|
||||
a_bufs_len += b_chunk.length;
|
||||
});
|
||||
a_res.on("end", function(){
|
||||
/** @type {Buffer} */
|
||||
var b_bdat = Buffer.concat(a_bufs, a_bufs_len);
|
||||
resolve(b_bdat);
|
||||
});
|
||||
});
|
||||
hreq.on("error", function(a_err){
|
||||
throw a_err;
|
||||
});
|
||||
hreq.end(dat, encoding);
|
||||
});
|
||||
};
|
||||
|
||||
require("./zgacertsutil.js")(z);
|
||||
require("./zgapdfcryptor.js")(z);
|
||||
require("./zgapdfsigner.js")(z);
|
||||
module.exports = z;
|
|
@ -1,11 +1,18 @@
|
|||
'use strict';
|
||||
|
||||
// This module was migrated from [TCPDF](http://www.tcpdf.org)
|
||||
/**
|
||||
* @param {Object<string, *>} z
|
||||
*/
|
||||
function supplyZgaCryptor(z){
|
||||
|
||||
//Only for nodejs Start//
|
||||
if(z.forge){
|
||||
var forge = z.forge;
|
||||
}
|
||||
if(z.PDFLib){
|
||||
var PDFLib = z.PDFLib;
|
||||
}
|
||||
//Only for nodejs End//
|
||||
|
||||
/**
|
||||
* @param {PDFLib.PDFDocument|Array<number>|Uint8Array|ArrayBuffer|string} pdf
|
||||
* @return {Promise<PDFLib.PDFDocument>}
|
||||
|
@ -23,47 +30,6 @@ z.loadPdf = async function(pdf){
|
|||
return pdfdoc;
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} uarr
|
||||
* @return {string}
|
||||
*/
|
||||
z.u8arrToRaw = function(uarr){
|
||||
/** @type {Array<string>} */
|
||||
var arr = [];
|
||||
for(var i=0; i<uarr.length; i++){
|
||||
arr.push(String.fromCharCode(uarr[i]));
|
||||
}
|
||||
return arr.join("");
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} raw
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
z.rawToU8arr = function(raw){
|
||||
/** @type {Uint8Array} */
|
||||
var arr = new Uint8Array(raw.length);
|
||||
for(var i=0; i<raw.length; i++){
|
||||
arr[i] = raw.charCodeAt(i);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
/**
|
||||
* When converting to asn1, forge will encode the value of issuer to utf8 if the valueTagClass is UTF8.
|
||||
* But the value load from a file which is DER format, is already utf8 encoded,
|
||||
* so the encoding action will break the final data.
|
||||
* To avoid the broken data issue, we decode the value before the later actions.
|
||||
* @param {forge_cert} cert
|
||||
*/
|
||||
z.fixCertAttributes = function(cert){
|
||||
cert.issuer.attributes.forEach(function(a_ele){
|
||||
if(a_ele.valueTagClass === forge.asn1.Type.UTF8){
|
||||
a_ele.value = forge.util.decodeUtf8(a_ele.value);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
z.Crypto = {
|
||||
/**
|
||||
* @enum {number}
|
||||
|
@ -953,9 +919,7 @@ z.PdfCryptor = class{
|
|||
}else{
|
||||
a_cerstr = z.u8arrToRaw(new Uint8Array(/** @type {Array<number>|ArrayBuffer|Uint8Array} */(a_pubkey.c)));
|
||||
}
|
||||
/** @type {forge.asn1} */
|
||||
var a_asn1 = forge.asn1.fromDer(a_cerstr);
|
||||
a_cert = forge.pki.certificateFromAsn1(a_asn1);
|
||||
a_cert = z.loadCert(a_cerstr);
|
||||
z.fixCertAttributes(a_cert);
|
||||
}
|
||||
}else{
|
||||
|
@ -991,7 +955,6 @@ z.PdfCryptor = class{
|
|||
|
||||
}
|
||||
|
||||
if(!globalThis.Zga){
|
||||
globalThis.Zga = {};
|
||||
if(typeof exports === "object" && typeof module !== "undefined"){
|
||||
module.exports = supplyZgaCryptor;
|
||||
}
|
||||
supplyZgaCryptor(globalThis.Zga);
|
|
@ -1,37 +1,28 @@
|
|||
'use strict';
|
||||
|
||||
/**
|
||||
* @param {Object<string, *>} z
|
||||
*/
|
||||
function supplyZgaSigner(z){
|
||||
|
||||
//Only for nodejs Start//
|
||||
if(z.forge){
|
||||
var forge = z.forge;
|
||||
}
|
||||
if(z.PDFLib){
|
||||
var PDFLib = z.PDFLib;
|
||||
}
|
||||
//Only for nodejs End//
|
||||
|
||||
/** @type {Object<string, TsaServiceInfo>} */
|
||||
z.TSAURLS = {
|
||||
"1": {url: "http://ts.ssl.com", len: 15600},
|
||||
"2": {url: "http://timestamp.digicert.com", len: 15400},
|
||||
"3": {url: "http://timestamp.sectigo.com", len: 13400},
|
||||
"4": {url: "http://timestamp.entrust.net/TSS/RFC3161sha2TS", len: 14400},
|
||||
"5": {url: "http://timestamp.apple.com/ts01", len: 12100},
|
||||
"6": {url: "http://www.langedge.jp/tsa", len: 9200},
|
||||
"7": {url: "https://freetsa.org/tsr", len: 14500},
|
||||
"1": {url: "http://ts.ssl.com", len: 12100},
|
||||
"2": {url: "http://timestamp.digicert.com", len: 11900},
|
||||
"3": {url: "http://timestamp.sectigo.com", len: 9900},
|
||||
"4": {url: "http://timestamp.entrust.net/TSS/RFC3161sha2TS", len: 10850},
|
||||
"5": {url: "http://timestamp.apple.com/ts01", len: 8600},
|
||||
"6": {url: "http://www.langedge.jp/tsa", len: 5700},
|
||||
"7": {url: "https://freetsa.org/tsr", len: 11000},
|
||||
};
|
||||
|
||||
// Google Apps Script
|
||||
if(globalThis.UrlFetchApp){
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {UrlFetchParams} params
|
||||
* @return {Promise<Uint8Array>}
|
||||
*/
|
||||
z.urlFetch = function(url, params){
|
||||
return new Promise(function(resolve){
|
||||
/** @type {GBlob} */
|
||||
var tblob = UrlFetchApp.fetch(url, params).getBlob();
|
||||
resolve(new Uint8Array(tblob.getBytes()));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
z.NewRef = class{
|
||||
/**
|
||||
* @param {PDFLib.PDFRef} ref
|
||||
|
@ -222,6 +213,9 @@ z.PdfSigner = class{
|
|||
* @param {SignOption} signopt
|
||||
*/
|
||||
constructor(signopt){
|
||||
/** @public @type {Zga.TsaFetcher} */
|
||||
this.tsaFetcher = null;
|
||||
|
||||
/** @private @const {string} */
|
||||
this.DEFAULT_BYTE_RANGE_PLACEHOLDER = "**********";
|
||||
/** @private @const {number} */
|
||||
|
@ -230,27 +224,30 @@ z.PdfSigner = class{
|
|||
this.opt = signopt;
|
||||
/** @type {forge_key} */
|
||||
this.privateKey = null;
|
||||
/** @type {Array<forge_cert>} */
|
||||
this.certs = [];
|
||||
/** @type {number} */
|
||||
this.certIdx = 0;
|
||||
/** @type {Zga.CertsChain} */
|
||||
this.cchain = null;
|
||||
/** @private @type {?TsaServiceInfo} */
|
||||
this.tsainf = null;
|
||||
/** @private @type {string} */
|
||||
this.signature = "";
|
||||
/** @private @type {number} */
|
||||
this.siglen = 0;
|
||||
/** @private @type {PDFLib.PDFHexString} */
|
||||
this.sigContents = null;
|
||||
/** @private @type {Uint8Array} */
|
||||
this.oriU8pdf = null;
|
||||
/** @type {Array<PdfObjEntry>} */
|
||||
this.apobjs = [];
|
||||
/** @private @type {boolean} */
|
||||
this.debug = false;
|
||||
/** @private @type {Array<PdfObjEntry>} */
|
||||
this.apobjs = null;
|
||||
|
||||
if(!globalThis.PDFLib){
|
||||
if(typeof this.opt.debug == "boolean"){
|
||||
z.debug = this.opt.debug;
|
||||
}else if(globalThis.debug){
|
||||
z.debug = true;
|
||||
}
|
||||
if(!(globalThis.PDFLib || PDFLib)){
|
||||
throw new Error("pdf-lib is not imported.");
|
||||
}
|
||||
if(!globalThis.forge){
|
||||
if(!(globalThis.forge || forge)){
|
||||
throw new Error("node-forge is not imported.");
|
||||
}
|
||||
if(signopt.signdate){
|
||||
|
@ -275,10 +272,12 @@ z.PdfSigner = class{
|
|||
this.tsainf.len = 16000;
|
||||
}
|
||||
}
|
||||
if(typeof this.opt.debug == "boolean"){
|
||||
this.debug = this.opt.debug;
|
||||
}else if(globalThis.debug){
|
||||
this.debug = true;
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,10 +326,20 @@ z.PdfSigner = class{
|
|||
|
||||
/** @type {forge_cert} */
|
||||
var cert = _this.loadP12cert(_this.opt.p12cert, _this.opt.pwd);
|
||||
/** @type {Zga.CertsChain} */
|
||||
var cchain = null;
|
||||
if(cert){
|
||||
if(z.urlFetch){
|
||||
cchain = new z.CertsChain();
|
||||
/** @type {?boolean} */
|
||||
var rootok = await cchain.buildChain(cert);
|
||||
if(rootok){
|
||||
_this.cchain = cchain;
|
||||
}
|
||||
}
|
||||
z.fixCertAttributes(cert);
|
||||
}else if(_this.tsainf){
|
||||
_this.log("No certificate is specified, so only add a document timestamp.")
|
||||
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.");
|
||||
}
|
||||
|
@ -338,25 +347,17 @@ z.PdfSigner = class{
|
|||
/** @type {boolean} *///append mode or not
|
||||
var apmode = _this.addSignHolder(pdfdoc);
|
||||
await pdfdoc.flush();
|
||||
_this.log("A signature holder has been added to the pdf.");
|
||||
z.log("A signature holder has been added to the pdf.");
|
||||
|
||||
if(apmode){
|
||||
if(_this.oriU8pdf){
|
||||
_this.log("The pdf has been signed already, so we add a new signature to it.");
|
||||
z.log("The pdf has been signed already, so we add a new signature to it.");
|
||||
}else{
|
||||
throw new Error("When adding a new signature to a signed pdf, the original literal datas are necessary.");
|
||||
}
|
||||
|
||||
// Find the changed objects
|
||||
/** @type {PDFLib.PDFDocument} */
|
||||
var oriPdfdoc = await PDFLib.PDFDocument.load(_this.oriU8pdf);
|
||||
pdfdoc.context.enumerateIndirectObjects().forEach(function(/** @type {PdfObjEntry} */a_ele){
|
||||
/** @type {PDFLib.PDFObject} */
|
||||
var a_obj = oriPdfdoc.context.lookup(a_ele[0]);
|
||||
if(!(a_obj && _this.isamePdfObject(a_ele[1], a_obj))){
|
||||
_this.apobjs.push(a_ele);
|
||||
}
|
||||
});
|
||||
await _this.findChangedObjects(pdfdoc);
|
||||
|
||||
}else{
|
||||
// If the definitions of references are too chaotic, a signature contains DocMDP or after adding a new signature,
|
||||
|
@ -382,22 +383,17 @@ z.PdfSigner = class{
|
|||
/** @type {Zga.PdfCryptor} */
|
||||
var cypt = new z.PdfCryptor(cypopt);
|
||||
await cypt.encryptPdf(pdfdoc, encref);
|
||||
_this.log("Pdf data has been encrypted.");
|
||||
z.log("Pdf data has been encrypted.");
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {Uint8Array} */
|
||||
var ret = await _this.saveAndSign(pdfdoc);
|
||||
if(!ret){
|
||||
_this.log("Change size of signature's placeholder and retry.");
|
||||
_this.sigContents.value = "0".repeat(_this.siglen);
|
||||
z.log("Change size of signature's placeholder and retry.");
|
||||
_this.sigContents.value = "0".repeat(_this.siglen + 10);
|
||||
ret = await _this.saveAndSign(pdfdoc);
|
||||
}
|
||||
if(ret){
|
||||
_this.log("Signing pdf accomplished.");
|
||||
}else{
|
||||
throw new Error("Failed to sign the pdf.");
|
||||
}
|
||||
|
||||
// Because PDFRefs in PDFLib are stored staticly,
|
||||
// we need to restore all changed PDFRefs
|
||||
|
@ -406,6 +402,30 @@ z.PdfSigner = class{
|
|||
z.newRefs.restoreAll();
|
||||
}
|
||||
|
||||
if(ret){
|
||||
z.log("Pdf has been signed.");
|
||||
}else{
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -417,8 +437,8 @@ z.PdfSigner = class{
|
|||
async saveAndSign(pdfdoc){
|
||||
/** @type {Uint8Array} */
|
||||
var uarr = null;
|
||||
if(this.apobjs.length > 0){
|
||||
uarr = this.appendSignature(pdfdoc);
|
||||
if(this.apobjs && this.apobjs.length > 0){
|
||||
uarr = this.appendIncrement(pdfdoc);
|
||||
}else{
|
||||
uarr = await pdfdoc.save({"useObjectStreams": false});
|
||||
}
|
||||
|
@ -427,12 +447,36 @@ z.PdfSigner = class{
|
|||
return await this.signPdf(pdfstr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {PDFLib.PDFDocument} pdfdoc
|
||||
* @param {boolean=} ignoreInfo
|
||||
* @return {Promise}
|
||||
*/
|
||||
async findChangedObjects(pdfdoc, ignoreInfo){
|
||||
/** @const {z.PdfSigner} */
|
||||
const _this = this;
|
||||
// Find the changed objects
|
||||
/** @type {PDFLib.PDFDocument} */
|
||||
var oriPdfdoc = await PDFLib.PDFDocument.load(_this.oriU8pdf, {ignoreEncryption: true});
|
||||
_this.apobjs = [];
|
||||
pdfdoc.context.enumerateIndirectObjects().forEach(function(/** @type {PdfObjEntry} */a_ele){
|
||||
if(!(ignoreInfo && a_ele[0] == pdfdoc.context.trailerInfo.Info)){
|
||||
/** @type {PDFLib.PDFObject} */
|
||||
var a_obj = oriPdfdoc.context.lookup(a_ele[0]);
|
||||
if(!(a_obj && _this.isamePdfObject(a_ele[1], a_obj))){
|
||||
_this.apobjs.push(a_ele);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {PDFLib.PDFDocument} pdfdoc
|
||||
* @return {Uint8Array}
|
||||
*/
|
||||
appendSignature(pdfdoc){
|
||||
appendIncrement(pdfdoc){
|
||||
/** @const {z.PdfSigner} */
|
||||
const _this = this;
|
||||
/** @type {PDFLib.PDFCrossRefSection} */
|
||||
|
@ -476,7 +520,7 @@ z.PdfSigner = class{
|
|||
*/
|
||||
findPrev(u8pdf){
|
||||
/** @const {Uint8Array} */
|
||||
const eof = Zga.rawToU8arr("%%EOF");
|
||||
const eof = z.rawToU8arr("%%EOF");
|
||||
/** @const {number} */
|
||||
const c0 = "0".charCodeAt(0);
|
||||
/** @const {number} */
|
||||
|
@ -571,6 +615,8 @@ z.PdfSigner = class{
|
|||
* @return {forge_cert}
|
||||
*/
|
||||
loadP12cert(p12cert, pwd){
|
||||
/** @const {z.PdfSigner} */
|
||||
const _this = this;
|
||||
// load P12 certificate
|
||||
if(!p12cert){
|
||||
return null;
|
||||
|
@ -592,7 +638,12 @@ z.PdfSigner = class{
|
|||
var keyBags = p12.getBags({
|
||||
"bagType": forge.pki.oids.pkcs8ShroudedKeyBag,
|
||||
})[forge.pki.oids.pkcs8ShroudedKeyBag];
|
||||
this.privateKey = keyBags[0].key;
|
||||
_this.privateKey = keyBags[0].key;
|
||||
|
||||
/** @type {Array<forge_cert>} */
|
||||
var certs = [];
|
||||
/** @type {number} */
|
||||
var certIdx = -1;
|
||||
if(certBags){
|
||||
// Get all the certificates (-cacerts & -clcerts)
|
||||
// Keep track of the last found client certificate.
|
||||
|
@ -601,18 +652,22 @@ z.PdfSigner = class{
|
|||
/** @type {forge_cert} */
|
||||
var a_cert = certBags[a_ele].cert;
|
||||
|
||||
this.certs.push(a_cert);
|
||||
certs.push(a_cert);
|
||||
|
||||
// Try to find the certificate that matches the private key.
|
||||
if(this.privateKey.n.compareTo(a_cert.publicKey.n) === 0
|
||||
&& this.privateKey.e.compareTo(a_cert.publicKey.e) === 0){
|
||||
this.certIdx = this.certs.length;
|
||||
if(_this.privateKey.n.compareTo(a_cert.publicKey.n) === 0
|
||||
&& _this.privateKey.e.compareTo(a_cert.publicKey.e) === 0){
|
||||
certIdx = certs.length;
|
||||
}
|
||||
}.bind(this));
|
||||
});
|
||||
}
|
||||
if(this.certIdx > 0){
|
||||
return this.certs[--this.certIdx];
|
||||
// z.fixCertAttributes(this.certs[this.certIdx]);
|
||||
if(certIdx > 0){
|
||||
certIdx--;
|
||||
_this.cchain = new z.CertsChain(certs);
|
||||
if(_this.cchain.getSignCert() != certs[certIdx]){
|
||||
throw new Error("Chain of certificates is invalid.");
|
||||
}
|
||||
return certs[certIdx];
|
||||
}else{
|
||||
throw new Error("Failed to find a certificate.");
|
||||
}
|
||||
|
@ -627,7 +682,7 @@ z.PdfSigner = class{
|
|||
/** @const {z.PdfSigner} */
|
||||
const _this = this;
|
||||
/** @const {number} */
|
||||
const docMdp = (_this.certs.length > 0 && _this.opt.permission >= 1 && _this.opt.permission <= 3) ? _this.opt.permission : 0;
|
||||
const docMdp = (_this.cchain && _this.opt.permission >= 1 && _this.opt.permission <= 3) ? _this.opt.permission : 0;
|
||||
/** @const {PDFLib.PDFContext} */
|
||||
const pdfcont = pdfdoc.context;
|
||||
/** @const {z.SignatureCreator} */
|
||||
|
@ -692,7 +747,7 @@ z.PdfSigner = class{
|
|||
}),
|
||||
}),
|
||||
};
|
||||
if(_this.certs.length > 0){
|
||||
if(_this.cchain){
|
||||
signObj.M = PDFLib.PDFString.fromDate(signdate);
|
||||
}else{
|
||||
signObj.Type = "DocTimeStamp";
|
||||
|
@ -748,7 +803,7 @@ z.PdfSigner = class{
|
|||
var ans = page.node.Annots();
|
||||
if(!ans){
|
||||
ans = new PDFLib.PDFArray(pdfcont);
|
||||
page.node.set(PDFLib.PDFName.Annots, ans);
|
||||
page.node.set(PDFLib.PDFName.Annots, pdfcont.register(ans));
|
||||
}
|
||||
ans.push(widgetDictRef);
|
||||
|
||||
|
@ -823,13 +878,15 @@ z.PdfSigner = class{
|
|||
* @return {Promise<Uint8Array>}
|
||||
*/
|
||||
async signPdf(pdfstr){
|
||||
/** @const {z.PdfSigner} */
|
||||
const _this = this;
|
||||
// Finds ByteRange information within a given PDF Buffer if one exists
|
||||
/** @type {Array<string>} */
|
||||
var byteRangeStrings = pdfstr.match(/\/ByteRange\s*\[{1}\s*(?:(?:\d*|\/\*{10})\s+){3}(?:\d+|\/\*{10}){1}\s*]{1}/g);
|
||||
/** @type {string|undefined} */
|
||||
var byteRangePlaceholder = byteRangeStrings.find(function(a_str){
|
||||
return a_str.includes("/"+this.DEFAULT_BYTE_RANGE_PLACEHOLDER);
|
||||
}.bind(this));
|
||||
var byteRangePlaceholder = byteRangeStrings.find(function(/** @type {string} */a_str){
|
||||
return a_str.includes("/"+_this.DEFAULT_BYTE_RANGE_PLACEHOLDER);
|
||||
});
|
||||
if(!byteRangePlaceholder){
|
||||
throw new Error("no signature placeholder");
|
||||
}
|
||||
|
@ -862,11 +919,11 @@ z.PdfSigner = class{
|
|||
|
||||
/** @type {forge.asn1} */
|
||||
var asn1sig = null;
|
||||
if(this.certs.length > 0){
|
||||
if(_this.cchain){
|
||||
/** @type {Date} */
|
||||
var signdate = new Date();
|
||||
if(this.opt.signdate instanceof Date && !this.tsainf){
|
||||
signdate = this.opt.signdate;
|
||||
if(_this.opt.signdate instanceof Date && !_this.tsainf){
|
||||
signdate = _this.opt.signdate;
|
||||
}
|
||||
|
||||
// Here comes the actual PKCS#7 signing.
|
||||
|
@ -877,14 +934,14 @@ z.PdfSigner = class{
|
|||
p7.content = forge.util.createBuffer(pdfstr);
|
||||
|
||||
// Add all the certificates (-cacerts & -clcerts) to p7
|
||||
this.certs.forEach(function(a_cert){
|
||||
_this.cchain.getAllCerts().forEach(function(/** @type {forge_cert} */a_cert){
|
||||
p7.addCertificate(a_cert);
|
||||
});
|
||||
|
||||
// Add a sha256 signer. That's what Adobe.PPKLite adbe.pkcs7.detached expects.
|
||||
p7.addSigner({
|
||||
key: this.privateKey,
|
||||
certificate: this.certs[this.certIdx],
|
||||
key: _this.privateKey,
|
||||
certificate: _this.cchain.getSignCert(),
|
||||
digestAlgorithm: forge.pki.oids.sha256,
|
||||
authenticatedAttributes: [
|
||||
{
|
||||
|
@ -902,25 +959,28 @@ z.PdfSigner = class{
|
|||
// Sign in detached mode.
|
||||
p7.sign({"detached": true});
|
||||
|
||||
if(this.tsainf){
|
||||
if(_this.tsainf){
|
||||
/** @type {forge.asn1} */
|
||||
var tsatoken = await this.queryTsa(p7.signers[0].signature);
|
||||
var tsatoken = await _this.queryTsa(p7.signers[0].signature, true);
|
||||
p7.signerInfos[0].value.push(tsatoken);
|
||||
}
|
||||
asn1sig = p7.toAsn1();
|
||||
}else{
|
||||
asn1sig = await this.queryTsa(pdfstr, true);
|
||||
asn1sig = await _this.queryTsa(pdfstr);
|
||||
}
|
||||
|
||||
// Check if the PDF has a good enough placeholder to fit the signature.
|
||||
/** @type {forge.util.ByteStringBuffer} */
|
||||
var sigbuf = forge.asn1.toDer(asn1sig);
|
||||
/** @type {string} */
|
||||
var sighex = forge.asn1.toDer(asn1sig).toHex();
|
||||
var sighex = sigbuf.toHex();
|
||||
_this.signature = sigbuf.getBytes();
|
||||
// placeholderLength represents the length of the HEXified symbols but we're
|
||||
// checking the actual lengths.
|
||||
this.log("Size of signature is " + sighex.length + "/" + placeholderLength);
|
||||
z.log("Size of signature is " + sighex.length + "/" + placeholderLength);
|
||||
if(sighex.length > placeholderLength){
|
||||
// throw new Error("Signature is too big. Needs: " + sighex.length);
|
||||
this.siglen = sighex.length;
|
||||
_this.siglen = sighex.length;
|
||||
return null;
|
||||
}else{
|
||||
// Pad the signature with zeroes so the it is the same length as the placeholder
|
||||
|
@ -935,137 +995,174 @@ z.PdfSigner = class{
|
|||
/**
|
||||
* @private
|
||||
* @param {string=} data
|
||||
* @param {boolean=} nocert
|
||||
* @param {boolean=} forP7
|
||||
* @return {Promise<forge.asn1>}
|
||||
*/
|
||||
async queryTsa(data, nocert){
|
||||
/** @lends {forge.asn1} */
|
||||
const asn1 = forge.asn1;
|
||||
/** @lends {forge.asn1.Class} */
|
||||
const asnc = asn1.Class;
|
||||
/** @lends {forge.asn1.Type} */
|
||||
const asnt = asn1.Type;
|
||||
|
||||
/**
|
||||
* @param {string|number|boolean|forge.asn1|Array<forge.asn1>} aval
|
||||
* @param {number=} atyp
|
||||
* @param {number=} atag
|
||||
* @return {forge.asn1}
|
||||
*/
|
||||
var asncreate = function(aval, atyp, atag){
|
||||
/** @type {string|number|forge.asn1|Array<forge.asn1>} */
|
||||
var a_val = null;
|
||||
/** @type {number} */
|
||||
var a_typ = (atyp || atyp === asnt.NONE) ? atyp : -1;
|
||||
/** @type {boolean} */
|
||||
var a_con = false;
|
||||
if(Array.isArray(aval)){
|
||||
a_con = true;
|
||||
}else{
|
||||
switch(typeof aval){
|
||||
case "string":
|
||||
if(a_typ == asnt.OID){
|
||||
a_val = asn1.oidToDer(aval).getBytes();
|
||||
}else if(a_typ < 0){
|
||||
a_typ = asnt.OCTETSTRING;
|
||||
}
|
||||
break;
|
||||
case "number":
|
||||
a_val = asn1.integerToDer(aval).getBytes();
|
||||
if(a_typ < 0){
|
||||
a_typ = asnt.INTEGER;
|
||||
}
|
||||
break;
|
||||
case "boolean":
|
||||
if(aval){
|
||||
a_val = 1;
|
||||
}else{
|
||||
a_val = 0;
|
||||
}
|
||||
if(a_typ < 0){
|
||||
a_typ = asnt.BOOLEAN;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(a_typ < 0){
|
||||
a_typ = asnt.SEQUENCE;
|
||||
}
|
||||
if(!a_val && a_val !== 0){
|
||||
a_val = /** @type {Array|forge.asn1|number|string} */(aval);
|
||||
}
|
||||
return asn1.create(atag ? atag : asnc.UNIVERSAL, a_typ, a_con, a_val);
|
||||
};
|
||||
|
||||
// Generate SHA256 hash from data for TSA
|
||||
/** @type {forge.md.digest} */
|
||||
var md = forge.md.sha256.create();
|
||||
md.update(data);
|
||||
|
||||
// Generate TSA request
|
||||
/** @type {forge.asn1} */
|
||||
var asn1Req = asncreate([
|
||||
// Version
|
||||
asncreate(1),
|
||||
asncreate([
|
||||
asncreate([
|
||||
asncreate(forge.oids.sha256, asnt.OID),
|
||||
asncreate("", asnt.NULL),
|
||||
]),
|
||||
// Message imprint
|
||||
asncreate(md.digest().getBytes()),
|
||||
]),
|
||||
// Get REQ certificates
|
||||
asncreate(true),
|
||||
]);
|
||||
/** @type {string} */
|
||||
var tsr = asn1.toDer(asn1Req).getBytes();
|
||||
/** @type {Uint8Array} */
|
||||
var tu8s = z.rawToU8arr(tsr);
|
||||
/** @type {Object<string, *>} */
|
||||
var hds = this.tsainf.headers ? this.tsainf.headers : {};
|
||||
if(!hds["Content-Type"]){
|
||||
hds["Content-Type"] = "application/timestamp-query";
|
||||
}
|
||||
/** @type {UrlFetchParams} */
|
||||
var options = {
|
||||
"method": "POST",
|
||||
"headers": hds,
|
||||
"payload": tu8s,
|
||||
};
|
||||
/** @type {Uint8Array} */
|
||||
var tesp = await z.urlFetch(this.tsainf.url, options);
|
||||
/** @type {string} */
|
||||
var tstr = z.u8arrToRaw(tesp);
|
||||
/** @type {forge.asn1} */
|
||||
var token = asn1.fromDer(tstr).value[1];
|
||||
|
||||
/** @type {forge.asn1} */
|
||||
var ret = null;
|
||||
if(nocert){
|
||||
ret = token;
|
||||
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){
|
||||
throw new Error(err);
|
||||
}else{
|
||||
// create the asn1 to append to the signature
|
||||
ret = asncreate([
|
||||
asncreate([
|
||||
// Attribute Type (forge.pki.oids.timeStampToken)
|
||||
asncreate("1.2.840.113549.1.9.16.2.14", asnt.OID),
|
||||
// Attribute Value
|
||||
asncreate([token], asnt.SET),
|
||||
]),
|
||||
], 1, asnc.CONTEXT_SPECIFIC);
|
||||
/** @type {forge.asn1} */
|
||||
var asn1 = _this.tsaFetcher.getToken(forP7);
|
||||
z.log("Timestamp from " + _this.tsainf.url + " has been obtained.");
|
||||
return asn1;
|
||||
}
|
||||
this.log("Timestamp from " + this.tsainf.url + " has been obtained.");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
* @param {PDFLib.PDFDocument} pdfdoc
|
||||
* @param {Zga.CertsChain} chain
|
||||
* @param {boolean=} crlOnly
|
||||
* @return {Promise<Uint8Array>}
|
||||
*/
|
||||
async applyLTV(pdfdoc, chain, crlOnly){
|
||||
/** @const {z.PdfSigner} */
|
||||
const _this = this;
|
||||
/** @type {?DSSInfo} */
|
||||
var dssinf = await chain.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 {string} msg
|
||||
* @param {PDFLib.PDFDocument} pdfdoc
|
||||
* @param {string} sig signature
|
||||
* @param {DSSInfo=} dssinf
|
||||
* @return {boolean}
|
||||
*/
|
||||
log(msg){
|
||||
if(this.debug){
|
||||
console.log(msg);
|
||||
addDss(pdfdoc, sig, dssinf){
|
||||
/** @type {PDFLib.PDFContext} */
|
||||
var pdfcont = pdfdoc.context;
|
||||
/** @type {Array<PDFLib.PDFRef>} */
|
||||
var certRefs = null;
|
||||
/** @type {Array<PDFLib.PDFRef>} */
|
||||
var ocspRefs = null;
|
||||
/** @type {Array<PDFLib.PDFRef>} */
|
||||
var crlRefs = null;
|
||||
|
||||
if(dssinf && dssinf.ocsps && dssinf.ocsps.length > 0){
|
||||
ocspRefs = [];
|
||||
dssinf.ocsps.forEach(function(/** @type {string|Uint8Array} */a_ocsp){
|
||||
/** @type {PDFLib.PDFRawStream} */
|
||||
var a_strmOcsp = pdfcont.flateStream(a_ocsp);
|
||||
ocspRefs.push(pdfcont.register(a_strmOcsp));
|
||||
});
|
||||
}
|
||||
if(dssinf && dssinf.crls && dssinf.crls.length > 0){
|
||||
crlRefs = [];
|
||||
dssinf.crls.forEach(function(/** @type {string|Uint8Array} */a_crl){
|
||||
/** @type {PDFLib.PDFRawStream} */
|
||||
var a_strmCrl = pdfcont.flateStream(a_crl);
|
||||
crlRefs.push(pdfcont.register(a_strmCrl));
|
||||
});
|
||||
}
|
||||
|
||||
if(!(ocspRefs || crlRefs)){
|
||||
// Nothing to do.
|
||||
return false;
|
||||
}
|
||||
|
||||
if(dssinf && dssinf.certs && dssinf.certs.length > 0){
|
||||
certRefs = [];
|
||||
dssinf.certs.forEach(function(/** @type {forge_cert} */a_cert){
|
||||
/** @type {forge.asn1} */
|
||||
var a_asn1Cert = forge.pki.certificateToAsn1(a_cert);
|
||||
/** @type {PDFLib.PDFRawStream} */
|
||||
var a_strmCert = pdfcont.flateStream(forge.asn1.toDer(a_asn1Cert).getBytes());
|
||||
certRefs.push(pdfcont.register(a_strmCert));
|
||||
});
|
||||
}
|
||||
|
||||
/** @type {forge.md.digest} */
|
||||
var md = forge.md.sha1.create();
|
||||
md.update(sig);
|
||||
/** @type {string} */
|
||||
var sighex = md.digest().toHex().toUpperCase();
|
||||
|
||||
var dss = /** @type {PDFLib.PDFDict} */(pdfdoc.catalog.lookupMaybe(PDFLib.PDFName.of("DSS"), PDFLib.PDFDict));
|
||||
/** @type {PDFLib.PDFArray} */
|
||||
var certsarr = null;
|
||||
/** @type {PDFLib.PDFArray} */
|
||||
var ocpsarr = null;
|
||||
/** @type {PDFLib.PDFArray} */
|
||||
var crlsarr = null;
|
||||
/** @type {PDFLib.PDFDict} */
|
||||
var vri = null;
|
||||
/** @type {Object<string, *>} */
|
||||
var vriObj = {
|
||||
TU: PDFLib.PDFString.fromDate(new Date()),
|
||||
};
|
||||
/** @type {PDFLib.PDFArray} */
|
||||
var sigcertsarr = null;
|
||||
/** @type {PDFLib.PDFArray} */
|
||||
var sigocpsarr = null;
|
||||
/** @type {PDFLib.PDFArray} */
|
||||
var sigcrlsarr = null;
|
||||
if(dss){
|
||||
certsarr = /** @type {PDFLib.PDFArray} */(dss.lookupMaybe(PDFLib.PDFName.of("Certs"), PDFLib.PDFArray));
|
||||
crlsarr = /** @type {PDFLib.PDFArray} */(dss.lookupMaybe(PDFLib.PDFName.of("CRLs"), PDFLib.PDFArray));
|
||||
ocpsarr = /** @type {PDFLib.PDFArray} */(dss.lookupMaybe(PDFLib.PDFName.of("OCSPs"), PDFLib.PDFArray));
|
||||
vri = /** @type {PDFLib.PDFDict} */(dss.lookupMaybe(PDFLib.PDFName.of("VRI"), PDFLib.PDFDict));
|
||||
}else{
|
||||
dss = /** @type {PDFLib.PDFDict} */(pdfcont.obj({}));
|
||||
pdfdoc.catalog.set(PDFLib.PDFName.of("DSS"), pdfcont.register(dss));
|
||||
}
|
||||
if(certRefs){
|
||||
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);
|
||||
certsarr.push(a_ref);
|
||||
});
|
||||
}
|
||||
if(ocspRefs){
|
||||
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);
|
||||
ocpsarr.push(a_ref);
|
||||
});
|
||||
}
|
||||
if(crlRefs){
|
||||
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);
|
||||
crlsarr.push(a_ref);
|
||||
});
|
||||
}
|
||||
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)));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1309,7 +1406,8 @@ z.SignatureCreator = class{
|
|||
|
||||
}
|
||||
|
||||
if(!globalThis.Zga){
|
||||
globalThis.Zga = {};
|
||||
//Only for nodejs Start//
|
||||
if(typeof exports === "object" && typeof module !== "undefined"){
|
||||
module.exports = supplyZgaSigner;
|
||||
}
|
||||
supplyZgaSigner(globalThis.Zga);
|
||||
//Only for nodejs End//
|
26
package.json
26
package.json
|
@ -1,10 +1,32 @@
|
|||
{
|
||||
"name": "zgapdfsigner",
|
||||
"version": "2.3.0",
|
||||
"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,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/zboris12/zgapdfsigner"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/zboris12/zgapdfsigner/issues"
|
||||
},
|
||||
"license": "MIT",
|
||||
"main": "lib/zganode.js",
|
||||
"files": [
|
||||
"lib/*.js"
|
||||
],
|
||||
"keywords": [
|
||||
"pdf-sign",
|
||||
"pdf-signature",
|
||||
"pdf-protection",
|
||||
"pdf-encryption",
|
||||
"google-apps-script",
|
||||
"LTV",
|
||||
"TSA"
|
||||
],
|
||||
"scripts": {
|
||||
"dev": "",
|
||||
"build": ""
|
||||
"testsign": "node test4node.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"pdf-lib": "1.17.1",
|
||||
|
|
109
test4node.js
109
test4node.js
|
@ -1,86 +1,18 @@
|
|||
const m_fs = require("fs");
|
||||
const m_path = require("path");
|
||||
const m_urlparser = require("url");
|
||||
const m_h = {
|
||||
"http:": require("http"),
|
||||
"https:": require("http"),
|
||||
};
|
||||
const Zga = require("./lib/zganode.js");
|
||||
|
||||
globalThis.PDFLib = require("pdf-lib");
|
||||
globalThis.forge = require("node-forge");
|
||||
require("./zgapdfcryptor.js");
|
||||
require("./zgapdfsigner.js");
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {UrlFetchParams} params
|
||||
* @return {Promise<Uint8Array>}
|
||||
*/
|
||||
Zga.urlFetch = function(url, params){
|
||||
return new Promise(function(resolve, reject){
|
||||
/** @type {URL} */
|
||||
var opts = m_urlparser.parse(url);
|
||||
var http = m_h[opts.protocol];
|
||||
/** @type {string|Buffer} */
|
||||
var dat = null;
|
||||
var encoding = undefined;
|
||||
if(params.payload instanceof Buffer){
|
||||
dat = params.payload;
|
||||
}else if(params.payload instanceof Uint8Array){
|
||||
dat = Buffer.from(params.payload.buffer);
|
||||
}else if(params.payload instanceof ArrayBuffer){
|
||||
dat = Buffer.from(params.payload);
|
||||
}else{
|
||||
dat = params.payload;
|
||||
encoding = "binary";
|
||||
}
|
||||
|
||||
if(params.headers){
|
||||
opts.headers = params.headers;
|
||||
}
|
||||
if(params.method){
|
||||
opts.method = params.method;
|
||||
}
|
||||
if(params.validateHttpsCertificates === false){
|
||||
opts.rejectUnauthorized = false;
|
||||
}
|
||||
|
||||
/** @type {http.ClientRequest} */
|
||||
var hreq = http.request(opts, function(/** @type {http.IncomingMessage} */a_res){
|
||||
if(a_res.statusCode !== 200){
|
||||
var a_err = new Error("Failed to request url. " + url + "\n Status Code: " + a_res.statusCode);
|
||||
a_res.resume();
|
||||
throw a_err;
|
||||
}
|
||||
/** @type {Array<Buffer>} */
|
||||
var a_bufs = [];
|
||||
var a_bufs_len = 0;
|
||||
a_res.on("data", function(/** @type {Buffer} */b_chunk){
|
||||
a_bufs.push(b_chunk);
|
||||
a_bufs_len += b_chunk.length;
|
||||
});
|
||||
a_res.on("end", function(){
|
||||
/** @type {Buffer} */
|
||||
var b_bdat = Buffer.concat(a_bufs, a_bufs_len);
|
||||
resolve(b_bdat);
|
||||
});
|
||||
});
|
||||
hreq.on("error", function(a_err){
|
||||
throw a_err;
|
||||
});
|
||||
hreq.end(dat, encoding);
|
||||
});
|
||||
};
|
||||
const workpath = "test/";
|
||||
|
||||
async function main(){
|
||||
/** @type {string} */
|
||||
var pdfPath = m_path.join(__dirname, "test/_test.pdf");
|
||||
var pdfPath = m_path.join(__dirname, workpath+"_test.pdf");
|
||||
/** @type {string} */
|
||||
var pfxPath = m_path.join(__dirname, "test/_test.pfx");
|
||||
var pfxPath = m_path.join(__dirname, workpath+"_test.pfx");
|
||||
/** @type {string} */
|
||||
var ps = "";
|
||||
/** @type {string} */
|
||||
var imgPath = m_path.join(__dirname, "test/_test.png");
|
||||
var imgPath = m_path.join(__dirname, workpath+"_test.png");
|
||||
|
||||
if(process.argv.length > 3){
|
||||
pfxPath = process.argv[2];
|
||||
|
@ -116,11 +48,12 @@ async function main(){
|
|||
sopt = {
|
||||
p12cert: pfx,
|
||||
pwd: ps,
|
||||
permission: pfx ? 1 : 0,
|
||||
// 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){
|
||||
|
@ -157,11 +90,37 @@ async function main(){
|
|||
|
||||
if(u8dat){
|
||||
/** @type {string} */
|
||||
var outPath = m_path.join(__dirname, "test/test_test2.pdf");
|
||||
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();
|
||||
|
|
Loading…
Reference in New Issue