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.
| Alternativa | Quando é a melhor escolha |
|---|---|
| PKI Express | Seu 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 Core | Você 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 PKI | Você 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. |
| Signer | Você 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
| Linguagem | Pacote | Major atual | Registro |
|---|---|---|---|
| C# / .NET (NuGet) | Lacuna.Pki | 2 (mais recente: 2.22.7) | https://www.nuget.org/packages/Lacuna.Pki |
| C# / .NET, certificados em nuvem (opcional) | Lacuna.Pki.BrazilTrustServices | 1 (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.configpara junto doLacuna.Pki.dll, por exemplo com Build Action definido como Content e Copy to Output Directory como Copy Always. Web.configouApp.config: a chave deappSettingsLacunaPKI.LicensePath(caminho do arquivo) ouLacunaPKI.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
ValidationExceptionquando uma verificação falha, por exemplo um encoding de certificado ilegível ou um certificado expirado ou revogado; a propriedadeValidationResultsinforma o motivo, e os exemplos mostramex.ValidationResults.ToString()ao usuário. - As validações não lançam exceção:
PKCertificate.Validate,ValidateSignatureePKCertificateAuthentication.Completeretornam umValidationResultscomIsValid,Errors,WarningsePassedChecks; 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 exemploSetFailureLevel(ValidationItemTypes.CertificateRevocationStatusUnknown, ValidationFailureLevels.Warning). - Com uma política que exige carimbo de tempo, como a AD-RT, o
ComputeSignaturelançaTimestampExceptionse não conseguir o carimbo; aTimestampRequestException, que identifica erros na requisição do carimbo de tempo, deriva dela. - O
PdfMarkerlançaSignedPdfToMarkExceptionpara um PDF que tenha qualquer assinatura, a menos queThrowIfSignedPdfsejafalse.
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: