Коротко. TLS-клиент идентифицирует сервер только по альтернативному имени субъекта (Subject Alternative Name, SAN), то есть по записям dNSName и iPAddress, и никогда — по общему имени (Common Name, CN) субъекта. Key Usage (использование ключа) определяет, что ключу разрешено делать математически (подписывать, шифровать, подписывать сертификаты), а Extended Key Usage (расширенное использование ключа, EKU) — для каких применений (serverAuth, clientAuth, подпись кода). Право выпускать другие сертификаты даёт сертификату именно значение CA:TRUE в расширении Basic Constraints (основные ограничения); у конечного сертификата там стоит CA:FALSE. Authority Key Identifier и Subject Key Identifier — подсказки, которые помогают клиенту найти издателя, а не проверки безопасности. CRL Distribution Points и Authority Information Access сообщают клиенту, где проверить отзыв и откуда скачать недостающий промежуточный сертификат; в сертификатах Let's Encrypt URL OCSP больше нет. SCT доказывают, что сертификат записан в журналы Certificate Transparency, а этого требуют Chrome и Safari.
Где взять настоящий сертификат для разбора
openssl s_client -showcerts выводит все сертификаты, которые отдаёт сервер, а openssl x509 -text декодирует один из них. Все эксперименты выполнялись в debian:forky-20261005-slim с OpenSSL 3.6.5 и pyca/cryptography 49.0.0:
openssl s_client -connect letsencrypt.org:443 -servername letsencrypt.org -showcerts </dev/null 2>/dev/null \
| awk '/-----BEGIN CERTIFICATE-----/{n++; f=sprintf("letsencrypt.org-%d.pem", n); p=1} p{print >> f} /-----END CERTIFICATE-----/{p=0; close(f)}'
openssl x509 -in letsencrypt.org-1.pem -noout -text
-servername передаёт SNI: без него многие серверы возвращают сертификат по умолчанию, выпущенный на другое имя. Вот конечный сертификат letsencrypt.org в том виде, в каком сервер отдавал его в тот день (открытый ключ показан целиком, две подписи SCT сокращены):
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
05:43:93:3b:e3:86:b3:a2:b3:ad:d5:34:f2:10:3b:c8:b6:5f
Signature Algorithm: ecdsa-with-SHA384
Issuer: C=US, O=Let's Encrypt, CN=YE2
Validity
Not Before: Sep 4 14:34:32 2026 GMT
Not After : Dec 3 14:34:31 2026 GMT
Subject: CN=letsencrypt.org
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:e9:e5:40:7b:43:1e:46:b9:8e:45:e4:aa:cb:40:
ab:e6:3f:99:27:b8:b7:81:e3:32:56:e7:14:74:5f:
fa:43:5b:52:c6:a6:b5:66:c5:56:0e:07:21:56:e3:
4c:fe:35:55:4f:f0:92:2f:eb:22:4a:f1:7a:39:51:
39:04:c2:b3:0d
ASN1 OID: prime256v1
NIST CURVE: P-256
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature
X509v3 Extended Key Usage:
TLS Web Server Authentication
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Subject Key Identifier:
16:68:8D:1E:2A:32:48:8C:B5:F1:10:0A:16:08:37:08:3B:9D:30:23
X509v3 Authority Key Identifier:
B9:59:F2:8E:CF:22:F0:86:D3:37:48:FF:76:14:18:BA:82:D8:55:87
Authority Information Access:
CA Issuers - URI:http://ye2.i.lencr.org/
X509v3 Subject Alternative Name:
DNS:cp.letsencrypt.org, DNS:cp.root-x1.letsencrypt.org, DNS:cps.letsencrypt.org, DNS:cps.root-x1.letsencrypt.org, DNS:lencr.org, DNS:letsencrypt.com, DNS:letsencrypt.org, DNS:www.lencr.org, DNS:www.letsencrypt.com, DNS:www.letsencrypt.org
X509v3 Certificate Policies:
Policy: 2.23.140.1.2.1
X509v3 CRL Distribution Points:
Full Name:
URI:http://ye2.c.lencr.org/96.crl
CT Precertificate SCTs:
Signed Certificate Timestamp:
Version : v1 (0x0)
Log ID : D8:09:55:3B:94:4F:7A:FF:C8:16:19:6F:94:4F:85:AB:
B0:F8:FC:5E:87:55:26:0F:15:D1:2E:72:BB:45:4B:14
Timestamp : Sep 4 15:33:02.377 2026 GMT
Extensions: none
Signature : ecdsa-with-SHA256
30:46:02:21:00:D0:2E:25:A4:9D:7F:C8:18:D6:7B:5E:...
Signed Certificate Timestamp:
Version : v1 (0x0)
Log ID : 46:AF:86:3D:3B:3E:E5:9F:A5:77:DE:A8:24:5D:36:B0:
D9:ED:22:A2:23:F4:61:77:41:22:94:52:EE:95:50:5F
Timestamp : Sep 4 15:33:02.552 2026 GMT
Extensions: 00:00:05:00:24:DF:DB:0C
Signature : ecdsa-with-SHA256
30:44:02:20:31:2F:83:D6:42:65:26:0D:58:D7:DD:C3:...
Signature Algorithm: ecdsa-with-SHA384
Signature Value:
30:65:02:31:00:ee:1d:b0:7c:35:88:98:15:57:b8:88:73:23:
92:a9:bf:40:bf:e8:ae:02:48:e6:f4:7a:2d:18:fc:43:52:d3:
80:9a:ff:ce:e0:2a:0d:4a:29:e6:80:a2:94:f5:4f:14:d9:02:
30:35:08:99:3b:bc:89:5b:23:a0:ef:64:24:e2:b2:b3:b8:c2:
d8:d5:95:45:de:f3:03:e8:e7:82:78:a5:9e:c2:1f:ad:eb:14:
fc:39:51:c7:e1:18:e9:16:b4:a5:48:de:09
Дальше мы разберём его сверху вниз. Саму структуру определяет RFC 5280 (поля описаны в § 4.1, расширения — в § 4.2), а что может содержать публично доверенный TLS-сертификат, сказано в Базовых требованиях (Baseline Requirements, BR) CA/Browser Forum, версия 2.3.1 от 4 октября 2026 года, § 7.1.
Версия, серийный номер, алгоритм подписи, срок действия
- Версия 3 — единственная версия с расширениями; BR требуют именно её.
- Серийный номер. Уникален в пределах одного издателя, а у публичных CA это «непоследовательное число больше нуля (0) и меньше 2¹⁵⁹, содержащее не менее 64 бит выходных данных CSPRNG» (BR, § 7.1.2.7). Случайность здесь защищает от атак на хеш подписи, основанных на коллизиях с выбранным префиксом (chosen-prefix collision); из-за неё серийные номера и выглядят как шум. Именно серийные номера перечисляются в CRL.
- Signature Algorithm (алгоритм подписи) встречается дважды: внутри подписанной части и рядом с самой подписью, и оба значения должны совпадать байт в байт.
ecdsa-with-SHA384здесь говорит о ключе издателя (у YE2 ключ P-384), а не субъекта. Значение подписи в конце — подпись ECDSA в DER; этот формат разобран в статье Форматы подписей ECDSA. - Срок действия. Моменты
notBeforeиnotAfterвходят в срок действия. Этот сертификат действует 90 дней; максимум по BR — 200 дней для сертификатов, выпущенных с 15 марта 2026 года, 100 дней для выпущенных с 15 марта 2027 года и 47 дней для выпущенных с 15 марта 2029 года (BR, § 6.3.2). Let's Encrypt уже выпускает 45-дневные сертификаты в профилеtlsserver, который нужно выбрать явно, и планирует сократить срок по умолчанию до 64 дней 10 февраля 2027 года и до 45 дней 16 февраля 2028 года.
Субъект и альтернативное имя субъекта
Субъект — это отличительное имя (distinguished name, DN), здесь в нём только CN=letsencrypt.org. В сертификатах с проверкой домена больше ничего нет, потому что ничего другого CA и не проверял. Имена, которые сверяет клиент, находятся в альтернативном имени субъекта (SAN): у этого сертификата там 10 записей DNS:.
CN давно перестал учитываться. RFC 9525 (2023), заменивший RFC 6125, гласит: «RDN Common Name не должен (MUST NOT) использоваться для идентификации сервиса». Chrome перестал сверять имя с CN в версии 58 (2017), а Go отклоняет сертификаты, в которых есть только CN, с ошибкой x509: certificate relies on legacy Common Name field. BR оставляют CN только как копию: если он есть, он «должен (MUST) содержать ровно одну запись, совпадающую с одним из значений расширения subjectAltName сертификата» (§ 7.1.4.3).
В SAN можно указывать и IP-адреса. Сертификат DNS-резолвера Cloudflare:
subject=C=US, ST=California, L=San Francisco, O=Cloudflare, Inc., CN=cloudflare-dns.com
X509v3 Subject Alternative Name:
DNS:cloudflare-dns.com, DNS:*.cloudflare-dns.com, IP Address:1.0.0.1, IP Address:1.1.1.1, IP Address:162.159.36.1, IP Address:162.159.46.1, IP Address:2606:4700:4700:0:0:0:0:1001, IP Address:2606:4700:4700:0:0:0:0:1111, IP Address:2606:4700:4700:0:0:0:0:64, IP Address:2606:4700:4700:0:0:0:0:6400, DNS:one.one.one.one
https://1.1.1.1 работает, потому что 1.1.1.1 записан как iPAddress; запись DNS:1.1.1.1 не совпала бы с IP-адресом — об этом и говорит ошибка Go cannot validate certificate for … because it doesn't contain any IP SANs. В субъекте здесь указаны ещё организация и местонахождение, а сертификат содержит идентификатор политики OV 2.23.140.1.2.2 (см. DV, OV или EV); браузер ничего из этого не показывает. Сертификат выпущен 31 декабря 2025 года на 355 дней, ещё до того, как вступило в силу ограничение в 200 дней.
Wildcard-имя вида *.cloudflare-dns.com соответствует ровно одной метке: a.cloudflare-dns.com, но не самому cloudflare-dns.com и не a.b.cloudflare-dns.com (RFC 9525, § 6.3). OpenSSL сверяет имена так же:
Hostname www.lencr.org does match certificate
Hostname lencr.org.evil.test does NOT match certificate
Hostname mail.letsencrypt.org does NOT match certificate
Открытый ключ субъекта (SPKI)
Поле subjectPublicKeyInfo (SPKI) содержит алгоритм и сам ключ — несжатую точку P-256 (04, за которым следуют 32-байтовые координаты x и y). Это единственная часть сертификата, с которой связан закрытый ключ субъекта, поэтому при закреплении открытого ключа хешируют SPKI, а не весь сертификат: после продления с тем же ключом пин остаётся прежним (см. статью Закрепление сертификатов в 2026 году).
Key Usage и Extended Key Usage
Эти расширения отвечают на два разных вопроса:
- Key Usage (RFC 5280, § 4.2.1.3) ограничивает криптографические операции ключа:
digitalSignature,keyEncipherment,keyAgreement,keyCertSign,cRLSignи несколько более редких битов. BR требуют, чтобы в конечном сертификате с ключом ECDSA былdigitalSignature, и запрещаютkeyEncipherment(ключом EC нельзя шифровать). В конечный сертификат с ключом RSA можно добавитьkeyEnciphermentдля устаревшего обмена ключами RSA в TLS 1.2 — BR называют это поддержкой «небезопасных наборов шифров» (insecure ciphersuites). Кроме того, по BR это расширение следует (SHOULD) включать и помечать как критическое, как здесь. - Extended Key Usage (§ 4.2.1.12) перечисляет применения:
serverAuth(1.3.6.1.5.5.7.3.1),clientAuth(…3.2),codeSigning(…3.3),emailProtection(…3.4),timeStamping(…3.8). Серверный TLS-сертификат обязан (MUST) содержатьserverAuthи не должен (MUST NOT) содержать назначения для подписи кода, электронной почты, меток времени и подписи ответов OCSP, а такжеanyExtendedKeyUsage(BR, § 7.1.2.7.10).
Изменилось именно то, что касается clientAuth. BR по-прежнему его разрешают (MAY), но политика Chrome Root Program версии 1.8 требует иерархий, «выделенных для аутентификации TLS-серверов»: подчинённые CA, сведения о которых раскрыты начиная с 15 июня 2026 года, могут указывать только serverAuth, а с 15 марта 2027 года то же обязательно и для выпускаемых сертификатов подписчиков. Let's Encrypt убрал clientAuth из профиля по умолчанию 11 февраля 2026 года и полностью перестал выпускать сертификаты с ним 8 июля 2026 года; в конечном сертификате letsencrypt.org выше есть только serverAuth. Если ваш сервис аутентифицировал клиентов по публичным TLS-сертификатам, ему нужен частный CA; подробнее — в статье Взаимный TLS (mTLS) для API.
Проверяющие стороны действительно учитывают EKU. Вот проверка сертификата, выпущенного одноразовым тестовым CA, с единственным значением EKU clientAuth:
CN=worker-7
error 26 at 0 depth lookup: unsuitable certificate purpose
error worker-7.pem: verification failed
worker-7.pem: OK
Это ошибка 26 OpenSSL; Go в том же случае выдаёт ошибку x509: certificate specifies an incompatible key usage.
Basic Constraints: кто может выпускать сертификаты
Basic Constraints содержит один логический флаг и одно число. CA:TRUE разрешает ключу сертификата подписывать другие сертификаты, а pathlen ограничивает, сколько промежуточных CA может следовать ниже него. RFC 5280, § 4.2.1.9, прямо говорит, что без установленного cA «заверенный открытый ключ не должен (MUST NOT) использоваться для проверки подписей сертификатов». Вот как это выглядит вверх по цепочке letsencrypt.org:
| Сертификат | Basic Constraints | Key Usage |
|---|---|---|
CN=letsencrypt.org | критическое, CA:FALSE | Digital Signature |
CN=YE2 (промежуточный) | критическое, CA:TRUE, pathlen:0 | Digital Signature, Certificate Sign, CRL Sign |
CN=Root YE (с перекрёстной подписью ISRG Root X2) | критическое, CA:TRUE | Certificate Sign, CRL Sign |
pathlen:0 у YE2 означает, что он может выпускать только конечные сертификаты. Чтобы посмотреть, как соблюдаются эти правила, мы подписали другой сертификат ключом обычного серверного сертификата (CA:FALSE, Key Usage digitalSignature):
CN=api.example.test
error 79 at 1 depth lookup: invalid CA certificate
CN=api.example.test
error 32 at 1 depth lookup: key usage does not include certificate signing
error forged.example.test.pem: verification failed
Срабатывают оба правила: ошибка 79 из-за отсутствия CA:TRUE и ошибка 32 из-за отсутствия keyCertSign. Именно из-за пропуска этой проверки Internet Explorer в 2002 году принимал любой конечный сертификат в качестве CA. Python начиная с версии 3.13 включает и строгий режим OpenSSL, который дополнительно отклоняет сертификаты CA, где расширение Basic Constraints не помечено как критическое.
Идентификаторы ключа издателя и субъекта
Subject Key Identifier (SKI, идентификатор ключа субъекта) указывает на собственный ключ сертификата, а Authority Key Identifier (AKI, идентификатор ключа издателя) — на ключ, которым сертификат подписан. Каждый AKI равен SKI следующего сертификата вверх по цепочке:
subject=CN=letsencrypt.org
16:68:8D:1E:2A:32:48:8C:B5:F1:10:0A:16:08:37:08:3B:9D:30:23
B9:59:F2:8E:CF:22:F0:86:D3:37:48:FF:76:14:18:BA:82:D8:55:87
subject=C=US, O=Let's Encrypt, CN=YE2
B9:59:F2:8E:CF:22:F0:86:D3:37:48:FF:76:14:18:BA:82:D8:55:87
A3:C8:26:5A:8E:A1:4C:D0:35:63:FC:9B:23:C8:3A:AE:56:F3:4F:56
subject=C=US, O=ISRG, CN=Root YE
A3:C8:26:5A:8E:A1:4C:D0:35:63:FC:9B:23:C8:3A:AE:56:F3:4F:56
7C:42:96:AE:DE:4B:48:3B:FA:92:F8:9E:8C:CF:6D:8B:A9:72:37:95
subject=C=US, O=Internet Security Research Group, CN=ISRG Root X2
7C:42:96:AE:DE:4B:48:3B:FA:92:F8:9E:8C:CF:6D:8B:A9:72:37:95
79:B4:59:E6:7B:B6:E5:E4:01:73:80:08:88:C8:1A:58:F6:E9:9B:6E
Идентификаторы ключей помогают проверяющей стороне выбрать нужного издателя, когда у нескольких сертификатов CA одно и то же имя (CA со сменённым ключом или с перекрёстной подписью, как два существующих сертификата ISRG Root X2). Сами они ничего не доказывают: это делает проверка подписи. RFC 5280 требует AKI в каждом сертификате, кроме самоподписанных корневых, и SKI в каждом сертификате CA; RFC 5280 предлагает вычислять SKI, например, как хеш SHA-1 от ключа, но издатели могут использовать любое уникальное значение. BR относят SKI в конечном сертификате к «не рекомендуемым» (NOT RECOMMENDED) полям, но и Let's Encrypt, и Sectigo по-прежнему его включают. Строгие проверяющие стороны отклоняют сертификат CA без AKI, как показывает ошибка Python Missing Authority Key Identifier.
Цепочка показывает и то, что путь, который вы проверяете, не всегда совпадает с тем, что отдаёт сервер. letsencrypt.org отдаёт 4 сертификата, последний из которых — ISRG Root X2 с перекрёстной подписью ISRG Root X1; OpenSSL с хранилищем доверенных сертификатов Debian останавливается на корневом X2, которому уже доверяет:
letsencrypt.org-1.pem: OK
Chain:
depth=0: CN=letsencrypt.org (untrusted)
depth=1: C=US, O=Let's Encrypt, CN=YE2 (untrusted)
depth=2: C=US, O=ISRG, CN=Root YE (untrusted)
depth=3: C=US, O=Internet Security Research Group, CN=ISRG Root X2
CRL Distribution Points, AIA и OCSP
Два расширения содержат URL:
- CRL Distribution Points (точки распространения CRL, RFC 5280, § 4.2.1.13) указывает, где издатель публикует свой CRL. BR требуют это расширение, кроме случаев, когда сертификат краткосрочный (с 15 марта 2026 года — 7 дней или меньше) или содержит URL OCSP, а сам URL должен быть обычным
http: CRL подписан, поэтому TLS ничего не добавляет и создал бы замкнутый круг. - Authority Information Access (AIA, § 4.2.2.1):
CA Issuers— откуда скачать сертификат выпустившего CA, аOCSP— адрес ответчика OCSP издателя.
В конечном сертификате Let's Encrypt есть URL CRL и нет URL OCSP: в мае 2025 года Let's Encrypt убрал URL OCSP из сертификатов, а 6 августа 2025 года отключил свои ответчики, после того как BR сделали OCSP необязательным. Sectigo по-прежнему поддерживает OCSP; в конечном сертификате github.com есть оба метода доступа и две политики:
X509v3 Certificate Policies:
Policy: 1.3.6.1.4.1.6449.1.2.2.7
CPS: https://sectigo.com/CPS
Policy: 2.23.140.1.2.1
Authority Information Access:
CA Issuers - URI:http://crt.sectigo.com/SectigoPublicServerAuthenticationCADVE36.crt
OCSP - URI:http://ocsp.sectigo.com
Скачаем CRL, указанный в конечном сертификате letsencrypt.org, и проверим его по сертификату YE2:
crl=$(openssl x509 -in letsencrypt.org-1.pem -noout -ext crlDistributionPoints | grep -o 'http://[^ ]*')
curl -sS -o leaf.crl "$crl"
openssl crl -inform DER -in leaf.crl -noout -issuer -lastupdate -nextupdate
openssl crl -inform DER -in leaf.crl -noout -text | grep -c 'Serial Number:'
openssl crl -inform DER -in leaf.crl -noout -CAfile letsencrypt.org-2.pem
issuer=C=US, O=Let's Encrypt, CN=YE2
lastUpdate=Oct 20 15:03:57 2026 GMT
nextUpdate=Oct 29 15:03:56 2026 GMT
1590
verify OK
96.crl в URL — это шард: CA распределяет отозванные сертификаты по множеству небольших CRL и в каждом сертификате указывает нужный, поэтому клиент скачивает 1 590 записей, а не сведения обо всех отозванных сертификатах. Браузеры редко загружают CRL в момент проверки: Chrome и Firefox сами доставляют данные об отзыве на устройства пользователей. Плюсы и минусы обоих подходов разобраны в сравнении CRL или OCSP.
CA Issuers важен, когда сервер забывает отдать свой промежуточный сертификат. Браузеры и системные средства проверки Windows и macOS переходят по этому URL (так называемый AIA chasing); OpenSSL (а вместе с ним curl, Python и PHP в Linux), Go в Linux и Java с настройками по умолчанию — нет, поэтому один и тот же неправильно настроенный сервер работает в браузере, а в коде выдаёт ошибку «unable to get local issuer certificate».
Политики сертификата
Каждый публичный TLS-сертификат содержит ровно один идентификатор политики CA/Browser Forum, который говорит, как проверяли субъекта: 2.23.140.1.2.1 — проверка домена, 2.23.140.1.2.2 — проверка организации, 2.23.140.1.2.3 — проверка физического лица, 2.23.140.1.1 — расширенная проверка (BR, § 7.1.6.1). CA добавляют собственные OID политик и ссылку на свой регламент сертификации (CPS), как Sectigo выше. Клиенты, которые показывают уровень проверки, читают это расширение (Certificate Policies), а не субъект.
Подписанные метки времени сертификата (SCT)
Перед выпуском CA отправляет предсертификат (то же содержимое плюс poison-расширение) в журналы Certificate Transparency, и каждый журнал отвечает подписанным обещанием опубликовать его — SCT. CA встраивает эти SCT в итоговый сертификат (OID 1.3.6.1.4.1.11129.2.4.2, RFC 6962, § 3.3). ID журнала — это хеш ключа журнала; список журналов CT в Chrome сопоставляет ему название:
"""List the SCTs embedded in a certificate and name their logs from Chrome's CT log list."""
import base64
import json
import sys
from cryptography import x509
log_list = json.load(open(sys.argv[1]))
logs = {}
for operator in log_list["operators"]:
for log in operator["logs"] + operator.get("tiled_logs", []):
logs[base64.b64decode(log["log_id"])] = (operator["name"], log["description"])
cert = x509.load_pem_x509_certificate(open(sys.argv[2], "rb").read())
scts = cert.extensions.get_extension_for_class(x509.PrecertificateSignedCertificateTimestamps).value
for sct in scts:
operator, name = logs.get(sct.log_id, ("unknown operator", "unknown log"))
print(f"{sct.timestamp:%Y-%m-%d %H:%M:%S} UTC {operator}: {name} ({sct.entry_type.name})")
2026-09-04 15:33:02 UTC Google: Google Xenon2026h2 (PRE_CERTIFICATE)
2026-09-04 15:33:02 UTC Geomys: Geomys Tuscolo2026h2 (PRE_CERTIFICATE)
2026-09-01 00:10:32 UTC Google: Google Argon2026h2 (PRE_CERTIFICATE)
2026-09-01 00:10:31 UTC Sectigo: Sectigo Tiger2026h2 (PRE_CERTIFICATE)
Список журналов скачан с https://www.gstatic.com/ct/log_list/v3/log_list.json в тот же день. В каждом сертификате по два SCT от двух разных операторов — именно этого требует политика CT в Chrome для сертификата со сроком действия не более 180 дней (для более долгих — три). Extensions: 00:00:05:00:24:DF:DB:0C в SCT от Tuscolo — это расширение leaf_index, которое обязаны добавлять журналы, реализующие static CT API: тип 0, длина 5 и 40-битный индекс, здесь это запись 618 650 380 в этом журнале. Благодаря SCT любой может найти все сертификаты, выпущенные для его домена, а ошибочно выпущенный сертификат не может остаться тайной. Декодер SCT декодирует их без командной строки.
Типичные ошибки
- Имя указано только в CN. Браузеры выдают NET::ERR_CERT_COMMON_NAME_INVALID, Java — No subject alternative names present. Каждое имя, в том числе то, что стоит в CN, должно быть в SAN. Чаще всего на это натыкаются с внутренними CA и однострочными командами
openssl req, которые задают только-subj. - Нет промежуточного сертификата. Благодаря AIA chasing в браузерах и системных средствах проверки Windows и macOS всё работает, а в клиентах на базе OpenSSL, в Go в Linux и в Java — нет. Отдавайте полную цепочку (первым — конечный сертификат, корневой можно не включать).
- Расчёт на
clientAuthв публичном сертификате. Это значение исчезает из публичных TLS-иерархий; интеграциям с партнёрами, которые рассчитаны на «любой публичный сертификат сclientAuth», нужен частный CA. - Сертификат CA с
CA:TRUE, но безkeyCertSignили без пометки о критичности. Строгие проверяющие стороны (Python 3.13+, Go для некоторых полей) его отклоняют; в Go это ошибка parent certificate cannot sign this kind of certificate. - SKI вместо отпечатка. В SKI лежит то, что туда записал CA. Чтобы идентифицировать сертификат, хешируйте весь DER (
openssl x509 -fingerprint -sha256), а чтобы идентифицировать ключ — SPKI. - Чтение срока действия по местному времени.
notBeforeиnotAfterзаданы в UTC. Сервер, часы которого отстают на несколько часов, отклонит только что выпущенный сертификат как ещё не действительный.
Источники
- RFC 5280: § 4.1 (поля сертификата), §§ 4.2.1.1–4.2.1.13 (AKI, SKI, Key Usage, SAN, Basic Constraints, EKU, CRL DP), § 4.2.2.1 (AIA)
- RFC 9525: идентификаторы сервисов в TLS (§ 2, § 6.3)
- RFC 6962: Certificate Transparency (§ 3.3) и static CT API
- Базовые требования CA/Browser Forum 2.3.1 (4 октября 2026 года): § 6.3.2, § 7.1.2.7, § 7.1.2.10, § 7.1.2.11, § 7.1.4.3, § 7.1.6.1
- Политика Chrome Root Program, версия 1.8 и политика CT в Chrome
- Let's Encrypt: Прекращение поддержки OCSP в 2025 году, Прекращение поддержки сертификатов для аутентификации TLS-клиентов в 2026 году, Цепочки доверия