TLS Delegated Credentials (RFC 9345)¶
A delegated credential (DC) is a short-lived credential that a TLS 1.3 end-entity certificate authorizes its key to sign. It lets you hand a front end (a load balancer, an edge node, a third-party CDN) a credential valid for at most seven days — without ever giving that front end the long-lived certificate private key, and without a round trip to the CA to rotate it.
The mechanism has two halves, and secsy-pki implements both:
- Eligibility — mint the end-entity certificate so relying parties will
accept delegated credentials from it. This is a per-profile opt-in that stamps
the RFC 9345
DelegationUsageextension on the leaf. - Minting — construct and sign a
DelegatedCredentialstructure with the end-entity certificate's private key, and hand the wire form (plus the delegated key) to the TLS terminator.
The operator holds the leaf key¶
A delegated credential is signed by the end-entity certificate's private key — not by any CA or HSM key. This is the single most important thing to understand about minting.
For ordinary CSR-based issuance the subscriber generates its own keypair and the CA only ever sees the public half. In that model secsy-pki cannot mint a delegated credential for you, because it does not have (and by design never had) the leaf private key — you do. Run the offline
secsy-ca delegated-credential minthelper on the host that holds the key.The one case where the system can produce the leaf key is a certificate whose key it generated server-side: a PKCS#12 export whose subject key was escrowed under the M-of-N recovery policy (key escrow). The
POST /api/ca/{id}/delegated-credentialendpoint serves exactly that case — it recovers the escrowed key through a recovery-agent quorum, signs, and immediately zeroizes the plaintext. It never retains the key, and it never accepts a plaintext private key over the wire.
Making a certificate eligible¶
Set delegation_usage: true on an issuance profile. Every leaf issued under it
then carries the non-critical id-ce-delegationUsage extension
(OID 1.3.6.1.4.1.44363.44, an ASN.1 NULL, DER 05 00), which is the marker
relying parties look for before accepting a delegated credential.
A built-in profile is provided:
| Profile | Shape |
|---|---|
server-delegation |
serverAuth, digitalSignature + keyEncipherment, DelegationUsage, 397-day max |
Because eligibility is a profile property with no per-request knob, the operator
console surfaces it as a read-only indicator: selecting a
delegation-eligible profile on the Issue page flags it (RFC 9345
delegated-credential eligible) in the profile policy summary, so an operator
knows the resulting leaf can authorize delegated credentials before issuing it.
Or add your own:
profiles:
- name: edge-delegation
key_usages: [digitalSignature]
ext_key_usages: [serverAuth]
default_validity_days: 7
max_validity_days: 7
delegation_usage: true
Two rules are enforced fail-closed at profile-install time
(SetCustomProfiles) so a misconfiguration surfaces at startup, not at issuance:
digitalSignatureis required. RFC 9345 §4.2 requires the authorizing certificate to carry thedigitalSignaturekey usage; adelegation_usageprofile without it is rejected.- OCSP Must-Staple is forbidden. RFC 9345 §4.2 forbids combining the
DelegationUsagemarker with the RFC 7633 OCSP Must-Staple TLS Feature. A profile that sets bothdelegation_usage: trueand eithermust_staple: trueorallow_must_staple_override: trueis rejected — the latter too, so a per-request override can never sneak Must-Staple onto a delegation-eligible leaf.
The issuance path (buildLeaf, and the PQC/hybrid paths) also enforces the
mutual exclusion at signing time as defense-in-depth: even if a leaf somehow
reached issuance with both, it is refused before any HSM signature.
Because the eligibility short-circuits DC-key compromise to a seven-day window, delegation-eligible leaves are typically issued short-lived and rotated often — frequently via the PKCS#12 + escrow path so the system can mint DCs for them without a human ceremony each time.
Minting with the CLI (operator holds the leaf key)¶
The offline helper needs only the leaf certificate and its private key — no server, no database, no HSM:
$ secsy-ca delegated-credential mint \
-cert leaf.crt -key leaf.key \
-valid-for 24h \
-dc-key-type ecdsa-p256 -dc-key-out dc.key \
-out dc.bin
Minted server delegated credential
Signing algorithm: ecdsa_secp256r1_sha256
Delegated key scheme: ecdsa_secp256r1_sha256
valid_time: 86400 s
Not after: 2026-07-06T00:28:30Z
Delegated private key: dc.key (ecdsa-p256)
Wire credential: dc.bin
Delegated credential (base64):
AAFRgAQDAABbMFkw...
-dc-key-outreceives a freshly generated delegated private key (PKCS#8 PEM) — install it on the TLS terminator alongsidedc.bin. To reuse a key you already generated, pass its SPKI with-dc-pub key.pubinstead (then no key is generated and you keep the private half yourself).-dc-alg/-sign-algoverride the delegated-key scheme and the leaf-signing scheme; by default both are derived from the respective keys (RSA leaves sign with RSASSA-PSS, as RFC 9345 requires).-clientmints a client delegated credential; the default is a server one.-o jsonemits the credential, key, and metadata as JSON for automation.
Verify a credential against its certificate at any time:
$ secsy-ca delegated-credential verify -cert leaf.crt -dc dc.bin
Delegated credential is VALID (server)
Signing algorithm: ecdsa_secp256r1_sha256
valid_time: 86400 s
Not after: 2026-07-06T00:28:30Z
Currently in window: true
Minting with the API (system recovers an escrowed leaf key)¶
POST /api/ca/{id}/delegated-credential mints a DC for a leaf that was exported
as a PKCS#12 with escrow. The caller presents the escrow envelope (returned
at export time) and a quorum of recovery-agent IDs; the server recovers the leaf
key on the HSM, signs, and zeroizes it. It requires the issue capability on
the CA's tenant, exactly like the PKCS#12 export it depends on.
POST /api/ca/{id}/delegated-credential
{
"serial": "141086103…", // the escrowed leaf's serial
"escrow_envelope": { … }, // envelope returned by the PKCS#12 export
"recovery_agents": ["ops-a", "ops-b"], // a recovery quorum (M-of-N)
"valid_for_seconds": 86400,
"dc_key_type": "ecdsa-p256" // omit dc_public_key to have one generated
}
Response (201):
{
"serial": "141086103…",
"delegated_credential": "AAFRgAQD…", // base64 RFC 9345 wire form
"valid_time_seconds": 86400,
"not_after": "2026-07-06T00:28:30Z",
"endpoint": "server",
"algorithm": "ecdsa_secp256r1_sha256",
"expected_cert_verify_algorithm": "ecdsa_secp256r1_sha256",
"dc_public_key_pem": "-----BEGIN PUBLIC KEY-----\n…",
"dc_private_key_pem": "-----BEGIN PRIVATE KEY-----\n…" // present only when generated
}
A leaf that was not issued under a delegation_usage profile is refused (400),
as is any request whose recovery-agent set does not meet the escrow quorum
(400) or whose serial is unknown to the CA (404).
valid_time and the seven-day cap¶
RFC 9345 measures a credential's valid_time in seconds relative to the
certificate's notBefore, and caps it at seven days (604800 s). secsy-pki
computes valid_time = (now + valid_for) − notBefore and refuses to mint if that
exceeds the cap. The practical consequence: mint from a fresh certificate. A
credential minted from a leaf whose notBefore is already several days old has
little (or negative) headroom under the cap; the error names this cause so you
re-issue a short-lived leaf and mint again. This is why the DC workflow pairs so
naturally with short-lived, frequently rotated leaves.
Signature schemes¶
Both the leaf-signing algorithm and the delegated key's handshake algorithm are
TLS 1.3 SignatureScheme code points (RFC 8446). Supported: ecdsa_secp256r1_sha256,
ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256/384/512,
and ed25519. RSA must use RSASSA-PSS — RFC 9345 forbids PKCS#1 v1.5 for
delegated-credential signatures, so the PKCS#1 v1.5 schemes are not offered.
Audit & metrics¶
cert.delegated_credentialaudit events record each mint (target = CA, detail = serial, endpoint,valid_time, schemes); a pairedsecret.recoverevent records the escrow-recovery ceremony behind the API path.- The mint is counted in the
secsy_certificates_total{operation="delegated_credential"}metric alongside the other issuance operations.
Deployment¶
Install the wire credential (dc.bin / the delegated_credential field) and the
delegated private key into a TLS 1.3 terminator that supports RFC 9345 (recent
OpenSSL, BoringSSL, and rustls builds do). The terminator presents the delegated
credential in its Certificate message and uses the delegated key for the
handshake CertificateVerify; a supporting client validates it against the
end-entity certificate exactly as secsy-ca delegated-credential verify does.
Re-mint before not_after — and re-issue the eligible leaf on its own (shorter)
cadence.
```