Токены 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 mechanism list
            -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(C) 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.
    Метки:
    • +14
    • 7,1k
    • 6
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 6
    • +1
      Упустил. Токены PKCS#11 с поддержкой российской криптографии успешно можно использовать для доступа на портал Госуслуг и для доступа в личный кабанет налогоплательщика ФНС .

      Естественно у гражданина Российской Федерации на токене должен находиться личный сертификат, полученный в аккредитованном УЦ.
      :

      • 0

        Хотел задать вопрос про аппаратную начинку ваших токенов, но вопрос отпал сам собой.


        Программно-аппаратный токен lsToken входит в состав семейства токенов LS11. В качестве ключевого носителя используется обыкновенная флешка — USB-флеш-накопитель.

        А нормальных аппаратных токенов у вас вообще никаких нет? А то такой как-то стрёмно использовать даже для "домашних" применений.

        • +1
          Посмотрите линейку LS11HW.
          Есть еще Рутокен ЭЦП.
          Не забывайте, что здесь речь идет о PKCS#11 v.2.40 и новых ГОСТ-овых расширениях. В данном случае железо не успевает за математикой.
        • 0
          Мне не очевидны приведенные Вами аргументы за такую новую схему работы с ключевыми парами. Соответствующий сертификат надо искать «по-старому» — перебором по телу открытого ключа из сертификата.

          Схема со связкой тройки CKO_CERTIFICATE, CKO_PRIVATE_KEY, CKO_PUBLIC_KEY по CKA_ID работает для любого алгоритма. Работает уже очень давно.

          Озвученная Вами схема сопоставления закрытых и открытых ключей с помощью тестовой подписи и ее проверки также работает для любого алгоритма.

          Объекты можно защитить от изменения задав CKA_MODIFIABLE в CK_FALSE. Иначе где гарантия что, не только CKA_ID но еще и тело ключа кто-то подменил.
          • 0
            Интересно, как вы можете подменить тело сертификата? Никак — только положить новый сертификат. Как можно подменить «тело ключа» закрытого, если он неизвлекаемый? Но если вы подмените тело, то это будет другой ключ. Именно об этом и идет речь. Любая подмена, модификация при использовании описанных алгоритмов тут же ее обнаружит!!!

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