Skip to content

Advanced Certificate Settings

In addition to standard certificate options you can configure advanced certificate settings:

  • Extended Key Usages (other than client and server authentication)
  • SANS types (other than DNS names)
  • Custom X.509 extensions (arbitrary OIDs, gated by an operator allowlist)

Extended Key Usages

You can use the extended_key_usages JSON key to specify additional Extended Key Usage extensions beyond those provided by purposes.

Extended Key Usages

Supported values:

Value OID Description
TLS_WEB_SERVER_AUTHENTICATION 1.3.6.1.5.5.7.3.1 Server authentication
TLS_WEB_CLIENT_AUTHENTICATION 1.3.6.1.5.5.7.3.2 Client authentication
CODE_SIGNING 1.3.6.1.5.5.7.3.3 Code signing
EMAIL_PROTECTION 1.3.6.1.5.5.7.3.4 Email protection (S/MIME)
TIME_STAMPING 1.3.6.1.5.5.7.3.8 Trusted timestamping
OCSP_SIGNING 1.3.6.1.5.5.7.3.9 OCSP signing
IPSEC_END_SYSTEM 1.3.6.1.5.5.7.3.5 IPSec end system
IPSEC_TUNNEL 1.3.6.1.5.5.7.3.6 IPSec tunnel
IPSEC_USER 1.3.6.1.5.5.7.3.7 IPSec user
ANY 2.5.29.37.0 Any extended key usage
NONE - No additional extended key usages

You can also specify custom OIDs directly, e.g. "1.3.6.1.5.5.7.3.17" for Internationalized Email Addresses.

Example - Code signing certificate:

{
  "common_name": "my-code-signer",
  "purposes": ["client_auth"],
  "extended_key_usages": ["CODE_SIGNING"]
}

Example - Multiple extended key usages:

{
  "common_name": "my-cert",
  "purposes": ["client_auth"],
  "extended_key_usages": ["CODE_SIGNING", "EMAIL_PROTECTION", "TIME_STAMPING"]
}

Example - Custom OID:

{
  "common_name": "my-cert",
  "extended_key_usages": ["1.3.6.1.5.5.7.3.17"]
}

Extended key usages from both purposes and extended_key_usages are combined. Duplicate OIDs are automatically removed.

Subject Alternative Names

The sans JSON key allows you to specify Subject Alternative Names for the certificate. The module supports multiple SAN types and input formats.

Subject Alternative Names

Supported SAN Types

Type Description Example Value
DNS_NAME DNS hostname example.com, *.example.com
IP_ADDRESS IPv4 or IPv6 address 192.168.1.1, 2001:db8::1
EMAIL_ADDRESS Email address (RFC822) user@example.com
URL Uniform Resource Identifier https://example.com/path
DN Distinguished Name CN=Example,O=Org,C=US

Input Formats

The sans field accepts multiple input formats:

No SANs specified (default behavior): If sans is not specified, the common name will be used as a DNS_NAME SAN if it's a valid domain.

Single DNS name (string):

"sans": "example.com"

Multiple DNS names (list of strings):

"sans": ["example.com", "www.example.com", "*.example.com"]

Multiple SAN types using a map:

"sans": {
  "DNS_NAME": ["example.com", "www.example.com"],
  "IP_ADDRESS": ["192.168.1.1", "10.0.0.1"],
  "EMAIL_ADDRESS": "admin@example.com"
}

Multiple SAN types using a list of objects:

"sans": [
  {"type": "DNS_NAME", "value": "example.com"},
  {"type": "IP_ADDRESS", "value": "192.168.1.1"},
  {"type": "EMAIL_ADDRESS", "value": "admin@example.com"},
  {"type": "URL", "value": "https://example.com"},
  {"type": "DN", "value": "CN=Partner,O=Partner Org,C=US"}
]

Validation

All SAN values are validated based on their type:

  • DNS_NAME: Must be a valid domain name (wildcards supported)
  • IP_ADDRESS: Must be a valid IPv4 or IPv6 address
  • EMAIL_ADDRESS: Must be a valid email address format
  • URL: Must be a valid URL
  • DN: Must contain at least one valid DN attribute (e.g., CN=, O=, OU=, C=)

Invalid SANs are logged and excluded from the certificate but do not cause the request to fail.

Custom X.509 Extensions

The extensions JSON key lets callers embed arbitrary X.509 extensions in an issued certificate, identified by OID with a base64-encoded DER value. This is useful when an integration requires a certificate to carry a proprietary or organisation-specific extension that the standard fields (purposes, extended_key_usages, sans) cannot express.

Custom Extensions

This is an additive capability: custom extensions are appended to the certificate alongside the extensions the CA always emits (Key Usage, Extended Key Usage, Certificate Policies, Subject Key Identifier, and so on). It can never replace or override those.

Enabling the feature

Custom extensions are disabled by default. An operator opts in per deployment by listing the permitted OIDs in the custom_extension_allowlist Terraform variable:

module "certificate_authority" {
  source = "serverless-ca/ca/aws"

  custom_extension_allowlist = [
    "1.3.6.1.4.1.55555.1.1", # private OID for device-class metadata
  ]
}

A request that includes an extensions entry whose OID is not on the allowlist is rejected with a clear error (and an SNS Certificate Request Rejected notification), rather than being silently dropped. Unlike an invalid SAN or extended key usage, a custom extension is normally load-bearing for the integration that requested it, so a missing one should fail loudly rather than produce a certificate that is quietly incomplete.

The extensions the CA emits itself are always reserved and are rejected even if added to the allowlist, because the CA must control them unconditionally. Custom extensions can only add new OIDs, never replace or shadow one the CA manages. Reserving Subject Alternative Name in particular prevents a caller from forging the certificate's identity:

OID Extension
2.5.29.14 Subject Key Identifier
2.5.29.15 Key Usage
2.5.29.17 Subject Alternative Name
2.5.29.19 Basic Constraints
2.5.29.31 CRL Distribution Points
2.5.29.32 Certificate Policies
2.5.29.35 Authority Key Identifier
2.5.29.37 Extended Key Usage
1.3.6.1.5.5.7.1.1 Authority Information Access

Choosing what to allowlist. Some extensions carry authorization meaning, so allowlist deliberately. For example, Microsoft Active Directory's SID security extension (1.3.6.1.4.1.311.25.2) binds a certificate to an AD account's SID for strong certificate mapping; allowing callers to set it freely would let them mint certificates that map to arbitrary accounts. The reserved-OID denylist above blocks the CA's own structural extensions, but only you know which application-level OIDs are safe to delegate in your environment.

Request format

Each entry in the extensions list has the following fields:

Field Type Required Description
oid str yes Dotted-decimal object identifier, e.g. "1.3.6.1.4.1.55555.1.1"
value_b64 str yes Base64-encoded DER bytes of the extension value. The caller is responsible for the ASN.1 encoding.
critical bool no Whether the extension is marked critical. Defaults to false.

Example - organisation-specific device metadata (primary use case)

A common reason to embed a custom extension is to carry signed, machine-readable metadata that relying parties read at connection time. Suppose you run a fleet of devices that authenticate to your services with client certificates (mTLS), and you classify each device into a "class" that drives authorization - for example, only class A devices may call a privileged firmware-update API. Stamping that class into the certificate at issuance, under a private OID, has three advantages:

  • It is signed by the CA. The device cannot forge or change its own class, unlike a value it asserts out-of-band (e.g. an application-layer header).
  • It needs no extra round-trip. The class is already present in the certificate the device presents during the TLS handshake, so the relying party reads it locally instead of looking the device up in a separate class database or directory.
  • It uses a dedicated OID. The value is structured and purpose-built, rather than overloading an identity field such as the Common Name, which is meant for naming and has its own format expectations.

Here the value is a simple UTF8String of device-class-a, placed under a private OID beneath your organisation's Private Enterprise Number (PEN). The 55555 arc below is a placeholder - substitute your own PEN. The DER encoding of that UTF8String (0c 0e 64 65 76 69 63 65 2d 63 6c 61 73 73 2d 61) base64-encodes to DA5kZXZpY2UtY2xhc3MtYQ==:

{
  "common_name": "device-001.example.com",
  "purposes": ["client_auth"],
  "extensions": [
    {
      "oid": "1.3.6.1.4.1.55555.1.1",
      "value_b64": "DA5kZXZpY2UtY2xhc3MtYQ==",
      "critical": false
    }
  ]
}

With 1.3.6.1.4.1.55555.1.1 on the allowlist, the issued certificate carries the extension verbatim. A relying party terminating the mTLS connection reads the extension off the client certificate and authorises (or rejects) the request based on the class - trusting only what the CA signed, with no separate lookup.

Advanced use case - step-ca stepProvisioner

This feature also makes it possible to put Serverless CA behind a custom step-ca-compatible facade, so that step-ca's renewal model can be driven by a Serverless CA backend. step-ca embeds a proprietary stepProvisioner extension (OID 1.3.6.1.4.1.37476.9000.64.1, an ASN.1 SEQUENCE) in the certificates it issues, and reads it back at renewal time to determine which provisioner to use. Allowlisting that OID lets the facade request certificates that carry it.

This is a workaround pattern, not the canonical way to integrate step-ca: both the facade and the ASN.1 SEQUENCE encoding of the stepProvisioner value are non-trivial, and you own that complexity. It is documented here as a concrete example of the kind of integration the extensions field unlocks, rather than as a turnkey step-ca story.