A certificate binds a public key to an identity, purpose, and validity period, and is signed by the issuer. For a client to accept a certificate, it must also have a trusted path and perform application-level identity validation; the TLS handshake further proves that the peer possesses the corresponding private key. Being able to read a certificate, or even verify its self-signature, does not mean you already know who the other party is.
Using https://api.example.com:8443/orders as a running example: the connection destination address comes from resolution, the reference identity comes from the application's access target, and the certificate provides a verifiable identity claim. Which IP the DNS returned and which name the server sent do not determine for the client whom it should trust.
Problem to Solve
Reference Location
How to match SAN, CN, port, and wildcards
Service Identity
Why is the chain incomplete even with a root certificate?
Trust Path
What exactly should be written for DNS-01 TXT?
ACME Challenges
Why reject a certificate that hasn't expired?
Revocation, Purpose, and Client Policy
Can CT prevent erroneous issuance?
Public Audit
Renewal succeeded but the site is still expired?
Publication Loop
Distinguish Four Objects First
Object
Carries
Cannot Replace
Private Key
Secret required for signing or other private key operations
Cannot be publicly distributed with the certificate
Certificate
Public key, identity, constraints, issuer signature
Does not prove the current peer holds the private key
CSR
Information requested for issuance and key signature
Not a CA-recognized certificate
Trust Store
Anchors recognized by the client and related policies
Not an arbitrary collection of certificates provided by the server
Certificates are often encoded in DER binary, while PEM is a text wrapper with boundary markers. A file named .pem is insufficient to determine whether it contains a certificate, a private key, or multiple certificates; read the actual object. The certificate's own signature algorithm is used by the issuer, while SubjectPublicKeyInfo describes the holder's public key algorithm; these two need not be the same.
Service Identity: Determine Who to Access First, Then Compare SAN
For the HTTPS scenario in this article, match the dNSName or iPAddress in the SAN. Do not fall back to using the Subject CN to impersonate DNS identity. Domain names handle case sensitivity and internationalized names according to applicable rules; wildcards can only occupy the leftmost complete label and cover only one level.
Certificate SAN
Access Target
Name Result
DNS:api.example.com
https://api.example.com:8443/
Match; port is not written in DNS SAN
DNS:*.example.com
https://api.example.com/
Match
DNS:*.example.com
https://example.com/
No match for bare domain
DNS:*.example.com
https://v2.api.example.com/
No match for multi-level
DNS:api.example.com
https://192.0.2.10/
No match; requires appropriate IP SAN
SNI helps the server select the virtual host certificate but does not perform name validation for the client. A DNS CNAME pointing to a hoster does not mean you can use the hoster's arbitrary name to replace the original service identity. Here is the name matching result; final authentication also depends on the chain and other checks. RFC 9525: Service Identity
Trust Path: Valid Signature is Only One Condition
A typical server sends the leaf certificate and necessary intermediate certificates, usually without sending the root. The client builds candidate paths using local trust material, then verifies signatures, validity periods, critical extensions, and constraints. Do not skip signature verification just because the Issuer text equals another certificate's Subject; AKI/SKI can help select candidates but are not independent sources of trust.
Field or Extension
Key Reading Points
serialNumber
Identifies the certificate jointly with the issuer, not a globally unique ID
BasicConstraints
CA qualification; pathLenConstraint limits the number of non-self-issued intermediate CAs in the path
KeyUsage
e.g., digitalSignature, keyCertSign; cannot be used arbitrarily by name
ExtendedKeyUsage
e.g., serverAuth, clientAuth; must be satisfied simultaneously with KU if both exist
NameConstraints
Limits the range of names usable by subordinates
critical extensions
Critical constraints that cannot be processed must not be ignored directly
An intermediate CA with pathLenConstraint=0 can still issue leaf certificates; it does not mean "cannot sign any certificate." Root certificates are often self-signed, but trust comes from external configuration, not from circularly validating their own signatures. Cross-signing creates multiple candidates for path construction, so a client working on one machine might fail on another due to differences in anchors, path building capabilities, or policies. RFC 5280: Certificates and Paths
For example, if the server only sends the leaf certificate: the browser might have the intermediate certificate cached and the connection succeeds, but a new container fails. You should complete the service chain; do not use "this browser can open it" as proof of a complete chain. The trust scope of the public Web PKI and a self-built enterprise PKI also differs; installing an enterprise root expands the sources of issuance that client can accept.
The chain connects, so why still reject?
First, input the bare domain example.com or multi-level v2.api.example.com to observe that wildcards cannot cover them. After restoring the name, pathLen=0 still allows direct signing of leaf certificates; only inserting another CA triggers this constraint. Other checks remain independent.
Preparing the visual
The chain connects, so why still reject?
Finding a candidate relationship in path construction does not equal successful path validation. Name matching, purpose, time, and CA constraints are conditions that must be satisfied simultaneously, not replaced by a single valid signature.
{"id":"pki-path-constraints","title":"The chain connects, so why still reject?","summary":"Finding a candidate relationship in path construction does not equal successful path validation. Name matching, purpose, time, and CA constraints are conditions that must be satisfied simultaneously, not replaced by a single valid signature.","height":1000,"html":"<h2 data-i18n=\"heading\"></h2><p class=\"intro\" data-i18n=\"intro\"></p><label class=\"form-field\"><span data-i18n=\"name\"></span><input type=\"text\" id=\"name\" value=\"api.example.com\" maxlength=\"40\" autocomplete=\"off\" spellcheck=\"false\"></label><label class=\"form-field\"><span data-i18n=\"purpose\"></span><select id=\"purpose\"><option value=\"server\">serverAuth</option><option value=\"client\">clientAuth</option></select></label><label class=\"check\"><input type=\"checkbox\" id=\"trust\" checked><span data-i18n=\"trust\"></span></label><label class=\"check\"><input type=\"checkbox\" id=\"extra\"><span data-i18n=\"extra\"></span></label><div class=\"control\"><label for=\"limit\" data-i18n=\"limit\"></label><output for=\"limit\" id=\"limit-value\"></output><input id=\"limit\" type=\"range\" min=\"0\" max=\"2\" value=\"0\" step=\"1\"></div><div class=\"control\"><label for=\"time\" data-i18n=\"time\"></label><output for=\"time\" id=\"time-value\"></output><input id=\"time\" type=\"range\" min=\"0\" max=\"120\" value=\"50\" step=\"1\"></div><section class=\"panel\"><h3 data-i18n=\"checks\"></h3><div id=\"checks\"></div></section><div class=\"metrics\"><div><span data-i18n=\"result\"></span><strong id=\"result\"></strong></div></div><details class=\"scope\"><summary data-i18n=\"scopeLabel\"></summary><p data-i18n=\"scope\"></p></details>","css":".intro{margin:8px 0 18px;color:var(--muted);font-size:14px}.caption,.hint{font-size:12px;color:var(--muted);margin-top:12px}.control{display:grid;grid-template-columns:minmax(0,1fr) auto;gap:6px 12px;align-items:center;margin:14px 0;font-size:13px}.control label{font-size:13px}.control output{color:var(--accent);min-width:5em;text-align:right;font-variant-numeric:tabular-nums}.control input{grid-column:1/-1;width:100%;margin:0}.presets{display:flex;gap:4px;align-items:stretch}.presets button{flex:1 1 0;min-width:0;overflow-wrap:anywhere;font-size:13px}.actions{display:flex;gap:8px;flex-wrap:wrap;margin:14px 0}.actions button{flex:1;min-width:0;font-size:13px;min-height:42px}.copy{display:grid;font-size:14px;margin:16px 0}.copy>*{grid-area:1/1;margin:0}.reserve{visibility:hidden}.scope{margin-top:14px;color:var(--muted);font-size:12px}.scope summary{padding:8px 0;cursor:pointer}.scope p{margin-top:10px}.two{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.card{border:1px solid var(--rule);border-radius:9px;padding:14px;min-width:0}.card header{font-size:14px;font-weight:600;margin-bottom:10px}.card button{font-size:12px;width:100%;min-width:0}.card small{display:block;font-size:12px;color:var(--muted)}.card code{font-size:12px;overflow-wrap:anywhere}.metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px;margin:16px 0}.metrics>div{border-left:2px solid var(--accent);padding-left:10px;min-width:0}.metrics span{display:block;min-height:3.2em;color:var(--muted);font-size:12px}.metrics strong{font-size:23px;display:block;font-weight:500;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.formula{border-top:1px solid var(--rule);padding-top:12px;margin-top:14px;font-size:13px;font-variant-numeric:tabular-nums;overflow-wrap:anywhere}.value{font:500 20px var(--viz-font-ui)}.legend{display:flex;flex-wrap:wrap;gap:6px 14px;font-size:12px;color:var(--muted);margin:12px 0}.legend span:before{content:'';display:inline-block;width:8px;height:8px;border-radius:2px;background:var(--accent);margin-right:6px}.legend .second:before{background:var(--second)}.check{display:flex;align-items:center;gap:8px;font-size:13px;margin:12px 0}.check input{width:16px;height:16px;flex-shrink:0;accent-color:var(--accent)}select,input[type=text],input[type=number]{background:var(--paper);color:var(--ink);font:inherit;font-size:13px;padding:10px;border:1px solid var(--rule);border-radius:7px;max-width:100%;min-width:0}select{width:100%;min-height:42px}.band{height:20px;display:flex;background:var(--surface);border-radius:4px;overflow:hidden}.band i{height:100%;display:block;background:var(--accent);transition:width .25s}.band i.second{background:var(--second)}button:disabled{cursor:default}.status{font-size:13px;min-height:4.8em}.mono{font-family:var(--viz-font-mono);font-variant-numeric:tabular-nums}.muted{color:var(--muted)}\n@media(max-width:480px){.two{gap:8px}.card{padding:12px 9px}.actions button,.presets button{font-size:12px}.metrics strong{font-size:20px}.metrics{gap:8px}}\n@media(prefers-reduced-motion:reduce){*,*::before,*::after{transition:none!important;animation:none!important}}\n.track-row{margin:16px 0}.track-row>span{display:block;font-size:12px;color:var(--muted);margin-bottom:7px}.track{display:flex;gap:5px;min-width:0}.cell{flex:1;min-width:0;min-height:48px;border:1px solid var(--rule);border-radius:5px;background:var(--surface);display:flex;align-items:center;justify-content:center;text-align:center;padding:5px 3px;font-size:12px;overflow-wrap:anywhere;font-variant-numeric:tabular-nums}.cell.on{background:var(--accent-soft);border-color:var(--accent)}.cell.gold{background:var(--second-soft);border-color:var(--second)}.cell.empty{border-style:dashed;color:var(--muted)}.cell.error{border-color:var(--second);text-decoration:line-through}.choices{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px;margin:14px 0}.choices button{min-width:0;padding:10px 4px;font-size:12px;overflow-wrap:anywhere}.choices button[aria-current=true]{border-color:var(--accent);background:var(--accent-soft)}.relation{display:grid;grid-template-columns:minmax(0,1fr) 28px minmax(0,1fr);align-items:center;gap:6px;margin:12px 0}.relation .cell{min-height:72px}.relation>i{text-align:center;font-style:normal;color:var(--accent)}.readout{font-size:13px;white-space:pre-line;min-height:6.4em;overflow-wrap:anywhere}.plot{position:relative;border-left:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:8px 0;margin:18px 0}.plot-row{height:28px;position:relative;margin:4px 0;background:var(--surface)}.plot-row i{position:absolute;inset-block:5px;background:var(--accent);min-width:2px;transition:width .2s,left .2s}.plot-row b{position:relative;padding:4px 6px;font:11px/28px var(--viz-font-ui);background:var(--paper);border-radius:3px}.axis{display:flex;justify-content:space-between;font-size:11px;color:var(--muted)}\n\n/* Action labels must remain readable across locales and narrow embeds. */\n.actions{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}\n.actions button{overflow-wrap:anywhere;white-space:normal;min-height:58px;padding:8px}\nbutton{overflow-wrap:anywhere}\n@media(min-width:640px){.actions{grid-template-columns:repeat(3,minmax(0,1fr))}}\n\n.metrics span{overflow-wrap:anywhere}\n.panel{border:1px solid var(--rule);border-radius:9px;padding:12px;margin:14px 0;min-width:0}.panel h3{font-size:13px;margin:0 0 10px;color:var(--muted)}.source{font:13px/1.8 var(--viz-font-mono);white-space:pre-wrap;overflow-wrap:anywhere;margin:12px 0}.source mark{background:var(--accent-soft);color:var(--ink);border-bottom:2px solid var(--accent)}.source .fault{background:var(--second-soft);border-color:var(--second)}.diagram{display:block;width:100%;height:auto;max-height:320px;margin:14px 0;overflow:visible}.diagram text{fill:var(--ink);font:12px var(--viz-font-ui)}.diagram .edge{stroke:var(--muted);stroke-width:1.5;fill:none}.diagram .edge.on{stroke:var(--accent);stroke-width:3}.diagram .node{fill:var(--surface);stroke:var(--rule);stroke-width:1.5}.diagram .node.on{fill:var(--accent-soft);stroke:var(--accent)}.diagram .node.gold{fill:var(--second-soft);stroke:var(--second)}.log{font:12px/1.8 var(--viz-font-mono);white-space:pre-line;overflow-wrap:anywhere;min-height:7.2em}.table{display:grid;gap:5px;margin:14px 0}.table .tr{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:5px}.table .tr>*{padding:8px 5px;background:var(--surface);border-radius:4px;min-width:0;overflow-wrap:anywhere;font-size:12px}.table .tr .on{background:var(--accent-soft)}.table .tr .gold{background:var(--second-soft)}.choices button{min-height:44px}.metrics strong{min-height:1.6em}.code-line{display:block}.code-line.on{background:var(--accent-soft);border-left:2px solid var(--accent);padding-left:6px}.status-badge{font-size:12px;color:var(--accent)}.tile-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:6px}.tile-grid .cell{min-height:40px}.actions button{min-height:48px}.form-field{display:grid;gap:8px;margin:14px 0;font-size:13px}.form-field input{width:100%;font-family:var(--viz-font-ui)}.metrics.three{grid-template-columns:repeat(3,minmax(0,1fr))}.formula{min-height:3.2em}.presets{flex-wrap:wrap}.presets button{flex-basis:25%;min-height:44px}\n\n.budget{display:flex;height:28px;margin:16px 0;background:var(--surface);border-radius:5px;overflow:hidden}.budget i{display:block;background:var(--accent);transition:width .2s}.budget i.gold{background:var(--second)}.intervals{margin:16px 0}.interval{position:relative;height:36px;background:var(--surface);margin:6px 0;border-radius:4px;overflow:hidden}.interval i{position:absolute;height:100%;background:var(--accent-soft);border-left:2px solid var(--accent)}.interval i.gold{background:var(--second-soft);border-color:var(--second)}.interval span{position:relative;z-index:1;font:12px/36px var(--viz-font-ui);padding-left:7px}.table .tr>*{min-height:4.8em}.formula{white-space:pre-line;min-height:7.2em}.actions:has(>button:only-child){grid-template-columns:1fr}.metrics strong{min-height:3.2em}.rangegrid{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:3px;margin:14px 0}.rangegrid .cell{min-height:30px;padding:3px 1px;font-size:10px}.message{min-height:7.2em;white-space:pre-line;font:13px/1.8 var(--viz-font-ui);overflow-wrap:anywhere}\n/* Allow CJK hanging punctuation without overflowing a metric's border box. */\n.metrics>div{padding-right:4px}.metrics span{line-break:anywhere}\n\n#checks{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:6px}.metrics strong{font-size:18px}","js":"const $=s=>document.querySelector(s);const set=(id,v)=>$('#'+id).textContent=v;const t=k=>viz.t(k);function cells(id,values){$('#'+id).replaceChildren(...values.map(v=>{const e=document.createElement('div');e.className='cell '+(v.cls||'');e.textContent=v.text;e.title=v.title||v.text;return e;}));}function pressed(mode){document.querySelectorAll('[data-mode]').forEach(e=>e.setAttribute('aria-pressed',String(e.dataset.mode===mode)));}const esc=s=>String(s).replace(/[&<>\"']/g,c=>({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''}[c]));const fmt=s=>'{'+[...s].sort().join(', ')+'}';function graph(id,nodes,edges){const el=$('#'+id);el.setAttribute('viewBox','0 0 360 200');el.innerHTML='<defs><marker id=\"arrow-'+id+'\" viewBox=\"0 0 10 10\" refX=\"8\" refY=\"5\" markerWidth=\"5\" markerHeight=\"5\" orient=\"auto-start-reverse\"><path d=\"M 0 0 L 10 5 L 0 10 z\" fill=\"var(--muted)\"/></marker></defs>'+edges.map(e=>{const a=nodes[e[0]],b=nodes[e[1]],dx=b.x-a.x,dy=b.y-a.y,d=Math.hypot(dx,dy)||1;if(edges.some(r=>r[0]===e[1]&&r[1]===e[0])){const nx=-dy/d,ny=dx/d;return '<path class=\"edge '+(e[2]?'on':'')+'\" d=\"M '+(a.x+dx/d*22+nx*12)+' '+(a.y+dy/d*22+ny*12)+' Q '+((a.x+b.x)/2+nx*38)+' '+((a.y+b.y)/2+ny*38)+' '+(b.x-dx/d*25+nx*12)+' '+(b.y-dy/d*25+ny*12)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}return '<line class=\"edge '+(e[2]?'on':'')+'\" x1=\"'+(a.x+dx/d*25)+'\" y1=\"'+(a.y+dy/d*25)+'\" x2=\"'+(b.x-dx/d*28)+'\" y2=\"'+(b.y-dy/d*28)+'\" marker-end=\"url(#arrow-'+id+')\"/>';}).join('')+nodes.map(n=>'<g><circle class=\"node '+(n.cls||'')+'\" cx=\"'+n.x+'\" cy=\"'+n.y+'\" r=\"25\"/><text x=\"'+n.x+'\" y=\"'+(n.y+4)+'\" text-anchor=\"middle\">'+esc(n.name)+'</text>'+(n.sub?'<text x=\"'+n.x+'\" y=\"'+(n.y+43)+'\" text-anchor=\"middle\">'+esc(n.sub)+'</text>':'')+'</g>').join('');}function table(id,rows){$('#'+id).classList.add('table');$('#'+id).replaceChildren(...rows.map(r=>{const row=document.createElement('div');row.className='tr';r.forEach(x=>{const c=document.createElement('div');if(typeof x==='object'){c.textContent=x.text;c.className=x.cls||'';}else c.textContent=x;row.append(c)});return row;}));}function band(id,parts,total){$('#'+id).replaceChildren(...parts.map((p,i)=>{const e=document.createElement('i');e.style.width=(100*p.value/total)+'%';e.className=i%2?'gold':'on';e.title=p.name+': '+p.value;return e}));}function intervals(id,items,total){$('#'+id).replaceChildren(...items.map(p=>{const row=document.createElement('div');row.className='interval';const bar=document.createElement('i');bar.style.left=(100*p.start/total)+'%';bar.style.width=(100*(p.end-p.start)/total)+'%';bar.className=p.cls||'';const label=document.createElement('span');label.textContent=p.label;row.append(bar,label);return row;}));}function draw(){const name=$('#name').value.toLowerCase(),checks={anchor:$('#trust').checked,SAN:/^[a-z0-9-]+\\.example\\.com$/.test(name),EKU:$('#purpose').value==='server',pathLen:Number($('#extra').checked)<=+$('#limit').value,time:+$('#time').value<=100};set('limit-value',$('#limit').value);set('time-value',$('#time').value);cells('checks',Object.entries(checks).map(([k,v])=>({text:k+' '+(v?'✓':'✗'),cls:v?'on':'error'})));const ok=Object.values(checks).every(Boolean);set('result',ok?'ACCEPT':'REJECT');$('#result').dataset.checks=JSON.stringify(checks);}document.querySelectorAll('input,select').forEach(e=>e.oninput=draw);draw();","audio":false,"strings":{"checks":"Independent Checks","extra":"Insert another CA under the intermediate CA","heading":"The chain connects, so why still reject?","intro":"Modify reference name, purpose, trust anchor, and intermediate CA limits; check item by item before summarizing.","limit":"Intermediate CA pathLenConstraint","name":"Application Reference Name","purpose":"Verification Purpose","reset":"Reset","result":"Final Verification","scope":"Fixed root → intermediate CA → leaf certificate, all signatures mathematically valid; SAN=*.example.com, EKU=serverAuth, certificate time[0,100]. You can add a non-self-issued CA under the intermediate CA; do not implement full PKIX or revocation.","scopeLabel":"Model Scope and Conventions","time":"Verification Time","trust":"Local Trust Root Anchor"}}
ACME: Prove Domain Control, Then Issue for Applied Public Key
The ACME account key is used to authenticate account requests, while the certificate key is used for service handshakes; they bear different responsibilities. The order describes identifiers, authorization/challenge records prove control verification, and the CSR submits the public key for signing. After the order is ready, finalize it; only after processing completes to valid can you download the certificate; a single successful POST does not mean all steps are finished.
Let keyAuthorization = token + "." + accountKeyThumbprint, where the thumbprint is the canonical fingerprint of the account public key JWK. The two basic challenges are carried as follows:
Challenge
CA Check Location
Value Placed
HTTP-01
HTTP port 80 of the target domain, specified challenge path
Raw keyAuthorization
DNS-01
TXT record for _acme-challenge.<domain>
base64url(SHA256(keyAuthorization)), no padding
When applying for *.example.com, DNS verification uses _acme-challenge.example.com. Multiple challenge TXTs may exist with the same name; automation should maintain them according to their respective values to avoid concurrent orders overwriting each other. After publication, confirm the authoritative response and verification visibility; do not treat a local recursive cache hit as global readiness. RFC 8555: Orders and Challenges
TLS-ALPN-01 uses port 443 and negotiates acme-tls/1; the proof material is placed in the dedicated self-signed certificate's critical acmeIdentifier extension, which is the SHA256 digest of keyAuthorization. The ALPN string itself does not carry the challenge value. This dedicated certificate also requires a corresponding single DNS SAN and cannot be directly replaced by a regular service certificate. RFC 8737
Runnable Example: Verify Challenge Bytes Instead of Guessing TXT
Below, using a teaching token and given fingerprint, demonstrate the differences in the three carriers; this does not implement JWK canonicalization, signed requests, or real CA verification.
importbase64importhashlibdefb64url(data):returnbase64.urlsafe_b64encode(data).rstrip(b'=').decode('ascii')token='teaching-token'thumbprint=b64url(hashlib.sha256(b'teaching-account-key').digest())key_auth=(token+'.'+thumbprint).encode('ascii')digest=hashlib.sha256(key_auth).digest()dns_txt=b64url(digest)# The inner layer of acmeIdentifier's extnValue is DER OCTET STRING: tag + length + 32 B.
acme_identifier_der=bytes([0x04,0x20])+digestassertlen(digest)==32andlen(dns_txt)==43assertbase64.urlsafe_b64decode(dns_txt+'=')==digestassertacme_identifier_der[:2]==bytes([4,32])assertdns_txt!=key_auth.decode('ascii')asserthashlib.sha256(key_auth+b'').digest()!=digestprint('HTTP raw, DNS digest encoding, TLS extension inner length:',len(key_auth),len(dns_txt),len(acme_identifier_der))
Validity Period, Revocation, and Status Freshness
A certificate not being expired does not mean the private key has never been leaked. Revocation expresses the withdrawal of recognition after issuance; how clients obtain and enforce status is determined by protocol capabilities and product policies jointly.
Mechanism
What to Obtain and Verify
Boundaries to Note
CRL
Signed list and its range, update time
Can be partitioned or use deltas, not necessarily a single giant table for the entire CA
OCSP
Signature status for a specific issuer/serial number
good is not a complete proof of certificate validity
OCSP Stapling
Server-attached cached OCSP response
Must verify signature, certificate association, and freshness
Short Validity
Shortens the survival window of unupdated certificates
Does not revoke existing credentials the instant they are leaked
OCSP's good/revoked/unknown cannot be simplified to "safe"/"unsafe"/"network anomaly". good at least expresses that it has not been identified as revoked, but does not guarantee that the name, purpose, and chain are valid. Cached responses also have time boundaries: assuming thisUpdate is 10:00 and nextUpdate is 12:00, obtaining it at 11:00 does not mean the status was regenerated at 11:00; 12:30 cannot be taken as definitive evidence of freshness. RFC 6960
Stapling in TLS 1.2 is common in the CertificateStatus message; TLS 1.3 uses extensions in CertificateEntry, so you cannot copy the same message location. Must-Staple is a usage of the TLS Feature extension, not CT for code signing; its actual constraint effect also depends on client support. RFC 7633, TLS 1.3 Certificate Messages
As of September 2026, you can no longer use Let's Encrypt's old OCSP address as a general deployment example: its OCSP service ended in August 2025, shifting to CRL publication. Which status mechanisms a specific CA supports should be read from the current certificate extensions and their policies. Let's Encrypt Announcement
CT: Make Issuance Auditable, Not Replace Domain Verification
Logs return SCTs, promising to incorporate submitted items into the log within a specified period; obtaining an SCT does not mean you have already obtained an inclusion proof. Merkle inclusion proof proves that an item belongs to a specific tree head, while consistency proof is used to prove the log's append-only consistency. Monitors use this to discover anomalous issuances, then proceed with investigation, revocation, or trust policy handling; the log itself does not authorize issuance on behalf of the domain holder. RFC 9162: CT v2
Protocol versions and browser deployment policies must be checked separately. Do not write "all certificates fix two SCTs"; for example, the current Chrome embedded SCT path requires at least two different logs for a lifecycle not exceeding 180 days, and three for over 180 days, with operator diversity and log status conditions; the TLS carrier path has other conditions. Enterprise private trust and public Web PKI cannot directly apply the same conclusion. Chrome CT Policy
CT logs are public information sources; if internal naming from an application enters public certificates, it forms a visible record. It improves discoverability, does not hide domains, and cannot alone prevent malicious certificates from being briefly used.
Renewal is a Publication Loop
Validity period is not a permanently fixed constant in X.509, nor can you write the duration of a specific CA's specific product as a protocol upper limit. As of September 2026, the Baseline Requirements for public TLS Subscriber Certificates stipulate a maximum of 200 days for certificates issued between 2026-03-15 and 2027-03-15; subsequent phases have tightening plans, and specific products can be shorter. Self-built PKIs should not apply this value indiscriminately. CA/B Forum: 6.3.2
Assume a teaching certificate has 20 days remaining, the automated task runs every 24 hours, and normal distribution takes 2 hours. The available recovery window is not "20 definitely successful renewals": the challenge system, issuance rate limiting, DNS permissions, and distribution services may share failures. You should monitor the actual remaining time of service certificates, the most recent successful publication, and consistency across entries, while retaining margin for handling failures.
After material verification passes, atomically replace files, reload users, and then confirm the actual certificate delivered from each entry. Updating the certificate file does not mean the process has loaded it; updating a single node does not mean the entire cluster is complete, and existing connections will not automatically re-handshake. Certificate renewal and private key rotation should be recorded separately to avoid assuming that a new certificate necessarily means a new key.
Continue Reading
TLS 1.2 Handshake: Division of labor for certificate signatures, key exchange, and Finished.