Использование ключа КриптоПро c криптопровайдером Bouncy Castle для создания ЭЦП

в 16:46, , рубрики: java, информационная безопасность, криптография, криптопро, эцп

Задача: реализовать протокол обмена данными с контрагентом на основе SOAP с подписью ГОСТ и ключом КриптоПро. Java, Linux.

Скачиваю КриптоПро CSP. Устанавливаю. Перезагружаясь в черный экран, понимаю, что КриптоПро убил мою рабочую Win7. Пустяки, дело житейское. Пока восстанавливается система рассматриваю изображенного на их логотипе проколотого насквозь Пакмена, извергающего кровавый фонтан.
Уставливаю VirtualBox с XP, на него КриптоПро CSP — работает. Но мне перспектива переносить разработку на виртуальную машину, потом установить КриптоПро каким-то образом на боевой linux и попытаться со всем этим взлететь, показалась совсем не радужной.
Благо есть прекрасная библиотека BouncyCastle, которая поддерживает ГОСТ. Осталось дело за малым — достать ключ из контейнера КриптоПро.
Использование ключа КриптоПро c криптопровайдером Bouncy Castle для создания ЭЦП

Экспорт средствами Windows дает файл, который можно сразу выбросить, т.к. перевести его в нужный формат не получится. Поиск выводит на утилиту P12FromGostCSP, которая умеет экспортировать сертификат и ключ в контейнер PKCS#12. Вроде бы то, что нужно. Но не тут то было. BouncyCastle отказался работать с таким контейнером.
Ладно,— думаю. Значит надо достать из PKCS#12 ключ и перевести его в нужный формат. Для этого как нельзя лучше подходит OpenSSL с поддержкой ГОСТ.

В конфигурационный файл openssl.cfg необходимо добавить

openssl_conf = openssl_def

[openssl_def]
engines = engine_section

[engine_section]
gost = gost_section

[gost_section]
engine_id = gost
dynamic_path = C:/OpenSSL-Win32/bin/gost.dll
default_algorithms = ALL
CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet

изменив путь к gost.dll на свой.

Экспортирую приватный ключ

openssl pkcs12 -in yourP12File.p12 -nocerts -out test.key

Пытаюсь скормить его BouncyCastle (BC), который радостно сообщает, что я ему опять какую-то фигню подсовываю, а вовсе даже и не гостовый ключ.

Ладно, ладно.
Использование ключа КриптоПро c криптопровайдером Bouncy Castle для создания ЭЦП

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

Security.addProvider(new BouncyCastleProvider());

KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("ECGOST3410", "BC");
keyPairGenerator.initialize(new ECGenParameterSpec("GostR3410-2001-CryptoPro-A"));
KeyPair keyPair = keyPairGenerator.generateKeyPair();

PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyPair.getPrivate().getEncoded());
FileOutputStream fos = new FileOutputStream("c:\test\template.key");
fos.write(pkcs8EncodedKeySpec.getEncoded());
fos.close();

Созданный BC ключ послужит в качестве шаблона.
Для просмотра структуры ключа использую ASN1Editor. Запускаю два экземпляра ASN редактора, в которых открываю test.key и template.key.

Использование ключа КриптоПро c криптопровайдером Bouncy Castle для создания ЭЦП

Использование ключа КриптоПро c криптопровайдером Bouncy Castle для создания ЭЦП

Из структуры test.key копирую параметры ключа в template.key, а также сам ключ. Здесь меня ждал еще один сюрприз. В начало ключа КриптоПро добавлен еще один байт. Копировать нужно только 32 байта, без первого.

Расшифровка параметров ключа

1.2.643.2.2.35.1 — szOID_GostR3410_2001_CryptoPro_A_ParamSet
Параметры a, b, p,q, (x,y) цифровой подписи и алгоритма Диффи-Хеллмана на базе алгоритма ГОСТ Р 34.10-2001, вариант криптопровайдера
1.2.643.2.2.35.2 — szOID_GostR3410_2001_CryptoPro_B_ParamSet
Параметры a, b, p,q, (x,y) цифровой подписи и алгоритма Диффи-Хеллмана на базе алгоритма ГОСТ Р 34.10-2001, вариант карты КриптоРИК
1.2.643.2.2.35.2 — szOID_GostR3410_2001_CryptoPro_C_ParamSet
Параметры a, b, p,q, (x,y) цифровой подписи и алгоритма Диффи-Хеллмана на базе алгоритма ГОСТ Р 34.10-2001, вариант 1
1.2.643.2.2.36.0 — szOID_GostR3410_2001_CryptoPro_XchA_ParamSet
Параметры a, b, p,q, (x,y) алгоритма Диффи-Хеллмана на базе алгоритма ГОСТ Р 34.10-2001, вариант криптопровайдера. Используются те же параметры, что и с идентификатором szOID_GostR3410_2001_CryptoPro_A_ParamSet
1.2.643.2.2.35.3 — szOID_GostR3410_2001_CryptoPro_XchB_ParamSet
Параметры a, b, p,q, (x,y) цифровой подписи и алгоритма Диффи-Хеллмана на базе алгоритма ГОСТ Р 34.10-2001, вариант 1

Сохраняю ключ template.key под другим именем. Теперь BC не ругается и создает ЭЦП. Но ЭЦП не проходит проверку. Да что же это такое?!

Использование ключа КриптоПро c криптопровайдером Bouncy Castle для создания ЭЦП

Выяснилось, что ЭЦП нужно еще и развернуть!

Security.addProvider(new BouncyCastleProvider());

Signature s = Signature.getInstance("ECGOST3410", "BC");

InputStream inputStream = Utils.getResourceAsStream(pathToPrivateKey);
byte[] encodedPrivateKey = IOUtils.toByteArray(inputStream);

KeyFactory keyFactory = KeyFactory.getInstance("ECGOST3410", "BC");

PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
BCECGOST3410PrivateKey pKey = (BCECGOST3410PrivateKey) keyFactory.generatePrivate(privateKeySpec);

s.initSign(pKey);

s.update(xml.getBytes());

byte[] sigBytes = s.sign();
ArrayUtils.reverse(sigBytes);
String signature = Base64.encode(sigBytes).replaceAll("n", "");

Интересно, с чем связаны все эти различия в структуре ключей и алгоритме формирования ЭЦП?

Автор: state13

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js