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