Pular para o conteúdo principal

Printer-friendly version

A printed copy of a signed document does not carry its digital signatures. A printer-friendly version makes up for it: each page shows who signed the document and a document verification code, so that whoever receives the paper can go to your website, enter the code and get the digitally signed file back.

With PKI SDK, AlphaCode generates the verification code and PdfMarker (namespace Lacuna.Pki.Pdf) writes marks, made of text and images, on the pages of a PDF.

How it works​

  1. The first time a document is requested, generate its verification code with AlphaCode.Generate() and store it with the document. The code is unformatted: show it to users with AlphaCode.Format, for instance as XXXX-XXXX-XXXX-XXXX. See Generating alphanumeric codes.
  2. Open the signed PDF with PadesSignature.Open and read its Signers, for instance the name on each signing certificate and the signing time.
  3. Create a PdfMarker and add one PdfMark per stamp with AddMark.
  4. Set ThrowIfSignedPdf to false. By default it is true, and PdfMarker throws SignedPdfToMarkException (namespace Lacuna.Pki.Exceptions) when the PDF has any signature.
  5. Call WriteMarks(pdfContent), which returns the PDF with the marks. All signatures are removed from the file before the marks are applied, so the printer-friendly version is not a signed file.
  6. On your verification page, turn the code the user typed back into the stored form with AlphaCode.Parse, look up the document and validate its signatures as usual, with PadesSignature.Open and ValidateSignature. The sample waits two seconds before answering an unknown code, to slow down brute-force attempts.

Example​

Adapted from the sample: a summary on the bottom margin of every page, and a new page at the end of the document with the verification code.

var formattedCode = AlphaCode.Format(verificationCode);

// Names of the signers, for the summary
var signature = PadesSignature.Open(pdfContent);
var signerNames = string.Join(", ", signature.Signers.Select(s => s.Signer.SigningCertificate.SubjectName.CommonName));
var summary = $"This document was digitally signed by {signerNames}.\nTo verify the signatures go to {verificationSite} and inform the code {formattedCode}";

var pdfMarker = new PdfMarker();

// Summary on the bottom margin of every page
pdfMarker.AddMark(new PdfMark() {
PageOption = PdfMarkPageOptions.AllPages,
Container = new PadesVisualRectangle() { Height = 2, Bottom = 0, Left = 1.5, Right = 3.5 },
Elements = new List<PdfMarkElement>() {
new PdfMarkText() {
Texts = new List<PdfTextSection>() {
new PdfTextSection() { Style = PdfTextStyle.Normal, Text = summary }
}
}
}
});

// New page at the end of the document, with 1-inch margins
var summaryPage = new PdfMark() {
PageOption = PdfMarkPageOptions.NewPage,
Container = new PadesVisualRectangle() { Top = 2.54, Bottom = 2.54, Left = 2.54, Right = 2.54 }
};
summaryPage.Elements.Add(new PdfMarkText() {
RelativeContainer = new PadesVisualRectangle() { Top = 0, Height = 2, Left = 0, Right = 0 },
Align = PadesHorizontalAlign.Center,
Texts = new List<PdfTextSection>() {
new PdfTextSection() { Style = PdfTextStyle.Normal, FontSize = 14, Text = $"Verification code: {formattedCode}" }
}
});
pdfMarker.AddMark(summaryPage);

pdfMarker.ThrowIfSignedPdf = false; // the PDF is signed: don't throw SignedPdfToMarkException
var pfvContent = pdfMarker.WriteMarks(pdfContent);

The sample's summary page also has the ICP-Brasil logo, a QR code of the verification link (a PNG made with the QRCoder library), and one line per signer with a green or red icon for the result of ValidateSignature. It also puts the summary on the right margin of every page, rotated with PdfMarkRotation.D90 so that the text reads upward.

Marks​

A PdfMark is a stamp applied on the pages of the PDF:

PropertyWhat it sets
PageOptionThe pages that get the mark, a PdfMarkPageOptions: AllPages; NewPage, a new page added at the end of the document; or SinglePage and SinglePageFromEnd, together with the PageOptionNumber property.
ContainerThe position of the mark on the page, a PadesVisualRectangle: the class that positions signature visual representations, explained in Defining a container.
MeasurementUnitsPadesMeasurementUnits.Centimeters or PadesMeasurementUnits.PdfPoints. The sample does not set it, and its measures are in centimeters (2.54 for 1-inch margins).
ElementsThe text and images of the mark: a list of PdfMarkText and PdfMarkImage.
BorderWidth, BorderColor, BackgroundColorThe border and the background of the mark.

Every element has a RelativeContainer, its position inside the mark's Container; a Rotation, D0, D90, D180 or D270 (in degrees); and an Opacity, 100 (opaque) by default. Then:

  • PdfMarkText has Texts, a list of PdfTextSection, each with its Text, Style (Normal, Bold, Italic or Underline, which can be combined), FontSize and Color (black by default); and Align, VerticalAlign and LineSpacing, a multiplier of the font size.
  • PdfMarkImage has ImageContent, the bytes of the image; the sample uses PNG images.

More options​

  • PreserveSignaturesVisualRepresentation: keep the images of the signatures' visual representations on the PDF, when present.
  • NewPagesTemplate: a PdfPageTemplate with the size and orientation of the pages that NewPage marks create. If not set, the most common paper size of the PDF's pages is used, in portrait.
  • CreateContext and ComputeRequiredArea: compute the area that a text needs before you place it, then call WriteMarks(context).
  • WriteMarks(Stream, Stream) reads the PDF from a stream and writes the result to another.

CAdES signatures​

A CMS/CAdES file is not a PDF, so the CAdES sample builds the printer-friendly version as a new document: it reads the signers with CadesSignature.Open and writes the summary mark on a blank PDF that ships with the sample.

See also​

View as Markdown