Сам себе PKI: Практика на примере OpenSSL и CA Smallstep. (Статья 2)

от автора

Практика Self-signed certificate

OpenSSL 

Начнем с классики криптографии OpenSSL. Сейчас широко используется версия 1.1.1, но ее поддержка заканчивается в 2023. Следующая версия 3.0 будет поддерживаться до 2026 года. Проверка установленной версии:

openssl version

OpenSSL 1.1.1k FIPS 25 Mar 2021

Если OpenSSL не установлен, то его можно установить на всех востребованных платформах.

Реализация TLS

  • Для нашего CA генерируем приватный ключ 2048-бит RSA. Администратор CA должен хранить его в секрете от всех: 

openssl genrsa -out root_ca.key 2048

  • Далее для нашего CA генерируем X.509 сертификат на 365 дней (root_ca.crt) и подписываем его приватным ключом (root_ca.key). Получается самоподписанный сертификат, его можно и нужно будет скопировать на все компьютеры, между которыми необходимо организовать TLS соединение. При ответах на вопросы, которые будет задавать OpenSSL, можно указывать произвольные данные, CA я назвал «My CN»: 

openssl req -x509 -new -key root_ca.key -days 365 -out root_ca.crt

  • Смотрим получившийся сертификат, убеждаемся, что данные в нем корректные:

openssl x509 -text -in root_ca.crt

  • Генерируем приватный ключ для HTTPS сервера, его нужно будет скопировать только на компьютер, на котором он будет работать и никуда больше:

openssl genrsa -out server.key 2048

  • Генерируем запрос на сертификат CSR. В этом запросе нужно передать конкретные значения в параметре «subj». Это должны быть один или несколько параметров CN (Common Name), которые будут прописаны в атрибутах сертификата «Subject» и «Subject Alternative Name».  Фактически CN-ы — это IP адреса и имена хостов, по которым будет осуществляться доступ к серверу. Имена хостов могут быть как в формате доменного имени, так и FQDN

openssl req -new -key server.key -subj "/CN=xx.xx.xx.xx/CN=server/CN=server.example.com" -out server.csr

  • Можно посмотреть получившийся запрос на сертификат и убедиться в его корректности:

openssl req -text -in server.csr

  • Теперь генерируем сертификат X.509 (server.crt) на 365 дней для HTTPS сервера и подписываем его приватным ключом CA (root_ca.key):

openssl x509 -req -in server.csr -CA root_ca.crt -CAkey root_ca.key -CAcreateserial -out server.crt -days 365 -extensions SAN -extfile openssl.cnf

  • Файл openssl.cnf должен содержать список SAN (Subject Alternative Name) идентичный тому, что был в CSR запросе на сертификат:

[SAN] subjectAltName = @alt_names [alt_names] IP.1 = xx.xx.xx.xx DNS.1 = server DNS.2 = server.example.com
  • Можно посмотреть на получившийся сертификат и убедиться в его корректности:

openssl x509 -text -in root_ca.crt

  • С ключами и сертификатами закончили, переходим к запуску HTTPS сервера. Копируем следующие файлы на компьютер, на котором будет работать HTTPS сервер: 

    • root_ca.crt

    • server.key

    • server.crt

  • Пишем простейший HTTPS сервер, на JS:

const fs = require('fs'); const https = require('https');   https   .createServer(     {       requestCert: false,       rejectUnauthorized: false,       ca: fs.readFileSync('root_ca.crt'),       cert: fs.readFileSync('server.crt'),       key: fs.readFileSync('server.key')     },     (req, res) => {       console.log("req.client.authorized: ", req.client.authorized)       if (req.client.authorized) {         const cert = req.socket.getPeerCertificate(false) // true - получить все сертификаты, false - последний         console.log("cert: ", cert)     }       res.end('Hello, world!');     }   )   .listen(9443);
  • Запускать данный скрипт будем с помощью Node.js. Если Node.js не установлен, то устанавливаем:

sudo yum groupinstall 'Development Tools'

sudo yum install nodejs

  • Проверяем, что Node и Npm установились успешно:

npm -v

node -v

  • Запускаем сервер:

node server.js

  • Проверяем сервер curl-ом:

curl https://server.example.com:9443

При первом запросе возникнет ошибка: 

curl: (60) SSL certificate problem: self signed certificate in certificate chain

Это значит, что самоподписанный корневой сертификат нашего CA (root_ca.crt) не находится в хранилище доверительных сертификатов. Процесс загрузки сертификатов в это хранилище специфичен для операционной системы. Приведу алгоритм для RH Linux:

  • Если сертификат не в PEM (текстовом) формате, то его надо сконвертировать в текстовой формат:

openssl x509 -in root_ca.crt -out root_ca.pem -outform PEM

  • Копируем сертификат в определенный каталог на сервере. ДляRH 7 и выше это каталог:

/etc/pki/ca-trust/source/anchors

  • Выполняем команду обновления хранилища сертификатов:

update-ca-trust

  • Теперь curl проходит 

curl https://server.example.com:9443

Hello, world!

  • на Windows есть специфика — в curl необходимо добавлять параметр «ssl-no-revoke», чтобы не проверялся список отозванных сертификатов:

curl --ssl-no-revoke https://server.example.com:9443

  • При этом сервер выведет в консоль строку:

req.client.authorized: false

Это значит, что мы установили TLS соединение только с проверкой сертификата сервера, а не клиента. Переходим к mTLS.

Реализация mTLS 

В коде сервера включаем mTLS, а именно выставляем в true параметр requestCert, чтобы HTTPS сервер запрашивал клиентский сертификат и параметр rejectUnauthorized, чтобы сервер отклонял запросы клиентов, которые не прошли авторизацию:

{ requestCert: true, rejectUnauthorized: true, }

Алгоритм генерации клиентских сертификатов аналогичен серверным.

  • Генерируем приватный ключ для клиента:

openssl genrsa -out client.key 2048

  • Генерируем запрос на сертификат CSR со списком CN:

openssl req -new -key client.key -subj "/CN=xx.xx.xx.xx/CN=client/CN=client.example.com" -out client.csr

  • Генерируем сертификат для клиента и подписываем его приватным ключом CA:

openssl x509 -req -in client.csr -CA root_ca.crt -CAkey root_ca.key -CAcreateserial -out client.crt -days 365 -extensions SAN -extfile openssl.cnf

Файл openssl.cnf — такой же как и для сервера, только вместо «server» пишем «client». 

  • Копируем следующие файлы на компьютер, на котором будет работать клиент: 

    • root_ca.crt

    • client.key

    • client.crt

  • Теперь если просто вызвать curl:

curl https://server.example.com:9443

  • То произойдет ошибка авторизации

curl: (35) error:14094410:SSL routines:ssl3_read_bytes:sslv3 alert handshake failure

  • Поэтому вызываем curl с дополнительными параметрами, в которых указываем ключ клиента и сертификаты клиента и СА: 

curl --cert ./client.crt --key client.key --cacert root_ca.crt https://server.example.com:9443

  • Теперь запрос проходит, при этом сервер выведет в консоль следующую строку и далее содержимое полученного от клиента сертификата:

req.client.authorized: true 

Это значит, что мы установили mTLS соединение с проверкой сертификата как сервера, так и клиента. Теперь установим mTLS из браузера.

Реализация mTLS в браузере 

Если просто запустить наш запрос к серверу (https://server.example.com:9443) из браузера, то выдается ошибка клиентского сертификата ERR_BAD_SSL_CLIENT_AUTH_CERT. Рабочие адреса закрасил желтым:

Для загрузки в браузер клиентский сертификат и приватный ключ надо сконвертировать в формат p12. Этот формат определяется стандартом PKCS #12 и является архивом для хранения нескольких объектов. В нашем случае файл будет хранить клиентский сертификат и его приватный ключ. Понятно, что приватный ключ — это чувствительная к разглашению информация и ее владелец не должен выкладывать p12 сертификат в открытый доступ. Для конвертации тоже используем OpenSSL:

openssl pkcs12 -inkey client.key -in client.crt -export -out client.p12

Импортируем client.p12 в браузер. Для этого, например в Chrome, идем в меню: Settings / Privacy and security / Manage certificates, в открывшемся окне нажимаем Import, выбираем наш файл client.p12 и место хранения «Personal». Видим, что клиентский сертификат, выданный нашим удостоверяющим центром «My CN», появился  в хранилище персональных сертификатов:

Вызываем запрос заново, уже лучше, но браузер все равно пишет, что соединение не безопасно. Это из-за того, что клиентский сертификат выдан удостоверяющим центром «My CN», которого нет в списке доверительных CA (Trusted Root Certification Authorities). 

Добавляем корневой сертификат нашего CA (root_ca.crt) в Trusted Root Certification Authorities. Делается с помощью того же вызова Import, что и для клиентского сертификата, но хранилище указываем Trusted Root Certification Authorities. Проверяем, что наш CA «My CN» появился в хранилище:

Повторяем наш запрос (https://server.example.com:9443). При первом запросе браузер находит подходящий клиентский сертификат и просит явно подтвердить его использование:

Дальнейшие вызовы проходят без запросов на подтверждение клиентского сертификата:

Если щелкнуть на замочке а адресной строке, то можно посмотреть сертификат сервера и убедиться, что мы взаимодействуем с правильным сервером (Issued to: server.example.com), сертификат которому выдан нашим удостоверяющим центром (Issued by: My CN) и время действия сертификата еще не прошло (Valid to: 31.05.2023):

Таким образом мы организовали mTLS соединение из браузера. Далее приведу для справки несколько дополнительных возможностей OpenSSL, которые могут пригодиться в процессе работы. 

Дополнительные возможности OpenSSL

Конвертация форматов сертификатов

  • Конвертируем сертификат в der (бинарный формат):

openssl x509 -outform der -in client.crt -out client.der

  • Конвертируем сертификат в PEM (текстовой формат)

openssl x509 -inform der -in client.cer -out client.pem

Отладка с помощью OpenSSL

  • Проверка, что сертификат подписан определенным (root_ca.pem) удостоверяющим центром:

openssl verify -verbose -x509_strict -CAfile root_ca.pem server.crt

  • Запускаем OpenSSL server

openssl s_server -accept 9443 -cert server.crt -key server.key

  • Запускаем OpenSSL client

openssl s_client -connect xx.xx.xx.xx:9443 -prexit -showcerts -state -status -tlsextdebug -verify 10 

  • При вызове curl параметр:

    • -k — позволяет подсоединяться к серверу без проверки цепочки сертификатов (certificate path validation). 

    • —ssl-no-revoke  — предотвращает проверку отозванных сертификатов 

Практика CA Smallstep

Установка Smallstep и CLI step 

CA Smallstep — это open source CA, который можно развернуть локально для автоматического получения X.509 сертификатов. На официальном сайте есть ряд инструкций по его установке и настройке.  Для экспериментов я использовал вариант с docker:

  • Краткая выжимка из инструкции по установке на RH Linux. Сначала устанавливаем docker:

sudo yum -y update

sudo yum install -y yum-utils

sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

sudo yum -y install docker-ce

sudo systemctl start docker

  • Проверяем корректность установки docker:

sudo docker run hello-world

  • Запускаем Smallstep:

sudo docker pull smallstep/step-ca

  • Инициализируем наш CA:

docker run -it -v step:/home/step smallstep/step-ca step ca init

  • При инициализации по умолчанию будет ряд ограничений, например, будут одинаковые пароли для корневого и промежуточного приватного ключа. Для разворота в промышленную среду необходимо внимательно изучить рекомендации Production considerations when running a certificate authority server.

При создании CA в консоль будет выведен fingerprint корневого сертификата в виде шестнадцатеричной строки (например: 55de8dc05cd…8a70f725a56). Ее надо сохранить для последующей инициализации утилиты CLI step, которая используется для взаимодействия с CA Smallstep. Проверяем, что CA Smallstep запущен корректно. xx.xx.xx.xx:9000 — это IP и порт, на котором запущен Smallstep CA:

curl https://xx.xx.xx.xx:9000/health 

должна вернуться строка:

"status": "ok"

На этом же или другом компьютере по инструкции  устанавливаем утилиту CLI step. Краткая выжимка:

tar -xf step_linux_0.19.0_386.tar.gz

  • Копируем утилиту step в /usr/bin

  • Регистрируем CLI step для использования с нашим CA. Этот процесс называется «bootstrap».  Значение параметра «ca-url» — это IP и порт, на котором запущен Smallstep CA, «fingerprint » — это сохраненное значение при инициализации CA:

step ca bootstrap --ca-url https://xx.xx.xx.xx:9000 --fingerprint 55de8dc05cd...8a70f725a56 --install

  • Проверка связи с CA:

step ca health

должна вернуться строка:

"ok"

Все готово для генерации ключей и сертификатов.

Генерация ключей и сертификатов 

Также как и в случае с OpenSSL, сначала генерируем ключ и сертификат для сервера и установки TLS соединения, потом для клиента и установки mTLS. 

  • В отличии от OpenSSL, CA Smallstep может одновременно сформировать приватный ключ сервера 2048-бит RSA (server.key) и запрос на сертификат (server.csr). В запросе явно указываем, что пароль должен быть пустой (no-password), xx.xx.xx.xx — это IP адрес сервера, для которого генерируется запрос: 

step certificate create --csr --no-password --insecure --kty=RSA --size=2048 "xx.xx.xx.xx" server.csr server.key

  • Подписываем сертификат на нашем CA Smallstep:

step ca sign server.csr server.crt

  • Смотрим полученный сертификат: 

step certificate inspect server.crt

  • Получаем с CA Smallstep его корневой сертификат: 

step ca root root_ca.crt

  • Смотрим корневой сертификат:

step certificate inspect root_ca.crt

  • Аналогично OpenSSL копируем ключи и сертификаты на наш HTTPS сервер:

    • root_ca.crt

    • server.key

    • server.crt

  • Запускаем сервер:

node server.js

  • Проверяем сервер curl-ом:

curl https://xx.xx.xx.xx:9443

Hello, world!

Все нормально, теперь генерируем ключ и сертификат для клиента.

  • Аналогично серверу одновременно формируем приватный ключ клиента 2048-бит RSA (client.key) и запрос на сертификат (client.csr). Явно указываем, что пароль должен быть пустой (no-password), xx.xx.xx.xx — это IP адрес клиента, для которого генерируется запрос: 

step certificate create --csr --no-password --insecure --kty=RSA --size=2048 "xx.xx.xx.xx" client.csr client.key

  • Подписываем сертификат на CA:

step ca sign client.csr client.crt

  • Смотрим сертификат:

step certificate inspect client.crt

  • Получаем корневой сертификат CA:

step ca root root_ca.crt

  • Смотрим корневой сертификат:

step certificate inspect root_ca.crt

  • Копируем на клиента:

    • root_ca.crt

    • client.key

    • client.crt

  • Для проверки вызываем curl с параметрами:

curl --cert ./client.crt --key client.key --cacert root_ca.crt https://xx.xx.xx.xx:9443

Hello, world!

Отлично — mTLS тоже работает с выданными CA Smallstep сертификатами!

Дополнительные возможности CA Smallstep

С помощью CLI step можно проверить необходимость обновления сертификата: 

step certificate needs-renewal server.crt

  • Если обновление не требуется возвращается строка:

certificate does not need renewal

  • Если требуется, то строка пустая и тогда необходимо обновить сертификат:

step ca renew server.crt server.key

  • При необходимости сертификат можно отозвать, в этом случае его невозможно будет продлить, только получить заново:

step ca revoke --cert server.crt --key server.key

  • Посмотреть все сертификаты в цепочке: 

step certificate inspect server.crt --bundle

  • Запуск CLI step в режиме отладки:

STEPDEBUG=1 step /команда/

Заключение 

В данной статье мы рассмотрели две возможности генерации ключей и сертификатов для клиента и сервера:

  • С помощью OpenSSL.

  • Используя CA Smallstep.

И двумя способами установили TLS и mTLS соединения:

  • С помощью curl.

  • Из браузера, загрузив в него клиентский и корневой сертификаты.

Теперь у нас есть понимание, как организовать свою систему PKI с помощью CA любого производителя, т.к. логика взаимодействия у всех аналогичная. Более того, большинство CA используют под капотом OpenSSL, оборачивая его в свой API.


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