POST /api/session/certificate/challenge
Certificate login, leg 1: present a certificate and receive 32 bytes to sign.
Begins an X.509 certificate login (AM08). Send the caller's certificate, DER-encoded and
then base64-encoded; receive a pending id and a 32-byte challenge.
This route holds an open, half-authenticated key-server connection between the two
legs, which is why the number of concurrent exchanges is bounded and why an unanswered
exchange expires in 30 seconds. The pending id is SINGLE USE: it is consumed by the
first answer, whether that answer succeeds or fails.
The Gateway never sees the private key. The key server resolves the principal by
certificate digest, and only the signature over the challenge proves possession -- so
there is no Gateway identity in this flow, and no 503 ..._NOT_CONFIGURED arm.
WARNING: the signature is the RAW PKCS#1 v1.5 primitive over the 32 challenge bytes --
no digest and no DigestInfo wrapper. In Node that is
crypto.privateEncrypt({ key, padding: crypto.constants.RSA_PKCS1_PADDING }, challenge).
crypto.sign(null, ...) LOOKS like the no-digest form and is not: it silently defaults
to SHA-256, and the key server then answers with a refusal indistinguishable from a wrong
key. This is the single commonest failure on this route.
Authentication
No session required. This operation is reachable without an Authorization header. See Authenticating.
Request
| Field | Type | Required | Description |
|---|---|---|---|
certificate | string | yes | The caller's X.509 certificate, DER-encoded then base64-encoded. NOT PEM -- base64 of PEM is base64 of base64, and is refused with a code saying so. |
Example
{
"certificate": "..."
}
Responses
200
A challenge was issued and a key-server connection is being held for you.
| Field | Type | Required | Description |
|---|---|---|---|
pending | string | yes | Single use. Present it to POST /api/session/certificate. |
challenge | string | yes | 32 bytes, base64. Sign these bytes, not a hash of them. |
expiresInSeconds | number | yes | |
detail | string | yes |
Example
{
"pending": "...",
"challenge": "...",
"expiresInSeconds": 0,
"detail": "..."
}
400
The certificate could not be used. MISSING_CERTIFICATE, MALFORMED_CERTIFICATE, or CERTIFICATE_NOT_DER -- the last covering both a PEM block and bytes that do not begin with an ASN.1 SEQUENCE tag.
401
SIGNATURE_REJECTED or CERTIFICATE_NOT_ENROLLED, relayed from the key server. Both can arrive on THIS leg rather than the second: the key server refuses an unenrolled or disabled certificate as soon as it sees it, before issuing any challenge.
403
PRINCIPAL_DISABLED -- the certificate's principal is disabled.
500
CERT_EXCHANGE_UNEXPECTED -- the key server completed the authentication without issuing a challenge, or issued one of the wrong size. No session was created.
503
CERT_EXCHANGE_LIMIT -- every concurrent exchange slot is in use. Each holds an open key-server connection, so the number is bounded on purpose. Nothing was sent to the key server. Carries Retry-After.
Error handling
Every failure answers JSON carrying at least code and detail. Match on code — detail is written for a human debugging the call and its wording is not part of the contract. See the error model.
| Status | Meaning |
|---|---|
400 | The certificate could not be used. MISSING_CERTIFICATE, MALFORMED_CERTIFICATE, or CERTIFICATE_NOT_DER -- the last covering both a PEM block and bytes that do not begin with an ASN.1 SEQUENCE tag. |
401 | SIGNATURE_REJECTED or CERTIFICATE_NOT_ENROLLED, relayed from the key server. Both can arrive on THIS leg rather than the second: the key server refuses an unenrolled or disabled certificate as soon as it sees it, before issuing any challenge. |
403 | PRINCIPAL_DISABLED -- the certificate's principal is disabled. |
500 | CERT_EXCHANGE_UNEXPECTED -- the key server completed the authentication without issuing a challenge, or issued one of the wrong size. No session was created. |
503 | CERT_EXCHANGE_LIMIT -- every concurrent exchange slot is in use. Each holds an open key-server connection, so the number is bounded on purpose. Nothing was sent to the key server. Carries Retry-After. |