Все крупные сайты давно перешли на протокол 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 это будет выглядеть так:

nginx ssl

Запускаем браузер, открываем https://mysite.localhost и видим:



Браузер не доверяет этому сертификату. Как быть?

Нужно отметить выпущенный нами сертификат как Trusted. На Linux (Ubuntu и, наверное, остальных Debian-based дистрибутивах) это можно сделать через сам браузер. В Mac OS X это можно сделать через приложение Keychain Access. Запускаем приложение и перетаскиваем в окно файл mysite.localhost.crt. Затем открываем добавленный файл и выбираем Always Trust:



Обновляем страницу в браузере и:



Успех! Браузер доверяет нашему сертификату.

Сертификатом можно поделиться с другими разработчиками, чтобы они добавили его к себе. А если вы используете Docker, то сертификат можно сохранить там. Именно так это реализовано на всех наших проектах.

Делитесь в комментариях, используете ли вы https для локальной разработки?

Максим Ковтун,
Руководитель отдела разработки

Комментарии (45)


  1. medvedevia
    03.04.2018 18:35
    +1

    Проще купить любой дешёвый домен и получить сертификат от letsencrypt бесплатно, чем заниматься этой ерундой…


    1. ponich
      03.04.2018 18:50

      Я кстати так и сделал. Зарегистрировал себе wildcard SSL от letsencrypt, после горя не знаю при создании суб-домена.


    1. Daniyar94
      03.04.2018 19:11

      А можно ли использовать LetsEncrypt для коммерческих проектов? Я не уверен, что можно


      1. NO_oB
        03.04.2018 19:13

        Можно, никаких ограничений на испольщование сертификатов от letsencrypt нет.


    1. Token2
      03.04.2018 19:15

      А если внутри корпоративной сети только нужно?
      Для сетей с MS AD можно развернуть CA и все клиенты будут доверять через GPO


    1. medvedevia
      04.04.2018 12:30

      Для тех кто не понял:
      Нужно купить самый дешевый понравившийся домен (цена 200 р. и меньше на год), ну и пофиг что продление будет стоить дороже, через год можно купить опять новый домен.
      Чтобы получить сертификат, нужен комп, который торчит в интернет. Если на работе проблематично получить доступ к такому компу, то можно использовать домашний комп (мы же разработчики, у всех есть домашний комп). Получаем сертификат от letsencrypt (теперь уже даже wildcard можно получить), далее копируем к себе куда надо все это добро, в hosts прописываем локальный ip для этого домена. Такая схема работает и на винде и в линукс, сам лично пользуюсь. Можно даже обойтись без hosts: у регистратора домена прописываешь в dns локальный ip, тогда в локальной сети работает как будто комп торчит в интернет.


      1. kvaps
        04.04.2018 15:22

        можно и не покупать ничего, а просто использовать xip.io


      1. medvedevia
        04.04.2018 23:23

        Чтобы получить сертификат, нужен комп, который торчит в интернет.

        Оказывается даже это необязательно, можно в ручном режиме получить с подтверждением с помощью dns, см. certbot.eff.org/docs/using.html#manual


  1. mrHobbY
    03.04.2018 18:52
    -1

    Если вебдев, то скорей всего установлены туча браузеров, где надо каждому настроить доверие. А как насчет доверия со стороны системы? А если curl/wget? Как сказанно выше — зачем городить костыли, если есть доступные методы получения сертификатов.


    1. nitso
      03.04.2018 22:54
      -1

      Достаточно добавить корневой сертификат в хранилище сертификатов на уровне ОС. Для Java есть оговорки. И сразу удалить/спрятать его, чтобы самому себе бэкдор не устроить :)


      1. mrHobbY
        03.04.2018 23:39

        Так это понятно. Это риторические вопросы к статье, а не к Вам. Статья учит новичков антипатернам, которые им потом аукнутся.


    1. VolCh
      04.04.2018 00:25

      Вполне настраивается всё. Главное, что один раз настроить доверие к корневому, а потом плоди сколько хочешь. И всё на localhost, никому не нужно давать доступ к нему.


    1. VolCh
      04.04.2018 08:46

      Вот для ubuntu рецепт:


      sudo cp my.crt /usr/local/share/ca-certificates/
      sudo update-ca-certificates

      А вот так называемые "доступные методы" не очень хорошо подходят для условий локальной разработки без прямого доступа из интернета к защищаемому хосту (NAT, прокси). В общем случае с 4 сторонними лицами надо взаимодействовать:


      • администратор локальной сети, который пробросит порт с внешнего адреса на внутренний, а потом уберёт проброс, если он вообще согласится. "Локальной сетью" вполне может оказаться сеть оператора, например мобильного, который даже временно, динамические публичные адреса не даёт
      • регистратор доменов
      • DNS-сервис, адреса которого нужно дать регистратору домена


      1. medvedevia
        04.04.2018 12:17

        Чтобы получить сертификат, подойдет любой комп, который торчит в интернет, в самом плохом случае можно использовать домашний комп. Далее полученный сертификат и секретный ключ копируешь куда надо, для использования доступ из интернета не нужен.
        А вот просить всех добавить свой левый сгенеренный сертификат к себе в доверенные так себе идея с точки зрения безопасности.


        1. VolCh
          04.04.2018 15:28

          Домашний комп тоже нередко в локалке: или роутер с NAT на входе в квартиру, или провайдер белых адресов не даёт, равзе что за отдельную плату, а по дефолту пуская через NAT, или и то, и другое, разве что админские права могут быть на роутер дома у разработчика. А могут и не быть, например сын — разработчик, а папа — админ :)


          Если речь идёт не о локальной разработке, а о, например, QA-сервере в корпоративной сети, то там хорошим вариантом может быть поднятие своего CA полноценного, особенно если активно используются локальные домены, которые в принципе не резолвятся публичными DNS. И с точки зрения безопасности вариант отличный — сертификат корпоративного CA добавляется на все сервера и рабочие станции, подписывать им можно сертификаты для любых целей, а не только для сайтов, вносить в проверенную любую информацию, например, отдел.


          1. medvedevia
            04.04.2018 23:24

            Домашний комп тоже нередко в локалке: или роутер с NAT на входе в квартиру, или провайдер белых адресов не даёт

            Можно в ручном режиме certbot.eff.org/docs/using.html#manual


            1. VolCh
              05.04.2018 12:04

              Лучше, да, часть кейсов для разработки закрывает.


  1. Splean
    03.04.2018 19:21
    +2

    Уважаемые комментаторы, статья ориентирована на dev-среду, ни в коем случае не на prod. При переносе проекта на продакшн конечно же выпускаются правильные сертификаты.
    Я пользуюсь шпаргалочкой попроще, к тому же она позволяет выпускать wildcard-сертификаты ) gist.github.com/croxton/ebfb5f3ac143cd86542788f972434c96


    1. evAPPs Автор
      04.04.2018 10:04
      +1

      Именно! Это исключительно для dev. А шпаргалка хорошая, спасибо.


  1. kentov
    03.04.2018 20:49
    -2

    Можно просто купить хостинг на Бегет и установить бесплатный сертификат, да и все, непонимаю к чему такой гемор


    1. zelenin
      03.04.2018 22:11

      зачем нужен хостинг при локальной разработке?


      1. ustasby
        03.04.2018 22:50

        Что бы сунуть в ссылку рефералку.


        1. zelenin
          04.04.2018 00:22

          а, вижу)


    1. daniillnull
      04.04.2018 10:04

      Рефералки на хабре, серьезно?


      1. bluetooth
        04.04.2018 10:59

        Пытается выехать на хабраэффекте


  1. lasc
    04.04.2018 01:42

    Еще проще можно сделать через ngrok, и показать за натом тоже можно.


  1. shushu
    04.04.2018 08:13

    1. Можно выпустить сертификат (wildcard использую letsencrypt)
    2. Можно выпустить сертификат самому. (тоже вайлдкард)
    2.1 сгенерировать рутовый сертификат
    2.2 сгенерировать сертификат
    2.3 добавить рутовый сертификат как доверенный в браузер. (после этого все сертификаты сгенерированные с помошью рутового — будут валидны.)

    Я в общим-то 2й вариант использую для дев окружение. Если интересно могу написать шпаргалку по этому поводу.


    1. evAPPs Автор
      04.04.2018 10:01

      Да, интересно. Напишите, пожалуйста


      1. 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


  1. Keroro
    04.04.2018 08:56

    А где-нибудь можно посмотреть уже готовый рабочий create_certificate_for_domain.sh? А то мой что-то не работает…


    1. evAPPs Автор
      04.04.2018 11:49

      Вот, выложил на GitHub.
      сначала запустите файл create_root_cert_and_key.sh для генерации корневого сертификата, потом create_certificate_for_domain.sh


    1. SlavniyTeo
      04.04.2018 14:36

      Используйте easy-rsa.


  1. Chingul
    04.04.2018 09:38

    Как настроить работу Wildcard на субдоменах работающих на VPS?


  1. papkinv
    04.04.2018 10:06

    Пользуюсь утилитой Portecle (Java) и горя не знаю. Все в пару кликов. Плюс поддержка разных форматов keystore


  1. SemaIVVV
    04.04.2018 10:10

    Как в Windows заставить доверять такому сертификату? FireFox помечает его как недоверенный, при добавлении в доверенные, https работает, но на зеленом значке появляется оранжевый щит, при показе свойств которого, говорится, что это добавленный в исключения безопасности сертификат.


    1. evAPPs Автор
      04.04.2018 10:13

      Честно говоря, на Windows не пробовал. Попробую запустить проект и посмотреть как быть с сертификатом там. Вы только в FireFox используете?


    1. bluetooth
      04.04.2018 11:06

      Попробуйте так.


    1. den_po
      04.04.2018 11:15

      У FF своё персональное хранилище. Можно добавить в настройках.


    1. JekaKey
      04.04.2018 14:25

      Надо добавить сертификат в хранилище
      certutil -addstore Root PATHTOCERT\cacert.pem
      И в случае с FF включить в about:config флаг security.enterprise_roots.enabled на true


  1. Talkerbox
    04.04.2018 12:57

    Пробовали подсунуть такой CA в мобильные девайсы? Были с этим проблемы. Если ваш импортируется и домены потом зеленые, тогда было бы просто супер.


    1. Stanislavvv
      04.04.2018 16:27

      Всё нормально импортируется и даже авторизуется клиентскими сертификатами.
      Проблемы есть только с мобильным фф, для которого протухло дополнение, рулившее этими самыми клиентскими сертификатами…


    1. Stanislavvv
      04.04.2018 16:31

      Забыл дописать — всё делалось на основе конфига easy-rsa, которым довольно долго пользовались без какой-либо модификации, недавно были запилены уже свои скрипты.


  1. ALexhha
    04.04.2018 13:20

    А чем не подошел тот же easy-rsa?


  1. ssl
    04.04.2018 17:12

    Иногда, для более полной отладки требуется чтобы не просто самоподписанный сертификат использовался веб-сервисом — а более сложный сертификат с Intermediate'ом и Root'ом, т.к., к примеру, от наличия Intermediate цепочти сертификатов на самом веб-сервере зависит то — сможет ли клиент веб-сервиса построить цепочку доверия до конечного сертификат веб-сервера имея в своем доверенном хранилище сертификатов только Root.

    Проверить какие сертификаты публикует веб сервис можно тем-же openssl'ем — «openssl s_client -showcerts -connect :».

    Для себя, как и вы, я написал скрипты с использованием openssl для генерации сертификатов с разной длиной цепочки доверия =)


    1. evAPPs Автор
      05.04.2018 11:24

      Для себя, как и вы, я написал скрипты с использованием openssl для генерации сертификатов с разной длиной цепочки доверия =)

      Поделитесь, если есть возможность )