Помимо перехода на "сверхкороткие" сертификаты, который быстро приближается, есть ещё несколько интересных новых моментов, связанных с TLS-сертификатами для веба. В феврале 2025 года Let's Encrypt выпустили демонстрационный "шестидневный" сертификат от одного из своих основных промежуточных УЦ (E6). На примере этого сертификата можно увидеть челый ряд новшеств и неочевидных моментов, которые разобраны в статье. Для упрощения изложения, в качестве иллюстрации при разборе я использую не байты сертификата, а текстовую распечатку значений полей, полученную при помощи утилиты x509 из OpenSSL.
Итак, мы рассмотрим простую текстовую распечатку. Вообще, исходный TLS-сертификат состоит из блоков двоичных данных, объединённых структурой, которая задаётся спецификациями. Типы блоков, их длина - определяются внутренними идентификаторами. Собирательно, схема называется ASN.1, а исходный формат именно сертификатов - это X.509, который очень давно придумали для целей документальной электросвязи (для телеграфа).
Низкоуровневая интерпретация значений отдельных блоков сертификата выполняется в соответствии с их ASN.1-типами и расположением в заданной структуре, а высокоуровневая интерпретация - в соответствии с "ожиданиями" интерпретирующей программы и по так называемым OID ("идентификаторы объектов"), которые входят в состав сертификата на правах обычных блоков.
Текстовая распечатка ниже - это нестрогое человекопонятное представление содержательных данных, которые утилита просмотра смогла извлечь из сертификата и успешно интерпретировать. Тем не менее, данный текстовый формат очень удобен для первоначального знакомства со структурами TLS-сертификатов.
Все комментарии - дальше по тексту, а тут, для справки, полная распечатка без комментариев и сам сертификат в Base64 (внутри PEM).
Скрытый текст
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
03:b0:b0:15:c1:a4:e2:64:16:11:73:1a:71:1b:71:1d:e0:ef
Signature Algorithm: ecdsa-with-SHA384
Issuer: C = US, O = Let's Encrypt, CN = E6
Validity
Not Before: Feb 19 17:30:01 2025 GMT
Not After : Feb 26 09:30:00 2025 GMT
Subject:
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:28:48:8b:6d:d9:5d:5a:a1:c2:39:77:1a:ca:47:
c8:8b:7e:69:b4:2a:25:6f:3a:18:b0:28:1c:b3:bb:
69:0b:78:2e:c2:d0:17:5f:02:0b:70:74:80:9d:92:
e1:21:01:7a:24:85:72:ea:e8:33:59:66:09:a1:e5:
3e:1f:95:5c:19
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 Authority Key Identifier:
keyid:93:27:46:98:03:A9:51:68:8E:98:D6:C4:42:48:DB:23:BF:58:94:D2
Authority Information Access:
OCSP - URI:http://e6.o.lencr.org
CA Issuers - URI:http://e6.i.lencr.org/
X509v3 Subject Alternative Name: critical
DNS:helloworld.letsencrypt.org
X509v3 Certificate Policies:
Policy: 2.23.140.1.2.1
CT Precertificate SCTs:
Signed Certificate Timestamp:
Version : v1 (0x0)
Log ID : CC:FB:0F:6A:85:71:09:65:FE:95:9B:53:CE:E9:B2:7C:
22:E9:85:5C:0D:97:8D:B6:A9:7E:54:C0:FE:4C:0D:B0
Timestamp : Feb 19 18:28:32.078 2025 GMT
Extensions: none
Signature : ecdsa-with-SHA256
30:45:02:20:53:5D:E7:54:DF:48:D8:89:AC:EF:B6:F7:
8B:78:F4:2E:40:35:CE:28:0B:B8:13:53:37:FB:C6:79:
4D:96:12:AC:02:21:00:C2:2E:61:7E:20:BD:4A:C2:8B:
C6:54:D0:D2:C7:2D:53:18:4B:99:D6:21:E3:4A:FA:10:
25:90:4B:FC:96:C3:60
Signed Certificate Timestamp:
Version : v1 (0x0)
Log ID : E0:92:B3:FC:0C:1D:C8:E7:68:36:1F:DE:61:B9:96:4D:
0A:52:78:19:8A:72:D6:72:C4:B0:4D:A5:6D:6F:54:04
Timestamp : Feb 19 18:28:32.147 2025 GMT
Extensions: none
Signature : ecdsa-with-SHA256
30:46:02:21:00:AC:D8:DB:99:21:42:25:A0:E6:87:D6:
DF:5E:5C:32:9B:F1:B8:E8:58:44:81:3E:C2:B8:8B:60:
71:32:F1:08:B3:02:21:00:E3:0D:69:03:72:AF:56:24:
CE:B7:0D:53:3D:79:A8:65:74:A1:52:E0:E4:12:4D:FA:
29:16:C5:73:9D:71:11:C5
Signature Algorithm: ecdsa-with-SHA384
30:65:02:30:30:0d:ab:2e:c6:d3:d0:08:c3:35:dc:77:b4:8d:
97:bb:85:c2:10:be:c6:57:dd:ba:fa:75:3d:e1:03:1d:cf:c5:
03:d2:b7:99:16:24:19:7b:8a:b7:33:5d:3a:1e:f0:70:02:31:
00:b8:c4:30:39:81:42:2c:17:6c:1e:38:ee:81:a4:69:90:1e:
d2:ba:b1:03:71:2d:35:5e:70:8f:4a:1b:78:e6:e5:ba:3f:cd:
81:4b:15:10:6f:4e:aa:20:48:a2:08:05:47
-----BEGIN CERTIFICATE-----
MIIDSzCCAtGgAwIBAgISA7CwFcGk4mQWEXMacRtxHeDvMAoGCCqGSM49BAMDMDIx
CzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MQswCQYDVQQDEwJF
NjAeFw0yNTAyMTkxNzMwMDFaFw0yNTAyMjYwOTMwMDBaMAAwWTATBgcqhkjOPQIB
BggqhkjOPQMBBwNCAAQoSItt2V1aocI5dxrKR8iLfmm0KiVvOhiwKByzu2kLeC7C
0BdfAgtwdICdkuEhAXokhXLq6DNZZgmh5T4flVwZo4IB9zCCAfMwDgYDVR0PAQH/
BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHwYDVR0j
BBgwFoAUkydGmAOpUWiOmNbEQkjbI79YlNIwVQYIKwYBBQUHAQEESTBHMCEGCCsG
AQUFBzABhhVodHRwOi8vZTYuby5sZW5jci5vcmcwIgYIKwYBBQUHMAKGFmh0dHA6
Ly9lNi5pLmxlbmNyLm9yZy8wKAYDVR0RAQH/BB4wHIIaaGVsbG93b3JsZC5sZXRz
ZW5jcnlwdC5vcmcwEwYDVR0gBAwwCjAIBgZngQwBAgEwggEFBgorBgEEAdZ5AgQC
BIH2BIHzAPEAdgDM+w9qhXEJZf6Vm1PO6bJ8IumFXA2XjbapflTA/kwNsAAAAZUf
d/zOAAAEAwBHMEUCIFNd51TfSNiJrO+294t49C5ANc4oC7gTUzf7xnlNlhKsAiEA
wi5hfiC9SsKLxlTQ0sctUxhLmdYh40r6ECWQS/yWw2AAdwDgkrP8DB3I52g2H95h
uZZNClJ4GYpy1nLEsE2lbW9UBAAAAZUfd/0TAAAEAwBIMEYCIQCs2NuZIUIloOaH
1t9eXDKb8bjoWESBPsK4i2BxMvEIswIhAOMNaQNyr1YkzrcNUz15qGV0oVLg5BJN
+ikWxXOdcRHFMAoGCCqGSM49BAMDA2gAMGUCMDANqy7G09AIwzXcd7SNl7uFwhC+
xlfduvp1PeEDHc/FA9K3mRYkGXuKtzNdOh7wcAIxALjEMDmBQiwXbB447oGkaZAe
0rqxA3EtNV5wj0obeObluj/NgUsVEG9OqiBIoggFRw==
-----END CERTIFICATE-----
Итак, разбор.
Certificate:
Data:
Version: 3 (0x2)
Serial Number:
03:b0:b0:15:c1:a4:e2:64:16:11:73:1a:71:1b:71:1d:e0:ef
Версию формата пропускаем - это просто 0x02. Смотрим на серийный номер. И уже здесь начинаются неочевидные моменты. Этот серийный номер начинается с 0x03. Это не простое совпадение. Почему? Потому что согласно спецификации серийный номер должен быть положительным целым числом. Хитрость в том, что в ASN.1 и в сертификатах для записи этого поля исторически используется представление со знаком, так что первый слева бит - это бит знака, и он должен иметь значение 0. При этом актуальные рекомендации требуют, чтобы серийные номера сертификатов имели достаточно большую разрядность и были "непредсказуемыми". Поэтому сейчас серийные номера генерируют (псевдо)случайным образом. И вот чтобы случайно или псевдослучайно не попасть в отрицательную область, левый полубайт (ниббл) принудительно делают равным нулю. Иначе, значение байта больше 0x80 приведет к тому, что запись серийного номера окажется отрицательным числом. Вообще никак не влияет на практическую обработку серийных номеров, но нарушает требования к формату. "Знаковые ошибки" в серийном номере уже приводили к массовому отзыву дефектных сертификатов. Впрочем, механизмы отзыва тоже уходят в прошлое.
Много энтропии в серийном номере ввели для того, чтобы затруднить атаки, основанные на предсказании состава ещё не выпущенного сертификата. Значения прочих полей нетрудно предсказать, и эффект когда-то давно был актуален для MD5, так как позволяет предвычислить коллизии хеш-функций.
Вообще, первый байт (слева) серийного номера правильно целиком использовать для каких-то внутренних целей, как это делают все грамотные и прозорливые УЦ - Let's Encrypt и не только. Например, 0x03 может обозначать поколение ключей, версию ПО или ещё что-то такое.
Signature Algorithm: ecdsa-with-SHA384
"Историческое" поле в сертификате, повторяющее указание на тип подписи. (Подпись ставит УЦ, подпись указана в конце сертификата.)
Issuer: C = US, O = Let's Encrypt, CN = E6
Выпущен УЦ E6 от Let's Encrypt, находящегося в юрисдикции США (US). Полное значение Issuer должно совпадать с Subject в вышестоящем сертификате, который содержит открытый ключ подписывающего УЦ.
Validity
Not Before: Feb 19 17:30:01 2025 GMT
Not After : Feb 26 09:30:00 2025 GMT
Те самые шесть дней валидности (плюс 16 часов "на погрешности").
Subject:
Легко было не заметить и пропустить. Subject. Но он пустой. Это одно из новшеств. Браузеры уже довольно давно требуют наличия имени домена в поле SAN, а не в Subject, CN из которого для DV-сертификатов просто игнорируют (тут есть совсем уж технические детали, касающиеся сортировки сертификатов при валидации в Firefox, но мы их тут не рассматриваем). Теперь Subject вообще будет пустым, как в этом сертификате, а имя домена мы встретим ниже.
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:28:48:8b:6d:d9:5d:5a:a1:c2:39:77:1a:ca:47:
c8:8b:7e:69:b4:2a:25:6f:3a:18:b0:28:1c:b3:bb:
69:0b:78:2e:c2:d0:17:5f:02:0b:70:74:80:9d:92:
e1:21:01:7a:24:85:72:ea:e8:33:59:66:09:a1:e5:
3e:1f:95:5c:19
ASN1 OID: prime256v1
NIST CURVE: P-256
Это открытый ключ ECDSA на кривой P-256 - самый ходовой набор, самая популярная кривая, одобрено NIST и многими другими профильными организациями.
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature
Открытый ключ разрешается использовать только для цифровой подписи. Это то, что требуется в TLS. Прочие варианты, типа передачи ключа и зашифрования - больше не должны использоваться. Например, RSA-шифрование для передачи сеансового секрета в TLS - прямо запрещено спецификацией TLS 1.3.
X509v3 Extended Key Usage:
TLS Web Server Authentication
Расширенные флаги для ключа: только использование для аутентификации сервера. В этом поле повсеместно можно встретить ещё и разрешение для аутентификации клиента. Например, те сертификаты, которые Let's Encrypt выпускает сейчас, содержат здесь TLS Web Server Authentication, TLS Web Client Authentication.
X509v3 Basic Constraints: critical
CA:FALSE
Это не ключ УЦ в сертификате (не должен подписывать другие сертификаты).
X509v3 Authority Key Identifier:
keyid:93:27:46:98:03:A9:51:68:8E:98:D6:C4:42:48:DB:23:BF:58:94:D2
Идентификатор ключа - это просто подсказка, облегчающая поиск ключа УЦ, пропускаем.
Authority Information Access:
OCSP - URI:http://e6.o.lencr.org
CA Issuers - URI:http://e6.i.lencr.org/
А этот блок, в принципе, должен отражать важное новшество, но в демонстрационном сертификате новшества пока что не видно. Это адреса двух точек получения информации УЦ. Точка первая (OCSP) - URI респондера OCSP, то есть, респондера, позволяющего получить сведения о статусе сертификата (отозван или не отозван).
Что тут интересно? Во-первых, дни OCSP в Let's Encrypt сочтены: УЦ отказывается от поддержки этого протокола, уже к августу 2025 года. Надо сказать, правильно делает, что отказывается: отзыв сертификатов и отслеживание их статуса вообще представляют одну из самых больших технических проблем для УЦ, а OCSP так вообще чрезвычайно неудобный, уязвимый, плохо масштабируемый сервис (но зато там простой протокол, да). Однако в этом сертификате OCSP указан. Почему? Потому что демонстрационный сертификат нужно было как-то отозвать, а статус отзыва - опубликовать штатным способом.
Во-вторых, современная версия рекомендаций CA/B-форума позволяет в "сверхкоротких" сертификатах вообще не указывать никаких сведений о способах проверки статуса сертификата. Раньше для всех TLS-сертификатов был один обязательный способ, - списки отзыва (CRL), - и один опциональный способ - OCSP-респондер. В современной версии рекомендаций введено понятие "сверхкоротких" сертификатов (со сроком валидности не более десяти дней) и для "сверхкоротких" можно не указывать никаких способов проверки того, отозван сертификат или нет.
Мотивация всё та же: отзыв всё равно в вебе фактически не работает, а сертификат со сроком действия в несколько дней быстро "протухает" автоматически. Выгода: экономия на сопровождении сервисов с информацией о статусе сертификатов - и это очень большая экономия для УЦ. Кроме того, если потребуется "отозвать" сразу миллионы сертификатов, то не нужно будет ничего делать - не придётся раздувать списки отзыва потоком серйиных номеров, а потоком записей о статусах - раздувать базы OSCP-сервисов. Если потребуется исключить миллионы доменов из перечня тех, кому разрешено выпускать сертификаты, то тоже достаточно будет перекрыть выпуск новых сертификатов - уже выпущенные быстро перестанут быть действительными, всё так же, без раздутия баз и списков. Удобно.
X509v3 Subject Alternative Name: critical
DNS:helloworld.letsencrypt.org
Это DNS-имя, для которого валиден данный сертификат: helloworld.letsencrypt.org.
X509v3 Certificate Policies:
Policy: 2.23.140.1.2.1
Политика, согласно которой сертификат был выпущен. Политики представляют собой своды правил, которым УЦ следовал при генерировании данного сертификата и при проверке права управления сетевым ресурсом, для которого сертификат валиден. Идентификаторы политик влияют на процесс валидации в браузерах. Например, именно политикой отличаются EV-сертификаты, а каких-то технических особенностей сертификата там нет. Но, всё же, это больше административный момент.
CT Precertificate SCTs:
Начинается блок меток SCT (подписанная метка времени - Signed Certificate Timestamp), которые выдают логи Certificate Transparency (далее - CT-логи). Метка содержит цифровую подпись (от ключа CT-лога) и привязана к составу сертификата. Наличие метки здесь означает, что соответствующий пресертификат был предъявлен оператору лога. Обратите внимание, что именно пресертификат - это отражено и в названии поля. То есть, пресертификат - это как бы такой же сертификат, но выпущенный раньше и имеющий в своём составе специальное расширение CT Poison, которое, будучи отмечено как критическое, должно препятствовать успешной валидации. Это "костыль" Certificate Transparency версии 1.0, позволяющий победить проблему курицы и яйца, получив SCT-метку для сертификата, выпуск которого требует наличия SCT-меток.
Signed Certificate Timestamp:
Version : v1 (0x0)
Log ID : CC:FB:0F:6A:85:71:09:65:FE:95:9B:53:CE:E9:B2:7C:
22:E9:85:5C:0D:97:8D:B6:A9:7E:54:C0:FE:4C:0D:B0
Timestamp : Feb 19 18:28:32.078 2025 GMT
Первая SCT-метка получена 19 февраля в 18:28:32, с "копейками" миллисекунд. Это таймстемп, формируемый CT-логом, который должен обозначать время, когда лог получил пресертификат. Log ID - это отпечаток файла открытого ключа лога. Это SCT от лога Cloudflare "Nimbus2025".
Однако обратите внимание на значение таймстемпа: 18:28:32, но сам сертификат начинает действовать в 17:30:01 (см. интервал валидности выше), почти на час раньше. Как так? Интервал валидности должен совпадать в пресертификате и в сертификате, иначе не сойдётся подпись в SCT-метке. Получается, пресертификат был выпущен за час до того, как УЦ предъявил его логу? Нет, конечно же. Это рядовой "бэкдейтинг", но скромный - время выпуска сертификата поставлено на час (примерно) раньше фактического, чтобы, как заявлено, компенсировать проблемы с неточными часами. Действительно, сертификаты Let's Encrypt выпускаются автоматом и сразу должны публиковаться на веб-сервере. Соответственно, если клиентские часы отстают, то свежий сертификат может оказаться по локальным часам "в будущем" (какая-то теория относительности, не находите?), то есть, ещё не действует. 60 минут выбраны потому, что в этот интервал, как считается, укладывается подавляющее большинство практических отклонений часов. Вот поэтому Let's Encrypt пишет в сертификаты время на час раньше.
Extensions: none
Signature : ecdsa-with-SHA256
30:45:02:20:53:5D:E7:54:DF:48:D8:89:AC:EF:B6:F7:
8B:78:F4:2E:40:35:CE:28:0B:B8:13:53:37:FB:C6:79:
4D:96:12:AC:02:21:00:C2:2E:61:7E:20:BD:4A:C2:8B:
C6:54:D0:D2:C7:2D:53:18:4B:99:D6:21:E3:4A:FA:10:
25:90:4B:FC:96:C3:60
Подпись из состава SCT-метки (ECDSA на кривой P-256).
Signed Certificate Timestamp:
Version : v1 (0x0)
Log ID : E0:92:B3:FC:0C:1D:C8:E7:68:36:1F:DE:61:B9:96:4D:
0A:52:78:19:8A:72:D6:72:C4:B0:4D:A5:6D:6F:54:04
Timestamp : Feb 19 18:28:32.147 2025 GMT
Extensions: none
Signature : ecdsa-with-SHA256
30:46:02:21:00:AC:D8:DB:99:21:42:25:A0:E6:87:D6:
DF:5E:5C:32:9B:F1:B8:E8:58:44:81:3E:C2:B8:8B:60:
71:32:F1:08:B3:02:21:00:E3:0D:69:03:72:AF:56:24:
CE:B7:0D:53:3D:79:A8:65:74:A1:52:E0:E4:12:4D:FA:
29:16:C5:73:9D:71:11:C5
Вторая SCT-метка. CT-лог Sectigo "Sabre2025h1". CT-логи ведут сами УЦ. Это может показаться странным, но как-то так сложилось.
Signature Algorithm: ecdsa-with-SHA384
30:65:02:30:30:0d:ab:2e:c6:d3:d0:08:c3:35:dc:77:b4:8d:
97:bb:85:c2:10:be:c6:57:dd:ba:fa:75:3d:e1:03:1d:cf:c5:
03:d2:b7:99:16:24:19:7b:8a:b7:33:5d:3a:1e:f0:70:02:31:
00:b8:c4:30:39:81:42:2c:17:6c:1e:38:ee:81:a4:69:90:1e:
d2:ba:b1:03:71:2d:35:5e:70:8f:4a:1b:78:e6:e5:ba:3f:cd:
81:4b:15:10:6f:4e:aa:20:48:a2:08:05:47
Завершается текстовый дамп сертификата значением подписи. Это просто ECDSA на кривой P-384, которую использует Let's Encrypt для УЦ E6. Подпись вычисляется для всего состава сертификата, который приведён выше. Корректное значение подписи - это то, что и делает сертификат полезным.