Все крупные сайты давно перешли на протокол https. Тенденция продолжается, и многие наши клиенты хотят, чтобы их сайт работал по защищенному протоколу. А если разрабатывается backend для мобильного приложения, то https обязателен. Например, Apple требует, чтобы обмен данными сервера с приложением велся по безопасному протоколу. Это требование введено с конца 2016 года.
На production нет проблем с сертификатами. Обычно хостинг провайдер предоставляет удобный интерфейс для подключения сертификата. Выпуск сертификата тоже дело не сложное. Но во время работы над проектом каждый разработчик должен позаботиться о сертификате сам.
В этой статье я расскажу, как выпустить самоподписанный SSL сертификат и заставить браузер доверять ему.
Чтобы выпустить сертификат для вашего локального домена, понадобится корневой сертификат. На его основе будут выпускаться все остальные сертификаты. Да, для каждого нового top level домена нужно выпускать свой сертификат. Получить корневой сертификат достаточно просто.
Сначала сформируем закрытый ключ:
openssl genrsa -out rootCA.key 2048
Затем сам сертификат:
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 1024 -out rootCA.pem
Нужно будет ввести страну, город, компанию и т.д. В результате получаем два файла: rootCA.key и rootCA.pem
Переходим к главному, выпуск самоподписанного сертификата. Так же как и в случае с корневым, это две команды. Но параметров у команд будет значительно больше. И нам понадобится вспомогательный конфигурационный файл. Поэтому оформим все это в виде bash скрипта create_certificate_for_domain.sh
Первый параметр обязателен, выведем небольшую инструкцию для пользователя.
if [ -z "$1" ]
then
echo "Please supply a subdomain to create a certificate for";
echo "e.g. mysite.localhost"
exit;
fi
Создадим новый приватный ключ, если он не существует или будем использовать существующий:
if [ -f device.key ]; then
KEY_OPT="-key"
else
KEY_OPT="-keyout"
fi
Запросим у пользователя название домена. Добавим возможность задания “общего имени” (оно используется при формировании сертификата):
DOMAIN=$1
COMMON_NAME=${2:-$1}
Чтобы не отвечать на вопросы в интерактивном режиме, сформируем строку с ответами. И зададим время действия сертификата:
SUBJECT="/C=CA/ST=None/L=NB/O=None/CN=$COMMON_NAME"
NUM_OF_DAYS=999
В переменной SUBJECT перечислены все те же вопросы, который задавались при создании корневого сертификата (страна, город, компания и т.д). Все значение, кроме CN можно поменять на свое усмотрение.
Сформируем csr файл (Certificate Signing Request) на основе ключа. Подробнее о файле запроса сертификата можно почитать в этой статье.
openssl req -new -newkey rsa:2048 -sha256 -nodes $KEY_OPT device.key -subj "$SUBJECT" -out device.csr
Формируем файл сертификата. Для этого нам понадобится вспомогательный файл с настройками. В этот файл мы запишем домены, для которых будет валиден сертификат и некоторые другие настройки. Назовем его v3.ext. Обращаю ваше внимание, что это отдельный файл, а не часть bash скрипта.
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = %%DOMAIN%%
DNS.2 = *.%%DOMAIN%%
Да, верно, наш сертификат будет валидным для основного домена, а также для всех поддоменов. Сохраняем указанные выше строки в файл v3.ext
Возвращаемся в наш bash скрипт. На основе вспомогательного файла v3.ext создаем временный файл с указанием нашего домена:
cat v3.ext | sed s/%%DOMAIN%%/$COMMON_NAME/g > /tmp/__v3.ext
Выпускаем сертификат:
openssl x509 -req -in device.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -out device.crt -days $NUM_OF_DAYS -sha256 -extfile /tmp/__v3.ext
Переименовываем сертификат и удаляем временный файл:
mv device.csr $DOMAIN.csr
cp device.crt $DOMAIN.crt
# remove temp file
rm -f device.crt;
Скрипт готов. Запускаем его:
./create_certificate_for_domain.sh mysite.localhost
Получаем два файла: mysite.localhost.crt и device.key
Теперь нужно указать web серверу пути к этим файлам. На примере nginx это будет выглядеть так:
Запускаем браузер, открываем https://mysite.localhost и видим:
Браузер не доверяет этому сертификату. Как быть?
Нужно отметить выпущенный нами сертификат как Trusted. На Linux (Ubuntu и, наверное, остальных Debian-based дистрибутивах) это можно сделать через сам браузер. В Mac OS X это можно сделать через приложение Keychain Access. Запускаем приложение и перетаскиваем в окно файл mysite.localhost.crt. Затем открываем добавленный файл и выбираем Always Trust:
Обновляем страницу в браузере и:
Успех! Браузер доверяет нашему сертификату.
Сертификатом можно поделиться с другими разработчиками, чтобы они добавили его к себе. А если вы используете Docker, то сертификат можно сохранить там. Именно так это реализовано на всех наших проектах.
Делитесь в комментариях, используете ли вы https для локальной разработки?
Максим Ковтун,
Руководитель отдела разработки
Комментарии (45)
mrHobbY
03.04.2018 18:52-1Если вебдев, то скорей всего установлены туча браузеров, где надо каждому настроить доверие. А как насчет доверия со стороны системы? А если curl/wget? Как сказанно выше — зачем городить костыли, если есть доступные методы получения сертификатов.
nitso
03.04.2018 22:54-1Достаточно добавить корневой сертификат в хранилище сертификатов на уровне ОС. Для Java есть оговорки. И сразу удалить/спрятать его, чтобы самому себе бэкдор не устроить :)
mrHobbY
03.04.2018 23:39Так это понятно. Это риторические вопросы к статье, а не к Вам. Статья учит новичков антипатернам, которые им потом аукнутся.
VolCh
04.04.2018 00:25Вполне настраивается всё. Главное, что один раз настроить доверие к корневому, а потом плоди сколько хочешь. И всё на localhost, никому не нужно давать доступ к нему.
VolCh
04.04.2018 08:46Вот для ubuntu рецепт:
sudo cp my.crt /usr/local/share/ca-certificates/ sudo update-ca-certificates
А вот так называемые "доступные методы" не очень хорошо подходят для условий локальной разработки без прямого доступа из интернета к защищаемому хосту (NAT, прокси). В общем случае с 4 сторонними лицами надо взаимодействовать:
- администратор локальной сети, который пробросит порт с внешнего адреса на внутренний, а потом уберёт проброс, если он вообще согласится. "Локальной сетью" вполне может оказаться сеть оператора, например мобильного, который даже временно, динамические публичные адреса не даёт
- регистратор доменов
- DNS-сервис, адреса которого нужно дать регистратору домена
medvedevia
04.04.2018 12:17Чтобы получить сертификат, подойдет любой комп, который торчит в интернет, в самом плохом случае можно использовать домашний комп. Далее полученный сертификат и секретный ключ копируешь куда надо, для использования доступ из интернета не нужен.
А вот просить всех добавить свой левый сгенеренный сертификат к себе в доверенные так себе идея с точки зрения безопасности.VolCh
04.04.2018 15:28Домашний комп тоже нередко в локалке: или роутер с NAT на входе в квартиру, или провайдер белых адресов не даёт, равзе что за отдельную плату, а по дефолту пуская через NAT, или и то, и другое, разве что админские права могут быть на роутер дома у разработчика. А могут и не быть, например сын — разработчик, а папа — админ :)
Если речь идёт не о локальной разработке, а о, например, QA-сервере в корпоративной сети, то там хорошим вариантом может быть поднятие своего CA полноценного, особенно если активно используются локальные домены, которые в принципе не резолвятся публичными DNS. И с точки зрения безопасности вариант отличный — сертификат корпоративного CA добавляется на все сервера и рабочие станции, подписывать им можно сертификаты для любых целей, а не только для сайтов, вносить в проверенную любую информацию, например, отдел.
medvedevia
04.04.2018 23:24Домашний комп тоже нередко в локалке: или роутер с NAT на входе в квартиру, или провайдер белых адресов не даёт
Можно в ручном режиме certbot.eff.org/docs/using.html#manual
Splean
03.04.2018 19:21+2Уважаемые комментаторы, статья ориентирована на dev-среду, ни в коем случае не на prod. При переносе проекта на продакшн конечно же выпускаются правильные сертификаты.
Я пользуюсь шпаргалочкой попроще, к тому же она позволяет выпускать wildcard-сертификаты ) gist.github.com/croxton/ebfb5f3ac143cd86542788f972434c96
kentov
03.04.2018 20:49-2Можно просто купить хостинг на Бегет и установить бесплатный сертификат, да и все, непонимаю к чему такой гемор
shushu
04.04.2018 08:131. Можно выпустить сертификат (wildcard использую letsencrypt)
2. Можно выпустить сертификат самому. (тоже вайлдкард)
2.1 сгенерировать рутовый сертификат
2.2 сгенерировать сертификат
2.3 добавить рутовый сертификат как доверенный в браузер. (после этого все сертификаты сгенерированные с помошью рутового — будут валидны.)
Я в общим-то 2й вариант использую для дев окружение. Если интересно могу написать шпаргалку по этому поводу.evAPPs Автор
04.04.2018 10:01Да, интересно. Напишите, пожалуйста
shushu
05.04.2018 03:41Генерация корневого сертификата:
openssl genrsa -des3 -passout pass:qwerty -out RootCA.key.pem 2048;
openssl req -x509 -new -nodes -key RootCA.key.pem \
-passin pass:qwerty \
-sha256 -days 1024 -out Root.cert.pem \
-subj "/C=AU/ST=NSW/L=Sydney/O=CompanyName/OU=Company Description/CN=www.company-name.com"
Выпускаем сертификат:
openssl req -new -sha256 -nodes -out server.csr.pem \
-newkey rsa:2048 -keyout server.key.pem \
-config <( cat server.csr.cnf );
openssl x509 -req \
-in server.csr.pem \
-passin pass:qwerty \
-CA Root.cert.pem \
-CAkey RootCA.key.pem -CAcreateserial -out server.cert.pem \
-days 500 -sha256 -extfile v3.ext;
v3.ext:
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = *.yourlocal-domain.com
server.csr.cnf:
[req]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
[dn]
C=AU
ST=NSW
L=Sydney
O=company name
OU=Development Department
emailAddress=your-email@mail.com
CN = *.yourlocal-domain.com
Keroro
04.04.2018 08:56А где-нибудь можно посмотреть уже готовый рабочий create_certificate_for_domain.sh? А то мой что-то не работает…
papkinv
04.04.2018 10:06Пользуюсь утилитой Portecle (Java) и горя не знаю. Все в пару кликов. Плюс поддержка разных форматов keystore
SemaIVVV
04.04.2018 10:10Как в Windows заставить доверять такому сертификату? FireFox помечает его как недоверенный, при добавлении в доверенные, https работает, но на зеленом значке появляется оранжевый щит, при показе свойств которого, говорится, что это добавленный в исключения безопасности сертификат.
evAPPs Автор
04.04.2018 10:13Честно говоря, на Windows не пробовал. Попробую запустить проект и посмотреть как быть с сертификатом там. Вы только в FireFox используете?
JekaKey
04.04.2018 14:25Надо добавить сертификат в хранилище
certutil -addstore Root PATHTOCERT\cacert.pem
И в случае с FF включить в about:config флаг security.enterprise_roots.enabled на true
Talkerbox
04.04.2018 12:57Пробовали подсунуть такой CA в мобильные девайсы? Были с этим проблемы. Если ваш импортируется и домены потом зеленые, тогда было бы просто супер.
Stanislavvv
04.04.2018 16:27Всё нормально импортируется и даже авторизуется клиентскими сертификатами.
Проблемы есть только с мобильным фф, для которого протухло дополнение, рулившее этими самыми клиентскими сертификатами…
Stanislavvv
04.04.2018 16:31Забыл дописать — всё делалось на основе конфига easy-rsa, которым довольно долго пользовались без какой-либо модификации, недавно были запилены уже свои скрипты.
ssl
04.04.2018 17:12Иногда, для более полной отладки требуется чтобы не просто самоподписанный сертификат использовался веб-сервисом — а более сложный сертификат с Intermediate'ом и Root'ом, т.к., к примеру, от наличия Intermediate цепочти сертификатов на самом веб-сервере зависит то — сможет ли клиент веб-сервиса построить цепочку доверия до конечного сертификат веб-сервера имея в своем доверенном хранилище сертификатов только Root.
Проверить какие сертификаты публикует веб сервис можно тем-же openssl'ем — «openssl s_client -showcerts -connect :».
Для себя, как и вы, я написал скрипты с использованием openssl для генерации сертификатов с разной длиной цепочки доверия =)
evAPPs Автор
05.04.2018 11:24Для себя, как и вы, я написал скрипты с использованием openssl для генерации сертификатов с разной длиной цепочки доверия =)
Поделитесь, если есть возможность )
medvedevia
Проще купить любой дешёвый домен и получить сертификат от letsencrypt бесплатно, чем заниматься этой ерундой…
ponich
Я кстати так и сделал. Зарегистрировал себе wildcard SSL от letsencrypt, после горя не знаю при создании суб-домена.
Daniyar94
А можно ли использовать LetsEncrypt для коммерческих проектов? Я не уверен, что можно
NO_oB
Можно, никаких ограничений на испольщование сертификатов от letsencrypt нет.
Token2
А если внутри корпоративной сети только нужно?
Для сетей с MS AD можно развернуть CA и все клиенты будут доверять через GPO
medvedevia
Для тех кто не понял:
Нужно купить самый дешевый понравившийся домен (цена 200 р. и меньше на год), ну и пофиг что продление будет стоить дороже, через год можно купить опять новый домен.
Чтобы получить сертификат, нужен комп, который торчит в интернет. Если на работе проблематично получить доступ к такому компу, то можно использовать домашний комп (мы же разработчики, у всех есть домашний комп). Получаем сертификат от letsencrypt (теперь уже даже wildcard можно получить), далее копируем к себе куда надо все это добро, в hosts прописываем локальный ip для этого домена. Такая схема работает и на винде и в линукс, сам лично пользуюсь. Можно даже обойтись без hosts: у регистратора домена прописываешь в dns локальный ip, тогда в локальной сети работает как будто комп торчит в интернет.
kvaps
можно и не покупать ничего, а просто использовать xip.io
medvedevia
Оказывается даже это необязательно, можно в ручном режиме получить с подтверждением с помощью dns, см. certbot.eff.org/docs/using.html#manual