PKCS#11 и OpenSSL имеют разный формат представления объектов. Так, они по-разному представляют ECDSA ключи и подписи на них. Поэтому, когда мы хотим проверить в OpenSSL сырую ECDSA подпись полученную через PKCS#11, ее необходимо сконвертировать в OpenSSL формат. Для проверки подписи нужно также сконвертировать и открытый ключ.
Ручной конвертации можно избежать воспользовавшись OpenSSL движком rtengine. Он предназначен для работы с объектами на токене средствами OpenSSL. Но не во всех задачах использование OpenSSL приемлемо, поэтому мы написали статью, как конвертировать объекты вручную.
Библиотека PKCS#11 возвращает открытый ключ ECDSA через атрибут CKA_EC_POINT. Значение атрибута – это число представленное в виде TLV структуры – OCTET_STRING:
Снизу представлен пример TLV структуры открытого ключа. Первый байт 04 – это тег OCTET_STRING. Второй – длина открытого ключа (64 байта). Последующие 64 байта – значение ключа.
04 41 04 8B 92 09 CC C0 A1 B4 19 BA 80 2E 44 5D A2 16 E6 92 AA 9C BB B9 CC B0 CE 5C 76 71 C6 DF E4 CA 83 46 BB 92 2B C8 6F FC 15 20 D8 11 5F 32 4F 7F CA BB DE 9F E3 62 5E 8E 2C D2 2D C2 51 EC 3B 8C 67
OpenSSL позволяет представлять открытый ECDSA ключ в виде ASN.1 структуры. Эта структура хранит не только значение открытого ключа, но и его параметры.
Пример открытого ключа в формате OpenSSL:
30 56 -- SEQUENCE. первый байт -- тег типа (30), второй -- длина значения (56). 30 10 -- SEQUENCE 06 07 2A 86 48 CE 3D 02 01 -- OBJECT IDENTIFIER. Идентифицирует типа ключа. В нашем случае это ecPublicKey (открытый ключ эл. кривой). 06 05 2B 81 04 00 0A -- OBJECT IDENTIFIER. Идентифицирует параметры ключа. В нашем случае это secp256k1 (параметр эл. кривой) 03 42 00 04 8B 92 09 CC C0 A1 B4 19 BA 80 2E 44 5D A2 16 E6 92 AA 9C BB B9 CC B0 CE 5C 76 71 C6 DF E4 CA 83 46 BB 92 2B C8 6F FC 15 20 D8 11 5F 32 4F 7F CA BB DE 9F E3 62 5E 8E 2C D2 2D C2 51 EC 3B 8C 67 -- BIT STRING. Значение открытого ключа |
Для того, чтобы сконцентрировать ECDSA ключ из PKCS#11 формата, нужно:
Конвертация из OCTET_STRING происходит следующим образом
Для открытого ключа сверху
Результат:
30 56 30 10 06 07 2A 86 48 CE 3D 02 01 06 05 2B 81 04 00 0A 03 42 00 04 8B 92 09 CC C0 A1 B4 19 BA 80 2E 44 5D A2 16 E6 92 AA 9C BB B9 CC B0 CE 5C 76 71 C6 DF E4 CA 83 46 BB 92 2B C8 6F FC 15 20 D8 11 5F 32 4F 7F CA BB DE 9F E3 62 5E 8E 2C D2 2D C2 51 EC 3B 8C 67
Если записать в файл байты в том виде, в котором мы их получили, мы получим открытый ключ в DER формате. Если мы хотим представить его в виде печатных символов, его нужно перевести в PEM формат. Это можно сделать с помощью команды:
|
Чтобы проверить, что ключ сконвертирован верно, выполните команду:
openssl ec -check -pubin -in pubkey.pem |
Для подписи данных библиотека PKCS#11 использует функцию С_Sign. Результат возвращается в виде R|S структуры. Формат у этой структуры простой:
43 1C 62 38 11 04 91 43 76 28 A5 D8 9E 5F EE 90 B0 DC 68 39 D2 B8 11 F3 22 2C D4 DB E2 05 49 BF -- R 8A EF 68 77 8B 0D B8 E6 11 FD 55 56 37 71 62 7E B7 31 22 67 8D 61 7F B6 41 EA 7E 1F 84 C7 36 41 -- S |
OpenSSL представляет подпись в виде ASN.1 структуры.
30 45 -- SEQUENCE 02 20 43 1C 62 38 11 04 91 43 76 28 A5 D8 9E 5F EE 90 B0 DC 68 39 D2 B8 11 F3 22 2C D4 DB E2 05 49 BF -- R 02 21 00 8A EF 68 77 8B 0D B8 E6 11 FD 55 56 37 71 62 7E B7 31 22 67 8D 61 7F B6 41 EA 7E 1F 84 C7 36 41 -- S |
Числа R и S приводятся к TLV. типу INTEGER с помощью такого алгоритма:
Проверка подписи можно с помощью команды openssl
openssl dgst -sha256 -verify pubkey.pem -signature sign.der data.bin |