---
title: "Validate signatures - PKI Express"
description: "How to open and validate PAdES, CAdES and XML signatures with PKI Express: the signature explorers, which roots are trusted, validation policies and the results per signer."
canonical: "https://docs.lacunasoftware.com/en-us/articles/pki-express/validate-signatures"
locale: "en"
product: "PKI Express"
last_updated: "2026-10-12"
---

# Validate signatures - PKI Express

[PKI Express](https://docs.lacunasoftware.com/en-us/articles/pki-express) opens a signed file on your server, lists its signers and, when asked, validates each signature: PDF signatures
(PAdES), CMS/CAdES files (`.p7s`) and XML signatures. 
PKI Express added the PAdES and CAdES explorers in 1.3.0 and XML in 1.25.0. 

The code is PHP, from the [PKI Suite samples](https://github.com/LacunaSoftware/PkiSuiteSamples), the only language with samples for the
three formats; it uses `lacuna/pki-express` (1.17.0). 

## Flow

1. Create the explorer for the format: `PadesSignatureExplorer`, `CadesSignatureExplorer` or `XmlSignatureExplorer`.
1. Apply the trust settings (see [Which certificates are trusted](https://docs.lacunasoftware.com/en-us/articles/pki-express/validate-signatures#trust)).
1. Set the signed file and `validate = true`; without it, the explorer only inspects the signatures.
1. Call `open()`, which runs PKI Express's `open-pades`, `open-cades` or `open-xml` command and returns the signers.
1. Read each signer's certificate, signing time and `validationResults`.

## PDF signatures (PAdES)

```php
$sigExplorer = new PadesSignatureExplorer();
Util::setPkiDefaults($sigExplorer);
$sigExplorer->setSignatureFile(StorageMock::getDataPath($fileId));
$sigExplorer->validate = true;
$signature = $sigExplorer->open();
```

## CAdES signatures

```php
$sigExplorer = new CadesSignatureExplorer();
Util::setPkiDefaults($sigExplorer);
$sigExplorer->setSignatureFile(StorageMock::getDataPath($fileId));
$sigExplorer->validate = true;
$signature = $sigExplorer->open();
```

To also get the signed content, set the path where PKI Express writes it, with `setExtractContentPath`; the Java, Node.js and Python
samples do this, and check `hasEncapsulatedContent` before offering the file: 

```java
CadesSignatureExplorer sigExplorer = new CadesSignatureExplorer();
Util.setPkiDefaults(sigExplorer);
sigExplorer.setSignatureFile(StorageMock.getDataPath(signatureFile));
sigExplorer.setValidate(true);
String outputFile = StorageMock.generateFileId("pdf");
sigExplorer.setExtractContentPath(StorageMock.getDataPath(outputFile));
CadesSignature signature = sigExplorer.open();
```

* The samples save the content as a PDF, whatever its type. 
* For a detached signature, give the signed data with `setDataFileFromPath` (Java: `setDataFile`). 
  TODO(docs): no sample shows a detached CAdES signature.

## XML signatures

`open()` returns an `XmlSignature` whose `signers` are the signatures in the document. The PHP explorer needs PKI Express 1.25.1 or later;
the Node.js and Python libraries have no XML explorer, and the Java library has one in 1.23.1 but not in 1.20.0. 

```php
$sigExplorer = new XmlSignatureExplorer();
Util::setPkiDefaults($sigExplorer);
$sigExplorer->setSignatureFile(StorageMock::getDataPath($fileId));
$sigExplorer->validate = true;
$signatures = $sigExplorer->open()->signers;
```

Each XML signer also has a `type` and the `signedElement` (`localName`, `namespaceUri`). 

## Which certificates are trusted

By default, PKI Express trusts only certificates issued under ICP-Brasil. 
The samples' helper adds the roots listed in their configuration and, for development, Lacuna's test root, which accepts the
[test certificates](https://docs.lacunasoftware.com/en-us/articles/pki-guide/test-certs): 

```php
$trustedRoots = $config['pkiExpress']['trustedRoots'];
foreach ($trustedRoots as $root) {
    $operator->addTrustedRoot(StorageMock::getResourcePath($root));
}
$operator->trustLacunaTestRoot = $config['trustLacunaTestRoot'];
```

* See "Trusting in custom certificates" for [Node.js](https://docs.lacunasoftware.com/en-us/articles/pki-express/nodejs/trusting-in-custom-certificates), [Java](https://docs.lacunasoftware.com/en-us/articles/pki-express/java/trusting-in-custom-certificates),
  [PHP](https://docs.lacunasoftware.com/en-us/articles/pki-express/php/trusting-in-custom-certificates), [Python](https://docs.lacunasoftware.com/en-us/articles/pki-express/python/trusting-in-custom-certificates) and [Ruby](https://docs.lacunasoftware.com/en-us/articles/pki-express/ruby/trusting-in-custom-certificates).
* PKI Express has no security context, unlike Rest PKI: none of its client libraries has one. 
* The Java library can also trust only ICP-Brasil (`setTrustOnlyICPBrasil`) or only the roots trusted by the operating system
  (`setTrustOnlySystem`). 

## Validation policies

For PAdES, PKI Express 1.20.0 and later accept a validation policy: `pades`, `pades-with-cert-protection`, `pki-brazil`,
`pki-brazil-with-cert-protection` or `adobe-reader`. Set it with `validationPolicy` (PHP) or `setPolicy` (Java), using
`StandardSignaturePoliciesForValidation`. 

* The Node.js and Python libraries have no validation policy for their explorers. The Node.js `signaturePolicy` property applies only when
  signing. 
* The Java 1.23.1 XML explorer takes an `XmlPoliciesForValidation`. 
* Since PKI Express 1.23.0, `trustUncertifiedSigningTime` (PHP) or `setTrustUncertifiedSigningTime` (Java) accepts the signing time the
  signer declared as the date reference. 

## Read the results

* A PAdES or CAdES signature has `signers`; each has its `certificate`, `signingTime`, `messageDigest`, `signaturePolicy`, `timestamps` and
  `validationResults`. A PAdES signer also has `isDocumentTimestamp` and `signatureFieldName`.
* `validationResults` has `isValid()`, `hasErrors()`, `hasWarnings()` and `getSummary()`, and converts to a text that lists every check.
* The certificate has `subjectName`, `emailAddress`, `issuerName`, `serialNumber`, the validity dates and, for ICP-Brasil certificates,
  `pkiBrazil` (`certificateType`, `cpf`, `cnpj`, `responsavel`, `companyName`, `dateOfBirth`, RG and OAB).

The sample's page shows each signer with a "valid" or "invalid" badge from `isValid()`, the signing time, the message digest, the policy,
the ICP-Brasil fields and the full text of the validation results. 

* PKI Express's return code 14 (`ValidationFailed`) and the validation errors are listed in [Return codes](https://docs.lacunasoftware.com/en-us/articles/pki-express/return-codes). 
* TODO(docs): whether `open-*` with `--validate` fails with code 14 for an invalid signature or returns the results with errors (the samples
  read `isValid()` per signer).

## See also

* [Timestamp a PDF](https://docs.lacunasoftware.com/en-us/articles/pki-express/timestamp-pdf)
* [Certificate authentication](https://docs.lacunasoftware.com/en-us/articles/pki-express/authentication)
* [PKI Express quick reference](https://docs.lacunasoftware.com/en-us/articles/pki-express/quick-reference)

**Demos and samples**

- Live demo: [Validate a signed file](https://try.lacunasoftware.com/en/pki-suite/validate-a-signed-file)
- Validate a signed PDF using PKI Express: [Java](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/OpenPadesExpressController.java) · [Node.js](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/open-pades-express.js) · [PHP](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/public/open-pades-express/index.php) · [Python](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/python/flask/sample/views/open_pades_express.py) · [Ruby](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/ruby/rails/app/controllers/open_pades_express_controller.rb)
- Validate a CMS/CAdES signature using PKI Express: [Java](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/java/springmvc/src/main/java/com/lacunasoftware/pkisuite/controller/OpenCadesExpressController.java) · [Node.js](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/nodejs/expressmvc/routes/open-cades-express.js) · [PHP](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/public/open-cades-express/index.php) · [Python](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/python/flask/sample/views/open_cades_express.py)
- Validate a signed XML using PKI Express: [PHP](https://github.com/LacunaSoftware/PkiSuiteSamples/blob/master/php/plain/public/open-xml-express/index.php)
