Validating an attribute certificate¶
Validating an attribute certificate means validating two certification paths — the holder's and the issuer's — and then six more checks on top.
The call¶
use SpomkyLabs\Pki\X509\AttributeCertificate\Validation\ACValidationConfig;
use SpomkyLabs\Pki\X509\AttributeCertificate\Validation\ACValidator;
use SpomkyLabs\Pki\X509\AttributeCertificate\Validation\Exception\ACValidationException;
use SpomkyLabs\Pki\X509\CertificationPath\CertificationPath;
$holderPath = CertificationPath::toTarget($holderCertificate, $trustAnchors, $intermediates);
$issuerPath = CertificationPath::toTarget($authorityCertificate, $trustAnchors, $intermediates);
$config = ACValidationConfig::create($holderPath, $issuerPath)
->withTrustedAttributeAuthorities($authorityCertificate) // see below — do not skip this
->withTargets(TargetName::create(DNSName::create('app.example.com')));
try {
$validated = ACValidator::create($ac, $config)->validate();
} catch (ACValidationException $e) {
// not authorised
}
validate() returns the attribute certificate, or throws. Pass a
crypto engine as the third argument of create() to use your own.
The check you have to configure¶
Without withTrustedAttributeAuthorities(), any end-entity certificate will do
RFC 5755 section 5 check 4 says the attribute certificate's issuer must be directly trusted as an attribute authority. The validator cannot know who you trust, so without this call it skips the check — and then accepts whatever end-entity certificate the issuer path happens to end in.
Any certificate from any CA in your trust store would then be able to grant any role.
$config = $config->withTrustedAttributeAuthorities($authorityA, $authorityB);
$config->trustedAttributeAuthorities(); // a CertificateBundle, or null
The check is CertificateBundle::contains(), so it is an exact match on the encoding.
What the validator checks¶
In order:
- The holder's certification path validates, and
$ac->isHeldBy()that path's end-entity certificate. A path failure is wrapped:Failed to validate holder PKC's certification path. - The issuer's certification path validates, and
$ac->isIssuedBy()its end-entity certificate. - The issuer profile, RFC 5755 section 4.5: the issuer certificate is not a CA, and its
keyUsage, if present, permitsdigitalSignatureornonRepudiation. - The attribute authority is trusted, if you configured the bundle.
- The AC's own signature, under the same policy as the paths — see below.
- The validity period contains the evaluation time.
- The targets match, if the AC carries a
targetInformationextension. - Every critical extension is one the validator processes, or one you declared.
The AC's own signature is held to the path policy
The attribute authority's key is never a working key of either path, so its size would never have
been measured, and the crypto engine would happily verify an MD5 signature. The signature that
carries the authorisation is checked against the same allowedSignatureAlgorithms() and
minimumRSAKeySize() as the paths — which is a reason to hand the config a hardened
PathValidationConfig.
Configuration¶
$config = ACValidationConfig::create($holderPath, $issuerPath);
$config = $config->withPathValidationConfig($pathConfig);
$config = $config->withEvaluationTime(new DateTimeImmutable('2026-06-01'));
$config = $config->withTrustedAttributeAuthorities(...$certificates);
$config = $config->withTargets(...$targets);
$config = $config->withAdditionalCriticalExtensions('1.3.6.1.4.1.99999.7');
$config->holderPath();
$config->issuerPath();
$config->hasPathValidationConfig();
$config->pathValidationConfig();
$config->evaluationTime();
$config->targets();
$config->additionalCriticalExtensions();
The path validation configuration¶
Pass a hardened one, since it governs the AC's own signature as well as the two paths:
use SpomkyLabs\Pki\X509\CertificationPath\PathValidation\PathValidationConfig;
$pathConfig = PathValidationConfig::defaultConfig()
->withAllowedSignatureAlgorithms(...PathValidationConfig::HARDENED_ALLOWED_SIGNATURE_ALGORITHMS)
->withMinimumRSAKeySize(PathValidationConfig::HARDENED_MINIMUM_RSA_KEY_SIZE);
$config = $config->withPathValidationConfig($pathConfig);
The evaluation time is applied to the path configuration for you, so both paths and the AC are judged at the same instant.
The maximum path length is derived only when you supply no configuration
With no withPathValidationConfig(), the maximum length is taken from each path's own length. Once
you supply a configuration, your withMaxLength() is respected — overriding it unconditionally made
maxLength() a no-op for both paths.
Targets¶
use SpomkyLabs\Pki\X509\Certificate\Extension\Target\TargetGroup;
use SpomkyLabs\Pki\X509\Certificate\Extension\Target\TargetName;
use SpomkyLabs\Pki\X509\GeneralName\DNSName;
$config = $config->withTargets(
TargetName::create(DNSName::create('app.example.com')),
TargetGroup::create(DNSName::create('example.com'))
);
An AC with a targetInformation extension is refused unless one of its targets is among these. An AC
without the extension is not targeted at all and passes whatever you configure — targeting constrains
only the ACs that opt into it.
Critical extensions¶
The validator processes targetInformation and noRevAvail. Anything else marked critical makes
validation fail, which is RFC 5755 section 5 check 7: an attribute certificate carries authorisation, a
critical extension is how its issuer narrows that authorisation, and honouring one you cannot process
would grant the attributes unconditionally.
withAdditionalCriticalExtensions() stops the objection. It does not implement the check — that is
yours.
What the exception tells you¶
| Message | Means |
|---|---|
Failed to validate holder PKC's certification path. |
the holder's path failed; $e->getPrevious() is the PathValidationException |
Name mismatch of AC's holder PKC. |
the path validated, but the AC is not held by that certificate |
Failed to validate issuer PKC's certification path. |
the issuer's path failed |
Name mismatch of AC's issuer PKC. |
the AC was not issued by that certificate |
Issuer PKC must not be a CA. |
the attribute authority's certificate asserts cA:TRUE |
Issuer PKC's Key Usage extension doesn't permit verification of digital signatures. |
neither digitalSignature nor nonRepudiation |
Issuer PKC is not a trusted attribute authority. |
not in the bundle you configured |
Signature algorithm … is not allowed. / RSA key size … is below the minimum … |
the AC's own signature, under the path policy |
Failed to verify signature. |
the AC's signature does not match |
Validity period has not started. / Attribute certificate has expired. |
the evaluation time |
Attribute certificate doesn't have a matching target. |
targetInformation does not include your targets |
Attribute certificate contains an unhandled critical extension: …. |
see above |
Using the result¶
$validated = ACValidator::create($ac, $config)->validate();
$attributes = $validated->acinfo()->attributes();
if ($attributes->hasRole()) {
$role = $attributes->role()->roleName()->string();
}
The attributes are authorisation statements. Mapping them to permissions in your application is your decision, as is what to do when the same holder presents two attribute certificates that disagree.
And remember what the two paths do not tell you: revocation is not checked, for the holder's certificate, the issuer's, or — there being nothing to check it with — the attribute certificate itself.