Токени PKCS#11: сертифікати і закриті ключі

imageТокени PKCS#11 виконують не тільки криптографічні функції (генерація ключових пар, формування та перевірка електронного підпису та інші), але і є сховищем для публічних (відкритих, PUBLIC KEY) і приватних (закритих, PRIVATE KEY) ключів. На токені також можуть зберігатися сертифікати. Як правило, на токені зберігаються особисті сертифікати разом з ключовою парою. При цьому на токені може зберігатися кілька особистих сертифікатів.

Постає дилема, як визначити який закритий ключ (та й відкритий теж) відповідає тому або іншому сертифікату.

Таке відповідність, як правило, встановлюється шляхом завдання ідентичних параметрів CKA_ID та/або CKA_LABEL для трійки об'єктів: сертифіката (CKO_CERTIFICATE), публічного ключа (CKO_PUBLIC_KEY) і приватного ключа (CKO_PRIVATE_KEY).

Виникає питання – як задавати ці значення, щоб, принаймні, не виникла колізія, і наскільки це безпечно з точки зору отримання коректного результату.

Найбільш поширений спосіб завдання CKA_ID – це використання значення хеш-функції від значення відкритого ключа. Саме такий спосіб для зв'язування трійки об'єктів використовується у проекті NSS (Network Security Services) і браузері Redfox. При цьому в якості хеш-функції використовується алгоритм SHA1. З урахуванням того, що на токені реально буде зберігатися чи більше десятка особистих сертифікатів, то з точки зору появи колізії цей спосіб є хорошим. Разом з тим CKA_ID для цієї трійки можуть встановлюватися в будь-який момент і будь-яке значення. Саме в цьому і полягає вся проблема. Якби RFC або Рекомендації ТК-26 вимагали встановлення CKA_ID в момент появи об'єкта на токені (наприклад, при генерації ключової пари CKM_GOSTR3410_KEY_GEN_PAIR), і його не можна було б змінити, то на цьому дане оповідання можна було б завершити. На жаль, це не так. Як вже було сказано, CKA_ID можна встановити в будь-який момент з будь-яким значенням. Таким чином, завжди існує ймовірність, що сертифікат виявиться пов'язаним з чужим приватним ключем. Не треба пояснювати, до яких це призведе наслідків.

А взагалі, існує строгий математичний алгоритм, який дозволяє зв'язати трійку CKO_CERTIFICATE x CKO_PRIVATE_KEY x CKO_PUBLIC_KEY в єдине ціле?

Так, такий алгоритм на базі криптографічних механізмів (CKM_) токена існує. Зв'язка сертифіката та публічного ключа перевіряється легко і просто. Беруться значення відкритого ключа та його параметрів з сертифіката і порівнюються з аналогічними значеннями публічного ключа.

Що стосується сертифіката і приватного ключа, то до недавнього часу цей алгоритм виглядав наступним чином. З допомогою приватного ключа формується підпис під деяким текстом (наприклад, «пошук закритого ключа»), а потім за допомогою відкритого ключа, отриманого з сертифіката, перевіряється коректність отриманої підпису. Якщо підпис коректна, значить, ми отримали закритий ключ для вибраного сертифіката. Якщо ні, то вибирається наступний закритий ключ на токені.

Все, тепер ми не залежимо ні від CKA_ID, CKA_LABEL.

Але ось з'являється документ «МЕТОДИЧНІ РЕКОМЕНДАЦІЇ. Розширення PKCS#11 для використання російських стандартів ГОСТ Р 34.10-2012, ГОСТ Р 34.11-2012, ГОСТ Р 34.12-2015 і ГОСТ Р 34.13-2015», в якому з'являється новий механізм CKM_GOSTR3410_PUBLIC_KEY_DERIVE — механізм створення відкритого ключа із закритого. Даний механізм використовується в C_DeriveKey. Тепер пошук закритого ключа для сертифіката значно спрощується. Достатньо отримати список закритих ключів на токені, потім для кожного закритого ключа отримати відкритий ключ:

...
CK_OBJECT_HANDLE priv_key = CK_INVALID_HANDLE;
CK_OBJECT_HANDLE pub_key = CK_INVALID_HANDLE;
CK_MECHANISM mechanism_der_desc =
{ CKM_GOSTR3410_PUBLIC_KEY_DERIVE, NULL, 0 };
CK_MECHANISM_PTR mechanism_der = &mechanism_der_desc;
...
// Отримуємо відкритий ключ по закритому
rc = funcs->C_DeriveKey(sess, mechanism_der, priv_key,
NULL, 0, &pub_key);
...

А далі порівнюємо значення отриманого публічного ключа, зі значеннями публічного ключа в сертифікаті.

Застосування кожного з цих алгоритмів позбавляє від необхідності стежити за значеннями CKA_ID/CKA_LABEL і робить використанням сертифікатів і приватних ключів, які зберігаються на токенах PKCS#11, надійним і безпечним.

Використання механізму CKM_GOSTR3410_PUBLIC_KEY_DERIVE передбачає його реалізацію на тому чи іншому токені. Подивитися список реалізованих механізмів зручно за допомогою доступною для вільного скачуванняутиліти p11conf:

$ /usr/local/bin64/p11conf -h
usage: /usr/local/bin64/p11conf [-hitsmIupPred] -A APIpath [-c slotID -U userPin -S SOPin -n newPin -L label]
-h display usage
-i display PKCS#11 library info
-s display slot(s) info (-c slotID is optional)
-t display token(s) info (-c slotID is optional)
Others must use -c slotID
-m display list mechanism
-I initialize token 
-u initialize user PIN
-p set the user PIN
-P set the SO PIN
-r remove all objects
-e enumerate objects
-d dump all object attributes
Copyright© LISSI-Soft Ltd (http://soft.lissi.ru) 2011-2016
$

Список доступних механізмів можна подивитися наступним чином:

$ /usr/local/bin64/p11conf -A /usr/local/lib64/libls11usb2016.so -m -c 0|grep GOSTR3410
Mechanism: CKM_GOSTR3410_KEY_PAIR_GEN (0x1200)
Mechanism: CKM_GOSTR3410_512_KEY_PAIR_GEN (0xD4321005)
Mechanism: CKM_GOSTR3410 (0x1201)
Mechanism: CKM_GOSTR3410_512 (0xD4321006)
Mechanism: CKM_GOSTR3410_WITH_GOSTR3411 (0x1202)
Mechanism: CKM_GOSTR3410_WITH_GOSTR3411_12_256 (0xD4321008)
Mechanism: CKM_GOSTR3410_WITH_GOSTR3411_12_512 (0xD4321009)
Mechanism: CKM_GOSTR3410_DERIVE (0x1204)
Mechanism: CKM_GOSTR3410_12_DERIVE (0xD4321007)
Mechanism: CKM_GOSTR3410_KEY_WRAP (0x1203)

Mechanism: CKM_GOSTR3410_PUBLIC_KEY_DERIVE (0xD432100A)

Mechanism: CKM_LISSI_GOSTR3410_PUBLIC_KEY_DERIVE (0xD4321037)
$

І останнє, чи є сьогодні токени, розроблені згідно з документом «МЕТОДИЧНІ РЕКОМЕНДАЦІЇ. Розширення PKCS#11 для використання російських стандартів ГОСТ Р 34.10-2012, ГОСТ Р 34.11-2012, ГОСТ Р 34.12-2015 і ГОСТ Р 34.13-2015»?

Так, є, це сімейство токенів LS11.
Джерело: Хабрахабр

0 коментарів

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