Состав TLS-сертификата на примере «шестидневного» варианта от Let’s Encrypt

от автора

Помимо перехода на «сверхкороткие» сертификаты, который быстро приближается, есть ещё несколько интересных новых моментов, связанных с 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. Подпись вычисляется для всего состава сертификата, который приведён выше. Корректное значение подписи — это то, что и делает сертификат полезным.


ссылка на оригинал статьи https://habr.com/ru/articles/901312/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *