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
- 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 comAlphaCode.Format, por exemplo comoXXXX-XXXX-XXXX-XXXX. Veja Gerando códigos alfanuméricos. - Abra o PDF assinado com
PadesSignature.Opene leia os signatários emSigners, por exemplo o nome no certificado de cada um e a data e hora da assinatura. - Crie um
PdfMarkere adicione umPdfMarkpor marca comAddMark. - Defina
ThrowIfSignedPdfcomofalse. O padrão étrue, e com ele oPdfMarkerlançaSignedPdfToMarkException(namespaceLacuna.Pki.Exceptions) se o PDF tiver qualquer assinatura. - 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. - 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, comPadesSignature.OpeneValidateSignature. 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:
| Propriedade | O que define |
|---|---|
PageOption | As 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. |
Container | A posição da marca na página, um PadesVisualRectangle: a classe que posiciona a representação visual das assinaturas, explicada em Definindo um container. |
MeasurementUnits | PadesMeasurementUnits.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). |
Elements | Os textos e as imagens da marca: uma lista de PdfMarkText e PdfMarkImage. |
BorderWidth, BorderColor, BackgroundColor | A 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
PdfMarkTexttemTexts, uma lista dePdfTextSection, cada um com seuText,Style(Normal,Bold,ItalicouUnderline, que podem ser combinados),FontSizeeColor(preto por padrão); eAlign,VerticalAligneLineSpacing, um multiplicador do tamanho da fonte. - O
PdfMarkImagetemImageContent, 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: umPdfPageTemplatecom o tamanho e a orientação das páginas criadas pelas marcasNewPage. Se não for definido, é usado o tamanho de papel mais comum entre as páginas do PDF, em retrato.CreateContexteComputeRequiredArea: calculam a área de que um texto precisa antes de você posicioná-lo; depois, chameWriteMarks(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.