Використання ключа КріптоПро 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, варіант 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 не лається і створює ЕЦП. Але ЕЦП не проходить перевірку. Та що ж це таке?!
 
 
 
З'ясувалося, що ЕЦП потрібно ще й розгорнути!
 
 
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", "");

 
Цікаво, з чим пов'язані всі ці відмінності в структурі ключів і алгоритмі формування ЕЦП?
    
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.