---
title: "PKI SDK quick reference"
description: "PKI SDK on one page: add the Lacuna.Pki package, load the license, validate certificates, sign PDF, CAdES and XML files, and handle errors."
canonical: "https://docs.lacunasoftware.com/en-us/articles/pki-sdk/quick-reference"
locale: "en"
product: "PKI SDK"
last_updated: "2026-10-12"
---

# PKI SDK quick reference

The essentials of PKI SDK on one page, with links to the full articles.

## What it is for

PKI SDK is a .NET library for public key infrastructure operations: decoding and validating X.509 certificates, and creating and validating CMS/CAdES, PDF/PAdES and XmlDSig/XAdES signatures. 
It runs inside your .NET application, on .NET Framework or, through .NET Standard 2.0, on .NET Core (including Linux servers); for web signatures, Web PKI does the browser part and PKI SDK the server part. 

| Choose instead | When it fits better |
| --- | --- |
| [PKI Express](https://docs.lacunasoftware.com/en-us/articles/pki-express/quick-reference) | Your backend is not .NET: PKI Express creates CAdES, PAdES and XAdES signatures from virtually any language, hosted on your own Windows or Linux servers. |
| [Rest PKI Core](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/quick-reference) | You prefer a REST API to a library: use it as a service at core.pki.rest or on your own instance, and it hosts the page where users sign. |
| [Web PKI](https://docs.lacunasoftware.com/en-us/articles/web-pki/quick-reference) | You need the browser side: Web PKI lists the user's certificates and signs with them; it complements PKI SDK in web remote signatures. |
| [Signer](https://docs.lacunasoftware.com/en-us/articles/signer/quick-reference) | You want a ready-made document and signature manager, as SaaS or on-premises, rather than building the signing flow yourself. |

## Install

| Language | Package | Current major | Registry |
| --- | --- | --- | --- |
| C# / .NET (NuGet) | `Lacuna.Pki` | 2 (latest 2.22.7) | https://www.nuget.org/packages/Lacuna.Pki |
| C# / .NET, cloud certificates (optional) | `Lacuna.Pki.BrazilTrustServices` | 1 (latest 1.7.4) | https://www.nuget.org/packages/Lacuna.Pki.BrazilTrustServices |

Add `Lacuna.Pki` with the NuGet Package Manager (package **Lacuna PKI SDK**); version 2.22.7 targets .NET Standard 2.0 and .NET Framework 4.5, 4.6 and 4.7 ([.NET Standard / .NET Core Support](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/net-standard)). 
Optional packages add storage, logging and nonce-store integrations: `Lacuna.Pki.AzureConnector`, `Lacuna.Pki.AmazonConnector`, `Lacuna.Pki.EntityFrameworkConnector` and `Lacuna.Pki.NLogConnector`; see [Optional Nuget packages](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/optional-packages) and the [Changelog](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/changelog). 

## Configuration and authentication

PKI SDK runs in your application's process and is enabled by a license, a `LacunaPKILicense.config` file or a Base64 string, loaded at application startup. 
Load it in one of three ways ([Licensing](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/get-started/licensing)):

- Web applications (recommended): copy `LacunaPKILicense.config` next to `Lacuna.Pki.dll`, for instance with **Build Action** set to **Content** and **Copy to Output Directory** set to **Copy Always**.
- `Web.config` or `App.config`: the `appSettings` key `LacunaPKI.LicensePath` (path to the file) or `LacunaPKI.BinaryLicense` (Base64 license).
- Desktop applications (recommended): `PkiConfig.LoadLicense(byte[])` with the decoded Base64 license; `PkiConfig.LoadLicense(string)` takes the file path instead. 

```csharp
byte[] binLicense = Convert.FromBase64String("[given Base64 License]");
PkiConfig.LoadLicense(binLicense);
```

Other settings are properties of [`PkiConfig`](https://docs.lacunasoftware.com/api/Lacuna.Pki/PkiConfig), such as `CrlDownloadTimeout`, `TimestampRequestTimeout`, `TempPath`, `PreemptiveCrlDownload` and `WebProxy` ([Configuration](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/config)). 
Every validation takes an `ITrustArbitrator` that decides which roots to trust: `TrustArbitrators.PkiBrazil`, `TrustArbitrators.Windows`, `TrustedRoots`, or several combined in a `LinkedTrustArbitrator`. 
There is no base URL to configure: the SDK downloads CA certificates, CRLs and OCSP responses online while building and validating chains, and policies with a timestamp need a `TimestampRequester` built with your timestamp authority's URI. 

## Core operations

The examples are in C#, trimmed from the docs and from the ASP.NET MVC project in PkiSuiteSamples, where `Util.GetTrustArbitrator()` returns the trust arbitrator (see [Test certificates and credentials](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/quick-reference#testing)). 

### Decode and validate a certificate

Load a certificate in BER, DER, Base64 or PEM encoding and validate it against the roots you trust: `PKCertificate.Decode` and `PKCertificate.Validate`. See [Decoding certificates](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/certificates/decoding) and [Validating certificates](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/certificates/validation). 

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

### Sign a PDF with Web PKI

The user's key stays in the browser: the server prepares the bytes to sign with `PadesSigner.GetToSignBytes`, Web PKI signs their hash (`readCertificate`, then `signHash`), and the server completes with `PadesSigner.SetPreComputedSignature`, keeping the transfer data between the two requests. See [Web remote signatures](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/signatures/web-remote) and [PAdES signatures](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/signatures/pades). 

```csharp
// 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();
```

### Sign a PDF with a server key

PKI SDK is the recommended way to implement [server signatures](https://docs.lacunasoftware.com/en-us/articles/pki-guide/server-signatures) in .NET: load a `PKCertificateWithKey` (from a PKCS#12 file with `Pkcs12CertificateStore.Load`, or from the Windows store with `WindowsCertificateStore`) and call `PadesSigner.ComputeSignature` directly. See [Certificates with private key](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/certificates/certs-with-key). 

```csharp
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();
```

### Create a CAdES signature

CAdES signs any binary file with `CadesSigner`; `GetSignature` returns the CMS package (a `.p7s` file in the samples). For signing in the browser, use `GenerateToSignBytes` and `SetPrecomputedSignature`; to finish later, for instance in a background worker, use `SetFinishSignature(false)` and `CadesSignatureFinisher`. See [CAdES signatures](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/signatures/cades) and [Asynchronous signature finishing](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/signatures/cades/finisher). 

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

### Sign an XML document

`FullXmlSigner` signs a whole XML document (by default, the signature becomes a child of the root element); `XmlElementSigner` with `SetToSignElementId` signs one element, which needs a unique ID attribute. Policies include `XmlPolicySpec.GetXadesBasic`, `BrazilXmlPolicySpec.GetAdrBasicaV22` and `BrazilXmlPolicySpec.GetNFePadraoNacional`; with Web PKI, use `GenerateToSignHash` and `SetPrecomputedSignature`. See [XML signatures](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/signatures/xml). 

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

### Open and validate a signature

Open a signed file and validate each signer against a policy: `PadesSignature.Open` and `ValidateSignature` for PDF, `CadesSignature.Open` for CAdES, and `XmlSignatureLocator.GetSignatures` for XML. Each validation returns a `ValidationResults`. See [CAdES signatures](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/signatures/cades). 

```csharp
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);
}
```

### Authenticate a user with a certificate

`PKCertificateAuthentication.Start` issues a nonce, the browser signs it with the user's certificate, and `Complete` checks the signature and validates the certificate. Nonces go to an `INonceStore`: the sample uses `FileSystemNonceStore`, and the Entity Framework connector offers `EntityFrameworkNonceStore`. See [Certificate authentication](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/certificates/auth). 

```csharp
// 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()
}
```

### Generate a printer-friendly version

Stamp a signed PDF with a verification code and a summary of its signers: `AlphaCode.Generate` creates the code, and `PdfMarker.WriteMarks` writes the marks; the signatures are removed from the marked copy. In the sample, `allPagesMessage` names the signers of `PadesSignature.Open(pdfContent)` and shows `AlphaCode.Format(verificationCode)`. See [Printer-friendly version](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/printer-friendly-version) and [Generating alphanumeric codes](https://docs.lacunasoftware.com/en-us/articles/pki-sdk/alpha-codes). 

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

## Errors

Errors come as exceptions from signers and as `ValidationResults` from validations:

- Signers throw [`ValidationException`](https://docs.lacunasoftware.com/api/Lacuna.Pki/ValidationException) when a check fails, for instance an unreadable certificate encoding or an expired or revoked certificate; its `ValidationResults` property says why, and the samples show `ex.ValidationResults.ToString()` to the user. 
- Validations do not throw: `PKCertificate.Validate`, `ValidateSignature` and `PKCertificateAuthentication.Complete` return a [`ValidationResults`](https://docs.lacunasoftware.com/api/Lacuna.Pki/ValidationResults) with `IsValid`, `Errors`, `Warnings` and `PassedChecks`; a failed authentication is normal for an expired or revoked certificate. 
- To treat a check as a warning instead of an error, set its level on `CertificateValidationOptions`, for instance `SetFailureLevel(ValidationItemTypes.CertificateRevocationStatusUnknown, ValidationFailureLevels.Warning)`. 
- With a policy that requires a timestamp, such as AD-RT, `ComputeSignature` throws [`TimestampException`](https://docs.lacunasoftware.com/api/Lacuna.Pki/TimestampException) if the timestamp cannot be obtained; `TimestampRequestException`, which identifies errors when requesting a timestamp, derives from it. 
- `PdfMarker` throws `SignedPdfToMarkException` on a PDF that has any signature, unless `ThrowIfSignedPdf` is `false`. 

TODO(docs): no article says when `LicenseException`, `InvalidPdfException`, `PasswordProtectedPdfException` or `PkiWebException` are thrown, for instance on a missing or expired license; the [API reference](https://docs.lacunasoftware.com/api/Lacuna.Pki) lists them without a description.

## Test certificates and credentials

Use the [test certificates](https://docs.lacunasoftware.com/en-us/articles/pki-guide/test-certs): PKCS #12 files with password **1234**, issued by the Lacuna Test PKI, which must never be trusted in production. 
With PKI SDK, trust them by adding the Lacuna test root as `TrustedRoots` to a `LinkedTrustArbitrator`, only in debug builds, as `Util.GetTrustArbitrator()` does in the samples. See [PKI SDK](https://docs.lacunasoftware.com/en-us/articles/pki-guide/test-certs#pki-sdk) on the test certificates page. 
ICP-Brasil policies such as `CadesPoliciesForGeneration.GetPkiBrazilAdrBasica()` and `BrazilXmlPolicySpec.GetNFePadraoNacional()` trust only the ICP-Brasil roots, so in debug builds the samples replace their trust arbitrator with `Util.GetTrustArbitrator()`: `ClearTrustArbitrators` and `AddTrustArbitrator` on an `XmlPolicySpec`, `ClearTrustArbitrator` and `AddTrustArbitrator` on a `CadesPolicySpec`. 
For a trial license, contact pki.lacunasoftware.com; the samples project ships its own `LacunaPkiLicense.config`, copied to the output directory. 

## Try it

Online demos and the PKI SDK samples for ASP.NET MVC and ASP.NET Core with Angular:

**Demos and samples**

- Live demo: [Sign a file with your certificate](https://try.lacunasoftware.com/en/pki-suite/sign-a-file)
- Live demo: [Sign a PDF with your cloud certificate](https://try.lacunasoftware.com/en/pki-suite/sign-with-cloud-certificate)
- Live demo: [Validate a signed file](https://try.lacunasoftware.com/en/pki-suite/validate-a-signed-file)
- Live demo: [Make a printer-friendly version](https://try.lacunasoftware.com/en/pki-suite/printer-friendly-version)
- Live demo: [Your certificates in the browser](https://try.lacunasoftware.com/en/web-pki/certificates-in-the-browser)
- Sign a PDF with PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/PadesSignatureSdkController.cs)
- Sign multiple PDFs with PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/BatchPadesSignatureSdkController.cs)
- Sign a PDF with a server key using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/PadesServerKeySdkController.cs)
- Sign a PDF with a cloud certificate (OAuth flow) using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/PadesCloudOauthSdkController.cs)
- Sign a PDF with a cloud certificate (password flow) using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/PadesCloudPwdSdkController.cs)
- Sign a file with CAdES using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/CadesSignatureSdkController.cs)
- Sign multiple files with CAdES using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/BatchCadesSignatureSdkController.cs)
- Sign a file with CAdES and a server key using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/CadesServerKeySdkController.cs)
- Merge CMS/CAdES signatures using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/MergeCadesSdkController.cs)
- Sign an XML document using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/XmlSignatureSdkController.cs)
- Sign an XML document with a server key using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/XmlServerKeySdkController.cs)
- Sign a Brazilian NF-e using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/XmlNFeSignatureSdkController.cs)
- Sign a COD XML using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/XmlCodSignatureSdkController.cs)
- Validate a signed PDF using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/OpenPadesSignatureSdkController.cs)
- Validate a CMS/CAdES signature using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/OpenCadesSignatureSdkController.cs)
- Validate a signed XML using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/OpenXmlSignatureSdkController.cs)
- Make a printer-friendly version of a signed PDF using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/PrinterFriendlyPadesSdkController.cs)
- Make a printer-friendly version of a CMS/CAdES signature using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/PrinterFriendlyCadesSdkController.cs)
- Authenticate with a digital certificate using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/AuthenticationSdkController.cs)
- Sign a medical prescription (Receita Simples) using PKI SDK: [.NET](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/ReceitaSimplesSdkController.cs)
