Контекст

Эта публикация – практический кейс c кратким туториалом по реализации TLS-соединения, на основе российских стандартов шифрования, основанных на компонентах КриптоПро (CP). Статья не содержит рекламы или попытки сподвигнуть к выбору конкретных компонентов. Я столкнулся с задачей реализации шифрованного подключения к системам бюро кредитных историй (БКИ) в части оценки платежеспособности физических лиц. У меня было ограничение - необходимо было использовать КриптоПро (CP). В процессе реализации задачи открыл для себя несколько интересных моментов. Процесс их "открытия" был чарующим и загадочным. Чары мне обеспечил CP, а с загадками помогали службы поддержки БКИ и CP. Этой статьей хочу помочь сэкономить время и сделать прозрачным процесс реализации шифрованного подключения. В начале статьи дам небольшой теоретический экскурс. Если Вас интересуют детали реализации, то сразу переходите к разделу "Установка и настройка CP компонентов", код, приведенный в статье выложен тут. Приятного чтения.

Используемые технологии

Технологический стек:

  • Java 17;

  • Spring Boot 3;

  • Maven;

  • JCP (компонент CP);

  • JCSP (компонент CP);

Какую пользу принесет эта статья

Статья написана для тех, кому необходимо/хочется:

  • Кратко и быстро погрузиться в аспекты шифрованного подключения;

  • Реализовать задачу шифрованного подключения на основе российских стандартов;

  • Использовать компоненты CP;

Про задачу

Задача состоит в реализации интеграции с сервисом проверки кредитного рейтинга физических лиц. Интеграция реализуется на SOAP и должна поддерживать шифрованное подключение на основе российских криптографических стандартов по ГОСТ 28147-89 и ГОСТ 34.12-2015. Сама по себе интеграция не содержит чего-то интересного, чем хотелось бы поделиться. Шифрование на основе криптографических стандартов задача не сложная, но интересная. Сконцентрируемся на ней. 

Варианты

Немного порывшись в недрах habr и интернет, сделал вывод, что реализация шифрования на основе криптографических ГОСТ возможна 2 вариантами:

  • bouncy castle:

    • open source проект, поддерживающий шифрование для Java и C#;

    • после ознакомления с проектом становится понятно, что разработчики CP поучаствовали в развитии проекта, имплементировав в него ГОСТ-алгоритмы. Так они реализовали возможность использовать bouncy castle в CP компонентах; 

  • СryptoPro:

    • компания, которая развивает собственные компоненты для выполнения криптографических операций - создание, проверка ЭЦП, шифрование, генерация ключей и пр.

С технической точки зрения оба подхода к реализации ГОСТ-шифрования хорошо разобраны тут в блоках "Почему КриптоПро JCP добро" и "Почему КриптоПро JCP зло" с поправкой на то, что на Java 17 и на Linux CP запускается без проблем. Выбор конкретного варианта для большой компании зависит не только от технических аспектов, но и от удобства сопровождения, возможности поддержки, и т.д. В АльфаСтрахование выбор был сделан в сторону CP. Основная причина такого выбора - возможность оперативной квалифицированной поддержки. 

CP компоненты

Как ранее было упомянуто, мне понадобились компоненты JCP и JCSP.

  • JCP:

    • Набор библиотек криптографической защиты информации, реализующий российские криптографические стандарты;

    • Подробнее;

  • JCSP

    • Набор библиотек реализующих:

      • Формирование и проверку электронной подписи;

      • Шифрование;

      • Другие функции;

    • Подробнее;

Для использования компонентов вам потребуется их активировать. Сначала работать можно на демо лицензиях (раскрою тему ниже). Затем все необходимое нужно будет закупить.

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

Создавая эту статью, я руководствовался, в том числе задачей сделать самодостаточное описание, которое будет содержать необходимую и достаточную теорию. В этом блоке я кратко опишу основные принципы и особенности работы TLS/SSL. Те, кто хорошо знаком с темой, что это, зачем оно, и как работает - могут переходить к блоку "Установка и настройка CP компонентов". Если тема TLS/SSL не до конца вам понятна или вызывает интерес, то продолжаем.

Общее

Сетевые устройства реализуют сетевую модель подключения OSI. Один из элементов OSI - уровни сетевого взаимодействия. Эти уровни предлагают модель ответственности сетевых компонентов при взаимодействии внутри сети. Безопасное соединение осуществляется на основе TLS/SSL технологии, которая рассредоточена по транспортному, сеансовому и уровню представления информации. TLS/SSL, по сути - это протоколы безопасной передачи данных по небезопасным сетям:

  • TLS (Transport Layer Security) - протокол защиты транспортного уровня;

    • Фактический стандарт в области шифрования подключения, признанный IETF (Internet Engineering Task Force) с 1999 года

  • SSL (Secure Sockets Layer) - уровень защищенных сокетов;

    • Устаревшая технология, которая была основой для разработки TLS; 

TLS

3 основных аспекта передачи данных, реализуемых TLS:

  • Приватность данных - данные не должен читать тот, кто не авторизован - обеспечивается шифрованием;

    • Шифрование связано с конкретным протоколом, по которому передаются данные;

  • Целостность - изменять данные не должен тот, кто не авторизован - обеспечиваются хэш-функциями;

    • Хэш-функция однозначно идентифицирует конкретный передаваемый артефакт;

  • Аутентификация - сервер именно тот, за кого он себя выдает - обеспечивается идентификацией;

    • Цифровая подпись, инфраструктура открытых ключей;

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

Шифрование

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

2 основных типа шифрования:

  • Симметричное:

    • Для шифрования и рас-/дешифрования используется один и тот же ключ:

      • Обозначение - разделяемый ключ;

      • Шифровщик/дешифровщик не должны знать значение ключа;

      • Пример симметричного шифрования - шифр цезаря;

      • Распространенные алгоритмы симметричного шифрования - AES, 3DES, RC4, RC5, RC6;

  • Ассиметричное:

    • Для шифрования и рас-/дешифрования используется разные ключи:

      • Открытый ключ передается. Используется шифрующей стороной;

      • Закрытый ключ скрыт. Используется расшифровывающей стороной;

      • Распространенный алгоритмы ассиметричного шифрования - RSA, DSA. DSS;

Каждый вид шифрования имеет свои преимущества и недостатки (Таб.1)

Вид шифрования

Преимущества

Недостатки

Симметричное

Работает быстрее, чем ассиметричное

Ключ должен храниться в тайне

Ассиметричное

Открытый ключ может передаваться публично

Работает медленней, чем ассиметричное

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

Что именно использовали

JCP, JCSP компоненты разработаны в соответствии со спецификацией JCA (Java Cryptography Architecture). JCA поддерживает алгоритм AES.

Если кратко:

  • AES - состоит из нескольких раундов, в каждом из которых над данными совершаются различные операции;

  • Над данными последовательно выполняются - замены (SubBytes), сдвиги строк (ShiftRows), смешивание столбцов (MixColumns) и добавление ключа раунда (AddRoundKey);

  • AES начинается с инициализации начального ключа и данных, которые затем проходят через серию раундов - обычно 10, 12 или 14, в зависимости от длины ключа;

Если подробно про AES.

Установка и настройка CP компонентов

Для того, чтобы у вас была возможность работать с CP-компонентами, необходимо скачать и установить себе соответствующий дистрибутив. Хорошая новость состоит в том, что можно установить себе все необходимое и работать с компонентами без ввода лицензий в течении 90 дней (демо лицензии). Это удобно. Установка не составляет труда. В итоге Вы получаете себе GUI приложение (Рис.1).

Рис.1
Рис.1

Выбранный нами способ работы не потребует больше работать с GUI (почти). Установленный дистрибутив позволит нам использовать CP.

Используемые компоненты и их настройка

У меня на руках были следующие артефакты:

  • Цепочка CER-сертификатов, выданных мне коллегами из БКИ:

    • CER-сертификаты - это сертификаты сервера:

    • Назначение - безопасное соединение между клиентом и сервером;

  • PFX-сертификат, выданный мне коллегами из департамента информационной безопасности:

    • PFX (Personal Exchange Formatб PKCS#12) сертификаты содержат частный ключ и соответствующий ему открытый ключ, защищенные паролем;

    • Назначение - безопасное соединение между клиентом и сервером;

Работа по использованию этих сертификатов для установки безопасного соединения будет описана дальше.

Импорт CER-сертификатов

CER-сертификаты являются разновидностью CRT-сертификатов. Для их использования под Windows вам нужно:

  • Перейти в следующую директорию, где располагается Ваша JVM - "[Диск]:\[Пусть до Java]\Java\jdk-17\bin"

  • В этой папке среди прочих находится утилита keytool.exe:

    • Назначение - работа с ключами безопасности и цифровыми сертификатами;

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

  • Из этой папки нужно выполнить следующие команды:

    • Команда →  keytool -import -keystore [путь до cacerts Java. Если в пути есть пробелы, то надо обрамить ""] -alias [название сертификата при импорте] -file [путь до файла с сертификатом] -storepass changeit -noprompt;

    • Примерkeytool -import -keystore "C:\Program Files\Java\jdk-17\lib\security\cacerts" -alias "name" -file c:\Users\user\Desktop\service\сертификаты\XXX_XXX.cer -storepass changeit -noprompt;

    • Так Вы импортируете для себя сертификат, который будет использоваться как часть TLS;

    • Рекомендую после того, как Вы выполните импорт, проверить, что сертификат был добавлен в cacerts ->  "keytool -v -list -cacerts storepass changeit" и поискать сертификат по alias;

      • Keytool может вернуть успешный ответ, но не добавить сертификат из-за проблем с параметрами команды;

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

Работа с PFX-сертификатами

PFX содержит сертификат и соответствующий ему секретный ключ. Этот формат используется, чтобы передать содержимое сертификата на конкретное рабочее место. Вот тут про него хорошо и подробно написано. Можно создать и установить сертификат, а так же секретный ключ с помощью утилит управления ключами, следом экспортировать в PFX и импортировать полученный файл в другое хранилище сертификатов. Как вариант, вы можете импортировать этот сертификат себе на рабочее место в реестр сертификатов. В моем случае я имею сформированный и подготовленный сертификат и мне надо добавить его к TLS-компонентам. PFX-компонент может быть подготовлен и установлен на ПО с помощью любого приложения, но именно для использования его вместе с CP, вам потребуется использовать cptools. Выгружаете себе сертификат через экспорт ключей и получаете все необходимое для того, чтобы двинуться дальше (Рис.2). В моем случае, кнопка недоступна. Увы, нет прав.

Рис.2
Рис.2

PFX, как наиболее изменчивая часть нашей интеграции, будет браться из папки ресурсов сервиса. Для активации нам еще понадобится пароль. Сохраним его в корпоративном хранилище данных (в моем случае это vault) и будем использовать в тестовой и продуктивной среде.

Рабочий проект

Подготовка компонентов

Первым шагом поместим в репозиторий артефактов (в моем случае это nexus) библиотеки JCP. Они будут реализовывать шифрование. Для начала скачаем дистрибутивы JCP c сайта CP. Так как я реализовал все на Java 17, то мне нужны дистрибутивы с литерой "-A" в конце названия. Из jcsp понадобятся следующие библиотеки:

  • JCP;

  • JCPRevTools;

  • JCPRevCheck;

  • asn1rt;

  • ASN1P;

  • JCSP;

  • sspiSSL;

Каждую из них нужно будет сложить локально/в nexus для разработки и сборки приложения в тестовом и продуктивном окружениях. С помощью maven команды помещаем библиотеки в nexus:

- Команда:
mvn deploy:deploy-file 
-DgroupId=[указать группу, куда кладем] 
-DartifactId=[название] 
-Dversion=[версия] 
-Dpackaging=[тип упаковывания] 
-Dfile=[откуда берем] 
-DrepositoryId=nexus 
-Durl=[url куда кладем]

- Пример:
mvn deploy:deploy-file -DgroupId=ru.crypto 
-DartifactId=cpSSL 
-Dversion=5.0.45549-A 
-Dpackaging=jar 
-Dfile=/c:/Users/user/Desktop/service/java-csp-jcsp.version/nexus/cpSSL.jar 
-DrepositoryId=nexus 
-Durl=https://alfastrah.alfastrah.ru/repository/odm-maven-releases

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

        <properties>
        	<jcsp.version>5.0.45549-A</jcsp.version>
    	</properties>
..........................................
		<dependency>
            <groupId>ru.crypto</groupId>
            <artifactId>JCP</artifactId>
            <version>${jcsp.version}</version>
        </dependency>
        <dependency>
            <groupId>ru.crypto</groupId>
            <artifactId>JCPRevCheck</artifactId>
            <version>${jcsp.version}</version>
        </dependency>
        <dependency>
            <groupId>ru.crypto</groupId>
            <artifactId>JCPRevTools</artifactId>
            <version>${jcsp.version}</version>
        </dependency>
        <dependency>
            <groupId>ru.crypto</groupId>
            <artifactId>ASN1P</artifactId>
            <version>${jcsp.version}</version>
        </dependency>
        <dependency>
            <groupId>ru.crypto</groupId>
            <artifactId>asn1rt</artifactId>
            <version>${jcsp.version}</version>
        </dependency>
        <dependency>
            <groupId>ru.crypto</groupId>
            <artifactId>JCSP</artifactId>
            <version>${jcsp.version}</version>
        </dependency>
        <dependency>
            <groupId>ru.crypto</groupId>
            <artifactId>sspiSSL</artifactId>
            <version>${jcsp.version}</version>
        </dependency>

Реализация

Чтобы реализовать TLS-шифрование нам потребуется собрать весь необходимый контекст и имплементировать его при подключении к серверу. Реализация контекста займет 6 компактных классов (Рис.3), логически разделенных по функционалу:

  • Подготовка хранилищ сертификатов;

  • Конфигурация сертификатов и ключей;

  • Подготовка TLS-контекста;

Рис.3
Рис.3

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

Подготовка хранилищ

Начнем с получения доверенных сертификатов, добавленных нами ранее в cacerts. Для этого мы будем использовать TrustManager. Этот компонент используется для проверки подлинности сертификатов, которыми обмениваются клиент и сервер при установлении защищенного соединения. TrustManager позволяет задавать политики безопасности, которые определяют, каким сертификатам можно доверять, а каким - нет. У нас получится такой конфигурационный компонент Spring:

@Slf4j
@Configuration
public class CertConfig {

    /**
     * Назначение ->
     * Получение списка доверительных сертификатов 
     * из хранилища сертификатов caserts (Java);
     */
    public List<X509Certificate> getCertsFromCacerts() {
        TrustManagerFactory trustManagerFactory;
        try {
            trustManagerFactory = 
              TrustManagerFactory.getInstance(
              TrustManagerFactory.getDefaultAlgorithm()
            );
            trustManagerFactory.init((KeyStore) null);
        } catch (NoSuchAlgorithmException | KeyStoreException exception) {
            log.error(
                    "CertConfig. Ошибка при инициализации trustManager для : {}",
                    exception.getMessage()
            );
            throw new CertException(exception.getMessage());
        }

        List<TrustManager> trustManagers = 
          Arrays.asList(trustManagerFactory.getTrustManagers());
        return trustManagers.stream()
                .filter(X509TrustManager.class::isInstance)
                .map(X509TrustManager.class::cast)
                .map(trustManager -> Arrays.asList(trustManager.getAcceptedIssuers()))
                .flatMap(Collection::stream)
                .toList();
    }

}

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

Рис.4
Рис.4

а его обработку реализуем в синглотоне с инстансом хранилища ключей в виде конфигурационного компонента, использующего объект с свойствами - PFX (пароль):

@Slf4j
@Configuration
@EnableConfigurationProperties(value = Pfx.class)
@RequiredArgsConstructor
public class PFXStore {

    private KeyStore keyStore;
    private final Pfx pfx;

    /**
     * Назначение ->
     * Хранилище pfx сертификата;
     */
    public KeyStore getKeyStore() {
        if (keyStore != null)
            return keyStore;
        try {              
			byte[] certByteArray = 
              new ClassPathResource("certs/cert.pfx").getContentAsByteArray();
            ByteArrayInputStream is = new ByteArrayInputStream(certByteArray);
            keyStore = KeyStore.getInstance("PFXSTORE");
            keyStore.load(is, pfx.passwordPFX().toCharArray());

        } catch (
                KeyStoreException | 
                IOException | 
                NoSuchAlgorithmException | 
          CertificateException exception) {
            log.error(
                    "Store. Ошибка при подготовке хранилища ключей: {}",
                    exception.getMessage()
            );
            throw new PXFStoreException(exception.getMessage());
        }
        return keyStore;
    }

}

Из хранилищ нам осталось подготовить CP-компоненты. Для этого потребуется инициализировать хранилище с типом CP и загрузить в него ранее полученные сертификаты cacerts:

@Slf4j
@Configuration
@RequiredArgsConstructor
public class JcpStore {

    private final CertStore certStore;

    /**
     * Назначение ->
     * Хранилище гост сертификатов в хранилище на основе JCP;
     */
    public KeyStore prepareKeyStoreWithJcpCert() {

        KeyStore keyStore;

        try {
            keyStore = KeyStore.getInstance(JCP.CERT_STORE_NAME);
        } catch (KeyStoreException exception) {
            log.error(
                    "JcpKeyStore. Ошибка при подготовке JCP хранилища: {}",
                    exception.getMessage()
            );
            throw new JcpKeyStoreException(exception.getMessage());
        }

        try {
            keyStore.load(null, null);
        } catch (
          IOException | 
          NoSuchAlgorithmException | 
          CertificateException exception) {
            log.error(
                    "JcpKeyStore. Ошибка при инициализации JCP хранилища: {}",
                    exception.getMessage()
            );
            throw new JcpKeyStoreException(exception.getMessage());
        }

        try {
            for (X509Certificate cert : certStore.getCertsFromCacerts()) {
                keyStore.setCertificateEntry(UUID.randomUUID().toString(), cert);
            }
        } catch (KeyStoreException exception) {
            log.error(
                    "JcpKeyStore. Ошибка при добавлении гост сертификатов : {}",
                    exception.getMessage()
            );
            throw new JcpKeyStoreException(exception.getMessage());
        }
        return keyStore;
    }
}

Подготовка конфигураций

Сконфигурируем массив доверенных ГОСТ сертификатов, которые понадобятся для установки безопасного соединения:

@Slf4j
@Configuration
@RequiredArgsConstructor
public class TrustManagerConfig {

    private final JcpStore jcpKeyStore;

    /**
     * Назначение ->
        * Массив доверительных сертификатов гост;
     */
    public TrustManager[] getGostTrustManager() {

        KeyStore keyStore =
                jcpKeyStore.prepareKeyStoreWithJcpCert();

        TrustManagerFactory factory;
        try {
            factory = TrustManagerFactory.getInstance("GostX509");
            factory.init(keyStore);
        } catch (NoSuchAlgorithmException | KeyStoreException exception) {
            log.error(
                    "TrustManager. Ошибка при инициализации trustManager: {}",
                    exception.getMessage()
            );
            throw new TrustManagerException(exception.getMessage());
        }
        return factory.getTrustManagers();
    }
}

Затем сформируем список доступных ключей по ГОСТ сертификатам, которые будут объединены в рабочие цепочки. Инициируем CP-компонент (JavaTLSCertPathManagerParameters), который используется для проверки доверия к сертификатам. Добавим к нему подготовленные цепочки сертификатов (PKIX). Нам потребуются собранные из cacerts сертификаты и подготовленное pfx хранилище:

@Slf4j
@Configuration
@RequiredArgsConstructor
public class KeyManagerConfig {

    private final CertStore certStore;
    private final PFXStore pfxStore;
    private final JcpStore jcpKeyStore;

    /**
     * Назначение ->
     * Формирование списка доступных ключей по гост сертификатам;
     */
    public KeyManager[] getGostKeyManagers() {

        KeyManagerFactory factory;
        try {
            factory = KeyManagerFactory.getInstance("GostX509");
        } catch (NoSuchAlgorithmException exception) {
            log.error(
                    "KeyManager. Ошибка при попытке подготовить контейнера: {}",
                    exception.getMessage()
            );
            throw new KeyManagerException(exception.getMessage());
        }

        PKIXBuilderParameters parameters;
        try {
            parameters = new PKIXBuilderParameters(
                    jcpKeyStore.prepareKeyStoreWithJcpCert(),
                    new X509CertSelector());
        } catch (KeyStoreException | InvalidAlgorithmParameterException exception) {
            log.error(
                    "KeyManager. Ошибка постороения цепочек сертификации X.509: {}",
                    exception.getMessage());
            throw new KeyManagerException(exception.getMessage());
        }

        parameters.setRevocationEnabled(true);
        try {
            parameters.setCertStores(
                    Collections.singletonList(
                            java.security.cert.CertStore.getInstance(
                                    TYPE_COLLECTION,
                                    new CollectionCertStoreParameters(
                                            certStore.getCertsFromCacerts()))));
        } catch (
                InvalidAlgorithmParameterException | 
                NoSuchAlgorithmException exception) {
            log.error(
                    "KeyManager. Ошибка при проверки сертификатов X.509 на отзыв: {}",
                    exception.getMessage());
            throw new KeyManagerException(exception.getMessage());
        }

        JavaTLSCertPathManagerParameters managerParameters =
                new JavaTLSCertPathManagerParameters(
                      pfxStore.getKeyStore(), EMPTY.toCharArray()
        );

        managerParameters.setParameters(parameters);
        try {
            factory.init(managerParameters);
        } catch (InvalidAlgorithmParameterException exception) {
            log.error(
                    "KeyManager. Ошибка при проверке доверия сертификатам TLS: {}",
                    exception.getMessage());
            throw new KeyManagerException(exception.getMessage());
        }
        return factory.getKeyManagers();
    }
}

Подготовка контекста

Остался последний (почти) шаг. Формирование TLS-контекста. Здесь потребуются подготовленные конфигурации. Несмотря на устаревший термин SSL для наименований в классах javax.net используется именно он. При инициировании контекста зададим актуальный протокол "GostTLSv1.3" и добавим настройку включения контекста по подготовленным нами конфигурациям:

@Slf4j
@Configuration
@RequiredArgsConstructor
public class SSLContextConfig {

    private final TrustManagerConfig trustManagerConfig;
    private final KeyManagerConfig keyManagerConfig;

    /**
     * Назначение ->
     * Контекст с готовыми сертификатами и подключениями для 
     * установки безопасного соединения;
     */
    public SSLContext getInstance(boolean isContext) {

        SSLContext context;

        try {
            context = SSLContext.getInstance("GostTLSv1.3");
            context.init(
                    isContext ? keyManagerConfig.getGostKeyManagers() : null,
                    trustManagerConfig.getGostTrustManager(),
                    null);
        } catch (NoSuchAlgorithmException | KeyManagementException exception) {
            log.error(
                    "SSLContext. Ошибка при формировании SSL контекста: {}",
                    exception.getMessage()
            );
            throw new SSLContextException(exception.getMessage());
        }
        return context;
    }

}

Имплементация контекста

Теперь точно все. При подключении к серверу БКИ зададим наш контекст. Он определяется через HttpClient, который я буду использовать в WebServiceTemplate:

@Configuration
@RequiredArgsConstructor
public class HttpClientConfig {

    private final SSLContextConfig contextConfig;

    public HttpClient prepareHttpClient() {

        return HttpClients.custom()
                // set custom context for cryptoPro crypt traffic
                .setSSLContext(contextConfig.getInstance(true))
                .build();
    }

}

...

@Configuration
public class WebServiceTemplateConfiguration {

    private final HttpClientConfig httpClientConfig;
    private final WebServiceTemplate webServiceTemplate;
    ...

    public WebServiceTemplateConfiguration(HttpClientConfig httpClientConfig, ) {
        this.httpClientConfig = httpClientConfig;
        this.webServiceTemplate = new WebServiceTemplate();
    }

    public WebServiceTemplate 
            prepareWebServiceTemplateForServiceIncomeSend(String uri) {
        messageSender.setHttpClient(httpClientConfig.prepareHttpClient());
        webServiceTemplate.set...;
        return webServiceTemplate;
    }

}

Настраиваем свой клиент: добавляем маршаллер, определям uri и т.д. Все! Мы имеем готовое подключение по TLS на основе отечественных алгоритмов ГОСТ-шифрования. Приятного использования.

Что еще будет полезно?

Оцените свои потребности

Я описал основные действия, которые требуются для настройки TLS соединения с помощью компонентов CP. Так вы получаете решение, которое будет формировать контекст используя сертификаты, помещенные в cacerts Java и ресурсные папки проекта. Это универсальное масштабируемое решение. Для его реализации вам нужно будет купить лицензии на компоненты CP. В моем случае было достаточно лицензии на 1 процессор. Если Вы планируете использовать сервис под высокими нагрузками, то имеет смысл посмотреть в сторону более производительных лицензий, информацию о которых можно найти на сайте CP.

Запуск сервиса

Обратите, пожалуйста, внимание на то, что для запуска функционала CP, в main основного application класс требуется добавить немного статических заклинаний, которые отвечают за активацию атрибутов, критичных для функционирования CP компонентов.

Логирование

В процессе разработки и отладки может потребоваться добавить логирование компонентов CP, которое не работает из коробки. Для этого потребуется:

  • Добавить настройки логирования в JVM;

  • Настроить логгер файлы/компоненты, ответственные за логирование Вашего сервиса;

Настройки логирования JVM заключаются в том, что вам надо в файл - "logging.properties.src", который находится по адресу: "С:\[Путь до Java]\Java\jdk-17\lib\", добавить следующее:

ru.CryptoPro.JCP.tools.JCPLogger.level = ALL
ru.CryptoPro.ssl.SSLLogger.level=ALL
ru.CryptoPro.ssl.SSLLogger.handlers=java.util.logging.ConsoleHandler

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

Вторым действием надо пробросить логирование в свой сервис. Для меня это logback и в нем добавленные разрешения выглядят так:

<logger name="ru.CryptoPro.ssl.SSLLogger" level="ALL" />
<logger name="ru.CryptoPro.ssl.SSLLogger" handlers="java.util.logging.ConsoleHandler" />
<logger name="java.util.logging.ConsoleHandler" level="ALL" />

Доступы

После того как разработка будет закончена, логично задуматься о подготовке тестового и прод окружений. От вас потребуется дать доступ к ресурсам CP, на которых проверяется цепочка сертификатов на предмет отзыва/валидности. Если этого не сделать, при попытке выполнить запрос вы будете получать ошибку на невалидность сертификатов. В моем случае доступ потребовался до:

URIName: http://cdp.cryptopro.ru/ra/cdp/...
URIName: http://vpnca.cryptopro.ru/cdp/...

Полезные ссылки

О чем

Формат

Длительность

Ссылка

1

TLS/SSL. Общая информация о защите сетевых протоколов

Видео

~ 20 минут

Тут

2

TLS/SSL. Шифрование

Видео

~ 20 минут

Тут

Вместо завершения и ссылка на репозиторий

Проект готов. Им можно пользоваться. Успехов в освоении новых горизонтов.

Благодарности

Огромное спасибо поддержке CP. Ребята помогли мне разобраться в новой для меня области и приложили усилия для того, чтобы общение было результативным. В качестве консультанта мне попался Санчир Момолдаев. Спасибо тебе, Санчир. Юра, Костя, Дима и Никита терпеливо помогли мне с содержанием и формой этого текста. Спасибо команде за поддержку и задачи.

Ссылка на репозиторий

Репозиторий с обезличенным проектом тут.

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


  1. amnik21
    26.06.2024 08:54

    Интересная статья, спасибо!