Практика 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. Краткая выжимка:
-
Загружаем бинарник step: https://github.com/smallstep/cli/releases/tag/v0.19.0
-
Распаковываем:
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/
Добавить комментарий