Added support to add a document timestamp from a TSA.

pull/2/head 2.4.0
zboris12 2022-10-24 21:36:47 +09:00
parent 528eaad5eb
commit 8b008ccc34
5 changed files with 282 additions and 238 deletions

View File

@ -1,6 +1,6 @@
# 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 it also can be used in Google Apps Script and nodejs.
PS: __ZGA__ is the abbreviation of my father's name.
And I use this name to hope the merits from this application will be dedicated to my parents.
@ -11,7 +11,8 @@ And I use this name to hope the merits from this application will be dedicated t
* Sign a pdf with a visible pkcs#7 signature by drawing an image.
* Sign a pdf and set DocMDP(document modification detection and prevention).
* Add a new signature to a pdf if it has been signed already. (An incremental update)
* Sign a pdf with a timestamp from TSA(Time Stamp Authority). (Only in Google Apps Script)
* Add a document timestamp from TSA(Time Stamp Authority). (Only in Google Apps Script and nodejs)
* Sign a pdf with a timestamp from TSA. (Only in Google Apps Script and nodejs)
* Set password protection to a pdf. Supported algorithms:
* 40bit RC4 Encryption
* 128bit RC4 Encryption
@ -138,8 +139,8 @@ fld.createFile(Utilities.newBlob(u8arr, "application/pdf").setName("signed_test.
## Detail of SignOption
* __p12cert__: Array<number>|Uint8Array|ArrayBuffer|string :point_right: Certificate's data
* __pwd__: string :point_right: The passphrase of the certificate
* __p12cert__: Array<number>|Uint8Array|ArrayBuffer|string :point_right: (Optional) Certificate's data. In the case of adding a document timestamp, it must be omitted.
* __pwd__: string :point_right: (Optional) The passphrase of the certificate. In the case of adding a document timestamp, it must be omitted.
* __permission__: number :point_right: (Optional) The modification permissions granted for this document.
This is a setting of DocMDP(document modification detection and prevention). Valid values are:
* 1: No changes to the document are permitted; any change to the document invalidates the signature.
@ -148,7 +149,7 @@ fld.createFile(Utilities.newBlob(u8arr, "application/pdf").setName("signed_test.
* __reason__: string :point_right: (Optional) The reason for signing
* __location__: string :point_right: (Optional) Your location
* __contact__: string :point_right: (Optional) Your contact information
* __signdate__: Date|string|_TsaServiceInfo_ :point_right: (Optional)
* __signdate__: Date|string|_TsaServiceInfo_ :point_right: (Optional) In the case of adding a document timestamp, it can't be omitted and can't be a Date.
* When it is a Date, it means the date and time of signing.
* When it is a string, it can be an url of TSA or an index of the preset TSAs as below:
* "1": http://ts.ssl.com
@ -161,6 +162,7 @@ fld.createFile(Utilities.newBlob(u8arr, "application/pdf").setName("signed_test.
* When it is a _TsaServiceInfo_, it means a full customized information of a TSA.
* __url__: string :point_right: The url of TSA
* __len__: number :point_right: (Optional) The length of signature's placeholder
* __headers__: Object<string, *> :point_right: (Optional) The customized headers for sending to tsa server
* When it is omitted, the system timestamp will be used.
* __signame__: string :point_right: (Optional) The name of the signature
* __drawinf__: _SignDrawInfo_ :point_right: (Optional) Visible signature's information

View File

@ -102,6 +102,8 @@ forge.asn1.integerToDer = function(num){};
forge.asn1.oidToDer = function(oid){};
forge.asn1.Type = {};
/** @type {number} */
forge.asn1.Type.NONE;
/** @type {number} */
forge.asn1.Type.UTF8;
/** @type {number} */
forge.asn1.Type.SET;
@ -126,7 +128,7 @@ forge.asn1.Class.CONTEXT_SPECIFIC;
* @param {number} tagClass
* @param {number} type
* @param {boolean} constructed
* @param {Array<string>|string} value
* @param {Array<forge.asn1>|forge.asn1|number|string} value
* @param {Object=} options
* @return {forge.asn1}
*/

View File

@ -3,6 +3,7 @@
* {{
* url: string,
* len: (number|undefined),
* headers: (Object<string, *>|undefined),
* }}
*/
var TsaServiceInfo;
@ -32,6 +33,8 @@ 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.
@ -39,8 +42,8 @@ var SignDrawInfo;
*
* @typedef
* {{
* p12cert: (Array<number>|Uint8Array|ArrayBuffer|string),
* pwd: string,
* p12cert: (Array<number>|Uint8Array|ArrayBuffer|string|undefined),
* pwd: (string|undefined),
* permission: (number|undefined),
* reason: (string|undefined),
* location: (string|undefined),

View File

@ -11,13 +11,12 @@ globalThis.forge = require("node-forge");
require("./zgapdfcryptor.js");
require("./zgapdfsigner.js");
Zga.UrlFetchApp = {};
/**
* @param {string} url
* @param {UrlFetchParams} params
* @return {Promise<Uint8Array>}
*/
Zga.UrlFetchApp.fetch = function(url, params){
Zga.urlFetch = function(url, params){
return new Promise(function(resolve, reject){
/** @type {URL} */
var opts = m_urlparser.parse(url);
@ -91,13 +90,17 @@ async function main(){
}
if(!ps){
throw new Error("The passphrase is not specified.");
// throw new Error("The passphrase is not specified.");
pfxPath = "";
}
/** @type {Buffer} */
var pdf = m_fs.readFileSync(pdfPath);
/** @type {Buffer} */
var pfx = m_fs.readFileSync(pfxPath);
var pfx = null;
if(pfxPath){
pfx = m_fs.readFileSync(pfxPath);
}
/** @type {Buffer} */
var img = null;
/** @type {string} */
@ -109,11 +112,11 @@ async function main(){
/** @type {SignOption} */
var sopt = null;
if(pfx){
if(pdf){
sopt = {
p12cert: pfx,
pwd: ps,
permission: 1,
permission: pfx ? 1 : 0,
signdate: "1",
reason: "I have a test reason.",
location: "I am on the earth.",

View File

@ -18,13 +18,12 @@ z.TSAURLS = {
// Google Apps Script
if(globalThis.UrlFetchApp){
z.UrlFetchApp = {};
/**
* @param {string} url
* @param {UrlFetchParams} params
* @return {Promise<Uint8Array>}
*/
z.UrlFetchApp.fetch = function(url, params){
z.urlFetch = function(url, params){
return new Promise(function(resolve){
/** @type {GBlob} */
var tblob = UrlFetchApp.fetch(url, params).getBlob();
@ -264,7 +263,7 @@ z.PdfSigner = class{
}
}
if(this.tsainf){
if(!z.UrlFetchApp){
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]){
@ -326,18 +325,21 @@ z.PdfSigner = class{
}
}
/** @type {forge_cert} */
var cert = _this.loadP12cert(_this.opt.p12cert, _this.opt.pwd);
if(cert){
z.fixCertAttributes(cert);
}else if(_this.tsainf){
_this.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.");
}
/** @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.");
/** @type {forge_cert} */
var cert = _this.loadP12cert(_this.opt.p12cert, _this.opt.pwd);
if(cert){
z.fixCertAttributes(cert);
}
if(apmode){
if(_this.oriU8pdf){
_this.log("The pdf has been signed already, so we add a new signature to it.");
@ -562,22 +564,78 @@ z.PdfSigner = class{
return buff.length - before;
}
/**
* @private
* @param {Array<number>|Uint8Array|ArrayBuffer|string=} p12cert
* @param {string=} pwd
* @return {forge_cert}
*/
loadP12cert(p12cert, pwd){
// load P12 certificate
if(!p12cert){
return null;
}else if(typeof p12cert !== "string"){
p12cert = z.u8arrToRaw(new Uint8Array(p12cert));
}
// Convert Buffer P12 to a forge implementation.
/** @type {forge.asn1} */
var p12Asn1 = forge.asn1.fromDer(p12cert);
/** @type {forge.pkcs12} */
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, true, pwd);
// Extract safe bags by type.
// We will need all the certificates and the private key.
/** @type {Object<string|number, P12Bag>} */
var certBags = p12.getBags({
"bagType": forge.pki.oids.certBag,
})[forge.pki.oids.certBag];
/** @type {Object<string|number, P12Bag>} */
var keyBags = p12.getBags({
"bagType": forge.pki.oids.pkcs8ShroudedKeyBag,
})[forge.pki.oids.pkcs8ShroudedKeyBag];
this.privateKey = keyBags[0].key;
if(certBags){
// Get all the certificates (-cacerts & -clcerts)
// Keep track of the last found client certificate.
// This will be the public key that will be bundled in the signature.
Object.keys(certBags).forEach(function(a_ele){
/** @type {forge_cert} */
var a_cert = certBags[a_ele].cert;
this.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;
}
}.bind(this));
}
if(this.certIdx > 0){
return this.certs[--this.certIdx];
// z.fixCertAttributes(this.certs[this.certIdx]);
}else{
throw new Error("Failed to find a certificate.");
}
}
/**
* @private
* @param {PDFLib.PDFDocument} pdfdoc
* @return {boolean} append mode or not
*/
addSignHolder(pdfdoc){
/** @const {z.PdfSigner} */
const _this = this;
/** @const {number} */
const docMdp = (this.opt.permission >= 1 && this.opt.permission <= 3) ? this.opt.permission : 0;
const docMdp = (_this.certs.length > 0 && _this.opt.permission >= 1 && _this.opt.permission <= 3) ? _this.opt.permission : 0;
/** @const {PDFLib.PDFContext} */
const pdfcont = pdfdoc.context;
/** @const {z.SignatureCreator} */
const signcrt = new z.SignatureCreator(this.opt.drawinf);
const signcrt = new z.SignatureCreator(_this.opt.drawinf);
/** @const {PDFLib.PDFPage} */
const page = pdfdoc.getPages()[signcrt.getPageIndex()];
/** @type {PDFLib.PDFRef} */
var strmRef = signcrt.createStream(pdfdoc, this.opt.signame);
var strmRef = signcrt.createStream(pdfdoc, _this.opt.signame);
if(docMdp && !strmRef){
strmRef = signcrt.createEmptyField(pdfcont);
@ -603,23 +661,23 @@ z.PdfSigner = class{
}
/** @type {string} */
var signm = this.fixSigName(oldSigs, this.opt.signame);
var signm = _this.fixSigName(oldSigs, _this.opt.signame);
/** @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;
}
/** @type {PDFLib.PDFArray} */
var bytrng = new PDFLib.PDFArray(pdfcont);
bytrng.push(PDFLib.PDFNumber.of(0));
bytrng.push(PDFLib.PDFName.of(this.DEFAULT_BYTE_RANGE_PLACEHOLDER));
bytrng.push(PDFLib.PDFName.of(this.DEFAULT_BYTE_RANGE_PLACEHOLDER));
bytrng.push(PDFLib.PDFName.of(this.DEFAULT_BYTE_RANGE_PLACEHOLDER));
bytrng.push(PDFLib.PDFName.of(_this.DEFAULT_BYTE_RANGE_PLACEHOLDER));
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.sigContents = PDFLib.PDFHexString.of("0".repeat(this.siglen));
_this.siglen = /** @type {number} */(_this.tsainf ? _this.tsainf.len : 3322);
_this.sigContents = PDFLib.PDFHexString.of("0".repeat(_this.siglen));
/** @type {Object<string, *>} */
var signObj = {
@ -627,14 +685,19 @@ z.PdfSigner = class{
"Filter": "Adobe.PPKLite",
"SubFilter": "adbe.pkcs7.detached",
"ByteRange": bytrng,
"Contents": this.sigContents,
"M": PDFLib.PDFString.fromDate(signdate),
"Contents": _this.sigContents,
"Prop_Build": pdfcont.obj({
"App": pdfcont.obj({
"Name": "ZgaPdfSinger",
}),
}),
};
if(_this.certs.length > 0){
signObj.M = PDFLib.PDFString.fromDate(signdate);
}else{
signObj.Type = "DocTimeStamp";
signObj.SubFilter = "ETSI.RFC3161";
}
if(docMdp){
/** @type {PDFLib.PDFArray} */
var rfrc = new PDFLib.PDFArray(pdfcont);
@ -649,14 +712,14 @@ z.PdfSigner = class{
}));
signObj["Reference"] = rfrc;
}
if(this.opt.reason){
signObj["Reason"] = this.convToPDFString(this.opt.reason);
if(_this.opt.reason){
signObj["Reason"] = _this.convToPDFString(_this.opt.reason);
}
if(this.opt.location){
signObj["Location"] = this.convToPDFString(this.opt.location);
if(_this.opt.location){
signObj["Location"] = _this.convToPDFString(_this.opt.location);
}
if(this.opt.contact){
signObj["ContactInfo"] = this.convToPDFString(this.opt.contact);
if(_this.opt.contact){
signObj["ContactInfo"] = _this.convToPDFString(_this.opt.contact);
}
/** @type {PDFLib.PDFRef} */
var signatureDictRef = pdfcont.register(pdfcont.obj(signObj));
@ -668,7 +731,7 @@ z.PdfSigner = class{
"FT": "Sig",
"Rect": signcrt.getSignRect(),
"V": signatureDictRef,
"T": this.convToPDFString(signm),
"T": _this.convToPDFString(signm),
"F": 132,
"P": page.ref,
};
@ -734,53 +797,23 @@ z.PdfSigner = class{
/**
* @private
* @param {Array<number>|Uint8Array|ArrayBuffer|string} p12cert
* @param {string} pwd
* @return {forge_cert}
* @param {string} str
* @return {PDFLib.PDFString|PDFLib.PDFHexString}
*/
loadP12cert(p12cert, pwd){
// load P12 certificate
if(typeof p12cert !== "string"){
p12cert = z.u8arrToRaw(new Uint8Array(p12cert));
convToPDFString(str){
// Check if there is a multi-bytes char in the string.
/** @type {boolean} */
var flg = false;
for(var i=0; i<str.length; i++){
if(str.charCodeAt(i) > 0xFF){
flg = true;
break;
}
}
// Convert Buffer P12 to a forge implementation.
/** @type {forge.asn1} */
var p12Asn1 = forge.asn1.fromDer(p12cert);
/** @type {forge.pkcs12} */
var p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, true, pwd);
// Extract safe bags by type.
// We will need all the certificates and the private key.
/** @type {Object<string|number, P12Bag>} */
var certBags = p12.getBags({
"bagType": forge.pki.oids.certBag,
})[forge.pki.oids.certBag];
/** @type {Object<string|number, P12Bag>} */
var keyBags = p12.getBags({
"bagType": forge.pki.oids.pkcs8ShroudedKeyBag,
})[forge.pki.oids.pkcs8ShroudedKeyBag];
this.privateKey = keyBags[0].key;
if(certBags){
// Get all the certificates (-cacerts & -clcerts)
// Keep track of the last found client certificate.
// This will be the public key that will be bundled in the signature.
Object.keys(certBags).forEach(function(a_ele){
/** @type {forge_cert} */
var a_cert = certBags[a_ele].cert;
this.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;
}
}.bind(this));
}
if(this.certIdx > 0){
return this.certs[--this.certIdx];
// z.fixCertAttributes(this.certs[this.certIdx]);
if(flg){
return PDFLib.PDFHexString.fromText(str);
}else{
throw new Error("Failed to find a certificate.");
return PDFLib.PDFString.of(str);
}
}
@ -790,12 +823,6 @@ z.PdfSigner = class{
* @return {Promise<Uint8Array>}
*/
async signPdf(pdfstr){
/** @type {Date} */
var signdate = new Date();
if(this.opt.signdate instanceof Date && !this.tsainf){
signdate = this.opt.signdate;
}
// 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);
@ -833,48 +860,61 @@ z.PdfSigner = class{
// Remove the placeholder signature
pdfstr = pdfstr.slice(0, byteRange[1]) + pdfstr.slice(byteRange[2], byteRange[2] + byteRange[3]);
// Here comes the actual PKCS#7 signing.
/** @type {forge.pkcs7} */
var p7 = forge.pkcs7.createSignedData();
// Start off by setting the content.
p7.content = forge.util.createBuffer(pdfstr);
/** @type {forge.asn1} */
var asn1sig = null;
if(this.certs.length > 0){
/** @type {Date} */
var signdate = new Date();
if(this.opt.signdate instanceof Date && !this.tsainf){
signdate = this.opt.signdate;
}
// Add all the certificates (-cacerts & -clcerts) to p7
this.certs.forEach(function(a_cert){
p7.addCertificate(a_cert);
});
// Here comes the actual PKCS#7 signing.
/** @type {forge.pkcs7} */
var p7 = null;
p7 = forge.pkcs7.createSignedData();
// Start off by setting the content.
p7.content = forge.util.createBuffer(pdfstr);
// Add a sha256 signer. That's what Adobe.PPKLite adbe.pkcs7.detached expects.
p7.addSigner({
key: this.privateKey,
certificate: this.certs[this.certIdx],
digestAlgorithm: forge.pki.oids.sha256,
authenticatedAttributes: [
{
"type": forge.pki.oids.contentType,
"value": forge.pki.oids.data,
}, {
"type": forge.pki.oids.messageDigest,
}, {
"type": forge.pki.oids.signingTime,
"value": signdate,
},
],
});
// Add all the certificates (-cacerts & -clcerts) to p7
this.certs.forEach(function(a_cert){
p7.addCertificate(a_cert);
});
// Sign in detached mode.
p7.sign({"detached": true});
// Add a sha256 signer. That's what Adobe.PPKLite adbe.pkcs7.detached expects.
p7.addSigner({
key: this.privateKey,
certificate: this.certs[this.certIdx],
digestAlgorithm: forge.pki.oids.sha256,
authenticatedAttributes: [
{
"type": forge.pki.oids.contentType,
"value": forge.pki.oids.data,
}, {
"type": forge.pki.oids.messageDigest,
}, {
"type": forge.pki.oids.signingTime,
"value": signdate,
},
],
});
if(this.tsainf){
/** @type {forge.asn1} */
var tsatoken = await this.queryTsa(p7.signers[0].signature);
p7.signerInfos[0].value.push(tsatoken);
this.log("Timestamp from " + this.tsainf.url + " has been added to the signature.");
// Sign in detached mode.
p7.sign({"detached": true});
if(this.tsainf){
/** @type {forge.asn1} */
var tsatoken = await this.queryTsa(p7.signers[0].signature);
p7.signerInfos[0].value.push(tsatoken);
}
asn1sig = p7.toAsn1();
}else{
asn1sig = await this.queryTsa(pdfstr, true);
}
// Check if the PDF has a good enough placeholder to fit the signature.
/** @type {string} */
var sighex = forge.asn1.toDer(p7.toAsn1()).toHex();
var sighex = forge.asn1.toDer(asn1sig).toHex();
// placeholderLength represents the length of the HEXified symbols but we're
// checking the actual lengths.
this.log("Size of signature is " + sighex.length + "/" + placeholderLength);
@ -894,134 +934,128 @@ z.PdfSigner = class{
/**
* @private
* @param {string} str
* @return {PDFLib.PDFString|PDFLib.PDFHexString}
*/
convToPDFString(str){
// Check if there is a multi-bytes char in the string.
/** @type {boolean} */
var flg = false;
for(var i=0; i<str.length; i++){
if(str.charCodeAt(i) > 0xFF){
flg = true;
break;
}
}
if(flg){
return PDFLib.PDFHexString.fromText(str);
}else{
return PDFLib.PDFString.of(str);
}
}
/**
* @private
* @param {string=} signature
* @return {string}
*/
genTsrData(signature){
// Generate SHA256 hash from signature content for TSA
/** @type {forge.md.digest} */
var md = forge.md.sha256.create();
md.update(signature);
// Generate TSA request
/** @type {forge.asn1} */
var asn1Req = forge.asn1.create(
forge.asn1.Class.UNIVERSAL,
forge.asn1.Type.SEQUENCE,
true,
[
// Version
{
composed: false,
constructed: false,
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.INTEGER,
value: forge.asn1.integerToDer(1).data,
},
{
composed: true,
constructed: true,
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.SEQUENCE,
value: [
{
composed: true,
constructed: true,
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.SEQUENCE,
value: [
{
composed: false,
constructed: false,
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.OID,
value: forge.asn1.oidToDer(forge.oids.sha256).data,
}, {
composed: false,
constructed: false,
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.NULL,
value: ""
}
]
}, {// Message imprint
composed: false,
constructed: false,
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.OCTETSTRING,
value: md.digest().data,
}
]
}, {
composed: false,
constructed: false,
tagClass: forge.asn1.Class.UNIVERSAL,
type: forge.asn1.Type.BOOLEAN,
value: 1, // Get REQ certificates
}
]
);
return forge.asn1.toDer(asn1Req).data;
}
/**
* @private
* @param {string=} signature
* @param {string=} data
* @param {boolean=} nocert
* @return {Promise<forge.asn1>}
*/
async queryTsa(signature){
async queryTsa(data, nocert){
/** @lends {forge.asn1} */
var asn1 = 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 = this.genTsrData(signature);
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": {"Content-Type": "application/timestamp-query"},
"headers": hds,
"payload": tu8s,
};
/** @type {Uint8Array} */
var tesp = await z.UrlFetchApp.fetch(this.tsainf.url, options);
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];
// create the asn1 to append to the signature
/** @type {string} *///forge.pki.oids.timeStampToken
var typstr = asn1.oidToDer("1.2.840.113549.1.9.16.2.14").getBytes();
return asn1.create(asn1.Class.CONTEXT_SPECIFIC, 1, true, [
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SEQUENCE, true, [
// Attribute Type
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.OID, false, typstr),
// Attribute Value
asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, [token]),
]),
]);
/** @type {forge.asn1} */
var ret = null;
if(nocert){
ret = token;
}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);
}
this.log("Timestamp from " + this.tsainf.url + " has been obtained.");
return ret;
}
/**