pkcs11, получить публичный ключ

Добрый день.
Есть необходимость программного получить публичный ключ сгенерированный RuToken Web плагином (использую v1.5) на RuToken Web токене.
Проблема в том что открытый ключ полученный мной через pkcs11

    CK_ATTRIBUTE t[] = {
        {CKA_VALUE, NULL_PTR, 0},
    };

    C_GetAttributeValue(hSession, hPublicKey, t, arraysize(t));

отличается от того что я получаю через http://php.rutokenweb.ru/api.php по нажатию на кнопку rtwGetPublicKey(container_name);
(но размер в обоих случаях идентичен)

Вопрос: Это я не правильно получаю публичный ключ или же rtwGetPublicKey производит какую либо его дополнительную обработку перед выдачей?

Re: pkcs11, получить публичный ключ

Здравствуйте!

Функцией rtwGetPublicKey производится преобразование массива байт полученного из PKCS11 публичного ключа следующим образом.

Получено из PKCS11: a[0]...a[N/2-1]a[N/2]...a[N-1]
Возвращено rtwGetPublicKey: a[N/2-1]...a[0]a[N-1]...a[N/2-1]

То есть массив ключа делится на две половины; элементы в каждой из частей переставляются в обратном порядке, после чего половинки конкатенируются.

(2013-08-27 12:08:45 отредактировано 929121)

Re: pkcs11, получить публичный ключ

Огромное(!) спасибо.

Вопрос из чистого любопытства: из каких соображений осуществляется такое забавное преобразование?

Re: pkcs11, получить публичный ключ

Как известно, открытым ключом в алгоритме ГОСТ 34.10 является точка эллиптической кривой Q=(x,y), где x и y -- 32х-байтные числа. В PKCS11 принято представление чисел в формате little-endian, в то время как в плагине Rutoken Web -- в формате big-endian. Этим и объясняется необходимость преобразования.

Re: pkcs11, получить публичный ключ

Спасибо. В этом случае это имеет смысл.

Re: pkcs11, получить публичный ключ

Задача: авторизация вместе с вэб пользователями на сервере с rutoken web из клиента автомата на C#. Тоже столкнулся с тем что ключи в PKCS11 и rutokenweb отличаются :) и благодаря этому топику решил эту проблему... НО!  Возникла вторая проблема: один и тот-же хэш подписанный в plugin.rtwSign(JS) и в session.Sign(signMechanism, privateKeys[0], hash); (C#) дают разные результаты! Видимо тоже где то надо что то перевернуть... Не подскажете?

(2018-03-27 00:22:46 отредактировано Юрий Ботов)

Re: pkcs11, получить публичный ключ

Очевидное "перевернуть 32 байта хеша и перевернуть 64 байта подписанного хэша(целиком или по 32)" - не работает.

Re: pkcs11, получить публичный ключ

Добрый день, подпись по ГОСТу недетерминированная, то есть 2 операции подписи одного и того же хеша на одном и том же приватном ключе дадут 2 совершенно разные подписи.
Вы сможете проверить правильность работы вашей криптосистемы только операцией проверки подписи, очевидно что она должна вернуть true для всех ваших подписей, хоть они и по разному внешне выглядят.

(2018-03-27 14:39:02 отредактировано Юрий Ботов)

Re: pkcs11, получить публичный ключ

В том то и дело, что RutokenWeb.CheckSignature(sig, userhash, z); возвращает false. То есть ситуация такая: один и тот же хэш, один и тот-же public key одного и того же контейнера одного и того же RutokenЭЦП.
Вариант с подписью хэша plugin.rtwSign('контейнер', хэш), потом на сервере RutokenWeb.CheckSignature(подписанныйхэш, хэш, открытыйключ) => true.
Вариант чистого sdk csharp:
session.Login(CKU.CKU_USER, "12345678");
List<ObjectHandle> privateKeys = session.FindAllObjects(PrivateKeyAttributes);
List<ObjectHandle> publicKeys = session.FindAllObjects(PublicKeyAttributes);
byte[] hasharray = StringToByteArray(хэш));
var signMechanism = new Mechanism((uint)Extended_CKM.CKM_GOSTR3410);
byte[] signature = session.Sign(signMechanism, privateKeys[0], hasharray);
bool isSignatureValid = false;
session.Verify(signMechanism, publicKeys[0], hasharray, signature, out isSignatureValid);
isSignatureValid => true.
А вариант при подписывании при помощи sdk csharp и проверке с помощью rutokenweb - не работает, вот он:
session.Login(CKU.CKU_USER, "12345678");
List<ObjectHandle> privateKeys = session.FindAllObjects(PrivateKeyAttributes);
List<ObjectHandle> publicKeys = session.FindAllObjects(PublicKeyAttributes);
byte[] hasharray = StringToByteArray(Hash32Convert(хэш)); // переворачиваем хэш
var signMechanism = new Mechanism((uint)Extended_CKM.CKM_GOSTR3410);
byte[] signature = session.Sign(signMechanism, privateKeys[0], hasharray);
// преобразуем подписанный хэш в строку
string sig1 = ByteArrayToString(signature);
// конвертим строку
string sig = Key64Convert(sig1); // переворачиваю 2 32 байтных слова как описано в топике
// читаем открытый ключ
var x = session.GetAttributeValue(publicKeys[0], new List<CKA> { CKA.CKA_VALUE });
// в строку
string y = ByteArrayToString( x[0].GetValueAsByteArray());
// конвертим строку
string z = Key64Convert(y); // переворачиваю 2 32 байтных слова как описано в топике

RutokenWeb.CheckSignature(sig, хэш, z) => false

При этом z = public key имеет тот же вид что и в первом случае, хэш тоже. Вопрос: как преобразовать подписанный хэш и надо ли переворачивать изначальный хэш при подписывании

Re: pkcs11, получить публичный ключ

Ок, если проверка не срабатывает и вы уверены что данные подаете правильно и одинаково подписываете, то вопрос остается в том, как правильно передавать подпись на проверку.
Спасибо за подробное описание того, что вы делаете - мы постараемся разобраться и подсказать.

Re: pkcs11, получить публичный ключ

Для простоты понимания прилагаю функции на которые ссылаюсь:

       public string ByteArrayToString(byte[] a)
        {
            StringBuilder o = new StringBuilder(a.Length * 2);
            for (int i = 0; i < a.Length; i++) o.Append(a[i].ToString("x2"));
            return o.ToString();
        }

        public byte[] StringToByteArray(string s)
        {
            string ss = s.ToUpper();
            byte[] o = new byte[s.Length / 2];
            for (int i = 0; i < s.Length / 2; i++) o[i] = (byte)int.Parse(s.Substring(i * 2, 2), System.Globalization.NumberStyles.HexNumber);
            return o;
        }

        public string Key64Convert(string PKCSKey)
        {
            string JSKey = "";
            for (var i = 31; i >= 0; i--) { JSKey += PKCSKey[2 * i]; JSKey += PKCSKey[2 * i + 1]; }
            for (var i = 63; i >= 32; i--) { JSKey += PKCSKey[2 * i]; JSKey += PKCSKey[2 * i + 1]; }
            return JSKey;
        }

        public string Hash32Convert(string s)
        {
            string o = "";
            for (var i = (s.Length)/2-1; i >= 0; i--) { o += s[2 * i]; o += s[2 * i + 1]; }
            return o;
        }

Убрал из них не имеющие отношения к основной функции проверки (для наглядности)

Re: pkcs11, получить публичный ключ

Добрый день!
Можно попробовать не переворачивать хеш, то есть использовать одно и то же значение в плагине и csharp.
Если проверка подписи и так не будет пройдена, прошу выслать контрольные данные до каких-либо переворачиваний:
- публичный ключ, хеш, соответствующая им подпись из плагина;
- публичный ключ, хеш, соответствующая им подпись из приложения csharp.

(2018-04-03 19:48:29 отредактировано Юрий Ботов)

Re: pkcs11, получить публичный ключ

Не пошло.

Вот так создаю хэш:
(C#)userhash = RutokenWeb.GetRandomHash();
Хэш:
d259f30e4fd9761b3de38b58e57f2038455dc41d0bcc46377bb8cf9c225ebfc8

Хэш подписанный в браузере,'178'- имя контейнера:
(JS)plugin.rtwSign('178', userhash)
FAF35341EA01FF7F1624FE069C0BA2FFF722D450A2059FF3B888B877E8562A8D0F42A019472DF31ABDF8E63C52E2B57751F256D19F28A3FCC1A3F0BDD37281DE
Использованный открытый ключ:
(JS)plugin.rtwGetPublicKey('178')
743FBDA620C5050F4E03FFA83DB433DB712A14F571065680CFC9F5E77481C861E13101223A079FB9117A70A973F9588D2C831CB3EA1663C8465EEB6CCC6FF568

результат:
(C#)RutokenWeb.CheckSignature(signedhash, userhash, publickey) -> true

Теперь подписываем средствами SDK csharp:
(C#)List<ObjectHandle> privateKeys = session.FindAllObjects(PrivateKeyAttributes);
(C#)var signMechanism = new Mechanism((uint)Extended_CKM.CKM_GOSTR3410);
(C#)byte[] signature = session.Sign(signMechanism, privateKeys[0], userhash);
Получаем:
24282daa267ad10ea9340f68dae9db82375e593376cbd46f654a713ffcdb7cb1d9c89bc185138802a42aa02e2c8f516abdd9aefb4455f33c04c9ac3d6b35fbbc
Проверяем средствами SDK csharp:
(C#)session.Verify(signMechanism, publicKeys[0], userhash, signature, out bool isSignatureValid) -> true
Достаем открытый ключ:
(C#)session.GetAttributeValue(publicKeys[0], new List<CKA> { CKA.CKA_VALUE })[0].GetValueAsByteArray()
61c88174e7f5c9cf80560671f5142a71db33b43da8ff034e0f05c520a6bd3f7468f56fcc6ceb5e46c86316eab31c832c8d58f973a9707a11b99f073a220131e1
Преобразуем его, получаем:
743fbda620c5050f4e03ffa83db433db712a14f571065680cfc9f5e77481c861e13101223a079fb9117a70a973f9588d2c831cb3ea1663c8465eeb6ccc6ff568
После преобразования он совпадает с полученным из JS.

Проверяем средствами RutokenWeb:
(C#)RutokenWeb.CheckSignature(signature, userhash, publickey) -> false

(не показываю преобразований из массива байт в строку и обратно - они разумеется есть там где необходимо)

(2018-04-03 16:50:17 отредактировано Алексей Караваев)

Re: pkcs11, получить публичный ключ

Проясните, пожалуйста, почему подписывается userhash, а в проверке участвует hash:

(C#)byte[] signature = session.Sign(signMechanism, privateKeys[0], userhash);
Получаем:
24282daa267ad10ea9340f68dae9db82375e593376cbd46f654a713ffcdb7cb1d9c89bc185138802a42aa02e2c8f516abdd9aefb4455f33c04c9ac3d6b35fbbc
Проверяем средствами SDK csharp:
(C#)session.Verify(signMechanism, publicKeys[0], hash, signature, out bool isSignatureValid) -> true

?

И какое всё-таки хеш-значение подписывается в C#?

(2018-04-03 19:54:08 отредактировано Юрий Ботов)

Re: pkcs11, получить публичный ключ

Подписывается именно то что проверяется, просто хотел "внести единообразие" и в процессе накосячил: на самом деле userhash - строка, а hash - байтовый массив, а я их для примера переименовывал "во что нибудь одно". Причем там где вы указали проверка проходит. Исправил в предыдущем топике, теперь везде userhash, но все должны понимать что это в зависимости от места где нужно - строка, а где нужно массив байтов.

И какое всё-таки хеш-значение подписывается в C#? - в C# подписывается массив байт полученный из строки userhash