Как стать автором
Обновить

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

Уровень сложностиСредний
Время на прочтение10 мин
Количество просмотров2.3K

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

Теги:
Хабы:
+8
Комментарии0

Публикации