Комментарии 20
Подавляющее большинство разработчиков программ считают стандарт ASN.1 сложным. Я тоже так думал до недавнего времени.
Статья только усугубила это мнение. И это еще без explicit/implicit и кастомных тегов.
Советую всем утилиту для просмотра ASN.1 структур — lapo.it/asn1js.очень помогает при дебаге
Есть много разных компиляторов для ASN.1, как платных, так и бесплатных, для разных языков программирования, но нам хотелось бы иметь под рукой что-то очень простое.
А вот и ваша цитата:
Вообще-то рекомендовать ASN.1 для криптографических задач не стоит: ASN.1 и его кодеки — сложны.
И вы предлагаете тащить Python в Си?
Предложенный алгоритм и утилита формирование asn1-структур не требует использования никаких ASN1-компиляторов и доролнительных библиотек (той же openssl)
для создания ASN1-структур.
Вариант 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 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
И если вы правильно будете распоковывать, то получите одно и тоже значение.
Страшно то, что ваш приватный ключ гуляет где хочет. Имейте ввиду это.
Вот что по этому поводу написано в документе ТК-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, а второй это всеже самодеятельность.
Но проблема с отсутствием надежного бесплатного компилятора. Платных компиляторов индустриального уровня всего два. И они не дешевые. И проблема с их использованием даже не в том, чтобы купить в вашей организации. Проблема, что если задействовать протокол, то все партнеры должны будут тоже купить. Потому что «в ручную» писать разбор — это дорого и больно. И поэтому — протобуф.
Мы его активно используем для разбора 3GPP и прочих около-телефонных записей.
Раньше пользовали платный Obj-Sys, и это просто ужас.
Простой ASN1-кодек на базе sprintf