Самоподписанные сертификаты

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

В качестве подопытного сервиса будет выступать небольшой REST-сервис на Spring Boot, который я хочу запускать в режиме HTTP2, он должен быть доступен по адресу https://localhost:8080 и https://127.0.0.1:8080. Для него я и буду создавать самоподписанный сертификат.

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

Мимикрия под CA

Первым делом я создам приватный ключ и сертификат, которые будут имитировать CA:

В процессе создания OpenSSL спросит пароль для создаваемого ключа. Кстати, именно так создаётся самоподписанный сертификат.

Сертификат для сервиса

Теперь можно создать ключ для сервиса:

Для созданного ключа нужно создать запрос на подпись сертификата:

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

Теперь можно подписывать:

Для удобства импорта сертификатов root_ca.crt и localhost.crt их можно объединить в цепочку сертификатов следующим образом:

На этом этапе у нас есть всё необходимое для использования SSL в сервисах вроде NGINX или Apache httpd. Однако в большинстве Java-приложений SSL настраивается при помощи хранилищ ключей (keystore), который можно создать следующим образом:

ВАЖНО! Если на этапе создания ключа для приложения было выбрано шифрование ключа, то полученное хранилище нужно импортировать в другое при помощи команды keytool:

Полученное хранилище ключей можно использовать в Java-приложении.

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

Теперь в файле application.yml можно настроить HTTP2 и SSL следующим образом:

Однако, если я сейчас попробую открыть в браузере адрес https://localhost:8080/api/greeting, то увижу предупреждение об использовании сайтом сертификата, подписанного недоверенным авторизационным центром.

Mozilla Firefox не доверяет самоподписанным сертификатам

Решается это добавлением CA-сертификата (root_ca.crt) в список центров сертификации в настройках браузера, после чего можно наблюдать корректную работу, в т.ч. и HTTP2:

После добавления CA-сертификата в доверенные всё работает нормально

CA-сертификат можно добавить в список доверенных сертификатов для всей системы, чтобы ему доверяли различные утилиты вроде cURL. На Ubuntu Linux это можно сделать следующим образом:

Если некоторому Java-приложению требуется работать с вашим сервисом через защищённое соединение, то сертификат необходимо добавить в хранилище доверенных сертификатов (truststore), используемое этим приложением: