Комментарии 32
В файле конфигурации сертификатов (как правило, файл называется
openssl.cnf
, в вашем случае ca.conf
, кстати, тоже непонятно, зачем придумывать стандартным конфигам свои имена файлов) была отмечена опция default_crl_days = 7
как Срок действия CRL
. Но, при этом, остальные опции CRL отсутствуют (например, директория отозванных сертификатов, следующий порядковый номер отзыва) и, вообще в статье не отмечена очень важная возможность отзывов сертификатов. Возможно (тьфу-тьфу), если из вашей компании кто-нибудь когда-нибудь уволится, не настроив openssl с самого начала, у вас возникнут проблемы. Предлагаю вам разобраться в CRL и дополнить статью!
Спасибо за внимательность!
в nginx можно дописать опцию проверки отозванных сертификатов, выглядит она вот так:
ssl_crl /path/to/nginx/conf/crl.pem;
Как сгенерировать файл с отозванными сертификатами статьи уже есть и не одна.
А если вы не подготовите команды или скрипты заранее, возможна и такая ситуация, когда надо будет переделывать все сертификаты из-за того, что не могли отозвать один.
В таком случае нужно делать SSL-авторизуемую часть или на другом домене, или открытую часть делать вовсе без SSL или вообще ставить Apache (он такое умеет).
ssl_verify_client
выставляется в optional и в нужных location проверяется переменная $ssl_client_verify
, см ответ Игоря (http://forum.nginx.org/read.php?29,173747,173809#msg-173809). Но, к сожалению, костыли.Т. е., если у пользователя нет сертификатов, подписанных теми CA, которые указаны в
ssl_client_certificate
, то в firefox и chrome (по крайней мере, на современных) пользователь не получит запроса на указание сертификата. Иначе — попросят указать сертификат (или отказаться, но тогда для входа по сертификату придется перезапустить браузер или зайти в порно-режиме).Потому и говорю, что костыли.
Большинство CA генерируется по одной и той-же статье. Есть ли шанс что у постороннего человека с ключами для другого сайта будет выскакивать окошко на нашем?
Смотрим в https://tools.ietf.org/html/rfc5246#section-7.4.4 и видим, что в CertificateRequest
отправляется набор DN
ов CA. Так что если DN вашего самоподписанного CA будет тупо /CN=Example CA или /CN=Root CA, то проблемы будут.
Название distinguished name кагбэ говорит, что оно должно быть отличным от других (уникальным).
Задача — поднять сайт с https на локалхосте (чисто в целях обучения).
Вот пошаговый мануал: по факту нужно не 14 файлов, а меньше: ключевая пара удостоверяющего центра, ключевая пара сервера, ключевая пара клиента и конфиг Nginx. 7 :-)
Создание сертификатов УЦ
openssl genrsa -out ca.key 2048 openssl req -new -x509 -days 3650 -key ca.key -out ca.crt
Создание сертификата сервера
Конфигурационный файл
server.cnf
, пример:
[req] prompt = no distinguished_name = dn req_extensions = ext [dn] CN = Сервер с аутентификацией клиентов emailAddress = my.email@example.com O = Private person OU = Alter ego dept L = Korolyov C = RU [ext] subjectAltName = DNS:example.com,DNS:*.example.com,IP:127.0.0.1
В
subjectAltName
должны быть указаны все используемые DNS-имена и IP-адреса.
Создание запроса на сертификат
openssl req -new -utf8 -nameopt multiline,utf8 -config server.cnf -newkey rsa:2048 -keyout server.key -nodes -out server.csr
Создание сертификата
openssl x509 -req -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out server.crt -extfile server.cnf -extensions ext
Настройка Nginx, пример (фрагмент)
http { ssl_certificate /path/to/server.crt; ssl_certificate_key /path/to/server.key; ssl_client_certificate /path/to/ca.crt; ssl_verify_client on; server { listen 8443 ssl default_server; listen [::]:8443 ssl default_server; } }
Создание сертификата клиента
openssl req -new -utf8 -nameopt multiline,utf8 -newkey rsa:2048 -nodes -keyout client.key -out client.csr openssl x509 -req -days 3650 -in client.csr -CA ca.crt -CAkey ca.key -set_serial 01 -out client.crt
Проверка
Следующая команда должна отрабатывать без ошибок:
curl -v --cacert path/to/ca.crt --cert path/to/client.crt --key path/to/client.key https://127.0.0.1:8443/
Следующая команда должна возвращать ошибку «не предоставлен сертификат клиента»:
curl -v --cacert path/to/ca.crt https://192.168.194.97:8443/
Добавление сертификата ЦА в RHEL (от имени
root
, гдеpath/to/ca.crt
— путь к файлу корневого сертификата)
cp path/to/ca.crt /etc/pki/ca-trust/source/anchors/ update-ca-trust
Последний пункт нужен, чтобы можно было не указывать явно сертификат ЦА в командах (в том же cURL).
Одна маленькая ремарка: лучше в серверном CN указывать хост. В SAN он тоже должен присутствовать, естественно.
Вот уж пару лет развлекаюсь тем, что пихаю в CN надписи на русском (в UTF-8) — пока проблем ни с каким софтом не возникало — все смотрят в SubjectAltNames.
Впрочем, раньше я это делал чисто из эстетических соображений — чтобы видеть эти русские буквы в информации о сертификате, сейчас же, когда в хроме надо пройти целый квест, чтобы эту информацию увидеть, большая часть смысла пропала :-)
Система Lubuntu, поэтому добавление сертификата не такое как в пункте 5, там просто нет ca-trust внутри /etc/pki и команды update-ca-trust.
Делал так (в целом это похожие варианты)
http://manuals.gfi.com/en/kerio/connect/content/server-configuration/ssl-certificates/adding-trusted-root-certificates-to-the-server-1605.html
https://askubuntu.com/questions/73287/how-do-i-install-a-root-certificate
https://unix.stackexchange.com/questions/90450/adding-a-self-signed-certificate-to-the-trusted-list
А вы добавляли корневой сертификат в браузер? Т.е. зелёный замочек в браузере есть? Дело может быть в этом. См. https://habrahabr.ru/post/213741/#comment_7814423, например.
Но добавление не помогло: теперь браузер опять выдает 400 Bad Request No required SSL certificate was sent
В смысле "помогла"? Вы просто выключили эту фичу IIRC
Но я бы сначала хотел в принципе разобраться что происходит в реальной жизни (когда юзер заходит на сайт по https) и что происходит под локалхолстом с самоподписанным сертификатом.
Как я понимаю, «ca» — это сертификат удостоверяющего центра (в реальной системе у меня его не будет, а будут вшитые в браузер); «server» — это сертификат сервера, то что мне выдадут например на let's encrypt для моего домена. А что такое сертификат клиента «client»? Откуда он берется в реальных условиях (браузер открывает https ссылку) и почему мы его генерируем для локалхоста?
Если на пальцах, то в случае использования клиентской аутентификации по tls есть две ветки PKI.
- Сторона сервера (которая есть и без клиентской authN), которая включает root CA, intermediate CAs и серверный сертификат. При подключении клиента к серверу при установлении соединения сервер отдаёт свой сертификат с реверсной цепочкой не включая CA (т. к. он есть у пользователя). Файл с этими сертификатами в nginx указывается в параметре
ssl_certificate
. - Сторона клиента имеет независимую ветку PKI (которая может спокойно использовать приватный CA). При включенной
ssl_verify_client
(необходимо также указатьssl_client_certificate
, если используетсяon
илиoptional
) сервер послеServerHello
передаёт дополнительно фреймCertificate Request
, в котором перечислены dn'ы CA которым данный endpoint доверяет в контексте аутентификации клиента (в случае использованияoptional_no_ca
этот список пустой). Клиент на этот запрос предоставляет сертификат (в случае браузера показывает окошко, где пользователь выбирает какой сертификат использовать).
И первая ветка PKI (серверная) в нормальном случае получается через публичный CA типа LetsEncrypt. Сторона же пользователя, в зависимости от сервиса может получаться:
- от публичного или государственного CA,
- от приватного CA самого сервиса (который, по сути, в статье и описан).
Если хочется разобраться, то рекомендую взять для начала wireshark, открыть rfc5246, выполнить описанное в 3 части статьи.
Я для тестов использовал сейчас более простую конфигурацию:
# генерируем ключ
openssl genrsa -out ca.pem 1024
# генерируем самоподписанный сертификат
openssl req -x509 -subj '/CN=test ca' -days 365 -key ca.pem -out ca.crt
# смотрим глазами на самоподписанный (root) CA
openssl x509 -in ca.crt -noout -text
# генерируем клиентский ключ
openssl genrsa -out client.pem 1024
# генерируем csr и приносим его на машину с CA
openssl req -key client.pem -new -out client.csr -subj '/CN=client'
# генерируем клиентский сертификат на основе csr
openssl x509 -req -in client.csr -out client.crt -CA ca.crt -CAkey ca.pem -CAcreateserial
# смотрим на него
openssl x509 -in client.crt -noout -text
# собираем на машине с ключом pkcs12 контейнер с ключом и сертификатом для браузера
openssl pkcs12 -export -out client.p12 -in client.crt -inkey client.pem
# !!! импортируем client.p12 в браузер
# прописываем в уже работающем по https nginx'е `ssl_client_verify` on, optional или optional_no_ca. в первых двух случаях не забываем притащить nginx'у ca.crt и прописать в `ssl_client_certificate`
# пользуемся
Для отладки ssl/tls chrome (и, вроде, ff) поддерживают переменную окружения SSLKEYLOGFILE
, в которой указывается файл куда сохранять сессионные ключи. Эту переменную можно использовать только при отладке. При наличии этого файла кто угодно может расшифровать содержание SSL/TLS сессий (в том числе при использовании PFS).
Я использую следующий скрипт-обёртку:
#!/bin/bash
export SSLKEYLOGFILE=~/.cache/tls-premaster
google-chrome-stable --user-data-dir=/tmp/tls-debug --incognito
rm -rf /tmp/tls-debug
shred -u ~/.cache/tls-premaster
Путь к этому файлу нужно указать wireshark'у в preferences -> protocols -> ssl, пункт (Pre)-Master-Secret log filename
.
Вот ещё хорошая статья, если хочется знать, что там под капотом и как работает: https://tls.dxdt.ru/tls.html
Ещё могу посоветовать стэнфордский курс Crypto на Coursera — хорошо объясняют про криптографию в целом (но уже чёрт знает сколько лет не могут выпустить вторую часть курса).
3.5 Подключение к полученному ssl cерверу с помощью curl
curl -k --key client.key --cert client1.crt --url "https://site.com"
Использована опция -k, потому что сертификат в примере самоподписанный
зашёл сказать, что не надо игнорировать проверку валидности (тот самый ключ
-k
), но в комментируемом комменте уже есть правильный рецепт:curl ... --cacert path/to/ca.crt
на что и хотел бы обратить внимание
Когда аутентификация клиента проходит успешно, то Nginx заполняет несколько переменных данными о клиентском сертификате: https://nginx.org/en/docs/http/ngx_http_ssl_module.html#variables
Эти переменные уже можно передать в заголовках приложению и оно там будет искать клиента у себя. Вам скорее всего нужен $ssl_client_escaped_cert
, $ssl_client_s_dn
или $ssl_client_serial
а кто нибудь сталкивался, почему мобильные телефоны не предлагают выбрать сертификат для авторизации , а сразу ошибка 400?
Авторизация клиентов в nginx посредством SSL сертификатов