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

Задача: реализовать протокол обмена данными с контрагентом на основе SOAP с подписью ГОСТ и ключом КриптоПро. Java, Linux.Скачиваю КриптоПро CSP. Устанавливаю. Перезагружаясь в черный экран, понимаю, что КриптоПро убил мою рабочую Win7. Пустяки, дело житейское. Пока восстанавливается система рассматриваю изображенного на их логотипе проколотого насквозь Пакмена, извергающего кровавый фонтан.Уставливаю VirtualBox с XP, на него КриптоПро CSP — работает. Но мне перспектива переносить разработку на виртуальную машину, потом установить КриптоПро каким-то образом на боевой linux и попытаться со всем этим взлететь, показалась совсем не радужной.Благо есть прекрасная библиотека BouncyCastle, которая поддерживает ГОСТ. Осталось дело за малым — достать ключ из контейнера КриптоПро.c8c1fcc4bf0fd4cd722170e91626afcb.jpgЭкспорт средствами 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), который радостно сообщает, что я ему опять какую-то фигню подсовываю, а вовсе даже и не гостовый ключ.

Ладно, ладно.01abbe69923a6db5c5e6d248d013eddc.jpg

Создаю приватный ключ средствами 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.

0eeee94928add013e9519399a831c3b6.png

edad518cb9f21f57dab94fe79a6fd635.png

Из структуры 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, вариант 11.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_ParamSet1.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 не ругается и создает ЭЦП. Но ЭЦП не проходит проверку. Да что же это такое?!

5a5d97dacaceebcc9472ef430b83e63c.jpg

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

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»,»); Интересно, с чем связаны все эти различия в структуре ключей и алгоритме формирования ЭЦП?

© Habrahabr.ru