# Certificate and CRL Profiles Normative field-by-field profiles. Modeled on *DoD PKI NIPRNet Certificate and CRL Profiles* v6.0 (11 Jun 2025), adapted for the root set in [architecture.md §2](architecture.md). This is deliberately a **separate document from the Certificate Policy**. The CP says *what assurance means*; this says *what bytes go in the certificate*. They change on different cadences and are approved by different people: profiles by the engineering working group, the CP by the PMA. --- ## 1. Conventions - `c=yes` / `c=no`: the extension's criticality bit. - Angle brackets `<…>` are variables; `{…}` are OIDs; `|` separates alternatives. - **Certificates shall not contain any critical extension not listed in these profiles.** - Values not explicitly prohibited are permitted; the profile lists an unambiguous combination of required / optional / prohibited. - Only certificates issued after this document's effective date need to comply. ### 1.1 Naming All DNs carry the fixed prefix: ``` C=, O=, OU=, OU=PKI ``` | Entity | Form | |---|---| | CA | `, CN=` | | OCSP responder | `, CN=` | | End entity | `, OU=, CN=` | - **One attribute value per RDN.** No multi-valued RDNs. - `PrintableString` for all directoryString attributes. `UTF8String` for CN only when PrintableString cannot encode it. - Human CN: `Last First MI [Gen] `. The `` is mandatory and immutable. - Device CN: FQDN | IP address | `-` | application name. - Wildcards: least-significant label only, and only when the certificate genuinely covers that entire namespace. - Subject and issuer DNs must match their repository/directory entries exactly. ### 1.2 Algorithms | Policy strength | Signature | Subject key | Hash | |---|---|---|---| | Policy OID | Signature | Subject key | Hash | |---|---|---|---| | `-hardware-112` | `sha256WithRSAEncryption` | **RSA 2048** | SHA-256 | | `-hardware-128` / `-device-128` | `ecdsa-with-SHA256` | **EC P-256** | SHA-256 | | `-hardware-192` | `ecdsa-with-SHA384` | **EC P-384** | SHA-384 | | `-medium-128` (RSA fallback) | `sha256WithRSAEncryption` | RSA 3072 | SHA-256 | | PQ (phase 2) | `id-ml-dsa-65` | ML-DSA-65 | - | A certificate is signed with strength **at least** that of the highest policy OID it asserts. CRLs use the same signature algorithm, key size, and hash as the issuing CA's certificates. ### 1.2b Token capability selects the policy OID The *DoD Token Platform Utilization and Variation Matrix* v2.11 (4 Apr 2025) lists **every current platform**, both CAC and Alternate Token, as `RSA EE 2048 with SHA256`. **No current DoD token does ECC.** That includes both ALT platforms: | Platform | Type | Chip | Comms | CAC Applet Pkg | Crypto | |---|---|---|---|---|---| | IDEMIA Cosmo V8.0 | Alternate Token | 128k | T=1 | V2.7.4 | RSA 2048 / SHA-256 | | G+D Sm@rtCafe Expert v7.0 144K DI | Alternate Token | 144k | T=1 | V2.7.5 | RSA 2048 / SHA-256 | | G+D Sm@rtCafe Expert v7.0 144K DI | CAC | 144k | T=0/T=CL | V2.7.5 | RSA 2048 / SHA-256 | | Gemalto TOP DL V2.1 144K FIPS | CAC | 144k | T=0/T=CL | V2.7.4 | RSA 2048 / SHA-256 | | IDEMIA ID-one 125 V8.0D | CAC | 128k | T=0/T=CL | V2.7.4 | RSA 2048 / SHA-256 | **ECC-capable tokens are also in scope.** The fleet is therefore mixed, and the profile does not branch; the **policy OID** does. The token's capability selects which OID the credential asserts: | Token capability | Policy OID | Subject key | Encryption cert `keyUsage` | |---|---|---|---| | DoD CAC / ALT stock (above) | `-hardware-112` | RSA 2048 | `keyEncipherment` | | ECC token, P-256 | `-hardware-128` | EC P-256 | `keyAgreement` | | ECC token, P-384 | `-hardware-192` | EC P-384 | `keyAgreement` | This is the DoD pattern from [architecture.md §5](architecture.md) working as designed: strength is a dimension of the OID, so a relying party that requires 128-bit expresses it as a *policy OID set* and RFC 5280 path validation does the rest. No custom certificate parsing, and no second profile document. **Three consequences:** 1. **`keyUsage` on the encryption certificate is key-type dependent**: `keyEncipherment` for RSA, `keyAgreement` for ECC. This is the only field in the human profiles that varies by algorithm, and it is exactly the split DoD's own Table 19 makes. Get it wrong and S/MIME silently fails. 2. **The 2030-12-31 sunset binds only the RSA-2048 population.** No RSA-2048 certificate may be valid past that date, so a 3-year credential on DoD stock is issuable only until **2027-12-31**, then shrinks month-for-month. ECC tokens are unaffected, which is a concrete argument for moving the fleet to them rather than re-carding on RSA. 3. **CA keys stay EC P-384 regardless.** An RSA-2048 subject key under a P-384 CA is correct: §1.2's rule is that the *signature* be at least as strong as the highest policy OID asserted, and `-112` is the ceiling for a card-bound certificate on DoD stock. ### 1.2c Container mapping: DoD does not use standard PIV slots Per the *DoD Implementation Guide for CAC PIV End-Point* v2.1.1 §5.3.1 and §4, DoD implements only these PIV containers: CCC, CHUID, Security Object, Fingerprints, Facial Image, and the **PIV Authentication certificate (0x0101)**: *"the only PIV mandatory certificate."* The PIV **Digital Signature Key** and **Key Management Key** containers are **not separately implemented**: *"The functionality of these optional PIV certificates (containers) is available through the existing CAC certificates."* Signature and encryption live in the **CAC applet**, not in PIV slots 9C/9D. The PIV **Card Authentication Key** was originally not implemented; the 2025 matrix records an asymmetric card authentication key (**aCAK**) added later for physical access. So the naive `9A / 9C / 9D` mapping is wrong for DoD-style card stock: | Credential | DoD CAC PIV End-Point location | Naive PIV assumption | |---|---|---| | Authentication | PIV Authentication cert, container **0x0101** | 9A | | Signature (non-repudiation) | **CAC applet** PKI Digital Signature key | ~~9C~~ | | Encryption (escrowed) | **CAC applet** PKI Encryption key | ~~9D~~ | | Card authentication | aCAK (later addition) | 9E | Also note the UPN differs between the PIV Authentication certificate and the CAC Signature certificate (endpoint guide §5.3.1 fn.17); they are not interchangeable for logon. **Which applet your token actually answers on is testable**, and must be tested before profiles are frozen: `opensc-tool -n`, then `piv-tool -n`, with the reader attached. ### 1.3 Universal rules - `AuthorityKeyIdentifier`: `c=no`, **keyIdentifier octet string only**. The `issuerDN + serialNumber` tuple form is **prohibited**: it breaks path building. - `SubjectKeyIdentifier`: `c=no`, octet string. - **First** entry of `AIA.caIssuers` and of `CRLDistributionPoints` must be an **HTTP URL reachable from the public Internet.** Additional LDAP or internal entries may follow. - `CRLDistributionPoints`: `distributionPoint` field only. The `reasons` and `cRLIssuer` fields and the `nameRelativeToCRLIssuer` CHOICE are **prohibited**. - Serial numbers: unique per issuer, and **≥ 64 bits of CSPRNG output** (unpredictability is a hash-collision defence; the DoD documents predate this requirement; add it). - Dates: `UTCTime` through 2049-12-31, `GeneralizedTime` from 2050-01-01. Always `Z`. - No end-entity certificate may assert `anyExtendedKeyUsage {2.5.29.37.0}`. --- ## 2. CA profiles ### 2.1 Root CA (self-signed) | Field | Value | |---|---| | Version | v3 (2) | | Serial | unique, ≥64 bits random | | Issuer DN | `, CN=` | | Subject DN | *same as issuer* | | Validity | **≤ 25 years** | | SPKI | P-384 (R1/R3), P-256 (R2), ML-DSA-65 (R4) | | Extension | Value | |---|---| | `subjectKeyIdentifier` | `c=no`; octet string | | `keyUsage` | `c=yes`; `keyCertSign`, `cRLSign`. Optional: `digitalSignature`. **Prohibited: all others** | | `basicConstraints` | `c=yes`; `cA=TRUE`; **pathLenConstraint absent** | | `subjectInfoAccess` | `c=no` (optional); `id-ad-caRepository {1.3.6.1.5.5.7.48.5}` → HTTP URL of the `.p7c` bundle of CAs issued by this root | No AIA, no CRLDP on a self-signed root. ### 2.2 Issuing CA | Field | Value | |---|---| | Validity | **≤ 6 years** | | Issuer DN | `, CN=` | | Subject DN | `, CN=` | | Extension | Value | |---|---| | `authorityKeyIdentifier` | `c=no`; keyIdentifier only | | `subjectKeyIdentifier` | `c=no`; as in the PKCS#10 request | | `keyUsage` | `c=yes`; `keyCertSign`, `cRLSign`. **Prohibited: all others** | | `certificatePolicies` | `c=no`; **exactly** the policy OIDs this CA is authorized to issue under, no more | | `basicConstraints` | `c=yes`; `cA=TRUE`; **`pathLenConstraint = 0`** | | `policyConstraints` | **`c=yes`**; `requireExplicitPolicy: skipCerts = 0`. RFC 5280 §4.2.1.11: *conforming CAs MUST mark this extension critical.* DoD Table 5 says `c=no`; we diverge, because a non-critical `policyConstraints` may be ignored by a validator that does not recognise it, and this is the control the whole policy model rests on | | `extendedKeyUsage` | `c=no` (optional but recommended); EKU-constrain the CA to the EKUs it may issue | | `authorityInformationAccess` | `c=no`; `id-ad-caIssuers` → HTTP URL for certs issued **to** this CA; `id-ad-ocsp` → HTTP URL of the root's responder | | `CRLDistributionPoints` | `c=no`; HTTP → full and complete root CRL | > **Enforcement rule:** an issuing CA shall not assert a policy OID in a subject certificate > if its own certificate does not carry that OID. Roots are exempt. Enforce this in the > issuance software, not only in the CPS. ### 2.3 Cross-certificate issued **by** the Interoperability Root | Field | Value | |---|---| | Validity | **≤ 3 years** | | Subject DN | exactly as supplied in the partner's PKCS#10; do not normalize | | Extension | Value | |---|---| | `keyUsage` | `c=yes`; `keyCertSign`, `cRLSign`. **Prohibited: all others** | | `certificatePolicies` | `c=no`; **only** the OIDs named in the cross-certification agreement | | `policyMappings` | `c=no`; **only** the mappings named in the agreement. Omit entirely if the partner asserts our OIDs directly | | `policyConstraints` | **`c=yes`**; `requireExplicitPolicy: skipCerts=0`; `inhibitPolicyMapping: skipCerts=0` unless transitive trust is deliberately intended | | `basicConstraints` | `c=yes`; `cA=TRUE`; `pathLenConstraint` per the agreement | | `nameConstraints` | **`c=yes`, REQUIRED**; permitted and/or excluded subtrees for `directoryName`, `rfc822Name`, `dNSName`, `uniformResourceIdentifier`, and UPN `otherName`. Never an empty SEQUENCE.
**UPN constraints are asserted but never relied on alone:** RFC 5280 §4.2.1.10 defines subtree matching only for `dNSName`, `rfc822Name`, `uniformResourceIdentifier`, `directoryName` and `iPAddress`: there is no matching rule for `otherName`, so a UPN `excludedSubtree` is silently unenforced on OpenSSL, NSS, Java and Go. Assert `directoryName` and `rfc822Name` constraints that independently achieve the same containment | | `authorityInformationAccess` | `c=no`; `caIssuers` HTTP + `id-ad-ocsp` HTTP | | `CRLDistributionPoints` | `c=no`; HTTP → full and complete CRL | RDN and directoryString encoding follow the **partner's** naming scheme, not ours. ### 2.4 OCSP responder (delegated) | Field | Value | |---|---| | Issuer | **The same CA key that signed the certificates this responder answers for** | | Validity | **≤ 45 days** from issuance (private key ≤ 3 years) | | Subject DN | `, CN=` | | Extension | Value | |---|---| | `keyUsage` | `c=yes`; `digitalSignature`. **Prohibited: all others** | | `extendedKeyUsage` | **`c=yes`**; `id-kp-OCSPSigning {1.3.6.1.5.5.7.3.9}`. **Prohibited: all others** | | `certificatePolicies` | `c=no`; **all** policy OIDs the issuing CA issues under | | `subjectAltName` | `c=no`; HTTP URL or DNS name of the responder | | `id-pkix-ocsp-nocheck` | `c=no`; **present** `{1.3.6.1.5.5.7.48.1.5}`; prevents infinite status recursion | A **CA-hosted** responder (collocated in the CA enclave with key protection equivalent to the CA signing key) may instead be valid for the life of the issuing CA. --- ## 3. End-entity profiles Common to all: `authorityKeyIdentifier` (keyIdentifier only), `subjectKeyIdentifier`, `certificatePolicies` (`c=no`), `authorityInformationAccess` (caIssuers HTTP + ocsp HTTP), `CRLDistributionPoints` (HTTP, full and complete). ### 3.1 Human: Authentication | | | |---|---| | Validity | ≤ 3 years | | Subject DN | `, OU=, CN=Last First MI ` | | `keyUsage` | `c=yes`; `digitalSignature` | | `extendedKeyUsage` | `c=no`; `id-kp-clientAuth {…3.2}`; optional `id-ms-smartcard-logon {1.3.6.1.4.1.311.20.2.2}` | | `subjectAltName` | `c=no`; `otherName` = UPN. Optional: `rfc822Name` | | Policy | `-hardware-112` \| `-128` \| `-192`, per token capability (§1.2b) | | Key storage | FIPS 140-3 **Level 2 hardware** token | ### 3.2 Human: Signature (non-repudiation) | | | |---|---| | Validity | ≤ 3 years | | `keyUsage` | `c=yes`; **`nonRepudiation`** (and `digitalSignature`) | | `extendedKeyUsage` | `c=no`; `id-kp-emailProtection {…3.4}` | | `subjectAltName` | `c=no`; `rfc822Name` | | Policy | `-hardware-112` \| `-128` \| `-192`, per token capability (§1.2b) | | Escrow | **Never.** This key is never backed up, never escrowed | ### 3.3 Human: Encryption | | | |---|---| | Validity | ≤ 3 years | | `keyUsage` | `c=yes`; `keyEncipherment` (RSA) \| `keyAgreement` (ECDH) | | `extendedKeyUsage` | `c=no`; `id-kp-emailProtection` | | `subjectAltName` | `c=no`; `rfc822Name` | | Policy | `-hardware-112` (§1.2b) | | Escrow | **Yes**: this is the only escrowed key type. Two-person control on third-party recovery | Three separate key pairs per human subscriber. Collapsing them forfeits either non-repudiation or data recovery. ### 3.4 TLS server | | | |---|---| | Validity | **≤ 90 days**, ACME-automated (DoD permits 3 years; do not copy that) | | Subject DN | `, OU=, CN=` | | `keyUsage` | `c=yes`; `digitalSignature` (+ `keyEncipherment` for RSA) | | `extendedKeyUsage` | `c=no`; `id-kp-serverAuth {…3.1}`, `id-kp-clientAuth {…3.2}`. **Prohibited:** `anyExtendedKeyUsage`, `emailProtection`, `codeSigning`, `OCSPSigning` | | `subjectAltName` | `c=no`; `dNSName` (one per name; multiple permitted). Optional `iPAddress` | | Policy | `-device-128` | `subjectAltName` is authoritative. CN is legacy decoration: populate it, never match on it. ### 3.5 Workload / device (NPE) | | | |---|---| | Validity | **≤ 24 hours** (workload) / ≤ 1 year (physical device) | | Subject DN | `, OU=, CN=` | | `keyUsage` | `c=yes`; `digitalSignature` (+ `keyEncipherment` for RSA) | | `extendedKeyUsage` | `c=no`; `id-kp-clientAuth`, optionally `id-kp-serverAuth` | | `subjectAltName` | `c=no`; `dNSName` \| `iPAddress` \| `uniformResourceIdentifier` (SPIFFE ID) | | Policy | `-device-128` or `-internal-device-128` | | Activation data | May be activated **without** a PIN/passphrase (this is the assurance carve-out that requires the separate `R2` root) | | Sponsor | A named PKI Sponsor holds subscriber obligations | ### 3.6 Code signing | | | |---|---| | Certificate validity | ≤ 6 years; **private key usage ≤ 13 months** | | `keyUsage` | `c=yes`; `digitalSignature` | | `extendedKeyUsage` | **`c=yes`**; `id-kp-codeSigning {…3.3}`. **Prohibited: all others** | | Policy | `-hardware-192` | | Key storage | FIPS 140-3 Level 2 hardware, minimum | | Timestamping | Signatures **must** be RFC 3161 timestamped so they survive certificate expiry | Issued only by a dedicated code-signing CA that carries no other policy OID. ### 3.7 Time Stamp Authority | | | |---|---| | Validity | ≤ 6 years | | `keyUsage` | `c=yes`; `digitalSignature`, `nonRepudiation` | | `extendedKeyUsage` | **`c=yes`**; `id-kp-timeStamping {…3.8}`. **Prohibited: all others** | Critical EKU with nothing else is what makes a TSA certificate unusable for anything else. --- ## 4. CRL profile | Field | Value | |---|---| | Version | **v2 (1)** | | Signature algorithm | same as the issuing CA's certificate signature algorithm | | Issuer | CA's DN, byte-identical to its certificate's subject | | `thisUpdate` | time of generation | | `nextUpdate` | per the cadence table below; **required** | | Revoked certs | `userCertificate` (serial), `revocationDate`, and `reasonCode` where known | | Extension | Value | |---|---| | `authorityKeyIdentifier` | `c=no`; keyIdentifier only | | `cRLNumber` | `c=no`; monotonically increasing, never reused, never reset | | `issuingDistributionPoint` | `c=yes` **only** if the CRL is partitioned; omit for full-and-complete | | Entry: `reasonCode` | `c=no` | | Entry: `invalidityDate` | `c=no`, optional | ### 4.0 Encoding CRLs published at an HTTP `CRLDistributionPoints` URL shall be **DER**, served as `Content-Type: application/pkix-crl`. PEM is not acceptable. This is not pedantry. `openssl` auto-detects PEM, so a PEM CRL passes every openssl-based check while NSS (and therefore Dogtag's own client, Firefox, and anything built on NSS) rejects it with `SEC_ERROR_CRL_INVALID`, cannot obtain the issuer's revocation status, and reports the chain as **revoked**. A misencoded CRL fails closed and looks like a revocation. ### 4.1 Cadence | CA class | Normal periodicity | Max latency, key/CA compromise | |---|---|---| | Root CA (offline) | ≥ every **28 days** | within **6 hours** of notification | | Issuing CA | ≥ **daily** | within **18 hours** | | Internal device CA | ≥ every **90 days** | within **18 hours** | - Issue on schedule **even when nothing changed**. - Publish within **4 hours** of generation, and never later than the previous CRL's `nextUpdate`. - Remove the superseded CRL on publication of its successor. - `nextUpdate` includes slack past the nominal period so a fetch failure does not immediately fail closed, but not so much that a stale CRL is indistinguishable from a current one. ### 4.2 Certificate removal from the CRL An entry may be dropped only after the certificate's own `notAfter` has passed **and** the CRL covering that period has been archived. Never shrink a CRL by dropping live entries. --- ## 5. Prohibited in end-entity certificates - `anyExtendedKeyUsage {2.5.29.37.0}` - `basicConstraints` with `cA=TRUE` - `keyUsage` including `keyCertSign` or `cRLSign` - `id-kp-OCSPSigning` outside a responder certificate - `authorityKeyIdentifier` in `issuerDN + serial` form - `nameConstraints` (CA-only extension) - any critical extension not named in this document - `subjectAltName` with an empty GeneralNames SEQUENCE - deprecated / retired policy OIDs (see the CP's deprecation table) --- ## 6. Test vectors Every profile in this document must have a corresponding fixture in `test/profiles/`, and CI must fail if an issued certificate deviates. Validate paths against the **NIST PKI Test Suite (PKITS)** before any cross-certificate goes live: the policy-mapping and name-constraint test cases are exactly the ones that break real deployments.