diff --git a/README.md b/README.md index ec1b16b..a370f8e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # 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. +And it is more powerful when used in [Google Apps Script](https://developers.google.com/apps-script) or [nodejs](https://nodejs.org/). 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,10 +11,11 @@ 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 and set [DocMDP](#note). * Add a new signature to a pdf if it has been signed already. (An incremental update) -* 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) +* Add a document timestamp from [TSA](#note). (__Not__ available in web browser) +* Sign a pdf with a timestamp from [TSA](#note). ((__Not__ available in web browser) +* Enable signature's [LTV](#note). (__Not__ available in web browser) * Set password protection to a pdf. Supported algorithms: * 40bit RC4 Encryption * 128bit RC4 Encryption @@ -23,10 +24,10 @@ And I use this name to hope the merits from this application will be dedicated t * Set public-key certificate protection to a pdf. Supported algorithms are as same as the password protection. -## About signing with TSA +## About signing with [TSA](#note) and [LTV](#note) -Because of the CORS security restrictions in web browser, -signing with a timestamp from TSA can only be used in Google Apps Script. +Because of the [CORS](#note) security restrictions in web browser, +signing with a timestamp from [TSA](#note) or enabling [LTV](#note) can only be used in [Google Apps Script](https://developers.google.com/apps-script) or [nodejs](https://nodejs.org/). ## The Dependencies @@ -35,11 +36,47 @@ signing with a timestamp from TSA can only be used in Google Apps Script. ## How to use this tool +### Web Browser Just import the dependencies and this tool. ```html - + +``` + +### [Google Apps Script](https://developers.google.com/apps-script) +Load the dependencies and this tool. +```js +// Simulate setTimeout function for pdf-lib +function setTimeout(func, sleep){ + Utilities.sleep(sleep); + func(); +} +// Simulate window for node-forge +var window = globalThis; +// Load pdf-lib +eval(UrlFetchApp.fetch("https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js").getContentText()); +// Load node-forge +eval(UrlFetchApp.fetch("https://unpkg.com/node-forge@1.3.1/dist/forge.min.js").getContentText()); +// Load ZgaPdfSigner +eval(UrlFetchApp.fetch("https://github.com/zboris12/zgapdfsigner/releases/download/2.5.0/zgapdfsigner.min.js").getContentText()); +``` +Or simply import the library of [ZgaPdfToolkit](https://script.google.com/macros/library/d/1T0UPf50gGp2fJ4dR1rZfEFgKYC5VpCwUVooCRNySiL7klvIUVsFBCZ9m/5) +1. Add the library of ZgaPdfToolkit to your project, and suppose the id of library you defined is "pdfkit". + Script id: `1T0UPf50gGp2fJ4dR1rZfEFgKYC5VpCwUVooCRNySiL7klvIUVsFBCZ9m` +2. Load the library. +```js +pdfkit.loadZga(globalThis); +``` + +### [nodejs](https://nodejs.org/) +1. Install +``` +npm install zgapdfsigner +``` +2. Import +```js +const Zga = require("zgapdfsigner"); ``` ## Let's sign @@ -105,38 +142,123 @@ Sign with a visible signature of drawing a text. //TODO ``` -Use it in Google Apps Script +Use it in [Google Apps Script](https://developers.google.com/apps-script) ```js -// Simulate setTimeout function for pdf-lib -function setTimeout(func, sleep){ - Utilities.sleep(sleep); - func(); +/** + * @param {string} pwd Passphrase of certificate + * @return {Promise} + */ +async function createPdf(pwd){ + // Load pdf, certificate + var pdfBlob = DriveApp.getFilesByName("_test.pdf").next().getBlob(); + var certBlob = DriveApp.getFilesByName("_test.pfx").next().getBlob(); + // Sign the pdf + /** @type {SignOption} */ + var sopt = { + p12cert: certBlob.getBytes(), + pwd, + signdate: "1", + ltv: 1, + }; + var signer = new Zga.PdfSigner(sopt); + var u8arr = await signer.sign(pdfBlob.getBytes()); + // Save the result pdf to some folder + var fld = DriveApp.getFolderById("a folder's id"); + fld.createFile(Utilities.newBlob(u8arr, "application/pdf").setName("signed_test.pdf")); +} +``` + +Use queryPassword function in [ZgaPdfToolkit](https://script.google.com/macros/library/d/1T0UPf50gGp2fJ4dR1rZfEFgKYC5VpCwUVooCRNySiL7klvIUVsFBCZ9m/5). + +```js +function myfunction(){ + var spd = SpreadsheetApp.getActiveSpreadsheet(); + pdfkit.queryPassword("createPdf", "Please input the passphrase", spd.getName()); +} +``` + +Use it in [nodejs](https://nodejs.org/) +```js +const m_fs = require("fs"); +const m_path = require("path"); +async function main(){ + /** @type {string} */ + var pdfPath = m_path.join(__dirname, "_test.pdf"); + /** @type {string} */ + var pfxPath = m_path.join(__dirname, "_test.pfx"); + /** @type {string} */ + var ps = ""; + /** @type {string} */ + var imgPath = m_path.join(__dirname, "_test.png"); + + if(process.argv.length > 3){ + pfxPath = process.argv[2]; + ps = process.argv[3]; + }else if(process.argv[2]){ + ps = process.argv[2]; + } + + if(!ps){ + // throw new Error("The passphrase is not specified."); + 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); + } + + /** @type {SignOption} */ + var 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, + }, + imgData: img, + imgType: imgType, + }; + } + + /** @type {Zga.PdfSigner} */ + var ser = new Zga.PdfSigner(sopt); + /** @type {Uint8Array} */ + var u8dat = await ser.sign(pdf); + + if(u8dat){ + /** @type {string} */ + var outPath = m_path.join(__dirname, "test_signed.pdf"); + m_fs.writeFileSync(outPath, u8dat); + console.log("Output file: " + outPath); + } + + console.log("Done"); } -// Simulate window for node-forge -var window = globalThis; -// Load pdf-lib -eval(UrlFetchApp.fetch("https://unpkg.com/pdf-lib@1.17.1/dist/pdf-lib.min.js").getContentText()); -// Load node-forge -eval(UrlFetchApp.fetch("https://unpkg.com/node-forge@1.3.1/dist/forge.min.js").getContentText()); -// Load ZgaPdfSigner -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(); -var certBlob = DriveApp.getFilesByName("_test.pfx").next().getBlob(); -// Sign the pdf -/** @type {SignOption} */ -var sopt = { - p12cert: certBlob.getBytes(), - pwd: "some passphrase", - signdate: "1", -}; -var signer = new Zga.PdfSigner(sopt); -var u8arr = await signer.sign(pdfBlob.getBytes()); -// Save the result pdf to some folder -var fld = DriveApp.getFolderById("a folder's id"); -fld.createFile(Utilities.newBlob(u8arr, "application/pdf").setName("signed_test.pdf")); ``` ## Detail of SignOption @@ -144,7 +266,7 @@ fld.createFile(Utilities.newBlob(u8arr, "application/pdf").setName("signed_test. * __p12cert__: Array|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: + This is a setting of [DocMDP](#note). 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. @@ -153,7 +275,7 @@ fld.createFile(Utilities.newBlob(u8arr, "application/pdf").setName("signed_test. * __contact__: string :point_right: (Optional) Your contact information * __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: + * When it is a string, it can be an url of [TSA](#note) or an index of the preset [TSA](#note)s as below: * "1": http://ts.ssl.com * "2": http://timestamp.digicert.com * "3": http://timestamp.sectigo.com @@ -161,12 +283,15 @@ fld.createFile(Utilities.newBlob(u8arr, "application/pdf").setName("signed_test. * "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 a TSA. - * __url__: string :point_right: The url of TSA + * When it is a _TsaServiceInfo_, it means a full customized information of a [TSA](#note). + * __url__: string :point_right: The url of [TSA](#note) * __len__: number :point_right: (Optional) The length of signature's placeholder - * __headers__: Object :point_right: (Optional) The customized headers for sending to tsa server + * __headers__: Object :point_right: (Optional) The customized headers for sending to [TSA](#note) server * When it is omitted, the system timestamp will be used. * __signame__: string :point_right: (Optional) The name of the signature +* __ltv__: number :point_right: (Optional) Type of [LTV](#note). Valid values are: + * 1: auto; Try using [OCSP](#note) only to enable the [LTV](#note) first; If can't, try using [CRL](#note) to enable the [LTV](#note). + * 2: crl only; Only try using [CRL](#note) to enable the [LTV](#note). * __drawinf__: _SignDrawInfo_ :point_right: (Optional) Visible signature's information * __area__: _SignAreaInfo_ :point_right: The signature's drawing area, these numbers are dots on 72dpi. * __x__: number :point_right: Distance from left @@ -315,3 +440,11 @@ async function signAndProtect2(pdf, cert, pwd){ This tool is available under the [MIT license](https://opensource.org/licenses/MIT). + +## Note +* __CORS__: Cross-Origin Resource Sharing +* __CRL__: Certificate Revocation Lists +* __DocMDP__: Document Modification Detection and Prevention +* __LTV__: Long-Term Validation +* __OCSP__: Online Certificate Status Protocol +* __TSA__: Time Stamp Authority diff --git a/package.json b/package.json index a610319..2768936 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "zgapdfsigner", - "version": "2.3.0", + "version": "2.5.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", @@ -24,7 +24,8 @@ "pdf-encryption", "google-apps-script", "LTV", - "TSA" + "TSA", + "長期署名" ], "scripts": { "build": "node closure.js",