Skip to content

Commit

Permalink
Added support to DocMDP and improved the way of appending TSA timestamp.
Browse files Browse the repository at this point in the history
  • Loading branch information
zboris12 committed Oct 14, 2022
1 parent adc260b commit fe2fd48
Show file tree
Hide file tree
Showing 8 changed files with 622 additions and 176 deletions.
44 changes: 25 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ And I use this name to hope the merits from this application will be dedicated t

* Sign a pdf with an invisible pkcs#7 signature.
* Sign a pdf with a visible pkcs#7 signature by drawing an image.
* Sign a pdf and set DocMDP(document modification detection and prevention).
* Sign a pdf with a timestamp from TSA(Time Stamp Authority). (Only in Google Apps Script)
* Set password protection to a pdf. Supported algorithms:
* 40bit RC4 Encryption
Expand All @@ -22,8 +23,6 @@ And I use this name to hope the merits from this application will be dedicated t

Because of the CORS security restrictions in web browser,
signing with a timestamp from TSA can only be used in Google Apps Script.
And because [node-forge](https://github.com/digitalbazaar/forge) hasn't supported unauthenticated attributes in pkcs#7 yet,
so when use this function, [the edited version](https://github.com/zboris12/zgapdfsigner/releases/download/1.2.0/forge.min.edited.js) needs to be imported.

## The Dependencies

Expand All @@ -34,9 +33,9 @@ so when use this function, [the edited version](https://github.com/zboris12/zgap

Just import the dependencies and this tool.
```html
<script src="https://unpkg.com/pdf-lib/dist/pdf-lib.min.js" type="text/javascript"></script>
<script src="https://unpkg.com/node-forge/dist/forge.min.js" type="text/javascript"></script>
<script src="https://github.com/zboris12/zgapdfsigner/releases/download/2.0.0/zgapdfsigner.min.js" type="text/javascript"></script>
<script src="https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js" type="text/javascript"></script>
<script src="https://unpkg.com/node-forge@1.3.1/dist/forge.min.js" type="text/javascript"></script>
<script src="https://github.com/zboris12/zgapdfsigner/releases/download/2.2.0/zgapdfsigner.min.js" type="text/javascript"></script>
```

## Let's sign
Expand All @@ -55,6 +54,7 @@ async function sign1(pdf, cert, pwd){
var sopt = {
p12cert: cert,
pwd: pwd,
permission: 1,
};
var signer = new Zga.PdfSigner(sopt);
var u8arr = await signer.sign(pdf);
Expand Down Expand Up @@ -114,9 +114,9 @@ var window = globalThis;
// Load pdf-lib
eval(UrlFetchApp.fetch("https://unpkg.com/[email protected]/dist/pdf-lib.min.js").getContentText());
// Load node-forge
eval(UrlFetchApp.fetch("https://github.com/zboris12/zgapdfsigner/releases/download/1.2.0/forge.min.edited.js").getContentText());
eval(UrlFetchApp.fetch("https://unpkg.com/[email protected]/dist/forge.min.js").getContentText());
// Load ZgaPdfSigner
eval(UrlFetchApp.fetch("https://github.com/zboris12/zgapdfsigner/releases/download/2.0.0/zgapdfsigner.min.js").getContentText());
eval(UrlFetchApp.fetch("https://github.com/zboris12/zgapdfsigner/releases/download/2.2.0/zgapdfsigner.min.js").getContentText());

// Load pdf, certificate
var pdfBlob = DriveApp.getFilesByName("_test.pdf").next().getBlob();
Expand All @@ -137,35 +137,41 @@ 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
* __reason__: string :point_right: (Optional) The reason for signing
* __p12cert__: Array<number>|Uint8Array|ArrayBuffer|string :point_right: Certificate's data
* __pwd__: string :point_right: The passphrase of the certificate
* __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.
* 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.
* __reason__: string :point_right: (Optional) The reason for signing
* __location__: string :point_right: (Optional) Your location
* __contact__: string :point_right: (Optional) Your contact information
* __contact__: string :point_right: (Optional) Your contact information
* __signdate__: Date|string|_TsaServiceInfo_ :point_right: (Optional)
* When it is a Date, it means the date and time for signing.
* When it is a string, it can be an url of TSA or an index of the preset TSA as below:
* 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
* "2": http://timestamp.digicert.com
* "3": http://timestamp.sectigo.com
* "4": http://timestamp.entrust.net/TSS/RFC3161sha2TS
* "5": http://timestamp.apple.com/ts01
* "6": http://www.langedge.jp/tsa
* "7": https://freetsa.org/tsr
* When it is a _TsaServiceInfo_, it means a full customized information of TSA.
* 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
* 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
* __area__: _SignAreaInfo_ :point_right: The signature's drawing area
* __area__: _SignAreaInfo_ :point_right: The signature's drawing area, these numbers are dots on 72dpi.
* __x__: number :point_right: Distance from left
* __y__: number :point_right: Distance from top
* __w__: number :point_right: Width
* __h__: number :point_right: Height
* __pageidx__: number :point_right: (Optional) The page index for drawing the signature
* __pageidx__: number :point_right: (Optional) The index of a page where the signature will be drawn.
* __imgData__: Array<number>|Uint8Array|ArrayBuffer|string :point_right: (Optional) The image's data
* __imgType__: string :point_right: (Optional) The image's type, <ins>only support jpg and png</ins>
* __text__: string :point_right: (Optional) A text drawing on signature, <ins>not implemented yet</ins>
* __text__: string :point_right: (Optional) A text drawing for the signature, <ins>not implemented yet</ins>
* __fontData__: PDFLib.StandardFonts|Array<number>|Uint8Array|ArrayBuffer|string :point_right: (Optional) The font's data for drawing text, <ins>not implemented yet</ins>

## Let's protect the pdf
Expand Down Expand Up @@ -293,8 +299,8 @@ async function signAndProtect2(pdf, cert, pwd){
* __userpwd__: string :point_right: (Optional) User password. Used when opening the pdf.
* __ownerpwd__: string :point_right: (Optional) Owner password. If not specified, a random value is used.
* __pubkeys__: Array<_PubKeyInfo_> :point_right: (Optional) Array of recipients containing public-key certificates ('c') and permissions ('p').
* __c__: string|forge_cert :point_right: (Optional) A public-key certificate.
Only if you want to encrypt the pdf by the certificate for signing, the c can be omitted.
* __c__: Array<number>|Uint8Array|ArrayBuffer|string|forge_cert :point_right: (Optional) A public-key certificate.
Only when you want to encrypt the pdf by the certificate used in signing, the c can be omitted.
* __p__: Array<string> :point_right: (Optional) Permissions

## Thanks
Expand Down
8 changes: 5 additions & 3 deletions closure/forge-ext.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,13 +118,15 @@ forge.asn1.Type.NULL;
/** @type {number} */
forge.asn1.Type.OCTETSTRING;
forge.asn1.Class = {};
/** @type {string} */
/** @type {number} */
forge.asn1.Class.UNIVERSAL;
/** @type {number} */
forge.asn1.Class.CONTEXT_SPECIFIC;
/**
* @param {string} tagClass
* @param {number} tagClass
* @param {number} type
* @param {boolean} constructed
* @param {Array<string>} value
* @param {Array<string>|string} value
* @param {Object=} options
* @return {forge.asn1}
*/
Expand Down
63 changes: 62 additions & 1 deletion closure/pdflib-ext.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ PDFLib.PDFDocument.prototype.embedPng = function(png){};
* @returns {Promise<PDFLib.PDFImage>}
*/
PDFLib.PDFDocument.prototype.embedJpg = function(jpg){};
/**
* @returns {Promise<number>}
*/
PDFLib.PDFDocument.prototype.flush = function(){};
/** @type {PDFLib.PDFCatalog} */
PDFLib.PDFDocument.prototype.catalog;
/** @type {PDFLib.PDFContext} */
Expand Down Expand Up @@ -95,6 +99,14 @@ PDFLib.PDFPageLeaf.prototype.set = function(name, object){};
PDFLib.PDFRef = function(){};
/** @type {number} */
PDFLib.PDFRef.prototype.objectNumber;
/** @type {number} */
PDFLib.PDFRef.prototype.generationNumber;
/**
* @param {number} objectNumber
* @param {number=} generationNumber
* @return {PDFLib.PDFRef}
*/
PDFLib.PDFRef.of = function(objectNumber, generationNumber){};

/** @constructor */
PDFLib.PDFContext = function(){};
Expand All @@ -108,18 +120,40 @@ PDFLib.PDFContext = function(){};
var PdfObjEntry;
/** @return {Array<PdfObjEntry>} */
PDFLib.PDFContext.prototype.enumerateIndirectObjects = function(){};
/** @type {Object<string, *>} */
/**
* @typedef
* {{
* Root: PDFLib.PDFRef,
* ID: (PDFLib.PDFArray|undefined),
* }}
*/
var PdfTrailerInfo;
/** @type {PdfTrailerInfo} */
PDFLib.PDFContext.prototype.trailerInfo;
/**
* @param {PDFLib.PDFRef} ref
* @param {PDFLib.PDFObject} object
*/
PDFLib.PDFContext.prototype.assign = function(ref, object){};
/**
* @param {PDFLib.PDFObject} object
* @return {PDFLib.PDFRef}
*/
PDFLib.PDFContext.prototype.register = function(object){};
/**
* @return {PDFLib.PDFRef}
*/
PDFLib.PDFContext.prototype.nextRef = function(){};
/**
* @param {*} literal
* @return {PDFLib.PDFObject}
*/
PDFLib.PDFContext.prototype.obj = function(literal){};
/**
* @param {PDFLib.PDFRef} ref
* @return {PDFLib.PDFObject}
*/
PDFLib.PDFContext.prototype.lookup = function(ref){};

/** @constructor */
PDFLib.PDFObject = function(){};
Expand Down Expand Up @@ -152,6 +186,11 @@ PDFLib.PDFArray = function(context){};
* @param {PDFLib.PDFObject} object
*/
PDFLib.PDFArray.prototype.push = function(object){};
/**
* @param {number} idx
* @return {PDFLib.PDFObject}
*/
PDFLib.PDFArray.prototype.get = function(idx){};

/**
* @constructor
Expand Down Expand Up @@ -251,10 +290,32 @@ var PdfDrawimgOption;
*/
PDFLib.drawImage = function(name, options){};

/**
* @constructor
*/
PDFLib.Cache = function(){};
/**
* @return {Uint8Array}
*/
PDFLib.Cache.prototype.access = function(){};
/** @type {Uint8Array} */
PDFLib.Cache.prototype.value;
/**
* @constructor
* @extends {PDFLib.PDFObject}
*/
PDFLib.PDFStream = function(){};
/**
* @constructor
* @extends {PDFLib.PDFStream}
*/
PDFLib.PDFFlateStream = function(){};
/** @type {PDFLib.Cache} */
PDFLib.PDFFlateStream.prototype.contentsCache;
/**
* @constructor
* @extends {PDFLib.PDFFlateStream}
*/
PDFLib.PDFContentStream = function(){};
/**
* @param {PDFLib.PDFObject} dict
Expand Down
36 changes: 32 additions & 4 deletions closure/zb-externs.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,16 @@ var SignAreaInfo;
*/
var SignDrawInfo;
/**
* 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.
*
* @typedef
* {{
* p12cert: (Array<number>|Uint8Array|ArrayBuffer|string),
* pwd: string,
* permission: (number|undefined),
* reason: (string|undefined),
* location: (string|undefined),
* contact: (string|undefined),
Expand All @@ -50,7 +56,7 @@ var SignOption;
/**
* @typedef
* {{
* c: (string|forge_cert|undefined),
* c: (Array<number>|Uint8Array|ArrayBuffer|string|forge_cert|undefined),
* p: (Array<string>|undefined),
* }}
*/
Expand Down Expand Up @@ -103,6 +109,17 @@ var CFType;
var RC4LastInfo;

var Zga = {};
/**
* @param {Uint8Array} uarr
* @return {string}
*/
Zga.u8arrToRaw = function(uarr){};
/**
* @param {string} raw
* @return {Uint8Array}
*/
Zga.rawToU8arr = function(raw){};

Zga.Crypto = {};
/** @enum {number} */
Zga.Crypto.Mode = {
Expand All @@ -118,8 +135,19 @@ Zga.Crypto.Mode = {
Zga.PdfCryptor = function(encopt){};
/**
* @param {PDFLib.PDFDocument|Array<number>|Uint8Array|ArrayBuffer|string} pdf
* @param {boolean=} reload
* @param {PDFLib.PDFRef=} ref
* @return {Promise<PDFLib.PDFDocument>}
*/
Zga.PdfCryptor.prototype.encryptPdf = function(pdf, reload){};

Zga.PdfCryptor.prototype.encryptPdf = function(pdf, ref){};
/**
* @constructor
* @param {SignOption} signopt
*/
Zga.PdfSigner = function(signopt){};
/**
* @public
* @param {PDFLib.PDFDocument|Array<number>|Uint8Array|ArrayBuffer|string} pdf
* @param {EncryptOption=} cypopt
* @return {Promise<Uint8Array>}
*/
Zga.PdfSigner.prototype.sign = function(pdf, cypopt){};
2 changes: 0 additions & 2 deletions forge.min.edited.js

This file was deleted.

Loading

0 comments on commit fe2fd48

Please sign in to comment.