Pular para o conteúdo principal

Referência rápida do PKI SDK

O essencial do PKI SDK em uma página, com links para os artigos completos.

Para que serve​

O PKI SDK é uma biblioteca .NET para operações de infraestrutura de chaves públicas: decodificar e validar certificados X.509 e criar e validar assinaturas CMS/CAdES, PDF/PAdES e XmlDSig/XAdES. Ele roda dentro da sua aplicação .NET, no .NET Framework ou, pelo .NET Standard 2.0, no .NET Core (inclusive em servidores Linux); nas assinaturas web, o Web PKI faz a parte do navegador e o PKI SDK a parte do servidor.

AlternativaQuando é a melhor escolha
PKI ExpressSeu backend não é .NET: o PKI Express cria assinaturas CAdES, PAdES e XAdES a partir de praticamente qualquer linguagem, hospedado nos seus próprios servidores Windows ou Linux.
Rest PKI CoreVocê prefere uma API REST a uma biblioteca: use-o como serviço em core.pki.rest ou numa instância própria, e ele hospeda a página em que os usuários assinam.
Web PKIVocê precisa da parte do navegador: o Web PKI lista os certificados do usuário e assina com eles; ele complementa o PKI SDK nas assinaturas web remotas.
SignerVocê quer um gerenciador de documentos e assinaturas pronto, como SaaS ou on-premises, em vez de construir o fluxo de assinatura por conta própria.

Instalação​

LinguagemPacoteMajor atualRegistro
C# / .NET (NuGet)Lacuna.Pki2 (mais recente: 2.22.7)https://www.nuget.org/packages/Lacuna.Pki
C# / .NET, certificados em nuvem (opcional)Lacuna.Pki.BrazilTrustServices1 (mais recente: 1.7.4)https://www.nuget.org/packages/Lacuna.Pki.BrazilTrustServices

Adicione o Lacuna.Pki pelo NuGet Package Manager (pacote Lacuna PKI SDK); a versão 2.22.7 tem como alvo o .NET Standard 2.0 e o .NET Framework 4.5, 4.6 e 4.7 (Suporte a .NET Standard / .NET Core). Pacotes opcionais trazem integrações de armazenamento, de log e de armazenamento de nonces: Lacuna.Pki.AzureConnector, Lacuna.Pki.AmazonConnector, Lacuna.Pki.EntityFrameworkConnector e Lacuna.Pki.NLogConnector; veja Pacotes nuget opcionais e o Histórico de versões.

Configuração e autenticação​

O PKI SDK roda no processo da sua aplicação e é habilitado por uma licença, um arquivo LacunaPKILicense.config ou uma string Base64, carregada na inicialização da aplicação. Carregue-a de uma destas três formas (Licenciamento):

  • Aplicações web (recomendado): copie o LacunaPKILicense.config para junto do Lacuna.Pki.dll, por exemplo com Build Action definido como Content e Copy to Output Directory como Copy Always.
  • Web.config ou App.config: a chave de appSettings LacunaPKI.LicensePath (caminho do arquivo) ou LacunaPKI.BinaryLicense (licença em Base64).
  • Aplicações desktop (recomendado): PkiConfig.LoadLicense(byte[]) com a licença Base64 decodificada; PkiConfig.LoadLicense(string) recebe o caminho do arquivo.
byte[] binLicense = Convert.FromBase64String("[given Base64 License]");
PkiConfig.LoadLicense(binLicense);

As demais configurações são propriedades da classe PkiConfig, como CrlDownloadTimeout, TimestampRequestTimeout, TempPath, PreemptiveCrlDownload e WebProxy (Configurações). Toda validação recebe um ITrustArbitrator, que decide em quais raízes confiar: TrustArbitrators.PkiBrazil, TrustArbitrators.Windows, TrustedRoots ou vários deles combinados num LinkedTrustArbitrator. Não há URL base a configurar: o SDK baixa online certificados de AC, LCRs (CRLs) e respostas OCSP ao montar e validar cadeias, e políticas com carimbo de tempo precisam de um TimestampRequester criado com a URI da sua carimbadora (autoridade de carimbo de tempo).

Operações principais​

Os exemplos são em C#, reduzidos a partir da documentação e do projeto ASP.NET MVC do PkiSuiteSamples, em que Util.GetTrustArbitrator() retorna o árbitro de confiança (trust arbitrator) usado (veja Certificados e credenciais de teste).

Decodificar e validar um certificado​

Carregue um certificado com encoding BER, DER, Base64 ou PEM e valide-o segundo as raízes em que você confia: PKCertificate.Decode e PKCertificate.Validate. Veja Carregando certificados e Validando certificados.

var cert = PKCertificate.Decode(pem);
var vr = cert.Validate(TrustArbitrators.PkiBrazil);
if (vr.IsValid) {
// the certificate is valid
}
Console.WriteLine(vr);

Assinar um PDF com o Web PKI​

A chave do usuário fica no navegador: o servidor prepara os bytes a assinar com PadesSigner.GetToSignBytes, o Web PKI assina o hash desses bytes (readCertificate e depois signHash) e o servidor conclui com PadesSigner.SetPreComputedSignature, guardando os dados de transferência (transfer data) entre as duas requisições. Veja Assinatura com chave remota (assinatura no browser) e Assinaturas padrão PAdES.

// Start: the certificate encoding was read in the browser by Web PKI
var padesSigner = new PadesSigner();
padesSigner.SetPdfToSign(userfilePath);
padesSigner.SetSigningCertificate(PKCertificate.Decode(model.CertContent));
padesSigner.SetPolicy(PadesPoliciesForGeneration.GetPadesBasic(Util.GetTrustArbitrator()));
toSignBytes = padesSigner.GetToSignBytes(out signatureAlg, out transferData);
var toSignHash = signatureAlg.DigestAlgorithm.ComputeHash(toSignBytes); // signed in the browser
// Complete: model.Signature is the signHash() output
padesSigner = new PadesSigner();
padesSigner.SetPolicy(PadesPoliciesForGeneration.GetPadesBasic(Util.GetTrustArbitrator()));
padesSigner.SetPreComputedSignature(model.Signature, transferDataContent);
padesSigner.ComputeSignature();
var signedPdf = padesSigner.GetPadesSignature();

Assinar um PDF com uma chave no servidor​

O PKI SDK é a forma recomendada de implementar assinaturas de servidor em .NET: carregue um PKCertificateWithKey (de um arquivo PKCS#12 com Pkcs12CertificateStore.Load ou do repositório de certificados do Windows com WindowsCertificateStore) e chame PadesSigner.ComputeSignature diretamente. Veja Certificados com chave privada associada.

var store = Pkcs12CertificateStore.Load(certContent, "1234");
var certWithKey = store.GetCertificatesWithKey().First();
var padesSigner = new PadesSigner();
padesSigner.SetPdfToSign(userfileContent);
padesSigner.SetSigningCertificate(certWithKey);
padesSigner.SetPolicy(PadesPoliciesForGeneration.GetPadesBasic(Util.GetTrustArbitrator()));
padesSigner.ComputeSignature();
var signedPdf = padesSigner.GetPadesSignature();

Criar uma assinatura CAdES​

O CAdES assina qualquer arquivo binário com o CadesSigner; GetSignature retorna o pacote CMS (um arquivo .p7s nos exemplos). Para assinar no navegador, use GenerateToSignBytes e SetPrecomputedSignature; para finalizar depois, por exemplo num background worker, use SetFinishSignature(false) e o CadesSignatureFinisher. Veja Assinaturas padrão CAdES e Finalização assíncrona de assinaturas.

var cadesSigner = new CadesSigner();
cadesSigner.SetDataToSign(userfileContent);
cadesSigner.SetSigningCertificate(certWithKey);
cadesSigner.SetPolicy(CadesPoliciesForGeneration.GetPkiBrazilAdrBasica());
cadesSigner.ComputeSignature();
var signatureContent = cadesSigner.GetSignature();

Assinar um documento XML​

O FullXmlSigner assina um documento XML inteiro (por padrão, a assinatura vira filha do elemento raiz); o XmlElementSigner com SetToSignElementId assina um único elemento, que precisa ter um atributo ID único. Entre as políticas estão XmlPolicySpec.GetXadesBasic, BrazilXmlPolicySpec.GetAdrBasicaV22 e BrazilXmlPolicySpec.GetNFePadraoNacional; com o Web PKI, use GenerateToSignHash e SetPrecomputedSignature. Veja Assinaturas padrão XML.

var signer = new FullXmlSigner();
signer.SetXml(xmlContent);
signer.SetSigningCertificate(certWithKey);
signer.SetPolicy(BrazilXmlPolicySpec.GetNFePadraoNacional());
signer.ComputeSignature();
var signedXml = signer.GetSignedXml();

Abrir e validar uma assinatura​

Abra um arquivo assinado e valide cada signatário segundo uma política: PadesSignature.Open e ValidateSignature para PDF, CadesSignature.Open para CAdES e XmlSignatureLocator.GetSignatures para XML. Cada validação retorna um ValidationResults. Veja Assinaturas padrão CAdES.

var signature = PadesSignature.Open(userfileContent);
var policyMapper = PadesPoliciesForValidation.GetPadesBasic(Util.GetTrustArbitrator());
foreach (var signerInfo in signature.Signers) {
ValidationResults vr = signature.ValidateSignature(signerInfo, policyMapper);
Console.WriteLine(vr);
}

Autenticar um usuário com certificado​

PKCertificateAuthentication.Start gera um nonce, o navegador o assina com o certificado do usuário e Complete verifica a assinatura e valida o certificado. Os nonces ficam num INonceStore: o exemplo usa o FileSystemNonceStore, e o conector do Entity Framework oferece o EntityFrameworkNonceStore. Veja Autenticação com certificado.

// GET: issue the nonce to be signed in the browser
var certAuth = new PKCertificateAuthentication(Util.GetNonceStore());
var nonce = certAuth.Start();
// POST: check the nonce signature and the certificate
PKCertificate certificate;
var vr = certAuth.Complete(model.Nonce, model.Certificate, model.Signature, Util.GetTrustArbitrator(), out certificate);
if (!vr.IsValid) {
// authentication failed: show vr.ToString()
}

Gerar uma versão para impressão​

Marque um PDF assinado com um código de verificação e um resumo dos signatários: AlphaCode.Generate cria o código e PdfMarker.WriteMarks grava as marcas; as assinaturas são removidas da cópia marcada. No exemplo, allPagesMessage cita os signatários de PadesSignature.Open(pdfContent) e mostra AlphaCode.Format(verificationCode). Veja Versão para impressão e Gerando códigos alfanuméricos.

var verificationCode = AlphaCode.Generate(); // store it with the document
var pdfMarker = new PdfMarker();
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 = allPagesMessage } } }
}
});
pdfMarker.ThrowIfSignedPdf = false;
var pfvContent = pdfMarker.WriteMarks(pdfContent);

Erros​

Os erros chegam como exceções, nos assinadores, e como ValidationResults, nas validações:

  • Os assinadores lançam ValidationException quando uma verificação falha, por exemplo um encoding de certificado ilegível ou um certificado expirado ou revogado; a propriedade ValidationResults informa o motivo, e os exemplos mostram ex.ValidationResults.ToString() ao usuário.
  • As validações não lançam exceção: PKCertificate.Validate, ValidateSignature e PKCertificateAuthentication.Complete retornam um ValidationResults com IsValid, Errors, Warnings e PassedChecks; uma autenticação que falha é normal com um certificado expirado ou revogado.
  • Para tratar uma verificação como alerta em vez de erro, defina o nível dela em CertificateValidationOptions, por exemplo SetFailureLevel(ValidationItemTypes.CertificateRevocationStatusUnknown, ValidationFailureLevels.Warning).
  • Com uma política que exige carimbo de tempo, como a AD-RT, o ComputeSignature lança TimestampException se não conseguir o carimbo; a TimestampRequestException, que identifica erros na requisição do carimbo de tempo, deriva dela.
  • O PdfMarker lança SignedPdfToMarkException para um PDF que tenha qualquer assinatura, a menos que ThrowIfSignedPdf seja false.

TODO(docs): nenhum artigo diz quando são lançadas LicenseException, InvalidPdfException, PasswordProtectedPdfException ou PkiWebException, por exemplo com uma licença ausente ou expirada; a referência da API as lista sem descrição.

Certificados e credenciais de teste​

Use os certificados de teste: arquivos PKCS #12 com a senha 1234, emitidos pela ICP de testes da Lacuna Software, que nunca deve ser aceita em ambiente de produção. Com o PKI SDK, confie neles adicionando a raiz de teste da Lacuna como TrustedRoots a um LinkedTrustArbitrator, só em builds de debug, como faz o Util.GetTrustArbitrator() dos exemplos. Veja PKI SDK na página de certificados de teste. Políticas da ICP-Brasil como CadesPoliciesForGeneration.GetPkiBrazilAdrBasica() e BrazilXmlPolicySpec.GetNFePadraoNacional() confiam só nas raízes da ICP-Brasil, por isso, em builds de debug, os exemplos substituem o árbitro de confiança delas pelo Util.GetTrustArbitrator(): ClearTrustArbitrators e AddTrustArbitrator num XmlPolicySpec, ClearTrustArbitrator e AddTrustArbitrator num CadesPolicySpec. Para obter uma licença de trial, entre em contato pelo site pki.lacunasoftware.com; o projeto de exemplos traz o próprio LacunaPkiLicense.config, copiado para o diretório de saída.

Experimente​

Demos online e os exemplos do PKI SDK para ASP.NET MVC e ASP.NET Core com Angular:

Ver em Markdown