A javascript tool to sign a pdf in web browser, google apps script and nodejs.
 
 
 
 
Go to file
zboris12 fe2fd4840d Added support to DocMDP and improved the way of appending TSA timestamp. 2022-10-14 21:12:43 +09:00
closure Added support to DocMDP and improved the way of appending TSA timestamp. 2022-10-14 21:12:43 +09:00
.gitignore The first commit. 2022-09-17 21:55:09 +09:00
LICENSE Initial commit 2022-09-17 21:48:13 +09:00
README.md Added support to DocMDP and improved the way of appending TSA timestamp. 2022-10-14 21:12:43 +09:00
test.html Added support to DocMDP and improved the way of appending TSA timestamp. 2022-10-14 21:12:43 +09:00
zgapdfcryptor.js Added support to DocMDP and improved the way of appending TSA timestamp. 2022-10-14 21:12:43 +09:00
zgapdfsigner.js Added support to DocMDP and improved the way of appending TSA timestamp. 2022-10-14 21:12:43 +09:00

README.md

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.

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.

Main features

  • 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
    • 128bit RC4 Encryption
    • 128bit AES Encryption
    • 256bit AES Encryption
  • Set public-key certificate protection to a pdf. Supported algorithms are as same as the password protection.

About signing with TSA

Because of the CORS security restrictions in web browser, signing with a timestamp from TSA can only be used in Google Apps Script.

The Dependencies

How to use this tool

Just import the dependencies and this tool.

<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

Sign with an invisible signature.

/**
 * @param {ArrayBuffer} pdf
 * @param {ArrayBuffer} cert
 * @param {string} pwd
 * @return {Promise<Blob>}
 */
async function sign1(pdf, cert, pwd){
  /** @type {SignOption} */
  var sopt = {
    p12cert: cert,
    pwd: pwd,
    permission: 1,
  };
  var signer = new Zga.PdfSigner(sopt);
  var u8arr = await signer.sign(pdf);
  return new Blob([u8arr], {"type" : "application/pdf"});
}

Sign with a visible signature of an image.

/**
 * @param {ArrayBuffer} pdf
 * @param {ArrayBuffer} cert
 * @param {string} pwd
 * @param {ArrayBuffer} imgdat
 * @param {string} imgtyp
 * @return {Promise<Blob>}
 */
async function sign2(pdf, cert, pwd, imgdat, imgtyp){
  /** @type {SignOption} */
  var sopt = {
    p12cert: cert,
    pwd: pwd,
    drawinf: {
      area: {
        x: 25,  // left
        y: 150, // top
        w: 60,  // width
        h: 60,  // height
      },
      imgData: imgdat,
      imgType: imgtyp,
    },
  };
  var signer = new Zga.PdfSigner(sopt);
  var u8arr = await signer.sign(pdf);
  return new Blob([u8arr], {"type" : "application/pdf"});
}

Sign with a visible signature of drawing a text.

//TODO

Use it in Google Apps Script

// 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.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

  • p12cert: Array|Uint8Array|ArrayBuffer|string 👉 Certificate's data
  • pwd: string 👉 The passphrase of the certificate
  • permission: number 👉 (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 👉 (Optional) The reason for signing
  • location: string 👉 (Optional) Your location
  • contact: string 👉 (Optional) Your contact information
  • signdate: Date|string|TsaServiceInfo 👉 (Optional)
  • signame: string 👉 (Optional) The name of the signature
  • drawinf: SignDrawInfo 👉 (Optional) Visible signature's information
    • area: SignAreaInfo 👉 The signature's drawing area, these numbers are dots on 72dpi.
      • x: number 👉 Distance from left
      • y: number 👉 Distance from top
      • w: number 👉 Width
      • h: number 👉 Height
    • pageidx: number 👉 (Optional) The index of a page where the signature will be drawn.
    • imgData: Array|Uint8Array|ArrayBuffer|string 👉 (Optional) The image's data
    • imgType: string 👉 (Optional) The image's type, only support jpg and png
    • text: string 👉 (Optional) A text drawing for the signature, not implemented yet
    • fontData: PDFLib.StandardFonts|Array|Uint8Array|ArrayBuffer|string 👉 (Optional) The font's data for drawing text, not implemented yet

Let's protect the pdf

Set password protection to the pdf.

/**
 * @param {ArrayBuffer} pdf
 * @param {string} upwd
 * @param {string} opwd
 * @return {Promise<Blob>}
 */
async function protect1(pdf, upwd, opwd){
  /** @type {EncryptOption} */
  var eopt = {
    mode: Zga.Crypto.Mode.RC4_40,
    permissions: ["modify", "annot-forms", "fill-forms", "extract", "assemble"],
    userpwd: upwd,
    ownerpwd: opwd,
  };
  var cyptor = new Zga.PdfCryptor(eopt);
  var pdfdoc = await cyptor.encryptPdf(pdf);
  u8arr = await pdfdoc.save({"useObjectStreams": false});
  return new Blob([u8arr], {"type" : "application/pdf"});
}

Set public-key certificate protection to the pdf.

/**
 * @param {ArrayBuffer} pdf
 * @param {ArrayBuffer} cert
 * @return {Promise<Blob>}
 */
async function protect2(pdf, cert){
  /** @type {EncryptOption} */
  var eopt = {
    mode: Zga.Crypto.Mode.AES_128,
    pubkeys: [{
      c: Zga.u8arrToRaw(new Uint8Array(cert)),
      p: ["copy", "modify", "copy-extract", "annot-forms", "fill-forms", "extract", "assemble"],
    }],
  };
  var cyptor = new Zga.PdfCryptor(eopt);
  var pdfdoc = await cyptor.encryptPdf(pdf);
  u8arr = await pdfdoc.save({"useObjectStreams": false});
  return new Blob([u8arr], {"type" : "application/pdf"});
}

Sign and set protection.

/**
 * @param {ArrayBuffer} pdf
 * @param {ArrayBuffer} cert
 * @param {string} pwd
 * @param {string} opwd
 * @return {Promise<Blob>}
 */
async function signAndProtect1(pdf, cert, pwd, opwd){
  /** @type {SignOption} */
  var sopt = {
    p12cert: cert,
    pwd: pwd,
  };
  /** @type {EncryptOption} */
  var eopt = {
    mode: Zga.Crypto.Mode.RC4_128,
    permissions: ["modify", "annot-forms", "fill-forms", "extract", "assemble"],
    ownerpwd: opwd,
  };
  var signer = new Zga.PdfSigner(sopt);
  var u8arr = await signer.sign(pdf, eopt);
  return new Blob([u8arr], {"type" : "application/pdf"});
}

Sign and set protection by the same certificate.

/**
 * @param {ArrayBuffer} pdf
 * @param {ArrayBuffer} cert
 * @param {string} pwd
 * @return {Promise<Blob>}
 */
async function signAndProtect2(pdf, cert, pwd){
  /** @type {SignOption} */
  var sopt = {
    p12cert: cert,
    pwd: pwd,
  };
  /** @type {EncryptOption} */
  var eopt = {
    mode: Zga.Crypto.Mode.AES_256,
    permissions: ["modify", "annot-forms", "fill-forms", "extract", "assemble"],
    pubkeys: [],
  };
  var signer = new Zga.PdfSigner(sopt);
  var u8arr = await signer.sign(pdf, eopt);
  return new Blob([u8arr], {"type" : "application/pdf"});
}

Detail of EncryptOption

  • mode: Zga.Crypto.Mode 👉 The values of Zga.Crypto.Mode
    • RC4_40: 40bit RC4 Encryption
    • RC4_128: 128bit RC4 Encryption
    • AES_128: 128bit AES Encryption
    • AES_256: 256bit AES Encryption
  • permissions: Array 👉 (Optional) The set of permissions to be blocked
    • "copy": (Only valid on public-key mode) Copy text and graphics from the document;
    • "print": Print the document;
    • "modify": Modify the contents of the document by operations other than those controlled by 'fill-forms', 'extract' and 'assemble';
    • "copy-extract": Copy or otherwise extract text and graphics from the document;
    • "annot-forms": Add or modify text annotations, fill in interactive form fields, and, if 'modify' is also set, create or modify interactive form fields (including signature fields);
    • "fill-forms": Fill in existing interactive form fields (including signature fields), even if 'annot-forms' is not specified;
    • "extract": Extract text and graphics (in support of accessibility to users with disabilities or for other purposes);
    • "assemble": Assemble the document (insert, rotate, or delete pages and create bookmarks or thumbnail images), even if 'modify' is not set;
    • "print-high": Print the document to a representation from which a faithful digital copy of the PDF content could be generated. When this is not set, printing is limited to a low-level representation of the appearance, possibly of degraded quality.
  • userpwd: string 👉 (Optional) User password. Used when opening the pdf.
  • ownerpwd: string 👉 (Optional) Owner password. If not specified, a random value is used.
  • pubkeys: Array<PubKeyInfo> 👉 (Optional) Array of recipients containing public-key certificates ('c') and permissions ('p').
    • c: Array|Uint8Array|ArrayBuffer|string|forge_cert 👉 (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 👉 (Optional) Permissions

Thanks

  • The module of setting protection was almost migrated from TCPDF.

License

This tool is available under the MIT license.