Service identity (RFC 9525)

Service identity: whether a certificate presents a name the client was trying to reach. RFC 9525 calls the names the client builds reference identities and the names the certificate carries presented identifiers, and the check is whether any of the first matches any of the second.

This is a separate answer from path validation, and RFC 9525 sec. 1.2 says so: it does not supersede certificate validation, and an application needs both. pki.path.validate takes opts.identity to run the two together and report each.

pki.identity.match

since 0.8.8 stable
pki.identity.match(cert, references, opts?) -> verdict

Check a certificate against the names a client expected to reach, which RFC 9525 calls reference identities. This answers a different question from path validation and stands in for none of it: a certificate that matches here may still chain to nothing, and pki.path.validate is what says whether it chains.

references is one reference identity or an array of them, and the caller builds that list from what it was trying to reach rather than from the certificate (sec. 6.1.1). Four forms are read. A domain name, "www.example.com", is a DNS-ID. A textual IP address, "192.0.2.1" or "2001:db8::1", or a 4- or 16-octet BufferSource, is an IP-ID. A record { type: "srv", service: "_imap", value: "example.com" } is an SRV-ID, where the leading underscore is part of the service name (RFC 4985 sec. 2). A record { type: "uri", scheme: "https", value: "www.example.com" } is a URI-ID. A record carries its service type and its domain together, so no comparison can pair the service of one reference with the domain of another (sec. 6.5).

The subject distinguished name is never a source of identity. The commonName is not read under any option or fallback, including when the certificate carries no subjectAltName at all, and neither is any other RDN (sec. 2, RFC 9110 sec. 4.3.4). A certificate presenting nothing this check reads fails with reason naming that, never with an absent field.

An IP-ID is compared octet for octet against an iPAddress entry and never against a dNSName carrying the same address as text, and no prefix or mask applies (sec. 6.2, sec. 6.4). ::ffff:192.0.2.1 and 192.0.2.1 pack to sixteen and four octets, so under that comparison they do not match.

A name is compared label by label under an ASCII-only case fold, with one trailing dot normalized on both sides. An A-label is compared as the ASCII it is and is not decoded (sec. 7.3), so a reference identity carrying a character outside ASCII is refused with identity/unsupported-reference: RFC 9525 sec. 6.3 requires a U-label to be converted first and this toolkit carries no IDNA implementation. Pass a name already in A-labels.

Wildcards are ON, and opts.wildcards: false turns them off, which sec. 3 requires a specification to state either way. A wildcard matches when it is the complete content of the left-most label and there is exactly one of them, and it reaches exactly one label: *.example.com matches foo.example.com, and matches neither example.com nor a.b.example.com. A presented name carrying a wildcard that fails those rules is ignored and the search continues, which is what sec. 6.3 directs; it does not refuse the certificate. A wildcard in a reference identity is a caller mistake and is refused.

On a match the verdict's matchedReference is what sec. 6.6 has the caller use as the validated identity of the service. A reference given as a string comes back as that string; a record or a BufferSource comes back as a frozen { type, value, ... } built from the values that were compared, with an address in its textual form, so what the verdict names is the identity that was checked rather than an object the caller can still write to.

Protection against a wildcard that spans an administrative boundary, such as *.co.uk, is out of scope here as it is in sec. 7.1: this toolkit ships no public-suffix list.

A name constraint and an identity check read the same subjectAltName entries to answer different questions, and neither is evidence for the other. A dNSName name constraint on an issuer says nothing about whether an SRV-ID or a URI-ID in a leaf is constrained (sec. 7.6); a client wanting that correspondence layers it on top of both.

Options

wildcards  Whether a presented identifier may carry a wildcard (RFC 9525 sec. 6.3),
                  default `true`; `false` puts every wildcard entry in `ignored`.

Example

async function example() {
  var pair = await pki.key.generate("Ed25519");
  var der = await pki.x509.sign({ subject: "example.com", subjectPublicKey: await pki.key.export(pair.publicKey),
    notBefore: new Date("2026-01-01T00:00:00Z"), notAfter: new Date("2036-01-01T00:00:00Z"),
    extensions: { subjectAltName: [{ dNSName: "www.example.com" }] } },
    { key: await pki.key.export(pair.privateKey) });
  var v = pki.identity.match(der, "www.example.com");
  console.log(v.matched, v.matchedReference);
}
example();

References