Автоматизировать создание сертификатов SSL

На прошлой неделе я начал внедрять брокера MQ Telemetry (MQTT). MQTT - это облегченный протокол публикации-подписки, используемый с решениями Internet of Thing (IoT). Этот брокер должен был получать данные датчиков и повторно отправлять их другим клиентам MQTT. Связь должна была быть безопасной.

Безопасность в данном случае означает использование сертификатов.

ОК, сертификаты, вздох, вот и мы. Я выдохнул через нос и взял себя в руки. Мне никогда не нравилось работать с сертификатами. Я поискал в Интернете и нашел, как создавать самозаверяющие сертификаты. «Мне действительно нужно это автоматизировать», - помню, пару раз до этого мне приходило в голову…

Но почему-то я всегда заканчиваю тем, что создаю сертификат, набирая команды в терминале. Затем я продолжаю проект и никогда не оглядываюсь назад.

У меня всегда были отношения любви-ненависти с сертификатами. Я никогда не понимаю, как они работают, но время от времени они нужны во время разработки.

Итак, я закончил создание этой статьи. Для меня это ссылка на будущее. Он содержит достаточно деталей, чтобы понять сертификаты с точки зрения разработчика.

Помимо этого, я наконец-то автоматизировал создание локально доверенного сертификата разработки. Смотрите этот репозиторий GitHub.

Надеюсь, вы сочтете это полезным.

Немного теории

Обещаю, совсем немного.

Нужны ли сертификаты?

Да. Сертификаты нам нужны по двум основным причинам.

  • Безопасная передача данных.
  • Идентификация отправителя или получателя.

Безопасная передача данных

Вы можете использовать два типа шифрования для безопасной передачи данных: симметричное и асимметричное. У обоих есть свои плюсы и минусы. Позже мы увидим, что когда браузер использует HTTPS для подключения к веб-сайту, он использует оба типа шифрования.

В чем разница между симметричным и асимметричным шифрованием?

Я считаю, что проще всего понять симметричное шифрование. И отправитель, и получатель имеют общий секретный ключ. Отправитель использует этот секретный ключ для изменения сообщения для отправки. Получатель использует тот же секретный ключ для восстановления исходного сообщения.

Асимметричное шифрование всегда кажется мне волшебством. В этом случае вы создаете два ключа, которые принадлежат друг другу. Первый - это открытый ключ. Вы можете поделиться им со всеми, кто хочет отправить вам сообщение. Второй ключ - это секретный ключ. Только тебе нужно это знать.

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

При использовании SSL используются оба типа шифрования.

Связь SSL (упрощенная)

Когда браузер подключается к веб-сайту, использующему SSL, браузер выполняет следующие шаги.

  1. Вы указываете браузеру подключиться к веб-сайту через HTTPS. Браузер запрашивает, чтобы веб-сервер идентифицировал себя.
  2. Веб-сервер отправляет копию своего сертификата обратно в браузер.
  3. Браузер проверяет подлинность сертификата. Соответствует ли имя в сертификате доменному имени? Доверяю ли я центру сертификации, подписавшему сертификат?
  4. Браузер генерирует пароль и шифрует его с помощью открытого ключа. Он получил открытый ключ из сертификата. Это асимметричное шифрование. Браузер отправляет зашифрованный пароль на веб-сервер.
  5. Веб-сервер расшифровывает пароль с помощью закрытого ключа.
  6. Браузер устанавливает зашифрованное соединение. Обе стороны шифруют и дешифруют данные, используя сгенерированный ранее пароль. Это симметричное шифрование.

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

Идентификация отправителя или получателя

Сертификаты также используются для идентификации. Таким образом, вы можете быть уверены, что действительно подключаетесь к правильному веб-сайту.

Проверка бывает двумя способами. Сначала браузер проверяет, можно ли доверять сертификату, возвращаемому веб-сервером. Это проверяется тем, что сертификат подписан центром сертификации (ЦС).

Операционная система вашего устройства содержит список доверенных сертификатов.

Например, на скриншоте ниже показан корневой сертификат IdenTrust. Корневой сертификат IdenTrust - один из наиболее часто используемых сертификатов CA. В основном потому, что сертификаты Let's Encrypt имеют перекрестную подпись с помощью IdenTrust.

Этот список доверенных сертификатов используется браузером для проверки того, что полученный сертификат подписан центром сертификации в этом списке.

Второй тип идентификации - это проверка того, соответствует ли общее имя сертификата домену, обслуживающему сертификат.

Форматы файлов сертификатов

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

Формат PEM

Формат Privacy Enhanced Mail (PEM) является наиболее распространенным форматом для хранения сертификатов закрытых ключей. Содержимое файла - ASCII в кодировке Base64.

Файл может иметь расширения .pem, .crt, .key или .ca-bundle. Чтобы убедиться, что файл на самом деле является файлом PEM, откройте его и убедитесь, что он начинается с BEGIN CERTIFICATE или BEGIN RSA PRIVATE KEY.

Формат P7B

Файл в формате P7B или PKCS # 7 также сохраняется в кодировке Base64 ASCII.

Файл имеет расширение .p7b или .p7c. Формат P7B можно использовать только для хранения сертификатов, но не закрытых ключей. Формат файла используется в Windows и Java Tomcat.

Формат DER

Формат Distinguished Encoding Rules (DER) хранит сертификаты и закрытые ключи в двоичном формате. Файл имеет расширение .der или .cer. Формат DER часто используется на платформах Java.

Формат PKCS # 12

Формат PKCS # 12 или PFX хранится в двоичном файле. Закрытый ключ и сертификат хранятся в одном файле.

Файл имеет расширение .pfx или .p12. Файлы используются в Windows и macOS для импорта и экспорта сертификатов и закрытых ключей.

Создание одноразовых самозаверяющих сертификатов

Самозаверяющий сертификат можно создать на любом компьютере. Использую macOS с версией 1.1.1 OpenSSL. OpenSSL - это полнофункциональный инструментарий и библиотека криптографии. Для большинства операционных систем существуют готовые к использованию бинарные пакеты OpenSSL.

Следующие шаги необходимы для создания самозаверяющего сертификата.

  • Сгенерируйте закрытый ключ.
  • Создайте запрос на подпись сертификата.
  • Сгенерируйте сертификат.

1. Создание закрытого ключа

Первый шаг - создать закрытый ключ, выполнив следующую команду.

openssl genpkey -algorithm RSA -des3 -out private-key.pem -pkeyopt rsa_keygen_bits:4096

genpkey - Команда OpenSSL для выполнения в данном случае генерации закрытого ключа

algorithm RSA - Какой алгоритм открытого ключа использовать. Доступны и другие варианты, такие как RSA-PSS, EC, X25519, X448, ED25519 и ED448.

-des3 - В этом параметре указано, что OpenSSL должен шифровать закрытый ключ с помощью Triple-DES. После выполнения команды он спрашивает, какой пароль использовать.

out privateKey.pem - OpenSSL должен хранить закрытый ключ в файле с именем privateKey.pem.

pkeyopt rsa_keygen_bits:4096 - указывает количество бит, которое должно использоваться сгенерированным ключом. Я использую 4096 бит.

Формат выходного файла privateKey.pem по умолчанию - файл PEM. С genpkey OpenSSL использует синтаксис PKCS # 8 для хранения ключа в файле.

Я использую genpkey вместо genrsa, потому что он использует более разумные значения по умолчанию.

Например, количество битов по умолчанию сгенерированного ключа genrsa составляет 512 бит. Которая, согласно ars TECHNICA в 2015 году, могла быть взломана за четыре часа с помощью Amazon EC2 за 75 долларов.

2. Создание запроса на подпись сертификата (CSR)

CSR подобен порядку создания сертификата. Если вам нужен официальный сертификат SSL, вы отправляете его в официальный центр сертификации (ЦС). Они используют CSR для создания официального сертификата.

Однако мы будем использовать этот запрос для создания сертификата самостоятельно, самозаверяющего сертификата.

Ранее я описал две основные причины использования сертификата, шифрования и идентификации. OpenSSL задаст ряд вопросов, чтобы узнать, какую информацию поместить в CSR.

Вопрос, который задает ваш Common Name, важен для идентификации. Вам следует указать полное доменное имя, для которого вы хотите использовать сертификат. Это также может быть внутренний домен, localhost или IP-адрес.

openssl req -new -key private-key.pem -out csr.pem

req - Создать запрос сертификата в формате PKCS # 10.

-new - Создать новый запрос сертификата. Спросите у пользователя всю необходимую информацию.

-key - Имя используемого закрытого ключа. Закрытый ключ был сгенерирован на предыдущем шаге.

-out - имя файла созданного запроса на сертификат.

OpenSSL задаст следующие вопросы:

  • Общее имя: полное доменное имя (полное доменное имя), которое вы хотите защитить с помощью сертификата.
  • Организация: полное название вашей организации. Важно только для доверенных сертификатов.
  • Организационное подразделение (OU): Ваш отдел. Важно только для доверенных сертификатов.
  • Город или населенный пункт: город, в котором расположена ваша организация.
  • Штат или провинция: штат или провинция, в которой расположена ваша организация.
  • Страна: официальный двухбуквенный код страны, в которой расположена ваша организация.

3. Создание самозаверяющего сертификата.

С помощью закрытого ключа из шага 1 и запроса сертификата из шага 2 вы можете создать самозаверяющий сертификат. Следующая команда OpenSSL создает сертификат.

openssl x509 -in csr.pem -out certificate.pem -req -signkey private-key.pem -days 365

x509 - выполнить команду сертификата.

-in - указывает на запрос на подпись сертификата (csr.pem).

-out - имя файла для сохранения сгенерированного сертификата. В данном случае certificate.pem.

-req - Укажите OpenSSL, что ввод - это CSR.

-signkey - Самоподписать запрос сертификата, используя указанный private-key.pem файл.

-days - количество дней, в течение которых сгенерированный сертификат действителен. Нормальные значения - 365, 730 и 1095 дней, чтобы указать продолжительность в один, два или три года.

Теперь у вас есть certificate.pem файл, который можно использовать для локальной разработки. В зависимости от вашей среды разработки вам может потребоваться преобразовать сертификат в другой формат.

Для разработки на Node.js вы можете напрямую использовать файл certificate.pem.

Создание нескольких самозаверяющих сертификатов

Простое создание самозаверяющего сертификата отлично подходит для одного проекта, выполняемого одним человеком. Если у вас несколько проектов или вы работаете с командой разработчиков, создание всех этих самозаверяющих сертификатов для каждого проекта и их импорт в хранилище сертификатов каждого разработчика может стать обременительным.

Конечно, вы можете создавать сценарии для их генерации, но я думаю, что создание центра сертификации (ЦС) самостоятельно - лучшее решение.

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

Чтобы стать центром сертификации и подписать самозаверяющий сертификат, вам необходимо выполнить следующие шаги:

  • Сгенерируйте закрытый ключ для CA.
  • Создайте корневой сертификат.
  • Создайте закрытый ключ для сертификата.
  • Создайте запрос на подпись сертификата.
  • Создайте сертификат и подпишите его закрытым ключом CA

1. Создайте закрытый ключ для центра сертификации.

Закрытый ключ для CA можно сгенерировать с помощью следующей команды.

openssl genpkey -algorithm RSA -des3 -out private-key-ca.pem -pkeyopt rsa_keygen_bits:4096

Эта команда ничем не отличается от команды для создания закрытого ключа для самозаверяющего сертификата. Единственное отличие - это имя файла private-key-ca.pem.

Эта команда создает файл private-key-ca.pem в текущем каталоге.

2. Создайте корневой сертификат.

С помощью следующей команды мы напрямую генерируем корневой сертификат на основе закрытого ключа, сгенерированного на предыдущем шаге.

openssl req -x509 -new -key private-key-ca.pem -sha256 -days 3650 -out ca-certificate.pem

OpenSSL снова запрашивает парольную фразу закрытого ключа и спрашивает, какую информацию поместить в корневой сертификат.

В этом случае ответы, которые вы даете, не совсем актуальны. Единственное, что я рекомендую, - это добавить информацию в одно из полей, чтобы вы могли видеть, что это корневой сертификат.

Команда создает файл ca-certificate.pem в текущем каталоге.

3. Создайте закрытый ключ для сертификата.

С помощью следующей команды, которую мы использовали ранее, мы генерируем закрытый ключ для этого конкретного сертификата.

openssl genpkey -algorithm RSA -des3 -out private-key.pem -pkeyopt rsa_keygen_bits:4096

Эта команда создает файл private-key.pem в текущем каталоге.

4. Создайте запрос на подпись сертификата.

С помощью следующей команды, которую мы также использовали ранее, мы генерируем запрос на подпись сертификата.

openssl req -new -key private-key.pem -out csr.pem

OpenSSL снова будет задавать вопросы для ввода запроса на подпись сертификата. Один важный вопрос - это Общее имя. Установите это имя домена или IP-адрес, на котором вы хотите использовать сертификат.

Новые браузеры используют альтернативное имя субъекта (SAN) для хранения имен DNS или IP-адресов. См. Часть о SAN далее в статье.

5. Создайте сертификат и подпишите его закрытым ключом ЦС.

Следующая команда создает самозаверяющий сертификат и подписывает его закрытым ключом ЦС.

openssl x509 -req -in csr.pem -CA ca-certificate.pem -CAkey private-key-ca.pem -CAcreateserial -out certificate.crt -days 3650

x509 - Выполнить команду сертификата.

-req - Команда x509 ожидает сертификат в качестве входных данных. С помощью -req вы указываете, что предоставите запрос сертификата в качестве входных данных.

-in - Запрос сертификата (csr.pem).

-CA - сертификат ЦС, который будет использоваться для подписи созданного сертификата. (ca-certificate.pem)

-CAkey - закрытый ключ ЦС, который будет использоваться для подписи созданного сертификата. (private-key-ca.pem)

-CAcreateserial - Каждый сертификат, выданный ЦС, должен содержать уникальный серийный номер. OpenSSL хранит использованные серийные номера в файле с тем же именем, что и сертификат, с расширением .srl. В этом случае в вашем локальном каталоге будет ca-certificate.srl.

-out - имя файла сертификата для создания (certificate.crt).

-days - количество дней, в течение которых сгенерированный сертификат действителен (3650).

Автоматизация создания локального центра сертификации и сертификатов

В предыдущих абзацах я показал вам команды OpenSSL, необходимые для создания локального центра сертификации и сертификатов.

Я не был бы хорошим разработчиком, если бы к тому же не попытался автоматизировать это.

Использование скриптов

Я создал два отдельных скрипта. Один для создания локального ЦС, а другой для создания сертификата, подписанного локальным ЦС. И то, и другое можно найти в этом репозитории GitHub.

Есть отдельные скрипты для Windows и macOS. Одно отличие от команд из предыдущих абзацев состоит в том, что сценарии используют файлы конфигурации для ответа на вопросы.

Например, следующий файл конфигурации используется при создании сертификата CA. В нем указаны ответы на различные вопросы OpenSSL. Вы можете изменить ответы по своему вкусу. Такой же тип конфигурации также доступен для генерации самозаверяющего сертификата.

Альтернативное имя субъекта

Ранее я описывал использование Common Name для хранения полного доменного имени. Начиная с версии 58, Chrome больше не поддерживает проверку общего имени. Это поле игнорирует.

Вместо этого правильное место для указания вашего домена будет в поле SAN (альтернативное имя субъекта). В репозитории GitHub есть файл конфигурации с именем certificate-ext.conf, который содержит домены и IP-адреса, которые помещаются в SAN.

Вы можете изменить этот файл конфигурации, чтобы добавить нужные доменные имена и IP-адреса.

После того, как вы сгенерируете сертификат с помощью команды ./generate-cert.sh, для сгенерированного сертификата будет правильно заполнено SAN.

Использование mkcert

Если вы не можете использовать предыдущий сценарий или хотите более обширное решение, вы можете использовать mkcert. Mkcert создан Филиппо Валсордой, и он является частью команды Google Go.

Mkcert - это простой инструмент для создания локально доверенных сертификатов разработки. Он реализован на Go и не требует настройки. Он работает в Linux, Windows и macOS.

Вы можете установить mkcert с помощью brew в macOS, выполнив следующую команду:

brew install mkcert

После установки мы можем использовать этот инструмент для создания собственного центра сертификации с помощью следующей команды:

mkcert -install

Инструмент создает новый центр сертификации и напрямую устанавливает его в корневом хранилище вашей операционной системы. Имя сертификата начинается с mkcert и содержит имя компьютера, который вы использовали для выполнения mkcert.

Секретный ключ CA и сертификат также хранятся в файловой системе. Вы можете получить местоположение, выполнив следующую команду:

mkcert -CAROOT

После установки нового CA. Вы можете использовать mkcert для создания сертификатов, которые автоматически подписываются ЦС. Например, следующая команда:

mkcert dev.com *.dev.com localhost 127.0.0.1

Создает сертификат, действительный для данных имен. Mkcert сохраняет указанные DNS-имена и IP-адрес в части сертификата в разделе «Альтернативное имя субъекта» (SAN).

Сгенерированный закрытый ключ и сертификат доступны в каталоге, в котором вы запустили команду.

Есть ли у меня отношения любви-ненависти к сертификатам SSL?

После создания сценариев автоматизации и написания этой статьи я ценю их больше. Я все еще не люблю их, но считаю, что они необходимы для надежной безопасности.

Спасибо за чтение.