Skip to content

Verification

Chain verification

ts
import {
  createSelfSignedCertificate,
  createCertificate,
  generateKeyPair,
  verifyCertificateChain,
} from 'micro509';

// Build a root CA
const root = await createSelfSignedCertificate({
  subject: { commonName: 'Demo Root CA' },
  extensions: {
    basicConstraints: { ca: true },
    keyUsage: ['keyCertSign', 'cRLSign'],
  },
});

// Intermediate CA signed by the root
const intKeys = await generateKeyPair();
const intermediate = await createCertificate({
  issuer: { commonName: 'Demo Root CA' },
  subject: { commonName: 'Demo Intermediate CA' },
  publicKey: intKeys.publicKey,
  signerPrivateKey: root.keyPair.privateKey,
  issuerPublicKey: root.keyPair.publicKey,
  extensions: {
    basicConstraints: { ca: true },
    keyUsage: ['keyCertSign', 'cRLSign'],
  },
});

// Leaf signed by the intermediate
const leafKeys = await generateKeyPair();
const leaf = await createCertificate({
  issuer: { commonName: 'Demo Intermediate CA' },
  subject: { commonName: 'api.example.com' },
  publicKey: leafKeys.publicKey,
  signerPrivateKey: intKeys.privateKey,
  issuerPublicKey: intKeys.publicKey,
  extensions: {
    extendedKeyUsage: ['serverAuth'],
    subjectAltNames: [
      { type: 'dns', value: 'api.example.com' },
    ],
  },
});

const result = await verifyCertificateChain({
  leaf: leaf.pem,
  intermediates: [intermediate.pem],
  roots: [root.certificate.pem],
  purpose: 'serverAuth',
  serviceIdentity: {
    type: 'dns',
    value: 'api.example.com',
  },
});

if (result.ok) {
  console.log(
    `Valid chain: ${result.value.chain.length} certificates`,
  );
} else {
  console.log(`\
Failed: ${result.error.code}
At index: ${result.error.index}`);
}

Verification purposes

Four built-in validation profiles. serverAuth, clientAuth, and ca are passed as purpose to verifyCertificateChain; code signing has its own validateForCodeSigning profile:

ts
import {
  createSelfSignedCertificate,
  createCertificate,
  generateKeyPair,
  verifyCertificateChain,
  validateForCodeSigning,
} from 'micro509';

// Shared root CA
const root = await createSelfSignedCertificate({
  subject: { commonName: 'Demo Root CA' },
  extensions: {
    basicConstraints: { ca: true },
    keyUsage: ['keyCertSign', 'cRLSign'],
  },
});

// Issue a leaf with a given EKU ('serverAuth' | 'clientAuth' | 'codeSigning')
async function issue(cn, eku) {
  const keys = await generateKeyPair();
  return createCertificate({
    issuer: { commonName: 'Demo Root CA' },
    subject: { commonName: cn },
    publicKey: keys.publicKey,
    signerPrivateKey: root.keyPair.privateKey,
    issuerPublicKey: root.keyPair.publicKey,
    extensions: {
      extendedKeyUsage: [eku],
      subjectAltNames: [{ type: 'dns', value: cn }],
    },
  });
}

// TLS server (default)
const server = await issue('srv.example', 'serverAuth');
const r1 = await verifyCertificateChain({
  leaf: server.pem,
  roots: [root.certificate.pem],
  purpose: 'serverAuth',
});

// TLS client
const client = await issue('cli.example', 'clientAuth');
const r2 = await verifyCertificateChain({
  leaf: client.pem,
  roots: [root.certificate.pem],
  purpose: 'clientAuth',
});

// Code signing
const signer = await issue('sign.example', 'codeSigning');
const r3 = await validateForCodeSigning({
  leaf: signer.pem,
  roots: [root.certificate.pem],
});

// CA certificate (verify an intermediate as a CA)
const caKeys = await generateKeyPair();
const intermediate = await createCertificate({
  issuer: { commonName: 'Demo Root CA' },
  subject: { commonName: 'Demo Intermediate CA' },
  publicKey: caKeys.publicKey,
  signerPrivateKey: root.keyPair.privateKey,
  issuerPublicKey: root.keyPair.publicKey,
  extensions: {
    basicConstraints: { ca: true },
    keyUsage: ['keyCertSign', 'cRLSign'],
  },
});
const r4 = await verifyCertificateChain({
  leaf: intermediate.pem,
  roots: [root.certificate.pem],
  purpose: 'ca',
});

console.log(`\
serverAuth:  ${r1.ok ? 'ok' : `${r1.error.code}@${r1.error.index}`}
clientAuth:  ${r2.ok ? 'ok' : `${r2.error.code}@${r2.error.index}`}
codeSigning: ${r3.ok ? 'ok' : `${r3.error.code}@${r3.error.index}`}
ca:          ${r4.ok ? 'ok' : `${r4.error.code}@${r4.error.index}`}`);

Service identity matching

ts
import {
  createSelfSignedCertificate,
  parseCertificatePem,
  unwrap,
} from 'micro509';
import { matchServiceIdentity } from 'micro509/verify';

// Create and parse a certificate to match against
const { certificate } = await createSelfSignedCertificate({
  subject: { commonName: 'example.com' },
  extensions: {
    subjectAltNames: [
      { type: 'dns', value: 'example.com' },
    ],
  },
});

const parsed = unwrap(parseCertificatePem(certificate.pem));

const result = matchServiceIdentity({
  certificate: parsed,
  serviceIdentity: { type: 'dns', value: 'example.com' },
});

if (result.ok) {
  console.log('Identity matches the certificate SAN');
} else {
  console.log(result.error.code);
  // 'subject_alt_name_mismatch' | ...
}

Supported identity types:

  • DNS-ID — with wildcard matching and case-insensitive comparison
  • IP-ID — with IPv6 normalization
  • URI-ID — scheme + host matching
  • SRV-ID — service name matching via otherName SAN

Error codes

The VerifyErrorCode type covers 21 failure modes:

CodeMeaning
no_trusted_rootNo trust anchor matched the chain
issuer_not_foundCould not find issuer for a certificate
signature_invalidCryptographic signature check failed
certificate_expiredCertificate outside validity window
ca_requiredNon-CA certificate used as issuer
key_cert_sign_requiredIssuer missing keyCertSign key usage
path_length_exceededChain exceeds pathLenConstraint
authority_key_identifier_mismatchAKI/SKI cross-check failed
extended_key_usage_invalidEKU doesn't match requested purpose
subject_alt_name_mismatchSAN doesn't match service identity
common_name_fallback_suppressedCN match suppressed by presented identifiers
self_signed_leaf_not_allowedSelf-signed leaf without explicit opt-in
unrecognized_critical_extensionUnknown critical extension
intermediate_eku_constraintIntermediate has restrictive EKU
policy_processing_not_implementedPolicy processing not implemented
name_constraints_violatedName constraints check failed
initial_name_constraints_not_implementedInitial name constraints not implemented
unsupported_name_constraintsUnsupported name constraint form
explicit_policy_requiredPolicy required but not satisfied
initial_policy_set_not_satisfiedInitial policy set not met
unsupported_signature_algorithm_parametersUnknown signature algorithm

CSR verification

ts
import {
  createCertificateSigningRequest,
  generateKeyPair,
  verifyCertificateSigningRequest,
} from 'micro509';

// Build a CSR to verify
const keyPair = await generateKeyPair({ kind: 'ed25519' });
const csr = await createCertificateSigningRequest({
  subject: { commonName: 'csr.example' },
  publicKey: keyPair.publicKey,
  signerPrivateKey: keyPair.privateKey,
  extensions: {
    subjectAltNames: [
      { type: 'dns', value: 'csr.example' },
    ],
  },
});

const result = await verifyCertificateSigningRequest(
  csr.pem,
);

if (result.ok) {
  console.log('CSR signature valid');
} else {
  console.log('CSR invalid:', result.error.code);
}

Released under the MIT License.