- Добавление корневого сертификата в Java
- Криптография в Java. Класс Certificate
- Оглавление:
- Java Certificate (Сертификат)
- Получение экземпляра сертификата
- getEncoded()
- getPublicKey()
- getType()
- verify()
- Java CertificateFactory (Фабрика сертификатов
- Создание экземпляра CertificateFactory
- Создание экземпляра Certificate
- Создание экземпляра CertPath
- Java CertPath (Цепочка сертификатов)
- Получение экземпляра CertPath
- getCertificates()
- getType()
- Введение во взаимную аутентификацию сервисов на Java c TLS/SSL
- Введение
- Терминология
- Формат сертификатов
- KeyStore & TrustStore
- Подключение SSL к NettyServer
- Подключение SSL в gRPC / RSocket
- Подключение SSL в Spring Boot для RestController
- Тестирование TLS/SSL
Добавление корневого сертификата в Java
При подключении к Web-кабинету в браузерах Google Chrome и Firefox появляется предупреждение системы безопасности Java о том, что сертификат запускаемого приложения не является доверенным (рис. 1).
Рисунок 1 – предупреждение безопасности Java
Данные сообщения открываются, если корневой сертификат Московской Биржи не добавлен в Справочник Java, независимо от того, внесен ли он в список доверенных сертификатов ОС Windows, или нет (см. раздел Добавление корневого сертификатов в список доверенных). Для добавления корневого сертификат Московской Биржи moex.cer в Справочник Java, необходимо выполнить следующее:
- открыть Панель управления Windows → Java Control Panel → Security (рис. 2,1);
Рисунок 2 – вкладка Безопасность
нажать кнопку Manage Certificates. (рис. 2,2). В результате откроется окно импортирования сертификата (рис. 3);
Рисунок 3 – окно импортирования сертификата
выбрать Secure site CA из раскрывающего списка (см. рис. 3,1) и нажать кнопку Import (см. рис. 3,2). В результате откроется окно (рис. 4), в котором следует перейти в папку где расположен сертификат Московской Биржи, выбрать файл сертификата (рис. 4,1) и нажать кнопку Open (рис. 4,2).
Рисунок 4 – выбор сертификата Московской биржи
Выбранный сертификат отобразится в окне импортирования сертификатов (рис. 5,1);
Рисунок 5 – результат добавления сертификата
Рисунок 6 – вкладка Безопасность
Источник
Криптография в Java. Класс Certificate
Привет, Хабр! Представляю вашему вниманию перевод заключительной статьи «Java Certificate» автора Jakob Jenkov из серии статей для начинающих, желающих освоить основы криптографии в Java.
Оглавление:
Java Certificate (Сертификат)
Класс сертификата (java.security.cert.Certificate) представляет собой сертификат удостоверяющий принадлежность некоторому субъекту, например, пользователю. Экземпляр класса сертификата содержит имя и другие сведения об объекте, который он идентифицирует, а также, возможно, цифровую подпись от центра сертификации (ЦС). Класс Certificate является абстрактным классом, поэтому, вы можете использовать в качестве типа переменной Certificate , а ваша переменная всегда будет указывать на подкласс. Этот класс имеет один подкласс — X509Certificate , который представляет сертификат X.509, использующийся в качестве сертификата в протоколах HTTPS и TLS.
Получение экземпляра сертификата
Вы можете получить экземпляр сертификата следующими способами:
Посмотрите эти два руководства для получения дополнительной информации о получении экземпляра сертификата.
getEncoded()
Метод getEncoded() сертификата возвращает закодированную версию сертификата в виде байтового массива. Например, если сертификат является сертификатом X509, возвращенный байтовый массив будет содержать версию экземпляра сертификата в кодировке X.590 (ASN.1 DER). Вот пример использования метода getEncoded() :
getPublicKey()
Метод сертификата getPublicKey() возвращает открытый ключ этого экземпляра сертификата. Вот пример метода getPublicKey() :
getType()
Метод getType() возвращает тип экземпляра сертификата. Пример getType() :
verify()
Класс сертификата содержит три метода verify() . Эти методы могут использоваться для проверки того, что сертификат действительно подписан с закрытым ключом, соответствующим ожидаемому открытому ключу. Вот пример проверки сертификата:
Метод verify() не возвращает значения. Если проверка не пройдена, будет выдано исключение InvalidKeyException . Если не сгенерировано исключение, экземпляр сертификата можно считать проверенным.
Java CertificateFactory (Фабрика сертификатов
Класс CertificateFactory (java.security.cert.CertificateFactory) способен создавать экземпляры сертификата ( Certificate ) из двоичных данных сертификатов с кодировками X.509 (ASN.1 DER). CertificateFactory также может создавать экземпляры CertPath . CertPath — это цепочка сертификатов, где каждый сертификат подписан следующим сертификатом в данной цепочке.
Создание экземпляра CertificateFactory
Прежде чем вы сможете создавать экземпляры Certificate , вы должны создать экземпляр CertificateFactory . Пример:
В этом примере создается экземпляр CertificateFactory , способный создавать экземпляры сертификата X.509 ( X509Certificate — подкласс Certificate ).
Создание экземпляра Certificate
Создав экземпляр CertificateFactory , вы можете начать создавать экземпляры Certificate . Это делается с помощью вызова метода generateCertificate() . Пример вызова метода generateCertificate() :
Создание экземпляра CertPath
CertificateFactory также может создавать экземпляр CertPath . Экземпляр CertPath создается вызовом метода generateCertPath() :
Java CertPath (Цепочка сертификатов)
Класс CertPath (java.security.cert.CertPath) представляет цепочку сертификатов (объекты Certificate ), где каждый сертификат является цифровым подписывающим лицом следующего сертификата в цепочке. Класс CertPath обычно используется для проверки сертификата личности вместе с сертификатами центров сертификации (ЦС), которые подписали сертификат.
Получение экземпляра CertPath
Обычно экземпляр CertPath получают из фабрики сертификатов ( CertificateFactory или CertPathBuilder ).
getCertificates()
Получив экземпляр CertPath , вы можете получить экземпляры Certificate , из которых состоит CertPath , вызвав метод getCertificates() . Вот пример получения сертификатов из экземпляра CertPath :
getType()
Метод getType() возвращает строку, указывающую, какой тип сертификатов (например, X.509) содержится в этом экземпляре CertPath . Вот пример получения типа CertPath через методом getType() :
Источник
Введение во взаимную аутентификацию сервисов на Java c TLS/SSL
Вопросы авторизации и аутентификации и в целом аспектов защиты информации все чаще возникают в процессе разработки приложений, и каждый с разной степенью фанатизма подходит к решению данных вопросов. С учетом того, что последние несколько лет сферой моей деятельности является разработка ПО в финансовом секторе, в частности, систем расчета рисков, я не мог пройти мимо этого, особенно учитывая соответствующее образование. Поэтому в рамках данной статьи решил осветить эту тему и рассказать, с чем мне пришлось столкнуться в процессе настройки наших приложений.
Введение
Если говорить о формате настройки сертификатов для безопасной передачи данных. Как правило, данные действия производят на каком-либо веб-сервере типа nginx или apache, стоящем на входе во внутреннюю сеть компании и dmz. Благодаря ему можно разделить защищенную внутреннюю сеть и внешнюю сеть интернет. Далее, внутри доверенной сети каждый поступает по-разному. Кто-то считает, что все внутренние сервисы могут взаимодействовать друг с другом без каких-то ограничений, и контроль пользователей управляется уже в GUI посредством логина и пароля для конкретного приложения с разграничением ролей в рамках приложения. Кто-то идет дальше, подключая LDAP и используя логин, пароль пользователя из общего хранилища.
Существуют различные протоколы и технологии типа RADIUS, Kerberos или OAuth/OpenID для работы с вопросами аутентификации. Кто-то использует схемы с базовой аунтефикацией, передавая логин и пароль в base64, кто-то использует JsonWebToken, еще существует возможность использования сертификатов для проверки не только сервера и клиента. В результате получается ситуация, что мы формируем защищенное соединение клиента и сервера, в котором шифруем передаваемые данные и доверяем не только серверу, с которого эти данные забираем, но и знаем о том, кто именно забирает эти данные с нашего сервера, так как он предоставляет клиентский сертификат.
В рамках моей работы в ТехЦентре Дойче Банка мы в обязательном порядке для всех межсервисных взаимодействий используем SSL-сертификаты — даже в UAT окружении. В Java используем JKS, как более привычный контейнер сертификатов и паролей для этой системы.
Причины, почему мы так делаем — большое количество регулирующих органов по всему миру, перед которыми мы должны отчитываться. С одной стороны, это добавляет надежности, но с другой — создает некоторые сложности в разработке и тестировании систем.
Для того чтобы продолжить раскрывать тему, я бы хотел немного вернуться к общей информации о протоколе и инструментах по управлению сертификатами и паролями, а потом уже перейти непосредственно к коду с разбором того, как можно использовать сертификаты для решения подобных задач.
Терминология
Формат сертификатов
В рамках работы с сертификатами обычно используется контейнер PKCS 12 для хранения ключей и сертификатов, но в рамках Java, в дополнение широко используется проприетарный формат JKS (Java KeyStore). Для работы с хранилищем JDK поставляется с консольной утилитой keytool.
Помимо команды, позволяющей создать ключи вместе с keystore, которая выглядит следующим образом:
Есть ряд других команд под катом, которые могут быть полезны в работе с JKS и просто с ключами и сертификатами в Java
- Создание запроса сертификата (CSR) для существующего Java keystore
- Загрузка корневого или промежуточного CA сертификата
- Импорт доверенного сертификата
- Генерация самоподписанного сертификата и keystore
- Просмотр сертификата
- Проверка списка сертификатов в keystore
- Проверка конкретного сертификата по алиасу в keystore
- Удаление сертификата из keystore
- Изменение пароля для keystore
- Экспорт сертификата из keystore
- Список доверенный корневых сертификатов
- Добавление нового корневого сертификата в trustStore
KeyStore & TrustStore
Говоря о JKS, стоит отметить, что данные файлы могут использоваться как KeyStore так и TrustStore. Это два различных типа хранилищ, которые находятся в JKS файлах. Одно из них (KeyStore) содержит более чувствительную информацию типа приватного ключа, и поэтому требует пароля для доступа к этой информации. В противовес чему TrustStore хранит информацию о доверенных сертификатах, которые конечно же присутствуют в операционной системе. Например, для Linux систем мы сможем их найти в /usr/local/share/ca-certificates/
Но так же эти сертификаты идут в поставке Java в файле cacerts, который по умолчанию расположен в директории java.home\lib\security и имеет пароль по умолчанию changeit.
Данная информация может быть полезна в тех случаях, когда установка JDK/JRE осуществляется в компании централизовано из одного источника, и имеется возможность добавления туда своих доверенных сертификатов компании для prod/uat окружения.
Ниже приведена таблица с некоторыми различиями KeyStore & TrustStore.
Keystore | TrustStore |
---|---|
Хранятся ваши приватные ключи и сертификаты (клиентские или серверные) | Хранятся доверенные сертификаты (корневые самоподписанные CA root) |
Необходим для настойки SSL на сервере | Необходим для успешного подключения к серверу на клиентской стороне |
Клиент будет хранить свой приватный ключ и сертификат в keystore | Сервер будет валидировать клиента при двусторонней аутентификации на основании сертификатов в trustStore |
javax.net.ssl.keyStore используется для работы с keystore | javax.net.ssl.trustStore используется для работы с trustStore |
Подключение SSL к NettyServer
При создании нового проекта, подразумевающего взаимодействие клиента и сервера бинарными данными(protobuf) через защищенные вебсокеты (wss), возник вопрос подключения SSL в netty сервер. Как оказалось, это не представляет особых проблем, достаточно в билдере сетевого интерфейса добавить метод .withSslContext(), в который необходимо передать созданный контекст.
Для того что бы сформировать серверный и клиентский SSL-контекст можно использовать один единственный билдер — SslContextBuilder и его методы — forServer и forClient. У этого билдера надо заполнить ряд обязательных полей, такие как trustManager и keyManager. Эти менеджеры мы можем получить из соответствующий фабрик — TrustManagerFactory и KeyManagerFactory.
Синтаксис данных фабрик практически аналогичен с разницей в том, что для trustManager-а мы используем только пароль для самого jks файла,
а для KeyStore при инициализации нам необходимо дополнительно передать пароль от самого ключа.
И в целом это все, что необходимо для добавления SSL в NettyServer.
Подключение SSL в gRPC / RSocket
Если говорить о двунаправленном обмене бинарными данными, современных тенденциях к написанию реактивных приложений, стоит отметить gRPC и RSocket для создания подобных приложений. Но поскольку в основании этих протоколов можно использовать Netty как транспорт, логика конфигурирования останется. Поэтому я не буду уделять этому много внимания.
Подключение SSL в Spring Boot для RestController
Но в мире Java разработки Spring стал де факто стандартом для DI. А вместе с внедрением зависимости люди используют и другие технологии, которые удобно собрать в одном Spring Boot приложении, не расходуя множество времени на конфигурирование всего зоопарка технологий. Конечно же, это приводит к избыточности зависимостей и к увеличению времени загрузки, но упрощает разработку. Куда проще написать аннотацию RestController, чем самому разбираться с тем, как корректно хендлить запросы через сервлеты. А для того, чтобы перенаправить всё взаимодействие через сервлеты в защищённый канал с использованием сертификатов, в Spring Boot есть два пути проcтой и более сложный для кастомных решений.
В первом случае достаточно воспользоваться набором пропертей
И все будет сделано за вас. Либо, если требуется более кастомная конфигурация, поднятие коннекторов на разных портах с нестандартными настройками и так далее, тогда путь лежит в сторону использования интерфейса WebServerFactoryCustomizer, имплементации которого существуют для всех основных контейнеров будь то Jetty, Tomcat или Undertow.
Поскольку это функциональный интерфейс, его довольно просто можно описать через lambda c параметром типа Connector. Для него мы можем выставить флаг setSecure(true), затем заполнить необходимые параметры для ProtocolHandler-а, выставив ему пути до jks c keystore и trustStore и соответствующие пароли к ним. Например, для tomcat код будет выглядеть подобным образом:
И после этого мы отдаем на откуп «магии» спринга перехват всех веб-запросов к нашему сервису, для того чтобы обеспечить безопасность соединения.
Тестирование TLS/SSL
Для того чтобы провести тестирование реализованного безопасного подключения имеется возможность создать keystore программно, используя классы из пакета java.security.* Это даст возможность тестировать различное поведение системы в случае разных ситуаций типа истекших сертификатов, проверки корректной валидации доверенных сертификатов и так далее.
Чтобы грамотно проверить работоспобность придется пройти по всем составным частям jks и воссоздать программно внутри KeyStore пару ключей KeyPair, свой сертификат X509Certificate, цепочку родительских сертификатов, подпись и доверенные корневые сертификаты.
Для упрощения этой задачи можно воспользоваться библиотекой bouncyСastle, которая предоставляет ряд дополнительный возможностей в дополнение к стандартным классам в Java, посвященным криптографии из Java Cryptography Architecture (JCA) и Java Cryptography Extension (JCE).
Некоторые аспекты работы с этой библиотекой присутствуют для kotlin и Java в зеркале их репозитория на github (https://github.com/bcgit/bc-java и https://github.com/bcgit/bc-kotlin).
На верхнем уровне абстракции создание keyStore для целей тестирования может выглядеть следующим образом:
Здесь мы, соответственно, можем увидеть наш доверительный корневой сертификат CA, сертификат cert, который выпущен промежуточным звеном, и нашу пару ключей (приватный и публичный), которые хранятся вместе с сертификатом в поле KeyPair keyPair класса X509Certificate2, расширяющем X509Certificate.
Благодаря билдерам подобного вида, мы легко сможем собрать различные тесткейсы для покрытия всех возможностей подключения к нашей системе.
Соответственно, остается нюанс в непосредственном написании двух билдеров — X509Certificate2Builder и KeyStoreBuilder. Конечно же в java существует java.security.KeyStore.Builder, но он весьма общего плана и имеет единственный ценный метод — newInstance, а хочется чего-то более явного для добавления доверенных сертификатов и приватных ключей. По этой причине был написан свой билдер.
Свой билдер использует в конечном итоге метод setEntry класса KeyStore для единообразного добавления сущностей доверенных сертификатов и приватных ключей, используя различные имплементации типа Entry (TrustedCertificateEntry или PrivateKeyEntry).
И поскольку KeyStore#setEntry имеет сигнатуру setEntry(String alias, Entry entry, ProtectionParameter protParam) с 3 параметрами, мы их можем объединить в один класс item и в итоге в методе KeyStoreBuilder#build() останется лишь следующий код:
А при добавлении сущностей в entries мы будем использовать сигнатуру аналогичную KeyStore#setEntry, но публичным интерфейсом, использующим этот setEntry будут более понятные методы addPrivateKey
и метод addTrustedCertificate,
которые мы использовали выше при генерации keyStore.
C билдером X509 сертификата дела обстоят чуть сложнее, поскольку основная часть логики там будет сосредоточена в методе build(). Чтобы не загромождать статью болейрплейт кодом сеттеров, которые просто устанавливают значения полей билдера, я сразу перейду к реализации метода build() для X509Certificate2, опустив методы, связанные с установкой значений в билдер, и использую вместо них локальные переменные:
Все недостающие в стандартной библиотеке классы импортированы из bouncycastle.
В начале работы необходимо проинициализировать провайдер bouncycastle, если это еще не было сделано ранее.
Пара ключей генерируется с использованием java.security.KeyPairGenerator, который позволяет создать ключи с заданный алгоритмом и хеш-функцией. В данном примере был использован RSA c SHA1PRNG.
Далее мы объявляем поля, необходимые для сертификата, такие как даты начала и окончания, эмитент и серийный номер.
Затем мы добавляем расширения для сертификата, описывающие субъект и указание корневого сертификата, подписавшего его. В конце концов, получаем сертификат.
Инициализируя различным способом исходные параметры типа сроков действия сертификата, списка доверенных сертификатов и различных алгоритмов, мы сможем проверить корректность работы нашей системы в различных ситуациях.
Ценностью таких операций является более прозрачное понимание работы системы в случаях, когда проблемы возникают до фактического получения запроса сервером на этапе установки рукопожатия, как следствие мы сможем более оперативно разбираться с возникающими ситуациями.
В результате, например, для проверки безопасного соединения в Spring boot приложении без использования стандартного пути с пропертями достаточно будет создать шаблонное приложение с вебом, например, через Spring Initializr и
Источник