Simple sprintf ASN1 codec

    imageASN.1 transport syntax defines a unique way to convert values ​​of variables of valid types into a sequence of bytes for transmission over a network. In ASN.1, it is called Basic Encoding Rules (BER). Rules are recursive, so encoding composite objects is a chaining of encoded sequences of component objects. The ASN.1 protocol describes the data structure in a simple and understandable language .

    Each transmitted value - both basic and derived type - consists of three fields:

    • identifier;
    • data field length (in bytes);
    • data field.

    If you always specify the length of the data field (I consider this a good practice), then the flag of the end of the data field is not used.

    There are many different compilers for ASN.1, both paid and free, for different programming languages, but we would like to have something very simple at hand.

    The vast majority of software developers find the ASN.1 standard complex. I thought so too until recently. Working in the field of PKI / PKI / cryptography almost every day you deal with ASN1 structures in the form of X509 certificates, certificate requests, lists of revoked certificates. And the list goes on. And so, while working on a utility for creating a certificate request in PKCS # 10 format with generating a key pair on a PKCS # 11 token / smartcard, I naturally had to form, in particular, the asn1-structure of the public key for writing it to the certificate request :

    C-Sequence  
       C-Sequence  (<длина>)
          Object Identifier  (<длина>)
             
          C-Sequence  (<длина>)
             Object Identifier  (<длина>)
                
             Object Identifier  (<длина>)
                
       Bit String  (<длина>)
    	<значение публичного ключа>

    Since we used the PKCS # 11 token with the support of Russian cryptography as a cryptographic information protection tool, the source material for this structure was obtained from the token in accordance with the following template:

        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_GOSTR3410PARAMS, gostr3410par, sizeof(gostr3410par)},
    	    {CKA_GOSTR3411PARAMS, gostr3411par, sizeof(gostr3410par)},
    	    {CKA_VALUE, pubkey, sizeof(pubkey)},
    	    {CKA_KEY_TYPE, &key_type, sizeof(key_type)}
        }

    Directly from this structure, the values ​​of the CKA_VALUE attribute containing the public key value and the values ​​of the CKA_GOSTR3410PARAMS and CKA_GOSTR3411PARAMS attributes that contain the oids of the signature parameter and the hash parameter will be used to fill asn1-publickeyinfo.

    The attribute CKA_KEY_TYPE, which can take the values ​​CKK_GOSTR3410 and CKK_GOSTR3410_512 (in conditions when the GOST R 34.10-2001 signature algorithm continues to function) ambiguously defines the key pair algorithm. If the value of the CKA_KEY_TYPE attribute is CKK_GOSTR3410_512, then, of course, it uniquely points to the GOST R 34.10-2012 algorithm with a key length of 512 bits (oid = 1.2.643.7.1.1.1.2). But if it is simply equal to CKK_GOSTR3410, then there is ambiguity about what type of key this key belongs to: GOST R 34.10-2001 or still it is GOST R 34.10-2012 with a key length of 256 bits. The CKA_GOSTR3411PARAMS attribute helps resolve this ambiguity.

    Immediately, we note that the parameters CKA_GOSTR3410PARAMS and CKA_GOSTR3411PARAMS on the token in accordance with the recommendations of TK-26 are stored as an object identifier encoded by oid, for example:

    \ x06 \ x06 \ x2a \ x85 \ x03 \ x02 \ x02 \ x13, where zero byte determines the type of sequence (0x06 is an object identifier, see the table below), the second byte indicates the length (in the general case, the length may take several bytes, but more on that below) the data field in which oid is stored in binary form.

    If this parameter contains the oid of the GOST R 34.10-2012 hash algorithm with a length of 256 bits (oid = 1.2.643.7.1.1.2.2, in binary form "\ x2a \ x 85 \ x 03 \ x 07 \ x 01 \ x 01 \ x 02 \ x02 "), then the key type must be set as GOST R 34.10-2012 with a key length of 256 bits. Otherwise, it is the key of GOST R 34.10-2001. The algorithm for determining the type of key may look like this:

    . . .
    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;
    . . .
      }
    }
    . . .

    Now we have all the input data for creating the asn1 public key structure.

    Recall that each element of the asn1-structure consists of three fields:

    • identifier;
    • data field length (in bytes);
    • data field.

    Here is a coding table for some types of identifiers used in PKI / PKI:

    Type NameShort descriptionType representation in DER encoding
    SEQUENCEUsed to describe a data structure consisting of various types.thirty
    INTEGERInteger.02
    OBJECT IDENTIFIERA sequence of integers.06
    UTCTimeTemporary type, contains 2 digits for determining the year17
    GeneralizedtimeExtended time type, contains 4 digits to indicate the year.18
    SETDescribes the data structure of various types.31
    UTF8StringDescribes string data.0C
    NullActually NULL05
    BIT STRING Type for storing a sequence of bits.03
    OCTET STRING Type for storing a sequence of bytes 04

    When working with asn1-structures, the greatest shock for the uninitiated is caused by the method of encoding the length of the data field, especially when it is formed, and if we take into account the architecture of the computer (littleendien, bigendien). This is a whole science . And so, in the process of discussing the algorithm for generating this field, the idea came to mind to use the sprintf function, which itself will take into account the architecture, and how the number of bytes for storing the length is determined can be seen by the function code, which prepares a buffer with the data type identifier and data length:

    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);
    }

    The function returns a pointer to a buffer with an asn1 structure, allocated taking into account the length of the data. It remains to copy this data into the received buffer with an offset by the length of the header. The length of the header is returned via the lenasn parameter.

    In order to check how this function works, we will write a simple utility:

    #include 
    #include 
    #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 \n");
            exit(-1);
        }
        type = atoi(argv[1]);
        length = atol(argv[2]);
    fprintf (stderr, "\n", type, length);
        if (length == 0) {
            fprintf (stderr, "Bad length=%s\nUsage: wrap_id_with_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);
    }

    Save it together with the wrap_id_with_length function in the wrap_id_with_length.c file.

    We broadcast:

    $cc –o wrap_id_with_length wrap_id_with_length.c
    $

    Run the resulting program with various source data. The data type is specified by a decimal number:

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

    You can check the correctness of the header formation using any calculator:



    We are all ready to create any ASN1-structure. But first, make small changes to the wrap_id_with_length function and call it

    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);
    }


    As you can see, the changes are minimal. As input parameters, the data itself is added, which inside the function is packed into an asn1-structure. Moreover, two buffers can be fed to the input at once. It seems to us convenient.

    Before presenting a test case, we give the codes of three more functions. The first oid2buffer function converts oid s from dotted decimal form to DER encoding. We will need this function to convert, in particular, oids of the key pair (see above).

    The function text is here:
    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'

    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;
    }

    The other two functions allow the binary buffer to be converted to a hexadecimal count (buffer2hex) and vice versa (hex2buffer).

    These functions are here:
    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 <len; i ++, dest + = 2)
    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;
    }

    These functions are very convenient for debugging and for sure many have them.

    And now we return to the solution of the task, obtaining an asn1-structure of the public key. We will write a utility that will generate and save the asn1 public key structure in the ASN1_PIBINFO.der file.

    This utility is located here:
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #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); 
    }


    To verify the result, we will use the derdump and pp utilities from the NSS package.

    The first utility will show us the asn1 structure of the public key:

    $ 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
    $

    The second will show the contents of the key:

    $ 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
    $

    Those who wish can double-check, for example, it is desirable to use openssl utility with a connected GOST 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         
    $

    As you can see, the resulting ASN1 structure is successfully tested everywhere.

    The proposed algorithm and utility for forming asn1 structures does not require the use of any ASN1 compilers and extension libraries (the same openssl) and turned out to be very convenient to use. We will recall them in the next article, when Pas wishes will be fulfilled and a graphical utility will be presented that does not only “parsing certificates” and verifies their validity, but also generates a key pair on PKCS # 11 tokens, generates and signs a request for a qualified certificate. With this request, you can safely go to the CA for a certificate. Ahead of the questions, I immediately note that in the latter case, the token must be certified as a cryptographic information protection system in the certification system of the FSB of Russia.

    Also popular now: