Главная » Хабрахабр » Простой ASN1-кодек на базе sprintf

Простой ASN1-кодек на базе sprintf

imageТранспортный синтаксис ASN.1 определяет однозначный способ преобразования значений переменных допустимых типов в последовательность байт для передачи по сети. В ASN.1 он называется базовыми правилами кодирования (Basic Encoding Rules, BER). Правила являются рекурсивными, так что кодирование составных объектов представляет собой составление в цепочку закодированных последовательностей составляющих объектов. Протокол ASN.1 описывает структуру данных простым и понятным языком.
Каждое передаваемое значение — как базового, так и производного типа — состоит из трех полей:

  • идентификатор;
  • длина поля данных (в байтах);
  • поле данных.

Если всегда указывать длину поля данных (я считаю это правилом хорошего тона), то флаг конца поля данных не используется.

1, как платных, так и бесплатных, для разных языков программирования, но нам хотелось бы иметь под рукой что-то очень простое. Есть много разных компиляторов для ASN.

1 сложным. Подавляющее большинство разработчиков программ считают стандарт ASN. Работая в сфере ИОК/PKI/криптографии практически каждый день имеешь дело с ASN1-структурами в виде сертификатов X509, запросов на сертификаты, списки отозванных сертификатов. Я тоже так думал до недавнего времени. И вот, работая над утилитой по созданию запроса на сертификат в формате PKCS#10 с генерацией ключевой пары на токене/смарткартк PKCS#11, мне, естественно, пришлось формировать, в частности, asn1-структуру публичного ключа для его записи в запрос на сертификат: И этот список можно продолжать.

C-Sequence C-Sequence (<длина>) Object Identifier (<длина>) <oid public key> C-Sequence (<длина>) Object Identifier (<длина>) <oid параметра подписи> Object Identifier (<длина>) <oid хэша> Bit String (<длина>) <значение публичного ключа>

Поскольку в качестве СКЗИ мы задействовали токен PKCS#11 с поддержкой российской криптографии, то исходный материал для данной структуры был получен с токена в соответствии со следующим шаблоном:

CK_BYTE gostr3410par[12]; CK_BYTE gostr3411par[12]; CK_ULONG gostr3410par_len; CK_ULONG gostr3411par_len; CK_BYTE pubkey[128]; CK_ULONG pubkeu_len; CK_KEY_TYPE key_type; CK_ATTRIBUTE templ_pk[] = , {CKA_GOSTR3411PARAMS, gostr3411par, sizeof(gostr3410par)}, {CKA_VALUE, pubkey, sizeof(pubkey)}, {CKA_KEY_TYPE, &key_type, sizeof(key_type)} }

Напрямую из этой структуры для заполнения asn1-publickeyinfo будут задействованы значения атрибута CKA_VALUE, содержащее значение открытого ключа, и значения атрибутов CKA_GOSTR3410PARAMS и CKA_GOSTR3411PARAMS, которые содержат oid-ы параметра подписи и параметра хэша.

10-2001 продолжает действовать) неоднозначно определяет алгоритм ключевой пары. Атрибут CKA_KEY_TYPE, который может принимать значения CKK_GOSTR3410 и CKK_GOSTR3410_512 (в условиях когда алгоритм подписи ГОСТ Р 34. 10-2012 с длиной ключа в 512 бит (oid = 1. Если значение атрибута CKA_KEY_TYPE равно CKK_GOSTR3410_512, то, конечно, он одназначно указывает на алгоритм ГОСТ Р 34. 643. 2. 1. 7. 1. 1. А вот если он равен просто CKK_GOSTR3410, то возникает двусмысленность, к какому типу ключа относится данный ключ: ГОСТ Р 34. 2). 10-2012 с длиной ключа 256 бит. 10-2001 или все же это ГОСТ Р 34. Эту двусмысленность помогает разрешить атрибут CKA_GOSTR3411PARAMS.

Сразу отметим, что параметры CKA_GOSTR3410PARAMS и CKA_GOSTR3411PARAMS на токене в соответствии с рекомендациями ТК-26 хранятся в виде объектного идентификатора, закодированного oid-а, например:

таблицу ниже), во втором байте указана длина (в общем случае длина может занимать несколько байт, но об этом ниже) поля данных, в котором хранится oid в бинарном виде. \x06\x06\x2a\x85\x03\x02\x02\x13, где нулевой байт определяет тип последовательности (0x06 – объектный идентификатор, см.

10-2012 с длиной 256 бита (oid=1. Если этот параметр содержит oid алгоритма хэша ГОСТ Р 34. 643. 2. 1. 7. 2. 1. 10-2012 с длиной ключа 256 бит. 2, в бинарном виде "\x2a\x 85\x 03\x 07\x 01\x 01\x 02\x02" ), то тип ключа должен быть установлен как ГОСТ Р 34. 10-2001. В противном случае это ключ ГОСТ Р 34. Алгоритм определения типа ключа может выглядеть так:

. . .
for (curr_attr_idx = 0; curr_attr_idx < (sizeof(templ_pk)/sizeof(templ_pk[0])); curr_attr_idx++){ curr_attr = &templ_pk[curr_attr_idx]; if (!curr_attr->pValue) { continue; } swith (curr_attr->type) { . . . case CKA_VALUE:
/*Длина публичного ключа*/ pubkey_len = curr_attr->ulValueLen; break; case CKA_GOSTR3410PARAMS:
/*Длина объектного идентификатора алгоритма подписи*/ gostr3410par_len = curr_attr->ulValueLen; break; case CKA_GOSTR3410PARAMS:
/*Длина объектного идентификатора хэш*/ gostr3411par_len = curr_attr->ulValueLen; break; case CKA_KEY_TYPE: ulattr = curr_attr->pValue; if (*ulattr == CKK_GOSTR3410) { if (!memmem(gostr3411par), gostr3411par_len,"\x06\x08\x2a\x85\x03\x07", 6)) {
/*Тип ключа ГОСТ Р 34.10-2001*/ strcpy(oid_key_type, "1.2.643.2.2.19"); memcpy(oid_key_type_asn1("\x06\x06\x2a\x85\x03\x02\x02\x13", 8); } else {
/*Тип ключа ГОСТ Р 34.10-2012-256*/ strcpy(oid_key_type, ("1 2 643 7 1 1 1 1"); memcpy(oid_key_type_asn1 ("\x06\x08\x2a\x85\x03\x07\x01\x01\x01\x01", 10); } } else if (*ulattr == CKK_GOSTR3410_512) {
/*Тип ключа ГОСТ Р 34.10-2012-512*/ strcpy(oid_key_type, ("1 2 643 7 1 1 1 2"); memcpy(oid_key_type_asn1 ("\x06\x08\x2a\x85\x03\x07\x01\x01\x01\x02", 10); } else {
fprintf(stderr, "tclpkcs11_perform_pki_keypair CKK_GOSTR ERROR\n"); return (-1) } break;
. . . }
}
. . .

Теперь у нас есть все исходные данные для создания asn1- структуры публичного ключа.

Напомним, что каждый элемент asn1-структуры состоит из трех полей:

  • идентификатор;
  • длина поля данных (в байтах);
  • поле данных.

Приведем таблицу кодирования некоторых типов идентификаторов, используемых в ИОК/PKI:

Наименование типа

Краткое описание

Представление типа в DER-кодировке

SEQUENCE

Используется для описания структуры данных, состоящей из различных типов.

30

INTEGER

Целое число.

02

OBJECT IDENTIFIER

Последовательность целых чисел.

06

UTCTime

Временной тип, содержит 2 цифры для определения года

17

GeneralizedTime

Расширенный временной тип, содержит 4 цифры для обозначения года.

18

SET

Описывает структуру данных разных типов.

31

UTF8String

Описывает строковые данные.

0C

NULL

Собственно NULL

05

BIT STRING

Тип для хранения последовательности бит.

03

OCTET STRING

Тип для хранения последовательности байт

04

При работе с asn1-структурами наибольший шок для непосвященных вызывает метод кодирования длины поля данных, особенно при его формировании, да если еще учитывать архитектуру компьютера (littleendien, bigendien). Это целая наука. И вот в процессе рассуждения об алгоритме формирования этого поля, на ум пришла мысль использовать функцию sprintf, которая сама будет учитывать архитектуру, а как определяется количество байт для хранения длины видно по коду функции, которая готовит буфер с идентификатором типа данных и длиной данных:

unsigned char *wrap_id_with_length(unsigned char type, //тип данных
unsigned long length, //Длина данных
unsigned long *lenasn) //Возврат длины asn1-структуры
{
// unsigned long length; int buflen = 0; unsigned char *buf; char *format; char *buf_for_len[100]; const char *s;
/*Формат вывода заголовка в зависимости от длины данных*/ char f0[] = "%02x%02x"; char f1[] = "%02x81%02x"; char f2[] = "%02x82%04x"; char f3[] = "%02x83%06x"; char f4[] = "%02x84%08x";
/*Определяем длину буфера для типа и длины данных */ buflen = ( length < 0x80 ? 1: length <= 0xff ? 2: length <= 0xffff ? 3: length <= 0xffffff ? 4: 5);
/*Выделяем буфер для asn-структуры*/ buf = malloc(length + buflen);
// buf = malloc(buflen);
/*В зависимости от длины данных выбираем формат для sprintf*/ switch (buflen - 1) { case 0: format = f0; break; case 1: format = f1; break; case 2: format = f2; break; case 3: format = f3; break; case 4: format = f4; break; }
//Через sprintf мы решаем проблемы little и bigendian и упаковываем тип поля и длину sprintf((char*)buf_for_len, (const char *)format, type, length); length = 0;
/*Печатаем asn1-заголовок*/ fprintf(stderr, "ASN1 - заголовок:%s\n", buf_for_len);
/*Из шестнадцатеричного вида в бинарный вид*/ for (s=(const char *)buf_for_len; *s; s +=2 ) { if (!hexdigitp (s) || (!hexdigitp (s+1) && hexdigitp (s+1) != 0) ){ fprintf (stderr, "invalid hex digits in \"%s\"\n", buf_for_len); *lenasn = 0; return NULL; } ((unsigned char*)buf)[length++] = xtoi_2 (s); } *lenasn = length; return (buf);
}

Функция возвращает указатель на буфер с asn1-структурой, выделенный с учетом длины данных. Осталось эти данные скопировать в полученный буфер со смещением на длину заголовка. Длина заголовка возвращается через параметр lenasn.

Для того, чтобы проверить как работает эта функция, напишем простую утилиту:

#include <stdio.h>
#include <stdlib.h>
#define digitp(p) (*(p) >= '0' && *(p) <= '9')
#define hexdigitp(a) (digitp (a) \ || (*(a) >= 'A' && *(a) <= 'F') \ || (*(a) >= 'a' && *(a) <= 'f'))
#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
int main (int argc, char *argv[]) { unsigned char *hdrasn; unsigned char type; unsigned long length; unsigned long lenasn; if (argc != 3) { fprintf (stderr, "Usage: wrap_id_with_length <id> <length>\n"); exit(-1); } type = atoi(argv[1]); length = atol(argv[2]);
fprintf (stderr, "<id=%02x> <length=%lu>\n", type, length); if (length == 0) { fprintf (stderr, "Bad length=%s\nUsage: wrap_id_with_length <id> <length>\n", argv[2]); exit(-1); } hdrasn = wrap_id_with_length(type, length, &lenasn); fprintf (stderr, "Length asn1-buffer=%lu, LEN_HEADER=%lu, LEN_DATA=%lu\n", lenasn, lenasn - length, length);
}

Сохраним ее вместе с функцией wrap_id_with_length в файле wrap_id_with_length.c.

Оттранслируем:

$cc –o wrap_id_with_length wrap_id_with_length.c
$

Полученную программу запустим с различными исходными данными. Тип данных задается десятичным числом.

Тип данных задается десятичным числом: Полученную программу запустим с различными исходными данными.

bash-4.3$ ./wrap_id_with_length 06 8 <id=06> <length=8> ASN1 - заголовок:0608 Length asn1-buffer=10, LEN_HEADER=2, LEN_DATA=8 bash-4.3$ ./wrap_id_with_length 06 127 <id=06> <length=127> ASN1 - заголовок:067f Length asn1-buffer=129, LEN_HEADER=2, LEN_DATA=127 bash-4.3$ ./wrap_id_with_length 48 128 <id=30> <length=128> ASN1 - заголовок:308180 Length asn1-buffer=131, LEN_HEADER=3, LEN_DATA=128 bash-4.3$ ./wrap_id_with_length 48 4097 <id=30> <length=4097> ASN1 - заголовок:30821001 Length asn1-buffer=4101, LEN_HEADER=4, LEN_DATA=4097 bash-4.3$

Проверить правильность формирования заголовка можно с помощью любого калькулятора:

Но прежде внесем небольшие изменения в функцию wrap_id_with_length и назовем ее Все мы готовы формировать любую ASN1-структуру.

wrap_for_asn1:

unsigned char *wrap_for_asn1(unsigned char type, unsigned char *prefix, unsigned long prefix_len, unsigned char *wrap, unsigned long wrap_len, unsigned long *lenasn){ unsigned long length; int buflen = 0; unsigned char *buf; char *format; const char buf_for_len[100]; const char *s; char f0[] = "%02x%02x"; char f1[] = "%02x81%02x"; char f2[] = "%02x82%04x"; char f3[] = "%02x83%06x"; char f4[] = "%02x84%08x"; length = prefix_len + wrap_len; buflen += ( length <= 0x80 ? 1: length <= 0xff ? 2: length <= 0xffff ? 3: length <= 0xffffff ? 4: 5); buf = malloc(length + buflen); switch (buflen - 1) { case 0: format = f0; break; case 1: format = f1; break; case 2: format = f2; break; case 3: format = f3; break; case 4: format = f4; break; }
//Через sprintf мы решаем проблемы little и bigendian и вычисляем длину sprintf((char*)buf_for_len, (const char *)format, type, length); length = 0; for (s=buf_for_len; *s; s +=2 ) { if (!hexdigitp (s) || (!hexdigitp (s+1) && hexdigitp (s+1) != 0) ){ fprintf (stderr, "invalid hex digits in \"%s\"\n", buf_for_len); } ((unsigned char*)buf)[length++] = xtoi_2 (s); } if (prefix_len > 0) { memcpy(buf + length, prefix, prefix_len); } memcpy(buf + length + prefix_len, wrap, wrap_len); *lenasn = (unsigned long)(length + prefix_len + wrap_len); return (buf);
}

Как видно, изменения минимальные. В качестве входных параметров добавлены сами данные, которые внутри функции пакуются в asn1-структуру. Причем, на вход можно подавать сразу два буфера. Это, как нам кажется, удобно.

Первая функция oid2buffer преобразует oid-ы из точечно-десятичной формы в DER-кодировку. Прежде чем представить контрольный пример дадим коды еще трех функций. Нам эта функция потребуется для преобразования, в частности, oid-ов ключевой пары (см.выше).

Текст функции находится здесь:

static char *oid2buffer(char* oid_str, unsigned long *len){
char *curstr;
char *curstr1;
char *nextstr;
unsigned int firstval;
unsigned int secondval;
unsigned int val;
unsigned char buf[5];
int count;
unsigned char oid_hex[100];
char *res;
int i;
if ( oid_str == NULL ) {
*len = 0;
return NULL;
}
*len = 0;
curstr = strdup((const char*)oid_str);
curstr1 = curstr;
nextstr = strchr(curstr, '.');
if ( nextstr == NULL ) {
*len = 0;
return NULL;
}
*nextstr = '\0';
firstval = atoi(curstr);
curstr = nextstr + 1;
nextstr = strchr(curstr, '.');
if ( nextstr ) {
*nextstr = '\0';
}
secondval = atoi(curstr);
if ( firstval > 2 ) {
*len = 0;
return NULL;
}
if ( secondval > 39 ) {
*len = 0;
return NULL;
}
oid_hex[0] = (unsigned char)(( firstval * 40 ) + secondval);
i = 1;
while ( nextstr ) {
curstr = nextstr + 1;

nextstr = strchr(curstr, '.');

if ( nextstr ) {
*nextstr = '\0';
}

memset(buf, 0, sizeof(buf));
val = atoi(curstr);
count = 0;
if(curstr[0] != '0')
while ( val ) {
buf[count] = ( val & 0x7f );
val = val >> 7;
count++;
}
else{
buf[count] = ( val & 0x7f );
val = val >> 7;
count++;
}
while ( count-- ) {
if ( count ) {
oid_hex[i] = buf[count] | 0x80;
} else {
oid_hex[i] = buf[count];
}
i++;
}
}
res = (char*) malloc(i);
if(res){
memcpy(res, oid_hex, i);
*len = i;
}
free(curstr1);
return res;
}

Две остальные функции позволяют бинарный буфер преобразовать в шестнадцатеричный кол (buffer2hex) и обратно (hex2buffer).

Эти функции находятся здесь:

static char*
buffer2hex (const unsigned char *src, size_t len)
{
int i;
char *dest;
char *res;
dest = (char *)malloc(len * 2 + 1);
res = dest;
if (dest)
{
for (i=0; i
sprintf (dest, "%02X", src[i]);
}
return res;
}

static void *
hex2buffer (const char *string, size_t *r_length)
{
const char *s;
unsigned char *buffer;
size_t length;

buffer = malloc (strlen(string)/2+1);
length = 0;
for (s=string; *s; s +=2 )
{
if (!hexdigitp (s) || !hexdigitp (s+1)){
fprintf (stderr, «invalid hex digits in \»%s\"\n", string);
}
((unsigned char*)buffer)[length++] = xtoi_2 (s);
}
*r_length = length;
return buffer;
}

Эти функции очень удобны при отладке и наверняка у многих они есть.

Напишем утилиту, которая сформирует и сохранит в файле ASN1_PIBINFO.der asn1-структуру публичного ключа. И вот теперь возвращаемся к решению поставленной задачи, получению asn1-структуры публичного ключа.

Эта утилита находится здесь:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdint.h>
#include <string.h> #define digitp(p) (*(p) >= '0' && *(p) <= '9')
#define hexdigitp(a) (digitp (a) \ || (*(a) >= 'A' && *(a) <= 'F') \ || (*(a) >= 'a' && *(a) <= 'f'))
#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
/*Вставьте код функции oid2buffer*/
/*Вставьте код функций buffer2hex и hex2buffer*/
/*Вставьте код функции wrap_for_asn1*/
int main() { int fd; unsigned char *asn, *asn1, *asn2, *asn3, *pubkeyalgo; unsigned char* pubkey_bin;
//Исходные данные char gost3410par[] = "\x06\x7\x2a\x85\x03\x02\x02\x23\x01"; unsigned long gost3410par_len = sizeof(gost3410par) - 1; char gost3411par[] = "\x06\x8\x2a\x85\x03\x07\x01\x01\x02\x02"; unsigned long gost3411par_len = sizeof(gost3411par) - 1; unsigned char pubkey_hex[] = "9af03570ed0c54cd4953f11ab19e551022cd48603326c1b9b630b1cff74e5a160ba1718166cc22bf70f82bdc957d924c501b9332491cb3a36ce45770f05487b5"; char pubkey_oid_2001[] = "1.2.643.2.2.19"; char pubkey_oid_2012_256[] = "1.2.643.7.1.1.1.1"; char pubkey_oid_2012_512[] = "1.2.643.7.1.1.1.2"; unsigned long pubkey_len, pubkey_len_full, len10, len11, len12, lenalgo; unsigned char *pkalgo; unsigned long pkalgo_len; uint16_t x = 1; /* 0x0001 */ printf("%s\n", *((uint8_t *) &x) == 0 ? "big-endian" : "little-endian"); ////pubkeyinfo
//Определяем тип ключа по алгоритмы хэш if (!memmem(gost3411par, 8, "\x2a\x85\x03\x07", 4)) {
//хэш ГОСТ Р 34.11-94, тип ключа ГОСТ Р 34.10-2001 - 1.2.643.2.2.19 pubkeyalgo = (unsigned char *)oid2buffer(pubkey_oid_2001, &lenalgo); } else if (!memcmp(gost3411par, "\x2a\x85\x03\x07\x01\x01\x02\x02", 8)){
//хэш ГОСТ Р 34.11-2012-256, тип ключа ГОСТ Р 34.10-2012-256 - 1.2.643.7.1.1.1.1 pubkeyalgo = (unsigned char *)oid2buffer(pubkey_oid_2012_256, &lenalgo); } else {
//хэш ГОСТ Р 34.11-2012-512, тип ключа ГОСТ Р 34.10-2012-512 - 1.2.643.7.1.1.1.2 pubkeyalgo = (unsigned char *)oid2buffer(pubkey_oid_2012_512, &lenalgo); } pubkey_bin =(unsigned char*)hex2buffer((const char *)pubkey_hex, &pubkey_len);
//Упаковываем значение публичного ключа asn1 = wrap_for_asn1_bin('\x04', (unsigned char *)"", 0, pubkey_bin, pubkey_len, &pubkey_len); asn = wrap_for_asn1_bin('\x03', (unsigned char *)"\x00", 1, asn1, pubkey_len, &pubkey_len_full);
fprintf(stderr, "PUBLIC_VALUE=%s\n", buffer2hex(asn, pubkey_len_full)); free(asn1); //Упаковываем параметры asn3 = wrap_for_asn1_bin('\x30', (unsigned char*)gost3410par, gost3410par_len, (unsigned char *)gost3411par, gost3411par_len, &len12);
fprintf(stderr, "\nPARAMS len12=%lu, FULL=%s\n", len12, buffer2hex(asn3, len12));
//Упаковываем тип ключа pkalgo = wrap_for_asn1_bin('\x06', (unsigned char *)"", 0, pubkeyalgo, lenalgo, &pkalgo_len);
//Упаковываем тип ключа с параметрами asn2 = wrap_for_asn1_bin('\x30', pkalgo, pkalgo_len, asn3, len12, &len11);
fprintf(stderr, "PubKEY=%s\n", buffer2hex(asn3, len11)); asn1 = wrap_for_asn1_bin('\x30', asn2, len11, asn, pubkey_len_full, &len10); free(asn2); free(asn3);
fprintf(stderr, "\n%s\n", buffer2hex(asn1, len10)); fd = open ("ASN1_PUBINFO.der", O_TRUNC|O_RDWR|O_CREAT,S_IRWXO); write(fd, asn1, len10); close(fd); free(asn1); chmod("ASN1_PUBINFO.der", 0666); }

Для проверки результата воспользуемся утилитами derdump и pp из состава пакета NSS.

Первая утилита нам покажет asn1-структуру публичного ключа:

$ derdump -i ASN1_PUBINFO.der C-Sequence (102) C-Sequence (31) Object Identifier (8) 1 2 643 7 1 1 1 2 (GOST R 34.10-2012 Key 512) C-Sequence (19) Object Identifier (7) 1 2 643 2 2 35 1 Object Identifier (8) 1 2 643 7 1 1 2 2 (GOST R 34.11-2012 256) Bit String (67) 00 04 40 9a f0 35 70 ed 0c 54 cd 49 53 f1 1a b1 9e 55 10 22 cd 48 60 33 26 c1 b9 b6 30 b1 cf f7 4e 5a 16 0b a1 71 81 66 cc 22 bf 70 f8 2b dc 95 7d 92 4c 50 1b 93 32 49 1c b3 a3 6c e4 57 70 f0 54 87 b5
$

Вторая покажет содержание ключа:

$ pp -t pk -i ASN1_PUBINFO.der
Public Key: Subject Public Key Info: Public Key Algorithm: GOST R 34.10-2012 512 Public Key: PublicValue: 9a:f0:35:70:ed:0c:54:cd:49:53:f1:1a:b1:9e:55:10: 22:cd:48:60:33:26:c1:b9:b6:30:b1:cf:f7:4e:5a:16: 0b:a1:71:81:66:cc:22:bf:70:f8:2b:dc:95:7d:92:4c: 50:1b:93:32:49:1c:b3:a3:6c:e4:57:70:f0:54:87:b5 GOSTR3410Params: OID.1.2.643.2.2.35.1 GOSTR3411Params: GOST R 34.11-2012 256
$

Желающие могут перепроверить, например утилитой openssl желательно с подключенным ГОСТ-овым engine:

$ /usr/local/lirssl_csp_64/bin/lirssl_static asn1parse -inform DER -in ASN1_PUBINFO.der 0:d=0 hl=2 l= 102 cons: SEQUENCE 2:d=1 hl=2 l= 31 cons: SEQUENCE 4:d=2 hl=2 l= 8 prim: OBJECT :GOST R 34.10-2012 with 512 bit modulus 14:d=2 hl=2 l= 19 cons: SEQUENCE 16:d=3 hl=2 l= 7 prim: OBJECT :id-GostR3410-2001-CryptoPro-A-ParamSet 25:d=3 hl=2 l= 8 prim: OBJECT :GOST R 34.11-2012 with 256 bit hash 35:d=1 hl=2 l= 67 prim: BIT STRING $

Как видим, полученная ASN1структура везде успешно проходит проверку.

Мы их еще вспомним в следующей статье, когда исполнится пожелание Pas и будет представлена графическая утилита, делающая не только «парсинг сертификатов» и проверку их валидности, но и генерирующая ключевую пару на токенах PKCS#11, формирующая и подписывающая запрос на квалифицированным сертификат. Предложенный алгоритм и утилита формирование asn1-структур не требует использования никаких ASN1-компиляторов и доролнительных библиотек (той же openssl) и оказались очень удобными в использовании. Опережая вопросы, сразу отмечу, что в последнем случае токен должен быть сертифицирован как СКЗИ в системе сертификации ФСБ России. С этим запросом можно смело отправляться на УЦ за сертификатом.


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

[Перевод] История транзистора, часть 2: из горнила войны

Другие статьи цикла: История реле История электронных компьютеров История транзистора Горнило войны подготовило почву для появления транзистора. С 1939 по 1945 года технические знания из области полупроводников невероятно сильно разрослись. И тому была одна простая причина: радар. Самая важная технология ...

Что можно сделать через разъем OBD в автомобиле

Ни для кого не секрет, что в современных автомобилях все системы под завязку забиты различной электроникой, даже простой стеклоподъемник имеет собственный микроконтроллер и адрес в общей сети. Мне, как интересующемуся владельцу, стало интересно, что же можно сделать имея просто доступ ...