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

Давайте проще, без купюр
index.html

<!DOCTYPE html>
<html lang="ru">
<head>
    <title>Тест рутокен</title>
    <script type="text/javascript" src="app.js"></script>
</head>
<object id="pluginRutokenWEB" type="application/x-rutoken" width="0" height="0"><param name="onload" value="pluginit" /></object>
<body>
</body>
</html>

app.js

window.onload = function () {
    // Загрузка плагина
    plugin = document.getElementById("pluginRutokenWEB");
    // Проверка плагина на валидность
    if (plugin && plugin.valid) {
        // Показать версию плагина
        document.body.innerHTML += "<br>Установлен плагин Рутокен WEB версии " + plugin.get_version();
        // Проверка наличия токена в системе
        if (plugin.rtwIsTokenPresentAndOK()) {
            // теперь пробуем стандартную процедуру аутентификации rutoken
            // сначала пошлем запрос на аутентификацию:
            sendjson({ requesttype: 'authentificationrequest', id: plugin.rtwGetDeviceID() },
                function (e) {
                    var r = JSON.parse(e.currentTarget.response);
                    sendjson({
                        requesttype: 'authentificationdata',
                        signedhash: plugin.rtwSign('178', r.random),
                        publickey: plugin.rtwGetPublicKey('178')
                    },
                        function (e) { if (this.status === 200) { document.body.innerHTML += "<br> received: " + this.responseText; } }
                    );
                }
            );
        } else { window.alert("Рутокен WEB не подключен"); } return;
    } else { window.alert("Установите плагин Рутокен WEB"); }
};

function sendjson(jsondata,callback) {
    var xhr = new XMLHttpRequest();
    xhr.open('POST', 'http://localhost:6266/load.ashx', true); //  у вас тут видимо будет другой порт
    xhr.onload = callback;
    xhr.send(JSON.stringify(jsondata));
}

load.ashx.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using Rutoken;
using Net.Pkcs11Interop.Common;
using Net.Pkcs11Interop.HighLevelAPI;
using RutokenPkcs11Interop;
using RutokenPkcs11Interop.Common;

namespace rutokenweb
{
    public class Load : IHttpHandler
    {
        // Шаблон для поиска закрытого ключа для цифровой подписи
        static readonly List<ObjectAttribute> PrivateKeyAttributes = new List<ObjectAttribute>
        {
            // ID пары
            new ObjectAttribute(CKA.CKA_ID, "178"),
            // Класс - закрытый ключ
            new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_PRIVATE_KEY)
        };

        // Шаблон для поиска открытого ключа для проверки цифровой подписи
        static readonly List<ObjectAttribute> PublicKeyAttributes = new List<ObjectAttribute>
        {
            // ID пары
            new ObjectAttribute(CKA.CKA_ID,"178"),
            // // Класс - открытый ключ
            new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_PUBLIC_KEY)
        };

        // словарь для представления пришедшего json запроса
        public static Dictionary<String, String> ext = new Dictionary<String, String>();
        public static string userhash = "";

        public void ProcessRequest(HttpContext context)
        {
            HttpResponse response = context.Response;
            // переводим запрос в строку
            string ss = GetAsString(context);
            // полученную json строку превращаем в словарь C#
            JsonToDic(ss);

            switch(ext["requesttype"])
            {
                case "authentificationrequest":
                    userhash = RutokenWeb.GetRandomHash();
                    response.Write("{\"random\":\"" + userhash + "\"}");
                    break;
                case "authentificationdata":
                    bool Auth = RutokenWeb.CheckSignature(ext["signedhash"], userhash, ext["publickey"]);
                    if (Auth) { response.Write("<br>Auth OK"); }
                    else { response.Write("<br>Auth none"); }

                    using (var pkcs11 = new Pkcs11(Settings.RutokenEcpDllDefaultPath, Settings.OsLockingDefault))
                    {
                        Console.WriteLine("Checking tokens available");
                        Slot slot = Helpers.GetUsableSlot(pkcs11);
                        List<CKM> mechanisms = slot.GetMechanismList();
                        bool isGostR3410Supported = mechanisms.Contains((CKM)Extended_CKM.CKM_GOSTR3410);
                        bool isGostR3411Supported = mechanisms.Contains((CKM)Extended_CKM.CKM_GOSTR3411);
                        using (Session session = slot.OpenSession(false))
                        {
                            session.Login(CKU.CKU_USER, "12345678");

                            List<ObjectHandle> privateKeys = session.FindAllObjects(PrivateKeyAttributes);
                            List<ObjectHandle> publicKeys = session.FindAllObjects(PublicKeyAttributes);

                            byte[] hash = StringToByteArray(userhash);

                            // Инициализация операции подписи данных по алгоритму ГОСТ Р 34.10-2001
                            var signMechanism = new Mechanism((uint)Extended_CKM.CKM_GOSTR3410);

                            // Подписать данные
                            byte[] signature = session.Sign(signMechanism, privateKeys[0], hash);

                            // собственная проверка
                            session.Verify(signMechanism, publicKeys[0], hash, signature, out bool isSignatureValid);

                            // преобразуем подписанный хэш в строку
                            string sig1 = ByteArrayToString(signature);
                            // конвертим строку
                            string sig = Hash32Convert(sig1);

                            // читаем открытый ключ
                            var x = session.GetAttributeValue(publicKeys[0], new List<CKA> { CKA.CKA_VALUE });
                            // в строку
                            var y = ByteArrayToString( x[0].GetValueAsByteArray());
                            // конвертим строку
                            var z = Key64Convert(y);

                            bool signed = RutokenWeb.CheckSignature(sig, userhash, z);
                        }
                    }
                    break;
                default: break;
            }
            ext.Clear();
        }

        public string GetAsString(HttpContext cnt)
        {
            string o = "";
            System.IO.Stream s = cnt.Request.GetBufferedInputStream();
            while (true) { int c = s.ReadByte(); if (c < 0) break; else o += ((char)c); }
            return o;
        }

        public void JsonToDic(string j)
        {
            Regex r = new Regex("\"([^\"]+)\":\"([^\"]+)\"", RegexOptions.Singleline);
            Match m = r.Match(j);
            while (m.Success) { ext.Add(m.Groups[1].ToString(), m.Groups[2].ToString()); m = m.NextMatch(); }
        }

        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 BigToLittle(string s)
        {
            string o = "";
            for (var i = 0; i < s.Length/8; i++) {
                o += s[8 * i + 6]; o += s[8 * i + 7]; o += s[8 * i + 4]; o += s[8 * i + 5];
                o += s[8 * i + 2]; o += s[8 * i + 3]; o += s[8 * i]; o += s[8 * i + 1];
            }
            return o;
        }

        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;
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }

    public static class Helpers
    {
        public static Slot GetUsableSlot(Pkcs11 pkcs11)
        {
            // Получить список слотов c подключенными токенами
            List<Slot> slots = pkcs11.GetSlotList(true);

            // Проверить, что слоты найдены
            if (slots == null)
                throw new NullReferenceException("No available slots");

            // Проверить, что число слотов больше 0
            if (slots.Count <= 0)
                throw new InvalidOperationException("No available slots");

            // Получить первый доступный слот
            Slot slot = slots[0];

            return slot;
        }

        public static void PrintByteArray(byte[] array)
        {
            var hexString = new StringBuilder();
            var width = 16;
            int byteCounter = 1;
            foreach (var item in array)
            {
                hexString.AppendFormat(" 0x{0:x2}", item);
                if (byteCounter == width)
                {
                    hexString.AppendLine();
                    byteCounter = 0;
                }
                byteCounter++;
            }

            Console.WriteLine(hexString);
        }
    }
}

(2018-04-04 14:43:02 отредактировано Алексей Караваев)

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

Здравствуйте, Юрий!
Попробуйте (между С# и JS):
- полностью переворачивать хеш (как и прежде);
- переворачивать каждую половину ключа (как и прежде);
- не переворачивая, переставить местами половины подписи.

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

Йес! Работает! Спасибо огромное!