Как стать автором
Обновить

Комментарии 20

Подавляющее большинство разработчиков программ считают стандарт ASN.1 сложным. Я тоже так думал до недавнего времени.

Статья только усугубила это мнение. И это еще без explicit/implicit и кастомных тегов.
Советую всем утилиту для просмотра ASN.1 структур — lapo.it/asn1js.очень помогает при дебаге
habr.com/ru/post/444272 — а вот тут pure Python библиотека на работы с BER/DER, состоящая из одного файла. С ASN.1 можно работать и удобно.
Есть много разных компиляторов для ASN.1, как платных, так и бесплатных, для разных языков программирования, но нам хотелось бы иметь под рукой что-то очень простое.

А вот и ваша цитата:


Вообще-то рекомендовать ASN.1 для криптографических задач не стоит: ASN.1 и его кодеки — сложны.

И вы предлагаете тащить Python в Си?


Предложенный алгоритм и утилита формирование asn1-структур не требует использования никаких ASN1-компиляторов и доролнительных библиотек (той же openssl)

для создания ASN1-структур.

Python в C конечно тащить не стоит. Мой комментарий исключительно чтобы показать что с ASN.1 не всегда страшно работать.

Тут я с вами полностью согласен.

Раз уж пошла такая пьянка. Вопрос эксперту если у нас однозначный способ то почему я могу задать приватный ключ двумя спосабами?

Вариант 1
  /*00*/  0x30,70,
  /*02*/    2,1,0, // Integer(0)
  /*05*/    0x30,31, 
  /*07*/      6,8, 0x2A,0x85,3, 7,1,1,1,1, // 1.2.643.7.1.1.1.1
  /*17*/      0x30,19,
  /*19*/        6,7, 42,0x85,3, 2,2,36,0,  // 1.2.643.2.2.36.0
  /*28*/        6,8, 42,0x85,3, 7,1,1,2,2, // 1.2.643.7.1.1.2.2
  /*38*/    4,32, // OctetString
  /*40*/          1,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,
  /*56*/          0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

Вариант 2
  /*00*/  0x30,72,
  /*02*/    2,1,0, // Integer(0)
  /*05*/    0x30,31, 
  /*07*/      6,8, 0x2A,0x85,3, 7,1,1,1,1, // 1.2.643.7.1.1.1.1
  /*17*/      0x30,19,
  /*19*/        6,7, 42,0x85,3, 2,2,36,0,  // 1.2.643.2.2.36.0
  /*28*/        6,8, 42,0x85,3, 7,1,1,2,2, // 1.2.643.7.1.1.2.2
  /*38*/    4,34, 2,32 // OctetString int
  /*42*/          0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  /*58*/          0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,1

Но байты ключа будут отличаться порядком следования (прямой и задом наперёд)

Значение приватного ключа это данные. Ваши данные (кстати, я надеюсь вы его не из PKCS#11 неизвлекаемого вытащили) и как вы их положите так и будет. В данном случае вы должны учитывать архитектуру вашего компьютера (bigendien, little endien) и, опираясь на соответствующий документ (если речь идет о ГОСТ-ах, то это рукомендации ТК-26), кодировать ваши данные. В данном случае знвчение приватного ключа. Привожу код для определения архитектуры машины (он, кстати, есть в коде процедуры main — забыл убрать):


 uint16_t x = 1; /* 0x0001 */
  printf("%s\n", *((uint8_t *) &x) == 0 ? "big-endian" : "little-endian");

Итак, данные это ваша зона ответственности.

Данные я вытащил из openssl он сгенерировал закрытый ключ в таком виде

openssl genpkey -algorithm gost2012_256 -pkeyopt paramset:XA -out priv.key
openssl asn1parse -in priv.key

при этом оба варианта работают с int внутри и без. Вызывает некое беспокойство что если приватный ключ (вдруг) будет начинаться с байта 0x02.

ps: иногда бывает что sizeof(uint16_t)==sizeof(uint8_t)

У вас два разных файла, в которых один и тотже приватный ключ упакован по разному.
В первом случае это просто octetstring:


/38/ 4,32, // OctetString
/40/ 1,2,3,0,0,0,0,0,0,0,0,0,0,0,0,0,
/56/ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

Во втором случае это int завернутый в octetstring:


/38/ 4,34, 2,32 // OctetString int
/42/ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
/58/ 0,0,0,0,0,0,0,0,0,0,0,0,0,3,2,1

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

Чем это страшно, если это просто тестовые ключи для опытов.
Страшно то во что превратили цифровую подпись — в механизм развода на деньги при любом чихе. Вот например сегодня очередной подарок от клиента Россельхозбанк-а прилетел, празднуйте 1-ое апреля же.

Вот что по этому поводу написано в документе ТК-26 "Транспортныйконтейнер
ключевой. Дополнения к PKCS#8 и PKCS#12.Версия 2.0"


Поскольку размерность типа INTEGER может изменяться при наличии
нулевых значений в старших разрядах числа, использование такого типа для
представления закрытого ключа создает неудобства при вычислении размерности.
Предлагается использовать для хранения значений ключа OCTET
STRING, то есть:
GostR3410-2012-KeyValueMask ::= OCTET STRING { KM |M1|M2|....|Mk }
или
GostR3410-2012-KeyValueInfo :: = SEQUENCE{
GostR3410-2012-KeyValueMask,
GostR3410-2012-PublicKey }
GostR3410-2012-PublicKey :: = OCTET STRING {PubKeyX|PubKeyY}
...
Для обеспечения унификации между представлениями ключей в PFX и
сертификатах формата X.509, как секретный, так и открытый ключи
представляются в формате little-endian (старший байт справа).

Итак, первый ваш файл с приватным ключом соответствует рекомендациям ТК-26, а второй это всеже самодеятельность.

А в этом документе ничего не написано про OID 1.2.840.113549.1.12.1.80?
ASN.1 — годный протокол. Пакует более эффективно чем имеющиеся альтернативы (например протобуф). А все потому, что позволяет более детально описывать типы и соответственно более эффективно их паковать.
Но проблема с отсутствием надежного бесплатного компилятора. Платных компиляторов индустриального уровня всего два. И они не дешевые. И проблема с их использованием даже не в том, чтобы купить в вашей организации. Проблема, что если задействовать протокол, то все партнеры должны будут тоже купить. Потому что «в ручную» писать разбор — это дорого и больно. И поэтому — протобуф.
Пожалуй самый лучший и тому же бесплатный ASN1 парсер/генератор: github.com/vlm/asn1c
Мы его активно используем для разбора 3GPP и прочих около-телефонных записей.
Раньше пользовали платный Obj-Sys, и это просто ужас.

Имеет право на жизнь. Насчет лучшего… И чем он лучше, кроме бесплатного. ?

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации