# Task: Assinar um arquivo com seu certificado

I want my application to do what Lacuna's "Assinar um arquivo com seu certificado" demo does: Assine um PDF, um arquivo XML ou qualquer outro arquivo com seu próprio certificado, com o PKI SDK e o Web PKI.

- Docs: https://docs.lacunasoftware.com/pt-br/articles/pki-sdk
- Live demo: https://try.lacunasoftware.com/pt/pki-suite/sign-a-file
- This demo for AI agents: https://try.lacunasoftware.com/pt/pki-suite/sign-a-file.md

## Samples

- .NET (aspnet-mvc): [Sign a PDF with PKI SDK](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/PadesSignatureSdkController.cs)
- .NET (aspnet-core-angular): [Sign a PDF with PKI SDK](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/spa/PkiSuiteAspNetSpaSample/Controllers/PadesSignatureSdkController.cs)
- .NET (aspnet-mvc): [Sign a local PDF in the browser with Web PKI](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/PadesSignatureWebController.cs)
- .NET (aspnet-mvc): [Sign a file with CAdES using PKI SDK](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/CadesSignatureSdkController.cs)
- .NET (aspnet-core-angular): [Sign a file with CAdES using PKI SDK](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/spa/PkiSuiteAspNetSpaSample/Controllers/CadesSignatureSdkController.cs)
- .NET (aspnet-mvc): [Sign a local file with CAdES in the browser using Web PKI](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/dotnet/mvc/PkiSuiteAspNetMvcSample/Controllers/CadesSignatureWebController.cs)

## Call sequence

Recorded on 2026-10-12 against the real products with Lacuna's public test certificates and data. Keys, tokens, sessions, ids, file contents and personal data are masked.

### 1. List the visitor's certificates, with no filter.

In the browser: `pki.listCertificates()`

### 2. Read the chosen certificate's public part: no PIN is asked, and the key stays where it is.

In the browser: `pki.readCertificate({ thumbprint })`

### 3. PKI SDK picks the Signature policy by the certificate's root (AD-RT, with a timestamp), draws the stamp on the PDF's last page and returns the hash to sign.

On the server, in process: `PadesSigner.GetToSignBytes(out algorithm, out transferData)`

### 4. Sign the hash with the certificate's private key, behind the system's PIN prompt: the key never leaves the browser's store or token.

In the browser: `pki.signHash({ thumbprint, hash, digestAlgorithm })`

### 5. PKI SDK completes the signature with the signed hash and Start's transfer data, and gets its timestamp from Rest PKI Core's tsp/PkiBrazil endpoint, never signing without it.

On the server, in process: `PadesSigner.SetPreComputedSignature(signature, transferData), then PadesSigner.ComputeSignature()`

### 6. PKI SDK validates the certificate and reopens the signed file to validate every signature in it, for the validation report shown beside the result.

On the server, in process: `PadesSignature.Open(signedPdf), then PadesSignature.ValidateSignature(signer, policy)`

## Patterns to copy

- The private key stays in the browser: the server only ever receives the public certificate (Web PKI's `readCertificate`, no PIN) and the raw signature from `signHash`.
- Sign in two server steps around Web PKI's `signHash`: Start returns the hash and its digest algorithm, which you pass to `signHash` as is, and Complete takes the signature back. Keep Start's state, which holds the file, on the server until Complete, use it once and never log it.
- At Complete, rebuild the PKI SDK signer with the same policy and options and feed it the signature with Start's transfer data. A PAdES visual stamp is drawn at Start, since it is part of what is signed.
- Load the PKI SDK licence with `PkiConfig.LoadLicense` from the decoded Base64 bytes (the string overload takes a file path), and run PKI SDK off the request thread: its work is synchronous and calls out for chain building, revocation and timestamps.
- For signature timestamps, PKI SDK's `TimestampRequester` calls Rest PKI Core's `tsp/PkiBrazil` with the API key; the Demo raises its timeout to 30 s and fails the signing rather than fall back to an untimestamped one.

## What to do

Implement this in my project with its existing stack, language, framework and conventions, following the call sequence and the patterns above, and adapting the samples rather than copying them whole. Ask me before adding any dependency or package. Keep API keys and private keys out of the browser and out of the repository.
