Pular para o conteúdo principal

Versão para impressão

A cópia impressa de um documento assinado não leva as assinaturas digitais. A versão para impressão compensa isso: cada página mostra quem assinou o documento e um código de verificação, para que quem receber o papel possa visitar o seu site, informar o código e obter de volta o arquivo assinado digitalmente.

No PKI SDK, a classe AlphaCode gera o código de verificação e o PdfMarker (namespace Lacuna.Pki.Pdf) grava marcas, compostas de textos e imagens, nas páginas de um PDF.

Como funciona​

  1. Na primeira vez em que um documento for solicitado, gere o código de verificação dele com AlphaCode.Generate() e guarde-o junto com o documento. O código é gerado sem formatação: mostre-o aos usuários com AlphaCode.Format, por exemplo como XXXX-XXXX-XXXX-XXXX. Veja Gerando códigos alfanuméricos.
  2. Abra o PDF assinado com PadesSignature.Open e leia os signatários em Signers, por exemplo o nome no certificado de cada um e a data e hora da assinatura.
  3. Crie um PdfMarker e adicione um PdfMark por marca com AddMark.
  4. Defina ThrowIfSignedPdf como false. O padrão é true, e com ele o PdfMarker lança SignedPdfToMarkException (namespace Lacuna.Pki.Exceptions) se o PDF tiver qualquer assinatura.
  5. Chame WriteMarks(pdfContent), que retorna o PDF com as marcas. Todas as assinaturas são removidas do arquivo antes de as marcas serem aplicadas, então a versão para impressão não é um arquivo assinado.
  6. Na sua página de verificação, converta o código digitado pelo usuário para a forma guardada com AlphaCode.Parse, localize o documento e valide as assinaturas como de costume, com PadesSignature.Open e ValidateSignature. O exemplo espera dois segundos antes de responder a um código desconhecido, para dificultar ataques de força bruta.

Exemplo​

Adaptado do exemplo: um resumo na margem inferior de todas as páginas e uma nova página no fim do documento com o código de verificação.

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);

A página de resumo do exemplo traz também o logotipo da ICP-Brasil, um QR code com o link de verificação (uma imagem PNG gerada com a biblioteca QRCoder) e uma linha por signatário, com um ícone verde ou vermelho conforme o resultado de ValidateSignature. O exemplo também põe o resumo na margem direita de todas as páginas, girado com PdfMarkRotation.D90 para que o texto fique na vertical, de baixo para cima.

Marcas​

Um PdfMark é uma marca aplicada nas páginas do PDF:

PropriedadeO que define
PageOptionAs páginas que recebem a marca, um PdfMarkPageOptions: AllPages; NewPage, uma nova página acrescentada no fim do documento; ou SinglePage e SinglePageFromEnd, junto com a propriedade PageOptionNumber.
ContainerA posição da marca na página, um PadesVisualRectangle: a classe que posiciona a representação visual das assinaturas, explicada em Definindo um container.
MeasurementUnitsPadesMeasurementUnits.Centimeters ou PadesMeasurementUnits.PdfPoints. O exemplo não a define, e as medidas dele estão em centímetros (2,54 para margens de 1 polegada).
ElementsOs textos e as imagens da marca: uma lista de PdfMarkText e PdfMarkImage.
BorderWidth, BorderColor, BackgroundColorA borda e o fundo da marca.

Todo elemento tem um RelativeContainer, a posição dele dentro do Container da marca; uma Rotation, D0, D90, D180 ou D270 (em graus); e uma Opacity, 100 (opaco) por padrão. Além disso:

  • O PdfMarkText tem Texts, uma lista de PdfTextSection, cada um com seu Text, Style (Normal, Bold, Italic ou Underline, que podem ser combinados), FontSize e Color (preto por padrão); e Align, VerticalAlign e LineSpacing, um multiplicador do tamanho da fonte.
  • O PdfMarkImage tem ImageContent, os bytes da imagem; o exemplo usa imagens PNG.

Outras opções​

  • PreserveSignaturesVisualRepresentation: mantém no PDF as imagens da representação visual das assinaturas, se houver.
  • NewPagesTemplate: um PdfPageTemplate com o tamanho e a orientação das páginas criadas pelas marcas NewPage. Se não for definido, é usado o tamanho de papel mais comum entre as páginas do PDF, em retrato.
  • CreateContext e ComputeRequiredArea: calculam a área de que um texto precisa antes de você posicioná-lo; depois, chame WriteMarks(context).
  • WriteMarks(Stream, Stream) lê o PDF de um stream e grava o resultado em outro.

Assinaturas CAdES​

Um arquivo CMS/CAdES não é um PDF, então o exemplo de CAdES monta a versão para impressão como um documento novo: ele lê os signatários com CadesSignature.Open e grava a marca de resumo num PDF em branco que acompanha o exemplo.

Veja também​

Ver em Markdown