Message protection¶
Every CMP request and response must carry PKIProtection. Unprotected messages are rejected, and transport encryption does not replace message protection, so an HTTP endpoint is still authenticated.
cmp-issuer supports two protection modes, configured in spec.protection.type:
| Mode | RFC basis | Credential | Use it when |
|---|---|---|---|
PasswordBasedMac |
RFC 4210 PasswordBasedMac | Shared reference and secret | The server issued you an enrollment code |
Signature |
RFC 4210 certificate-based protection | Bootstrap certificate and private key | The device or workload already holds an identity the server trusts |
The selected mode protects initial P10CR and compatibility P10CR renewal. Response validation is identical for both modes.
KUR always uses the current workload certificate and private key for request protection as required by the CMP profile. This replaces the configured bootstrap or shared-secret request protection only for the KUR exchange. The configured CMP trust policy still validates every response.
PasswordBasedMac¶
Secret format¶
Create a Secret in the issuer's namespace:
apiVersion: v1
kind: Secret
metadata:
name: cmp-pbm-credentials
type: Opaque
stringData:
reference: "<server-assigned-reference>"
secret: "<shared-secret>"
Default keys are reference and secret. Override them with referenceKey and secretKey.
Issuer configuration¶
spec:
protection:
type: PasswordBasedMac
passwordBasedMac:
secretRef:
name: cmp-pbm-credentials
referenceKey: reference
secretKey: secret
algorithm:
owf: SHA256
mac: HMACSHA256
iterationCount: 1024
Algorithm fields¶
| Field | Allowed values | Default |
|---|---|---|
owf |
SHA256, SHA384, SHA512 |
SHA256 |
mac |
HMACSHA256, HMACSHA384, HMACSHA512 |
HMACSHA256 |
iterationCount |
100-1048575 | 1024 |
These must match the server's PBM profile. The MAC digest must not be longer than the OWF output.
For example, SHA384 accepts HMACSHA256 or HMACSHA384, while SHA512 accepts all three MACs.
The selected algorithms protect enrollment, polling and confirmation requests without negotiation
or fallback. PBMAC1 is planned rather than implemented.
Response protection¶
Many servers sign every response regardless of how the request was protected. EJBCA does this on any
CMP alias left at its own default of responseprotection signature. For interoperability,
spec.protocol.macResponseProtection defaults to AllowSignature and both a MAC-protected and a
signed response are accepted. This is not the RFC 9483 profile behavior, which requires MAC-based
protection throughout a MAC-protected operation.
Accepting a signature does not weaken the response to an unauthenticated one. The signer must chain to
spec.cmpTrust.caSecretRef and the response sender must name spec.protocol.recipient, which is the
same authority check a Signature issuer already relies on for every response it receives.
Require MAC-based protection throughout with:
spec:
protocol:
macResponseProtection: Strict
Then the shared secret authenticates the response as well as the request, and a signature is rejected
however well it verifies. A MAC-protected response must also use at least 100 iterations, the floor
that applies to iterationCount on requests, so a server that protects its answers with fewer is
rejected as well. Set this where the CMP trust anchor is shared with authorities that must not
be able to answer for the recipient, or to conform to a profile that requires one protection type for a
whole PKI management operation. Confirm the server protects its answers with the shared secret before
turning it on, since a server that signs them will have issued the certificate before the response is
rejected.
Setting spec.protocol.validationProfile to RFC9483 enables Strict behavior implicitly. An
explicit AllowSignature conflicts with that profile and is rejected by API validation.
Server setup¶
The reference and secret must exist on the CMP server before enrollment. cmp-issuer does not register credentials over CMP.
EJBCA client mode treats the enrollment code as a one-time credential. After issuance the end entity
moves to GENERATED and refuses further requests until an administrator resets it. See
Tested PKIs.
Signature protection¶
Secret format¶
apiVersion: v1
kind: Secret
metadata:
name: cmp-signature-credentials
type: kubernetes.io/tls
stringData:
tls.crt: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
tls.key: |
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
ca.crt: |
-----BEGIN CERTIFICATE-----
... optional intermediate chain ...
-----END CERTIFICATE-----
| Key | Required | Purpose |
|---|---|---|
tls.crt |
yes | Protection certificate (default certificateKey) |
tls.key |
yes | Protection private key (default privateKeyKey) |
ca.crt |
optional | Intermediate chain sent in extraCerts (default chainKey) |
Custom key names are set with certificateKey, privateKeyKey and chainKey.
The private key may be RSA, ECDSA, Ed25519 or ML-DSA. PKCS #8 works for every type. PKCS #1 RSA keys and SEC 1 ECDSA keys are accepted too. An ML-DSA key must hold only the seed, see Known limitations.
Issuer configuration¶
spec:
protection:
type: Signature
signature:
secretRef:
name: cmp-signature-credentials
certificateKey: tls.crt
privateKeyKey: tls.key
chainKey: ca.crt
Sender name¶
Each message names the subject of the protection certificate as its sender, encoded exactly as in the
certificate, as RFC 9483 requires. A configured spec.protocol.sender replaces it, except in a KUR
request, which always names the certificate being updated.
extraCerts layout¶
cmp-issuer sends the protection end-entity certificate first, followed by the configured intermediates.
Some servers resolve the signing certificate from extraCerts using senderKID, and Nokia NCM requires the
end-entity certificate to appear there for P10CR.
EJBCA client mode¶
EJBCA resolves the authenticating certificate from its own database, so the credential needs only
tls.crt and tls.key and chainKey may be omitted.
Client mode enrolls only the identity that owns the authenticating certificate, and the requested subject DN must match the registered end entity. A signature-protected issuer therefore issues for its bootstrap identity rather than arbitrary workload names, unless the server profile allows otherwise.
What gets protected¶
The signer protects every message it sends:
- P10CR enrollment requests
- KUR requests with the current workload certificate
pollReqmessages during asynchronous transactionscertConfconfirmation messages when explicit confirmation is configured
What gets validated¶
Before accepting a certificate the signer validates:
- Response PKIProtection algorithm and integrity
- Signer trust against
spec.cmpTrust - That the response sender names the configured
recipient - Transaction identifier and nonces
- P10CR
certReqIdagainst policy or KURcertReqIdfixed at0 - Exactly one
CertResponsein CP or KUP - KUP
caPubsaccording to the selected validation profile - That the issued public key matches the CSR
- A leaf-first chain that validates against CMP trust
- RFC 9881 key usages for ML-DSA certificates
Signature verification tries certificates from the response extraCerts first, then the certificates
configured in spec.cmpTrust.caSecretRef, then the optional
spec.cmpTrust.signerCertificatesSecretRef
bundle. A response can omit a configured signer certificate from extraCerts. Every candidate must
pass chain, sender and signature validation. A signer that is neither configured nor supplied in
extraCerts must have been validated earlier in the same transaction.
Both Interoperable and RFC9483 accept an omitted configured signer. This is an interoperability allowance:
RFC 9810 section 5.1 makes extraCerts
optional, while RFC 9483 section 3.3
requires the protection certificate in the first signed response. The RFC9483 profile enforces its
documented P10CR identifier, MAC continuity and KUP caPubs checks without enforcing that presence rule.
Protected error responses are verified the same way. A verification failure fails the
CertificateRequest and stores no certificate. When the server has already issued the refused
certificate and did not grant implicit confirmation, cmp-issuer reports the refusal to the server in a
certConf with status rejection.
The default Interoperable profile accepts certificates from KUP caPubs and extraCerts only as
untrusted candidates for building the issued chain. They never become trust anchors. The completed
chain must still validate to a CA from spec.cmpTrust. Set kurResponseCaPubs: RequireAbsent for only
the RFC 9483 KUP rule or select validationProfile: RFC9483 to enable the bundled checks.
Confirmation signer retention¶
Some servers omit extraCerts and senderKID from polling and confirmation responses. cmp-issuer
stores the validated response signer in CMPTransaction.status.responseSigner, including when the
signer is a configured anchor omitted from the response. The retained certificate verifies later
responses in that transaction after a controller restart or delayed confirmation. Invalid protection
is rejected. See Tested PKIs for Nokia NCM behavior.
Credential rotation¶
Credentials are reloaded from the Secret on every reconcile. Rotating a Secret during an open
transaction invalidates in-flight protection and the transaction fails within maximumDuration. The
transaction configuration digest records the UID and resourceVersion of every referenced credential
Secret. For KUR it also records both workload Secrets by their UID and a digest of the consumed key
material, so a metadata-only write to a workload Secret does not stop the transaction. cmp-issuer
stops the unfinished transaction before sending more CMP traffic when any of those values changes.
See Known limitations.