Certificate Profiles Need a Single Source of Truth
Corey Bonnell
A CA’s certificate profile exists in at least three places: the policy documents (CP/CPS) that describes it in prose, the CA software configuration that actually produces certificates, and in user-facing product documentation. Those artifacts are maintained separately, usually by different teams, and nothing forces them to agree.
Software engineering named this problem decades ago. “Don’t Repeat Yourself” is the rule that every piece of knowledge must have a single, authoritative representation within a system. If information is duplicated, it will eventually be changed in one place and not the others, and the copies will silently disagree. But what happens when they do disagree?
For CAs participating in the Web PKI, that question has a specific and unpleasant answer.
When they disagree, the TLS, S/MIME, and Code Signing Baseline Requirements are unambiguous about the consequence. Section 4.9 of those documents require revocation within five days when a CA becomes aware that a certificate “was not issued in accordance with these Requirements or the CA’s Certificate Policy or Certification Practice Statement.” This rule applies even if the certificate perfectly complies with the Baseline Requirements profiles or any other external policy. They only have to disagree with the CA’s own document.
The CP/CPS is a copy of the certificate profile, written in prose, and the BRs make that copy normative. CAs in the Web PKI are maintaining duplicated knowledge under a rule that punishes divergence with mandatory revocation. These deviations are one of the main sources of mass revocation events in the Web PKI, and the incidents keep coming.
Microsoft: 75 million certificates over a copy-paste error
In July 2024, Microsoft PKI Services published an update to its Public TLS CPS.
While reformatting the document to add tables, it added a statement
that keyEncipherment was not present in Subscriber certificates with RSA keys. However,
Microsoft continued to issue certificates containing that key usage value.
A third-party researcher noticed this and sent a problem report in April 2025 to Microsoft, which became Mozilla bug 1962829. Every OV certificate with an RSA key issued between 2024-07-01 and 2025-04-21 was now mis-issued: almost 100 million certificates, of which over 75 million were still valid.
Microsoft said it could not revoke them. As documented in bug 1965612, revoking 75 million certificates at once would have produced CRLs exceeding 600 MB, overwhelming clients that consume those CRLs. The CA opened a delayed-revocation bug and spent six months revoking in weekly batches and answering questions from the Chrome Root Program and the community about automation, CRL partitioning, and why 90% of the affected certificate population turned out not to be in use at all. Three weeks into the plan, less than 1% of the affected certificates had been revoked.
HARICA: twice in one month, from the same class of defect
In March 2026, HARICA updated its CP/CPS to incorporate a recent Chrome Root Store Policy
change, which required removing id-kp-clientAuth from extKeyUsage effective this June.
Chrome subsequently extended that deadline to 2027-03-15 in the latest version of its policy. However, HARICA did not
update its CP/CPS to match, so the CA’s own document prohibited something the ecosystem
still permitted and the issuance system still did.
Bug 2055551, filed after
a third-party report, covers every TLS certificate issued after 2026-06-15, all to be
replaced and revoked within five days.
Reviewing that incident surfaced a second divergence: HARICA had removed the AIA OCSP URI access method from TLS certificates at the end of March 2026 but had not updated the CP/CPS, which still required it. Certificates issued between 2026-03-27 and 2026-07-20 were therefore mis-issued against HARICA’s own document.
The policy document and the issuance system are edited independently, and the only reconciliation mechanism is someone remembering (or reporting it).
Entrust: an edit applied to the wrong profile
The Entrust case shows how easily a mistake can be introduced.
In March 2024, Entrust published a CPS update, adding to the EV certificate profile a requirement that the Certificate Policies extension “must contain policyQualifier with cPSuri.” The requirement was intended for EV only. But, it landed on the OV profile as well.
Nothing about issuance changed, because OV certificates were never issued with the policy qualifier. But for several days, Entrust’s published CPS said they were. Bug 1890896 covers the over 6,000 OV TLS certificates issued over the span of those several days, when another CPS update corrected the error. As the report states, those certificates “were issued in accordance with the TLS Baseline Requirements and with the certificate profile intended by Entrust”; they were mis-issued solely because of a typographical error in the document.
Entrust declined to revoke, arguing that reissued certificates would be materially identical.
The community disagreed at length. Tim Callan’s
comment makes the case that
the CPS in force at issuance time is effectively part of the certificate, so a replacement
with a notBefore under a correct CPS is not the same artifact at all. The non-revocation
decisions in this incident and its EV counterpart,
bug 1890685, were later cited
in the Chrome Root Program’s
announcement of Entrust’s
distrust, as examples of willful violations of the industry’s revocation requirements.
A misplaced sentence in a Word document is a mis-issuance event with existential stakes.
Linters, and what they were built to check
The Web PKI’s answer to mis-issuance has been automated conformance checking, and it has worked remarkably well.
The CA/Browser Forum’s Baseline Requirements documents give the ecosystem written standards. The linters provide the automated, technical verification against those standards. certlint and ZLint were the first two tools that were released to let anyone check certificates against RFC 5280 and the BRs.
pkilint followed in 2023, architected and primarily maintained by me during my time at DigiCert as a linting framework initially aimed at
the S/MIME BRs, and later extended to cover the TLS certificate profiles, plus CRLs and OCSP responses.
The CA/Browser Forum made the use of linting tools mandatory. Ballot SC-75, proposed by HARICA and endorsed by DigiCert and Mozilla, made pre-issuance linting a requirement in March 2025, with an equivalent S/MIME requirement following that September.
The widespread adoption of linting is a success story. Whole categories of mis-issuance, such as bad ASN.1 encodings, malformed name attributes, and invalid validity periods are now caught before a certificate is signed.
Shortcomings of linters
The industry-standard linters check certificates against public requirements: RFC 5280, the TLS BRs, the S/MIME BRs, and ETSI EN 319 412.
But none of them read your CP/CPS.
Looking at what actually went wrong in the incidents above:
- Microsoft’s certificates set
keyEncipherment. The relevant standards permit it, so every linter passes. But the CPS said no. - HARICA’s certificates omitted the AIA OCSP URI, which aligns with the ecosystem’s gradual deprecation of OCSP. Every linter passes. But the CA’s own policy document required it.
- Entrust omitted with
cPSUriqualifier in OV certificates. This is best practice according to the Baseline Requirements, so no linter would flag its omission. However, according to its own CP/CPS, it was required for a few days.
In all three cases the certificates were fully compliant with every requirement a linter knows how to check, and were mis-issued anyway. The linters were doing their job correctly. But, they were not given the CA-specific half of the specification, because there is no machine-readable form to give them.
A CA can, in principle, code custom lints for its own profiles. But hand-written custom lints are a fourth copy of the same knowledge, and DRY (and Murphy’s Law) predicts exactly what happens next: the lint gets updated when the CA configuration changes and not when the CPS is reworded, or the reverse. The Microsoft and HARICA incidents are drift between two copies. Writing a third normative copy by hand doesn’t reduce the number of ways they can disagree. Rather, it merely increases it. Deduplication has to happen upstream, or it doesn’t happen at all.
The next generation: one profile definition, many outputs
The fix is the one software engineering settled on long ago. You don’t keep multiple copies in sync by being “more careful” and adding more manual reviews. Instead, you keep one copy and auto-generate the rest. A generated artifact can be wrong, but it cannot diverge, because it has no independent existence to diverge from.
The idea is to define the certificate profile once, in a machine-readable format, and generate everything else from that definition.
Concretely, a single profile definition should be treated exactly like system configuration files. They should be versioned in git and reviewed like code and should be the sole input to:
- Linting rules: Generate a CA-specific lint configuration that checks issued certificates against the CA’s own profile, not just the BRs. Feed it to a linter that understands the profile format alongside the shared lints, in the same pre-issuance call the BRs already require. A deviation between practice and policy becomes a pre-issuance failure rather than a Bugzilla bug and a revocation event.
- CP/CPS documentation: Generate Section 7 (the technical profiles) of CP/CPS documents as Markdown or AsciiDoc, ready for automatic inclusion by the same pipeline that builds the final published version of the policy document.
- CA configuration: Generate the certificate templates or profile configuration the CA software consumes. This ensures that the policy document cannot describe something the CA doesn’t do, because both come from the same file.
A change to a profile is a single diff, reviewable in one place by people who can see all three consequences at once. Adding a new profile means writing one definition rather than three artifacts in three formats maintained by three teams. And when an auditor asks you to show that your certificates match your CPS, that evidence is trivial to produce, because the two were never separately authored and both were directly derived from the same source.
However, there are hard parts to building this solution. Certificate profiles are more expressive than what a naive schema would capture. Such a profile format would need to support conditional presence of fields, cross-field constraints, or nitty-gritty technical details on how attributes are encoded in ASN.1 DER. Generated prose destined for inclusion in the CP/CPS has to be readable enough for auditors and root program reviewers, not just correct. Different CA software has different configuration formats, so backends will vary. Existing profiles need a migration path that doesn’t itself trigger a revocation incident.
Conclusion
Pre-issuance linting against the BRs is mandatory, the existing tooling is good, and it catches an enormous amount. However, the CA-specific part is untouched, and it is now responsible for revocation events measured in the hundreds of millions of certificates.
Under the Baseline Requirements, the CP/CPS contains a second normative copy of certificate profiles, carrying a five-day revocation penalty for disagreeing with the actual content of the certificates. No amount of careful review makes duplicated knowledge stay consistent. That’s the whole reason DRY exists as a rule rather than a suggestion.
Write the profile once. Generate the document, the configuration, and the lints from it.