---
title: "Certificate authentication - Rest PKI Core"
description: "Sign users in with their digital certificate: Rest PKI Core prepares a nonce, Web PKI signs it in the browser, and CompleteAuthenticationAsync validates the certificate."
canonical: "https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/cert-auth"
locale: "en"
product: "Rest PKI Core"
last_updated: "2026-10-12"
---

# Certificate authentication - Rest PKI Core

Users can sign in to your application with their digital certificate. [Rest PKI Core](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core) does the backend part of the
[certificate authentication](https://docs.lacunasoftware.com/en-us/articles/pki-guide/cert-auth) process, and [Web PKI](https://docs.lacunasoftware.com/en-us/articles/web-pki) the part that runs in the browser:

1. Your server asks Rest PKI Core to **prepare** an authentication. Rest PKI Core generates a random nonce and returns the hash to sign and the
   authentication **state**.
1. On your page, the user chooses a certificate, and Web PKI signs the hash with it. The page sends the certificate's encoding, the signature and the
   state to your server.
1. Your server sends them to Rest PKI Core, which **completes** the authentication: it checks the signature, validates the certificate and checks that
   the nonce was not used before.
1. If the authentication succeeds, your application finds the user by a field of the certificate, such as the CPF or the email address, and signs
   them in.

> **Tip**
>
> From this point on we'll describe calls to Rest PKI Core. Make sure you've read the [Get Started](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started) article to be able to keep up. The
> application's API key needs a role that allows certificate authentication, such as **Worker** (see [Choosing roles](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#roles)). Outside of
> localhost, Web PKI needs a [license](https://docs.lacunasoftware.com/en-us/articles/web-pki/licensing).

## Preparing the authentication

On .NET, call `IRestPkiService.PrepareAuthenticationAsync()`:

```cs
var prepareAuthResult = await restPkiService.PrepareAuthenticationAsync(securityContextId: securityContextId);

// Pass these to the page
var state = prepareAuthResult.State;
var toSignHash = Convert.ToBase64String(prepareAuthResult.ToSign.Hash);
var digestAlgorithm = prepareAuthResult.ToSign.SignatureAlgorithm.DigestAlgorithm.Oid;
```

* `securityContextId` decides which certificates are trusted. If omitted, Rest PKI Core uses the subscription's default security context or, without
  one, the instance's.
* `ignoreRevocationStatusUnknown` accepts certificates whose revocation status cannot be determined. If omitted, the subscription's setting applies
  or, without one, the instance's.

The result has the hash to sign in `ToSign.Hash`, its digest algorithm in `ToSign.SignatureAlgorithm.DigestAlgorithm`, and the state in `State`, an
opaque string that Rest PKI Core needs to complete the authentication. Preparing stores nothing on Rest PKI Core, so you can prepare an authentication
every time your login page is shown.

On Java, call `RestPkiService.prepareAuthentication()`:

```java
PrepareAuthenticationOptions options = new PrepareAuthenticationOptions();
options.setSecurityContext(SecurityContext.pkiBrazil);
PrepareAuthenticationResult prepareAuthResult = restPkiService.prepareAuthentication(options);

// Pass these to the page
String state = prepareAuthResult.getState();
String toSignHash = prepareAuthResult.getToSignHash().getValueAsBase64();
String digestAlgorithm = prepareAuthResult.getToSignHash().getAlgorithm().getName();
```

If calling the API directly, do:

```text
POST {endpoint}/api/v2/authentication
{
	"securityContextId": "...",
	"ignoreRevocationStatusUnknown": false
}
```

The response has the hash to sign in `toSign.hash` (Base64), its digest algorithm in `toSign.signatureAlgorithm.digestAlgorithmOid`, and the state in
`state`. It also repeats the hash in `toSignHash`, for older clients.

## Signing the hash on your page

The user's certificate never leaves their computer, so Web PKI signs the hash in the browser:

1. List the user's certificates with Web PKI's `listCertificates()`.
1. When the user chooses one, read its encoding with `readCertificate()` and sign the hash with `signHash()`.
1. Send the certificate's encoding and the signature, both in Base64, and the state to your server, which
   [completes the authentication](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/cert-auth#completing-the-authentication).

```javascript
pki.readCertificate({ thumbprint: thumbprint }).success(function (certificate) {
	pki.signHash({
		thumbprint: thumbprint,          // the certificate chosen by the user
		hash: toSignHash,                // the hash to sign, in Base64
		digestAlgorithm: digestAlgorithm // the digest algorithm, as given by your server
	}).success(function (signature) {
		// Send the certificate, the signature (both in Base64) and the state to your server
	});
});
```

The state is protected by Rest PKI Core, so you can keep it in a hidden field of the page. For `listCertificates()`, `readCertificate()` and
`signHash()`, see Web PKI's [Get started](https://docs.lacunasoftware.com/en-us/articles/web-pki/get-started).

## Completing the authentication

On .NET, call `IRestPkiService.CompleteAuthenticationAsync()` with the state, the certificate and the signature:

```cs
var authResult = await restPkiService.CompleteAuthenticationAsync(
	state,
	Convert.FromBase64String(certificate),
	Convert.FromBase64String(signature)
);
if (authResult.Success) {
	var userCert = authResult.Certificate;
	// Find the user by a field of the certificate, such as userCert.PkiBrazil.Cpf or userCert.EmailAddress, and sign them in
} else {
	// authResult.Failure says why
}
```

When `Success` is `false`, `Failure` says why:

| `Failure` | What happened | What to do |
| --- | --- | --- |
| `CertificateFailedValidation` | The certificate is not valid under the security context, for instance it is revoked or not trusted | Show the reasons, in `ValidationResults` |
| `StaleSession` | The authentication expired before it was completed: by default, an authentication lasts 60 minutes | Prepare a new authentication |
| `NonceReused` | The state was already used to complete an authentication, which can mean a replay attack | Prepare a new authentication |

If the signature doesn't match the hash, usually an error on your page, Rest PKI Core answers with HTTP 422 and the error code `BadSignature` instead
(see [Errors](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/get-started#dotnet-errors)).

On Java, call `RestPkiService.completeAuthentication()`:

```java
CompleteAuthenticationOptions request = new CompleteAuthenticationOptions();
request.setCertificateFromBase64(certificate);
request.setSignatureFromBase64(signature);
request.setState(state);

AuthenticationResult authResult = restPkiService.completeAuthentication(request);
if (authResult.isSuccess()) {
	// authResult.getCertificate() is the user's certificate
} else if (authResult.getFailure().equals(AuthenticationFailures.CERTIFICATEFAILEDVALIDATION)) {
	// authResult.getValidationResults() says why the certificate was rejected
} else {
	// Prepare a new authentication
}
```

If calling the API directly, do:

```text
POST {endpoint}/api/v2/authentication/completion
{
	"state": "...",
	"certificate": "Base64EncodedCertificate==",
	"signature": "Base64EncodedSignature=="
}
```

The response has `success`, `failure`, the user's certificate in `certificate` and, when the certificate was validated, the results in
`validationResults`.

> **Note**
>
> Rest PKI Core also serves the authentication API of the classic [Rest PKI](https://docs.lacunasoftware.com/en-us/articles/rest-pki/certificate-authentication) (`Api/Authentications`),
> which eases moving an application that authenticates with Rest PKI to Rest PKI Core. See
> [Moving from Rest PKI to Rest PKI Core](https://docs.lacunasoftware.com/en-us/articles/rest-pki/core/integration/migrating-from-rest-pki).

## Samples

* Java: [AuthenticationRestPkiCoreController.java](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/AuthenticationRestPkiCoreController.java)

**Demos and samples**

- Live demo: [Your certificates in the browser](https://try.lacunasoftware.com/en/web-pki/certificates-in-the-browser)
- Authenticate with a digital certificate using Rest PKI Core: [Java](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/AuthenticationRestPkiCoreController.java)
