Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

PKCS#11 и OpenSSL имеют разный формат представления объектов. Так, они по-разному представляют ECDSA ключи и подписи на них. Поэтому, когда мы хотим проверить сырую ECDSA подпись полученную через PKCS#11, ее необходимо сконвертировать в OpenSSL формат.  Для проверки подписи нужно также сконвертировать и открытый ключ.

Ручной конвертации можно избежать воспользовавшись OpenSSL движком rtengine. Он предназначен для работы с объектами на токене средствами OpenSSL. Но не во всех задачах использование OpenSSL приемлемо, поэтому мы написали статью, как конвертировать объекты вручную.

Конвертация открытого ключа

Описание формата открытого ключа в PKCS#11

Открытый ключ

Библиотека PKCS#11 возвращает открытый ключ ECDSA в через поле атрибут CKA_EC_POINT в формате . Значение атрибута – это число представленное в виде TLV структуры – OCTET_STRING:

  1. T – тег типа размером один байт. В нашем случае это 04 – OCTET_STRING.
  2. L – длина значения V. Она может быть представлена в виде нескольких байт. Если длина не превышает 7F байт, то L состоит из одного байта. Если больше 7F байт, то старший бит устанавливается в 0, а младшие 7 бит определяют длину L.  Например, L=7A – длина  0x7A (122). L=81 80 – длина 0x80(128). L=82 7F FF – длина 0x7FFF(32767).  
  3. V –  значение размером L байт.

Пример CKA_EC_POINT

Снизу представлен пример 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

разбор по частям

...

Что нужно сделать

  • добавить параметры эллиптической кривой
  • добавить OID открытого ключа
  • изменить первый тег 04 на 03
  • добавить 00 байт перед вторым тегом 04
  • увеличить размер ключа на единицу (до 42)

Что должно получиться

...

Описание формата открытого ключа в OpenSSL

OpenSSL позволяет представлять открытый ECDSA ключ в виде ASN.1  структуры. Эта структура хранит не только значение открытого ключа, но и его параметры.

Пример открытого ключа в формате OpenSSL:

Code Block
languagebash
titleASN.1 структуры открытого ECDSA ключа
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 формата, нужно:

  1. Создать SEQUENCE со значениями типа OBJECT IDENTIFIER. Первый элемент – OID типа открытого ключа на эллиптической кривой Второй – OID параметра эл. кривой.
  2. Сконвертировать открытый ключ из структуры OCTET_STRING в структуру BIT_STRING.
  3. Конкатенировать результат и положить его в SEQUENCE. Нужно проставить правильную длину выходной последовательности.


Конвертация из OCTET_STRING происходит следующим образом 

  1. Тег 04 заменяется на 03 (тег BIT STRING).
  2. После байтов длины (L) ставится байт 00.
  3. L увеличивается на 1.


Пример конвертации

Для открытого ключа сверху

  1. На первом этапе сформируем последовательность из идентификаторов типа открытого ключа и параметра эл. кривой: 
    30 10 06 07 2A 86 48 CE 3D 02 01 06 05 2B 81 04 00 0A
    1. 30 - тег
    2. 10 - размер
    3. 06 - тег
    4. 07 - размер
    5. 2A 86 48 CE 3D 02 01 - id-ecPublicKey
    6. 06 - тег
    7. 05 - размер
    8. 2B 81 04 00 0A - secp256k1
  2. Сконвертируем ключ из OCTET_STRING в BIT_STRING :
    03 42 00 04 8B 92 09 ...
    1. 03 – тег
    2. 42 – размер
    3. 00 – заполнитель
    4. 04 8B 92 09... – 0x41 байт открытого ключа
  3. Сложим результаты в SEQUENCE:
    30 56 30 10 06 07 2A 86 ...
    1. 30 – тег
    2. 56 – размер (0x2+0x2+0x42+0x10)

Результат:

...

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

Формат файла

...


Info
title DER и PEM форматы

Если записать в файл байты в том виде, в котором мы их получили. Мы получим открытый ключ в байтовом DER формате. Если мы хотим представить его в виде печатных символов, его нужно перевести в PEM

...

формат. Это можно сделать с помощью команды:

Code Block
languagebash
titleКонвертация в PEM

...

openssl ec -pubin -inform DER -in pubkey.der -outform PEM -out pubkey.pem


Проверка открытого ключа

Чтобы проверить, что ключ сконвертирован верно, выполните команду:

Code Block
languagebash
titleПроверка ключа
openssl ec -check -pubin -in pubkey.pem

Команда напишет EC Key valid. если открытый ключ корректный

Подпись

Конвертация подписи

Представление подписи в PKCS#11

Для подписи данных библиотека PKCS#11 использует функцию С_Sign. Результат возвращается Библиотека PKCS#11 возвращает подпись через буфер в функции С_Sign в виде R|S структуры. Формат у этой структуры простой:

  1. 32 байта числа R
  2. 32 байта числа S

Пример R|S подписи

...

EC 8D 6D 05 96 B9 8A AE 04 F6 AE 83 D8 04 99 FA D4 A3 EA 37 86 95 A9 DC 0D 3F 09 7F 76 5A F8 40 AA 09 E6 8D D6 56 82 02 4B CF 15 78 21 A6 23 75 81 6C 93 51 90 ED 58 91 33 6E E2 91 4D 57 B5 50

Первые 32 байта - R, остальные 32 байта - S

Что нужно сделать

  • добавить тег 30
  • добавить размер 44, 45 или 46
  • к каждой из половинок добавить тег 02
  • к каждой из половинок добавить длину (размер) 20 или 21
  • в зависимости от первого байта R и S, если первый байт больше 7F то нужно добавить лишний 00 байт в начало строки и увеличить длину на 1

Что должно получиться

...

Code Block
languagebash
titleПример 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

OpenSSL представляет подпись в виде ASN.1  структуры.

Code Block
languagebash
titleASN.1 структуры открытого ECDSA ключа
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

Описание процесса конвертации

  1. Перевести R и S в TLV тип INTEGER
  2. Результат сконкатенировать и положить в SEQUENCE. Нужно проставить правильную длину выходной последовательности.

Числа R и S приводятся к TLV. типу INTEGER с помощью такого алгоритма:

  1. T =  02.
  2. L = кол-во байт для хранения числа. Если первый байт числа превышает 7F, то длина увеличивается на 1 и после L записывается байт 00.
  3. V = значение числа.


Пример конвертации

  1. Число R преобразуется к типу INTEGER:
    02 20 43 1C 62 38 11 04 ...
    1. 02 - тег
    2. 20 - длина
    3. 43 1C 62 38 11 04 ... - само число
  2. Число S преобразуется к типу INTEGER:
    02 21 00 8A EF 68 77 8B
    1. 02 - тег
    2. 21 - длина. На единицу больше т.к. старший байт числа 0x8A больше 0x7F
    3. 00 - заполнитель
    4. 8A EF 68 77 8B ... - само число
  3. Запишем результат в SEQUENCE:
    30 45 02 20 43 1C
    1. 30 - тег
    2. 45 - длина последоватльности (0x02 + 0x02 + 0x20 +0x21)
    3. 02 20 43 1C ... - значение последовательности (R|S)

Проверка подписи

Проверка подписи можно с помощью команды openssl

Пример правильной структуры

30 46 02 21 00 EC 8D 6D 05 96 B9 8A AE 04 F6 AE 83 D8 04 99 FA D4 A3 EA 37 86 95 A9 DC 0D 3F 09 7F 76 5A F8 40 02 21 00 AA 09 E6 8D D6 56 82 02 4B CF 15 78 21 A6 23 75 81 6C 93 51 90 ED 58 91 33 6E E2 91 4D 57 B5 50

Формат файла

Никак преобразовывать подпись далее не нужно, можно оставить в этом же виде

Проверка подписи

Проверка подписи утилитой openssl

Code Block
languagebash
titleпроверка подписи
openssl dgst -sha256 -verify pubkey.pem -signature sign.der data.bin

...