Использование сертификатов для шифрования¶
Этот раздел довольно экстремальный, поскольку описанный в нём метод мало подходит для практического применения. Однако он описывает подход, применяемый в современном TLS-шифровании веб-трафика.
В общем случае X.509-сертификаты не предназначены для шифрования данных, единственный пригодный для этого алгоритм — RSA, однако это исключение. Тем не менее, в разделе Используемые криптоалгоритмы я рассказал, как можно использовать сертификаты совместно с протоколом Diffie-Hellman для безопасного обмена ключами симметричного шифрования. А в этом разделе я расскажу, как это можно реализовать на практике через команды openssl.
Действующие лица: Алиса и Боб, у каждого из них есть свой X.509-сертификат с закрытым ключом, причём Алиса и Боб заранее обменялись сертификатами каким-то надёжным способом (например, при личной встрече). И с этого момента весь обмен данными происходит через небезопасный открытый канал.
Алиса хочет организовать безопасный сеанс связи с Бобом поверх небезопасного канала. Для этого отлично подходит симметричный алгоритм AES-128-CBC, однако для него обе стороны должны использовать одинаковый ключ — набор байтов размером 128 бит. Алиса и Боб будут для обмена ключом пользоваться протоколом Diffie-Hellman с аутентификацией через цифровые подписи.
Создадим сначала сертификаты и закрытые ключи к ним для каждой из сторон. Алиса создаёт закрытый ключ alice-x509.key и самоподписанный сертификат alice-x509.crt:
openssl ecparam -name prime256v1 -genkey -noout -out alice-x509.key
openssl req -new -x509 -batch -days -key alice-x509.key -subj -out alice-x509.crt
И Боб делает то же самое — ключ в файле bob-x509.key и самоподписанный сертификат bob-x509.crt:
openssl ecparam -name prime256v1 -genkey -noout -out bob-x509.key
openssl req -new -x509 -batch -days -key bob-x509.key -subj -out bob-x509.crt
Дальше стороны должны заранее надёжным и безопасным способом передать друг-другу свои сертификаты.
Через какое-то время Алиса захотела обменяться важными данными с Бобом через e-mail. Это начало новой сессии обмена, в которой будут использоваться новые ключи (эфемерные, то есть не фиксированные, а созданные специально для этого момента, после сессии эфемерные ключи стираются), поэтому Алиса должна выбрать параметры протокола Diffie-Hellman, это делается такой командой:
openssl dhparam -dsaparam -out dhp.pem
Generating DSA parameters, 4096 bit long prime
Здесь 4096 означает размер случайного простого числа в битах, он в настоящее время считается безопасным. Теперь Алиса на основе этого файла с параметрами создаёт свой закрытый ключ dh-alice.key (это ещё не эфемерный ключ, а только заготовка для него!):
openssl genpkey -paramfile dhp.pem -out alice-dh.key
И выделяет из него открытый ключ:
openssl pkey -in alice-dh.key -pubout -out alice-dh-pub.pem
Затем Алиса конкатенирует два файла dhp.pem и alice-dh-pub.pem в один alice2bob.pem:
И создаёт цифровую подпись для него (в виде отдельного файла alice2bob.pem.sig), используя свой закрытый ключ от X.509-сертификата:
openssl dgst -sha256 -sign alice-x509.key -out alice2bob.pem.sig alice2bob.pem
Дальше Алиса отправляет Бобу два файла: alice2bob.pem и alice2bob.pem.sig.
Боб получает от Алисы файл и цифровую подпись. Для проверки подписи ему сначала нужно извлечь из сертификата Алисы открытый ключ в файл alice-x509-pubkey.pem:
И дальше проверить им цифровую подпись:
openssl dgst -sha256 -verify alice-x509-pubkey.pem -signature alice2bob.pem.sig alice2bob.pem
Проверка прошла успешно, теперь Боб уверен, что полученный им файл alice2bob.pem действительно пришёл от Алисы, поэтому можно продолжать. Боб создаёт свой закрытый DH-ключ на основе полученных параметров:
openssl genpkey -paramfile alice2bob.pem -out bob-dh.key
Обратите внимание, что в аргументе с параметрами указан файл alice2bob.pem, в котором записано два разных PEM-блока, но так как PEM — это контейнер, openssl берёт из него только те данные, которые ему нужны в текущем контексте, а здесь контекст — это параметры DH, именно такой блок и будет прочитан. Другими словами, нет никакой необходимости разделять файл на части.
Дальше Боб выделяет открытый ключ на основе только что созданного закрытого:
openssl pkey -in bob-dh.key -pubout -out bob-dh-pub.pem
И, наконец, создаёт свой эфемерный закрытый ключ в файле bob-secret.key:
openssl pkeyutl -derive -inkey bob-dh.key -peerkey alice2bob.pem -out bob-secret.key
Теперь Боб создаёт цифровую подпись для своего открытого DH-ключа:
openssl dgst -sha256 -sign bob-x509.key -out bob-dh-pub.pem.sig bob-dh-pub.pem
И отправляет Алисе два файла: bob-dh-pub.pem и bob-dh-pub.pem.sig.
Алиса проверяет цифровую подпись аналогичным образом: выделяет открытый ключ из сертификата Боба и верифицирует файл bob-dh-pub.pem с подписью bob-dh-pub.pem.sig:
И, наконец, Алиса создаёт свой эфемерный ключ на основе собственного закрытого DH-ключа (alice-dh.key) и полученного от Боба открытого DH-ключа (bob-dh-pub.pem):
openssl pkeyutl -derive -inkey alice-dh.key -peerkey bob-dh-pub.pem -out alice-secret.key
Мы можем убедиться, что оба их эфемерных ключа одинаковые:
diff -s alice-secret.key bob-secret.key
Files alice-secret.key and bob-secret.key are identical
Теперь Алиса может зашифровать файл alice-message.txt (внутри которого только одна строчка: Very secret message.), используя эфемерный ключ в качестве пароля:
openssl enc -e -md md5 -pass file:alice-secret.key -aes-128-cbc
-in alice-message.txt -out alice-message.txt.encrypted
Здесь мы используем алгоритм хеширования MD5, чтобы получить из файла эфемерного ключа ровно 128 бит AES-ключа.
Боб теперь может расшифровать файл так:
openssl enc -d -md md5 -pass file:bob-secret.key -aes-128-cbc -in alice-message.txt.encrypted
Very secret message.
Аналогичным образом Боб может зашифровать свой текст и отправить Алисе, ключ для его расшифровки есть только у них двоих. После завершения обмена сообщениями они оба удаляют свои эфемерные ключи и для следующего сеанса создают их заново.
Секретный ключ¶
Главный компонент всего — криптография, точнее криптографические алгоритмы.
Всё начинается с генерации пары ключей: секретного и публичного. Сначала при помощи математической и компьютерной магии генерируется секретный ключ, а затем из него вычисляется публичный. Чаще всего для сертификатов используется алгоритм RSA, вот как это делается в консоли через программу openssl:
openssl genrsa
Generating RSA private key, 2048 bit long modulus
e is 65537 (0x10001)
——BEGIN RSA PRIVATE KEY——
——END RSA PRIVATE KEY——
В этом примере мы сгенерили секретный ключ для алгоритма RSA. Собственно сам ключ находится между специальными маркерами ——BEGIN RSA PRIVATE KEY—— и ——END RSA PRIVATE KEY——. Чтобы при генерации ключа сохранить его сразу в файл, добавьте аргумент -out private.pem:
openssl genrsa -out private.pem
Generating RSA private key, 2048 bit long modulus
e is 65537 (0x10001)
С подобным форматом представления криптографических данных вам предстоит сталкиваться постоянно, называется он PEM, что расшифровывается как Privacy-enhanced Electronic Mail. Его предназначение — кодировать бинарные криптографические данные в виде ASCII-текста. И фактически между маркерами из минусов находятся закодированные в base64 бинарные данные, в данном случае — секретный ключ.
Мы можем при помощи openssl сконвертировать секретный ключ из текстового формата PEM в изначальный бинарный, сохраним его в файл private.der:
openssl rsa -inform PEM -outform DER -in private.pem -out private.der
writing RSA key
Имя формата данных DER расшифровывается как Distinguished Encoding Rules и является частью стандарта ASN.1. В стандарте ASN.1 описываются правила и структуры для кодирования, раскодирования и передачи данных по телекоммуникационным и компьютерным сетям. По сути можно считать ASN.1 форматом для сериализации структурированных бинарных данных. Существует специальный компилятор, который из формальных спецификаций ASN.1 генерирует C-код для работы с бинарными данными такой структуры.
Также существует консольная программа dumpasn1 для просмотра бинарных данных, в дебиане/убунте она ставится через sudo apt-get install dumpasn1, для макоси можно поставить через macports, через homebrew (инструкция), либо скомпилировать самостоятельно из исходников.
Вот так, например, выглядит дамп нашего созданного секретного ключа:
dumpasn1 private.der
4 1: INTEGER 0
7 257: INTEGER
: 00 CB F8 C1 9B 6E 29 63 39 3C 24 9B 2D A3 7D 00
: B0 B8 5C E8 D6 96 5D E4 76 67 7F 04 8F 48 D4 F4
: 35 64 68 57 10 3D 38 CE A0 B5 DC B2 FC DD 34 FF
: 2B AC 48 EE D1 05 37 65 4A 2F AE 8A E0 F8 1D CE
: 63 EB 2E C6 7A 09 4B B1 85 71 1E FA FF 55 17 0C
: 05 23 71 87 43 21 66 C4 70 6D E8 A8 B4 74 EA E4
: C1 75 7B AB 33 5B 8B 8D DB D4 67 BA DC B4 AD 50
: 12 AB FB 3E 74 44 AC 48 BE 94 C2 DD 4D 16 D6 0F
268 3: INTEGER 65537
273 256: INTEGER
: 07 01 7D 4C E4 64 C1 86 B6 BD 1F 23 5B 29 30 FB
: E0 E9 38 0A 1E D2 0C C5 D0 5A 39 82 DE 62 8A 1C
: C7 5D 1A 18 71 B1 E0 CE FE 50 1D 49 B8 23 58 DC
: 5C 27 89 24 5E C4 7F 53 23 FE 1F C1 08 64 A5 B1
: 22 E3 D1 67 61 A8 5A E9 95 70 15 F8 ED 28 44 7E
: 6C B0 3A 90 20 B6 91 EA B6 AB B6 17 B4 A8 58 C1
: 18 52 EE 17 6E 7E 85 99 D6 5A D5 BD 3C EB 73 03
: A1 2A 99 03 8F 54 47 8F 5C 36 B1 39 33 9E 98 9D
533 129: INTEGER
: 00 E7 D9 F7 E8 B2 FC A4 07 93 29 B8 60 75 02 80
: 10 EE C0 00 CF DA D8 C4 8C D0 B9 44 87 B9 ED 15
: 3D 60 17 1E 70 1E A0 E6 31 CA 2D CB D7 2B 05 1C
: FF 3F 21 57 44 87 47 92 90 11 8E 7C 1D D5 57 C5
: FC 9B 3D 22 E2 A0 E9 5E 4E 38 B7 E8 BC B0 AC 61
: AB 84 C6 19 C4 2C E6 64 0A 57 54 03 D6 2A EE CD
: 21 A1 FD BE AD B3 9B E7 2B C1 BF 37 80 84 18 A3
: 1E D0 CF 44 12 AA B6 3A 05 3B B0 5F DF 32 B2 AE
: 71
665 129: INTEGER
: 00 E1 37 68 C5 C0 92 F7 0F 2E 99 C0 74 13 04 79
: DD A7 EF 56 46 A2 C9 A7 96 41 5E 4A 43 F3 57 7E
: 3E 98 0D C6 B1 AF 85 F6 29 B9 F0 2A D2 54 8C CE
: C8 DB FA 67 2E 5E 56 AB CC 7B E4 F1 3B EC 55 EE
: 10 0E B1 6F 76 A8 59 5B 2B B2 FF E2 E8 9A E5 9B
: F1 90 D1 65 DC 6D AD DB B3 B5 EC 39 8C 20 88 1F
: CA 87 E6 5A 6C 92 B1 75 F5 15 2E B1 41 0C AA 88
: 75 88 77 E9 B2 F4 8D B6 16 E2 13 5F EF 89 20 40
: E5
797 128: INTEGER
: 27 3A D1 60 B5 50 5C 2C CF F0 C2 3A C7 F1 A9 5B
: B4 1A 16 C9 14 BD 92 DC 44 C0 E4 60 96 CC 0F C8
: F7 C6 51 A7 24 F7 92 9B A0 1B 09 9F 99 AE DE CE
: 2D 8F 65 A5 B9 C2 19 81 79 07 03 E7 44 5E FA A8
: 18 58 4A DB CF E0 4C CD AD 79 28 CF 2C 91 AE 61
: 08 31 40 D0 D9 CC 0D E7 56 09 68 30 C7 C8 EA 3A
: A3 9F 3C B1 45 6F BE B8 BF AA AC 28 79 B1 75 80
: 54 52 8D B1 1E E3 80 83 BC 2A C6 BE 0C 65 01 71
928 128: INTEGER
: 14 BC 57 47 2D CD DA 35 69 A2 FA 57 35 91 09 EF
: 60 90 E6 AE A6 3A 4E D5 C4 BA FB B7 79 E6 2A 57
: 75 04 7F B0 C8 6A 5B 19 C8 66 D6 6A 7B 22 63 BF
: 96 91 5D 82 A5 68 F1 74 68 4B D1 F2 24 76 5C EE
: D9 8B 78 A9 C2 22 48 04 A3 FC 6F 55 DF 3D 18 B8
: 8B 0E DC 84 09 0D 22 D7 4E FE AA E5 BD F1 0A 8C
: 49 2A EA 54 68 C5 32 09 18 A4 2D E9 C1 52 CA 31
: 98 19 02 49 59 BE DA 6F 0C ED 9F BD 9C 30 7E 09
1059 128: INTEGER
: 75 97 A2 6D 60 94 68 EF AB B4 3A 63 10 21 B8 AA
: 2B 98 13 9C 0E 58 B2 FF 29 13 AB 38 18 0E FE DF
: C2 7D 08 46 0F D9 70 0C EA AC 86 57 C5 A3 0E EF
: 31 C7 7A 13 8D 9B F6 5A 60 C5 1B 1C 0F C3 C3 D3
: C7 90 5A E2 1E A1 F0 91 CA E4 6D 6D 89 64 ED 63
: C8 D2 F1 8E A4 D4 56 6A 99 17 38 A6 2A 3B 35 D1
: 92 2E 35 01 5C BF 85 31 25 F3 11 6F 73 7D F1 63
: 99 D6 9A AC D1 B1 80 17 E7 50 2C 3A AB 88 86 58
0 warnings, 0 errors.
В нашем файле секретного ключа «упакованы» девять целых чисел, структура ключа для алгоритма RSA весьма простая и описана, например, в RFC 3447, в секции A.1.2 RSA private key syntax.
Ну и чтобы два раза не вставать, вытащим из секретного ключа публичный ключ в файл public.der, закодированный в бинарный формат DER, openssl умеет делать и это тоже:
openssl rsa -inform DER -outform DER -in private.der -pubout -out public.der
writing RSA key
И сразу же посмотрим на его структуру через dumpasn1:
dumpasn1 public.der
6 9: OBJECT IDENTIFIER rsaEncryption (1 2 840 113549 1 1 1)
17 0: NULL
: }
28 257: INTEGER
: 00 CB F8 C1 9B 6E 29 63 39 3C 24 9B 2D A3 7D 00
: B0 B8 5C E8 D6 96 5D E4 76 67 7F 04 8F 48 D4 F4
: 35 64 68 57 10 3D 38 CE A0 B5 DC B2 FC DD 34 FF
: 2B AC 48 EE D1 05 37 65 4A 2F AE 8A E0 F8 1D CE
: 63 EB 2E C6 7A 09 4B B1 85 71 1E FA FF 55 17 0C
: 05 23 71 87 43 21 66 C4 70 6D E8 A8 B4 74 EA E4
: C1 75 7B AB 33 5B 8B 8D DB D4 67 BA DC B4 AD 50
: 12 AB FB 3E 74 44 AC 48 BE 94 C2 DD 4D 16 D6 0F
289 3: INTEGER 65537
: }
: }
Структура публичного ключа также описана в RFC3447, в секции A.1.1, если вы туда посмотрите, то увидите, что дамп файла public.der не соответствует этому описанию. Поздравляю с первыми граблями в openssl, с ними вам тоже придётся сталкиваться постоянно. В данном случае зачем-то добавляется заголовок, указывающий, что дальше будет RSA, и на этом заголовке многим библиотекам срывает крышу.
Для манипуляциями публичными и секретными ключами можно также использовать команду pkey, она позволяет работать с произвольными поддерживаемыми типами ключей. Вот как выглядит её вызов для вытаскивания публичного ключа из приватного:
openssl pkey -inform DER -outform DER -in private.der -pubout -out public.der
Сертификат¶
Но хватит о ключах, поговорим теперь о сертификатах. Напомню, что сертификат — это обёртка над публичным ключом. Неформально файл сертификата состоит из двух частей: данных сертификата (DATA) и цифровой подписи (SIGNATURE).
В секции DATA содержится смысловая часть сертификата, главное поле там — Subject (он же Субъект), это владелец сертификата, именно его публичный ключ находится в поле Subject Public Key Info. Также в сертификат входят другие поля, о которых мы поговорим позже, когда будем рассматривать уточнённую схему.
В секции SIGNATURE содержится цифровая подпись всех данных из секции DATA. Напомню, что в модели PKI цифровую подпись генерирует центр авторизации, используя свой секретный ключ, удостоверяя подлинность данных сертификата.
Для описания структур данных используется уже знакомая нам нотация ASN.1, она достаточно простая и выразительная.
Вот так определяется сертификат (раздел 4.1. Basic Certificate Fields):
Это всё значит, что сертификат состоит из трёх компонентов: tbsCertificate, signatureAlgorithm и signatureValue
собственно тело сертификата, его данные, буквы tbs в названии поля расшифровываются как «to be signed», то есть это блок данных, который будет подписан, описывается ASN.1 стуктурой TBSCertificate;
в этом поле задаётся алгоритм подписи, определяется ASN.1 структурой AlgorithmIdentifier;
а в этом поле находится бинарный неструктурированный блок с собственно подписью.
Перед тем как погружаться в структуру тела сертификата, рассмотрим поле signatureAlgorithm, в его описании фигурирует очень важный для дальнейшего понимания элемент, но сначала формальное ASN.1-описание (раздел 4.1.1.2. signatureAlgorithm):
Здесь мы видим элемент с «типом» OBJECT IDENTIFIER, это очень важная сущность, которая дальше будет встречаться везде. O BJECT IDENTIFIER часто сокращается до OID и означает буквально «идентификатор объекта». Все OID являются «словарными», то есть они определены где-то в каком-то стандарте, где также описана вся сопутствующая семантика. В нашем случае через OID определяется криптографический алгоритм, используемый для генерации цифровой подписи сертификата.
Как и другие порождения ASN.1/X.509, OID имеет чётко заданную структуру, по которой можно однозначно идентифицировать тип и свойства объекта. Существуют разные способы представления OID. Рассмотрим их на простом примере. Для генерации цифровой подписи может использоваться алгоритм, который неформально можно описать так: посчитаем SHA256 тела сертификата и дальше зашифруем это значение приватным ключом центра авторизации. Для этого алгоритма существует OID, у этого OID есть несколько равнозначных способов представления:
Все эти способы отражают иерархичную сущность OID, по компонентам можно определить, например, кто внёс этот OID в реестр, какая страна, какая организация и так далее. Существует специальный сайт — http://www.oid-info.com, — на котором ведётся большой реестр всевозможных OID. В принципе, таких сайтов несколько, но этот самый популярный. Вот, например, страница OID для алгоритма sha256WithRSAEncryption: http://www.oid-info.com/get/1.2.840.113549.1.1.11.
Вернёмся теперь к сертификату, рассмотрим ASN.1-структуру TBSCertificate, вот её определение вместе с сопутствующими структурами:
Я не буду полностью описывать семантику этих полей, это всё приводится с подробностями в разделе 4.1. Basic Certificate Fields RFC 5280. Так, коротко пробежимся по основным полям.
В поле signature должно быть то же самое значение, что и в поле signatureAlgorithm, то есть алгоритм, которым CA подписывает сертификат.
В поле issuer находится имя центра авторизации, в поле subject — имя владельца сертификата. Оба этих имени также являются структурированными (ASN.1-структура Name) и должны быть непустыми, их тип определяется стандартом X.501 и носит название distinguished name, сокращённо DN. Это тот же самый DN, что и в LDAP.
Поля в структуре Name также задаются через OID. Вот небольшой пример, как поле issue «разворачивается» из текстового представления в строго формализованное. Возьмём DN-фрагмент реального сертификата (https://google.ru):
C=US, O=Google Inc, CN=Google Internet Authority G2
А вот, в какую ASN.1-структуру «разворачивается» этот текст:
По факту это последовательность пар ключ-значение, где в роли ключа выступает OID. То есть кажущаяся обычным текстом строка оказывается одной из форм представления строго структурированного объекта. Каждая такая пара ключ-значение называется RelativeDistinguishedName, в роли ключа выступает OID.
Итак, сначала посмотрим, как сертификат выглядит в «человечном» формате, вот его полная распечатка (немного отформатированная, чтобы длинная строка не распирала экран):
Разберём последовательно поля сертификата.
Version: 3 (0x2)
Это версия не конкретно этого сертификата, а используемого «профайла», на данный момент таких версий всего три: 1, 2, 3; чаще всего используется 3. Фактическое значение поля — 2, так как нумерация версий идёт с нуля.
Serial Number: 1a:c8:5e:b7:ae:c3:51:3c:d8:0d:85:38:5e:cf:d2:08
Это «серийный номер» сертификата, по сути он является уникальным идентификатором в базе CA, для каждого подписанного сертификата этот номер должен быть свой.
Signature Algorithm: sha256WithRSAEncryption
Это мы уже знаем: для подписывания сертификата используется алгоритм sha256WithRSAEncryption
Issuer: C=US, O=Symantec Corporation, OU=Symantec Trust Network, CN=Symantec Class 3 EV SSL CA — G3
Это имя (distinguished name, DN) центра авторизации, является по сути идентификатором в браузерной базе CA-сертификатов, и чтобы корректно проверифицировать серверный сертификат, браузер должен у себя иметь CA-сертификат, в поле subject которого стоит точно такое же значение, как здесь.
В этом поле две даты, с какой и по какую сертификат считается валидным. Это поле является главной кормушкой центров авторизации: каждые год или два нужно выписывать (=подписывать у центра авторизации) новый сертификат, не бесплатно, конечно.
Субъект. Тот, кому выписан сертификат. Это DN, то есть структурированное поле. Обратите внимание на самые первые компоненты — 1.3.6.1.4.1.311.60.2.1.3=US/1.3.6.1.4.1.311.60.2.1.2=Delaware, в них мы видим, что openssl не смог распознать эти OID и вывели их в «сыром» виде. Вот они в репозитории OID: 1.3.6.1.4.1.311.60.2.1.3 jurisdictionOfIncorporationCountryName, 1.3.6.1.4.1.311.60.2.1.2 jurisdictionOfIncorporationStateOrProvinceName. Дальше идут уже распознанные компоненты, в качестве разделителя компонентов используется запятая или слеш, такие особенности вывода openssl. Обратите внимание на компонент CN, это Common name, именно здесь находится имя домена, для которого выписывается сертификат.
Этот блок содержит публичный ключ субъекта, синтаксически там два поля: OID алгоритма шифрования и собственно тело публичного ключа. Openssl при показе сертификата пытается показать детали публичного ключа, в данном случае он показывает модуль и экспоненту RSA-ключа. Ниже я покажу, как openssl справляется с сертификатми, с алгоритмами которых он не знаком.
В этом блоке содержатся так называемые расширения сертификата. Хотя структура сертификата очень жёсткая, в ней были оставлены точки расширения, чтобы можно было в сертификат добавлять дополнительные данные, которые изначально не предусмотрели. Фактически, блок с расширениями — это набор пар OID-значение, где тип значения зависит от конкретного OID. Расширения допускаются только для сертификатов версии 3. Обычно в расширениях указываются дополнительные параметры сертификата. Например, в поле X509v3 Subject Alternative Name указываются дополнительные доменные адреса сайта, для которых сертификат применим. Или, например, в поле X509v3 Basic Constraints, указываются ограничения в применимости сертификата, в данном случае указано, что сертификат не может использоваться в качестве сертификата удостоверяющего центра.
Этим блоком сертификат всегда завершается, в нём содержится идентификатор алгоритма подписи и собственно подпись вышестоящего блока.
Инфраструктура УЦ и сертификаты¶
X.509-сертификат состоит из стандартного набора полей, часть из них обязательна, часть нет. Официального перевода этих названий на русский нет, поэтому я преимущественно буду пользоваться оригинальными именами с пояснениями при необходимости.
В каждом X.509-сертификате есть два обязательных поля: issuer и subject. Поле subject (то есть субъект, в философском смысле: носитель деятельности, осуществляющий активность) содержит личные данные заявителя и по сути является названием сертификата, его главным идентифицирующим признаком, именем. Я специально не использую термин идентификатор, поскольку в контексте сертификата такое поле уже есть, причём оно является необязательным. Содержимое берётся из одноимённого поля в CSR при создании сертификата и обычно представляет собой имя персоны, организации или домен веб-сайта.
В поле issuer хранится имя сертификата удостоворяющего центра. Постоянно помним, что открытые ключи у нас распространяются исключительно внутри сертификата. Поэтому в поле issuer находится содержимое поля subject сертификата УЦ, внутри которого находится открытый ключ, закрытая часть которого используется для подписи сертификата заявителя.
Поля issuer и subject являются структурированными, то есть это не просто строчки текста, а оформленные в жёстко заданную структуру данные типа distinguished name. Об этом подробнее я расскажу в практическом разделе.
Таким образом любой сертификат A в подобной инфраструктуре подписан закрытым ключом, открытая часть которого записана в каком-то другом сертификате B. Для простоты обычно в таком случае говорят, что сертификат A подписан сертификатом B. В свою очередь сертификат B подписан сертификатом C и так далее. Образуется цепочка зависимостей, которая завершается сертификатом Z специального вида, в котором поля issuer и subject совпадают. Это означает, что сертификат Z подписан закрытым ключом, открытая часть которого записана в этом же сертификате. Он так и называется — самоподписанный сертификат (self-signed certificate).
Считается, что вы как клиент доверяете (trust) сертификату A, если вы доверяете записанным в нём данным. Например, если вы получили сертификат организации через надёжный канал от надёжного представителя организации и записали на надёжном диске. Используя доверенный сертификат, а точнее, открытый ключ из него, вы можете организовать криптографически защищённый канал связи, например, до веб-сайта. Однако вы физически не сможете для каждого веб-сайта в таком режиме содержать «реестр» доверенных сертификатов и отслеживать его актуальность (сертификаты у сайтов часто меняются, например).
Описанная выше схема зависимостей между сертификатами лежит в основе системы доверия на базе удостоверяющих центров. Подразумевается, что если вы доверяете сертификату B, то вы также доверяете сертификату A, который им подписан. Так как организация-владелец сертификата B может им подписать множество других сертификатов, вы автоматически доверяете всем им. Следуя по цепочке доверия вы в итоге спускаетесь до самоподписанных сертификатов. Если вы доверяете самоподписанному сертификату Z, то вы автоматически доверяете всем остальным, которые имеют Z в цепочке доверия.
Естественно, бесконтрольное подписывание сертификатов ломает схему автоматического доверия, поэтому в сертификате есть специальное поле, которое разрешает или запрещает использовать этот сертификат при построении цепочки доверия. Если в вашем сертификате оно запрещает, то вы по-прежнему сможете подписать им другой сертификат, однако алгоритм верификации сразу такую цепочку «зарежет».
В итоге, только удостоверяющие центры (certification authority) обладают сертификатами, которые можно использовать для подписывания других сертификатов, чтобы они могли включаться в цепочку доверия. Их принято называть CA-сертификатами (CA certificate). А финальный самоподписанный сертификат в цепочке доверия называется сертификатом корневого удостоверяющего центра или просто корневым сертификатом (root certificate).
В итоге получается, что корневой сертификат лежит в основе огромного количества цепочек доверия, которые автоматически верифицируюся, если корневой сертификат находится в статусе доверенного. В любой современной операционной системе есть системное хранилище доверенных корневых сертификатов, эти сертификаты (и соответственно хранилище) меняются чрезвычайно редко и как правило это делается при обновлении операционной системы.
Также в сертификате есть множество других полей, которые ограничивают его применение. Например, диапазон дат, внутри которых сертификат можно считать доверенным. Практически все системы помечают сертификат недоверенным, если текущая дата в системе лежит вне указанного в сертификате диапазона. Диапазон действия корневых сертификатов обычно очень большой, порядка 10-20 лет. Этот диапазон дат указывается в полях notBefore и notAfter.
Здесь нужно отметить, что интерпретация «срока годности» сертификата целиком лежит на стороне программного обеспечения, которое сертификат использует. Нет никаких объективных причин, по которым сертификат перестаёт быть доверенным после достижения некоторой даты. Однако ограниченный срок жизни гарантирует стабильный источник дохода удостоверяющим центрам, которые выписывают сертификаты за весьма большие деньги.
Когда удостоверяющий центр выписывает сертификат, он записывает в поле serialNumber числовое значение, которое должно быть разным для каждого сертификата, заверенного этим конкретным сертификатом УЦ (X.509, п. 4.1.2.2). Туда можно записывать порядковый номер, можно текущую дату-время, можно случайное число, главное требование — это число должно быть уникальным для каждого выписанного сертификата.
X.509 — стандарт очень гибкий и позволяет добавлять произвольные дополнительные поля помимо базовых, это делается через расширения (extensions), я о них подробнее расскажу в практической части.
Цепочки удостоверяющих сертификатов¶
Удостоверяющий центр может выдать сертификат, в котором в поле X509v3 Basic Constraints разрешается его использование как удостоверяющего. Этим сертификатом тоже можно подписывать «доменные» сертификаты. Таким образом, все CA-сертификаты образуют цепочку, а в целом — дерево. В вершине этого дерева находится Самый Главный Сертификат, он называется корневым сертификатом (по-английски root certificate).
Система доверия PKI строится таким образом, что доверенным CA-сертификатом является тот, который подписан другим доверенным корневым CA-сертификатом. Вебсайт может вместе со своим сертификатом отдать браузеру также и CA-сертификат, которым он был подписан. И если этот CA-сертификат подписан кем-то из доверенных удостоверяющих центров, он автоматически становится доверенным.
Естественно, центры авторизации не выписывают CA-сертификаты кому попало, но крупная организация может этого добиться, например, у Google есть свой CA-сертификат.
Также в PKI существует механизм отзыва сертификатов, например, если кто-то из CA стал себя вести некорректно или у него украли секретный ключ, то вышестоящий удостоверяющий центр может отозвать сертификат провинившегося. Эта схема, к сожалению, на практике работает не так гладко, как это задумывалось.
Корневой сертификат является самоподписанным, то есть он подписан тем же самым ключом, публичный ключ для которого находится в теле сертификата. У корневого сертификата совпадают поля issuer и subject.
Сфера применения сертификатов не ограничивается браузером, конечно. В теории любой клиент может сам выбирать, кому он доверяет, а кому нет. В программу может быть жёстко вшит сертификат, которым она проверяет другие сертификаты, например.
Как посмотреть характеристики сертификата?
При желании пользователь может узнать характеристики сертификата, щёлкнув клавишей мыши на значке HTTPS. Например, в браузере Google Chrome этот значок выглядит так.
В раскрывшейся панели нужно щёлкнуть на ссылке «Детали» (Details).
Примечание: пользовательский интерфейс браузера Google Chrome был немного изменён, о новом порядке просмотра характеристик сертификата рассказано здесь.

В появившейся справа панели нужно щёлкнуть кнопку «Показать сертификат» (View certificate).

Откроется окно со сведениями о сертификате.
На вкладке «Общие» показано наиболее важное: назначение сертификата, кому и кем он выдан, срок действия сертификата.
В данном случае он выдан:

На вкладке «Состав» представлены подробные сведения о сертификате. К ним мы ещё вернёмся.

На вкладке «Путь сертификации» показано, кто и чей сертификат заверил.
Центров сертификации верхнего уровня существует много. Для взаимного подтверждения полномочий и удостоверения выданных сертификатов используются цепочки, одна из которых показана далее.
В данном случае SSL-сертификат для домена *.google.com.ru выдал центр сертификации Google Internet Authority G2 и, соответственно, подписал его своей цифровой подписью. А сертификат самого Google Internet Authority G2 подписал другой центр сертификации — GeoTrust Global CA.

Вернёмся к вкладке «Состав».
В ней представлено много характеристик сертификата. Для удобства просмотра их можно сгруппировать:





Используемые криптоалгоритмы¶
В инфраструктуре открытых ключей чаще всего используются RSA, DSA и ECC.
Из широко известных криптосистем только RSA позволяет собственными ключами шифровать и подписывать, все остальные непосредственно используются только для создания цифровой подписи. Однако можно пользоваться алгоритмом Diffie-Hellman для создания общего ключа для симметричного алгоритма (например, AES) и дальше уже им зашифровывать и расшифровывать данные.
Протокол Diffie-Hellman (Diffie–Hellman key exchange, дальше я буду его коротко называть DH) играет чрезвычайно большую роль в современной криптографии, поэтому я о нём расскажу подробно.
Суть DH в том, что две стороны могут безопасным образом создать одинаковый ключ через открытый канал связи. В википедии и других статьях принцип его работы обычно иллюстрируется через смешение цветов, но мне такое объяснение кажется не очень понятным, поэтому я его сильно упростил.
Итак, на разных сторонах открытого сетевого канала находятся Алиса и Боб которым нужно договориться о ключе (в моём примере это целое число) для симметричного шифра. Просто так передать его по сети нельзя, так как канал открытый и априори небезопасный.
Я выбрал операцию сложения ради простоты объяснения, в реальном протоколе используется сложно-обратимая операция. Если её обозначить через ⊕, то для произвольных значений X и Y вычисление Z = X ⊕ Y выполняется легко; а зная Z и X, вычислить Y исключительно сложно и затратно. В реальном DH используется функция возведения в степень в мультипликативной группе вычетов по простому модулю, её обратная операция — дискретный логарифм — считается крайне сложной и на данный момент не имеет эффективного решения.
Протокол DH, очевидно, уязвим для атак типа Man-in-the-middle, то есть третья сторона может незаметно вклиниться в обмен данными и полностью перехватывать и расшифровывать все данные. Поэтому в реальной жизни обмен блоками данных в DH сопровождается алгоритмами аутентификации, например, цифровой подписью.
Рассмотрим такую ситуацию: есть клиент и есть сервер, к которому подключается клиент через небезопасный и незащищённый канал. Также выбран некоторый криптоалгоритм цифровой подписи. У сервера есть закрытый ключ, а его открытая часть уже есть у клиента. Тогда аутентифицированный DH может работать, например, так:
В такой схеме клиент может быть уверен, что между ним и сервером нет третьей стороны, которая перехватывает данные, гарантией этому служит цифровая подпись. Такой ключ K, который создаётся на время сессии, называется эфемерным ключом (ephemeral key).
Ранее мы уже договорились, что контейнером для распространения открытого ключа является цифровой сертификат. И теперь остаётся вопрос: каким образом сертификат сервера оказывается у клиента перед установкой соединения.





