Printer-friendly version - PKI Express
A printed copy of a signed PDF loses its digital signatures. A printer-friendly version tells the reader who signed the document and how to
get the signed file back: PKI Express validates the signatures, marks every page with the signers' names and a verification code,
and adds a manifest page with a QR code. Your site then looks the document up by that code.
PKI Express added PDF editing (the edit-pdf command) in 1.3.0, "enabling generation of printer-friendly versions".
The code is Node.js, from the PKI Suite samples, with pki-express (1.16.2); the
Python sample does the same.
Flow
- Generate a verification code for the signed PDF and store it with the file.
- Open and validate the PDF's signatures with
PadesSignatureExplorer. - Build the marks with
PdfHelper: the signers on every page, and a manifest on a new page. - Call
PdfMarker.apply(), which runs PKI Express'sedit-pdfcommand and writes the marked PDF. - When someone types the code on your site, look the file up and show or download it.
1. The verification code
The sample generates a 16-character code from cryptographically strong random bytes and stores the code and the file id in the web session, both ways. Store them in your database instead.
let verificationCode = StorageMock.getVerificationCode(req.session, fileId);
if (!verificationCode) {
verificationCode = Util.generateVerificationCode();
StorageMock.setVerificationCode(req.session, fileId, verificationCode);
}
The code protects access to your documents: the Java library has an AlphaCode class to generate it, described in
Generating alphanumeric codes.
2. Open the signatures
const sigExplorer = new PadesSignatureExplorer();
Util.setPkiDefaults(sigExplorer);
sigExplorer.validate = true;
sigExplorer.setSignatureFileFromPathSync(pdfPath);
sigExplorer.open().then((signature) => { /* build the marks from signature.signers */ });
Each signer has its certificate, signingTime and validationResults; see Validate signatures.
3. Mark every page
PdfHelper is a fluent API that builds marks, containers and elements (text, image, QR code); PdfMarker holds the list of marks. The
sample puts the ICP-Brasil logo in the bottom-right corner and the message along the bottom and the right edge of every page:
const pdfMarker = new PdfMarker();
Util.setPkiDefaults(pdfMarker);
pdfMarker.setFileFromPathSync(pdfPath);
const pdf = new PdfHelper();
pdfMarker.marks.push(
pdf.mark()
.onAllPages()
.onContainer(pdf.container().width(1).anchorRight(1).height(1)
.anchorBottom(1))
.addElement(
pdf.imageElement()
.withOpacity(75)
.withImageContent(Util.getIcpBrasilLogoContent(), 'image/png'),
),
);
pdfMarker.marks.push(
pdf.mark()
.onAllPages()
.onContainer(pdf.container().height(2).anchorBottom().varWidth()
.margins(1.5, 3.5))
.addElement(pdf.textElement().withOpacity(75).addSectionFromText(allPagesMessage)),
);
allPagesMessagenames the signers and gives the verification site and the code.- Sizes and positions are in centimeters by default (
measurementUnits). - A text element can be rotated with
rotate90CounterClockwise(), as the sample does for the right edge.
4. Add the manifest page
A mark with onNewPage() adds a page. The sample fills it from the top down: the logo, a QR code with the verification link, the title, the
code, one line per signer with a valid or invalid icon, and the verification link.
const manifestMark = pdf.mark()
.onNewPage()
.onContainer(pdf.container().varWidthAndHeight().margins(2.54, 2.54));
manifestMark.addElement(
pdf.qrCodeElement()
.onContainer(pdf.container().height(elementHeight).anchorTop(verticalOffset)
.width(elementHeight)
.anchorRight())
.withQRCodeData(verificationLink),
);
// ... more elements
pdfMarker.marks.push(manifestMark);
outputFile = `${uuidv4()}.pdf`;
pdfMarker.outputFile = path.join(APP_ROOT, 'app-data', outputFile);
return pdfMarker.apply();
apply()writes the marked PDF tooutputFile, or over the original withoverwriteOriginalFile.- TODO(docs): whether the marked copy keeps the original signatures (PKI SDK's
PdfMarkerremoves them; see the PKI SDK page).
5. Check a code
The verification page parses the code, finds the file and opens its signatures again to show them:
const verificationCode = Util.parseVerificationCode(req.query.code);
const fileId = StorageMock.lookupVerificationCode(req.session, verificationCode);
const sigExplorer = new PadesSignatureExplorer();
Util.setPkiDefaults(sigExplorer);
sigExplorer.setSignatureFileFromPathSync(StorageMock.getDataPath(fileId));
sigExplorer.validate = true;
sigExplorer.open().then((signature) => { /* render the signers and a link to the file */ });
For a CAdES file
For a .p7s, the Node.js sample opens it with CadesSignatureExplorer and marks an empty PDF that ships with the sample, so the result is a
manifest that lists the signers, with the verification code and a QR code.
In each language
| Library | PdfMarker | PdfHelper (fluent API) |
|---|---|---|
| Node.js | yes; setFileFromPath, outputFile, marks, apply() | yes |
| Java | yes | yes |
| PHP | yes; setFile, setOutputFile, apply() | no: build PdfMark objects |
| Python | yes; set_file_from_path, output_file, marks, apply() | yes |
| Ruby | no | no |