Infraestructura de clave pública: biblioteca GCrypt como alternativa a OpenSSL con soporte para criptografía rusa

Se acerca la segunda mitad de 2018 y pronto llegará el "año 2000" en el PKI basado en la criptografía rusa. Esto se debe al hecho de que
¡El uso del esquema de firma GOST R 34.10-2001 para generar una firma después del 31 de diciembre de 2018 no está permitido!
Hoy no tiene sentido recibir certificados con una firma de acuerdo con GOST R 34.10-2001.
Al mismo tiempo, muchos servicios o aplicaciones se desarrollan sobre la base de OpenSSL, que apoyó el trabajo con GOST R 34.10-2001.

Pero hoy en la versión estándar de openssl no hay soporte para GOST R 34.11-2012 y GOST R 34.10-2012. Además, en la versión 1.1, el soporte de criptografía GOST está excluido de la distribución estándar ("El motor GOST estaba desactualizado y, por lo tanto, se ha eliminado").

Todo esto nos hace buscar formas alternativas de trabajar con certificados, con firma electrónica ("mensajes en formato CMS") y otros objetos PKI basados ​​en la nueva criptografía rusa.

Una de estas formas posibles es usar la biblioteca GCrypt . Esta biblioteca admite los nuevos algoritmos GOST R 34.11-2012 (algoritmos de hashing) y GOST R 34.10-2012 (algoritmos de firma).

Generación de pares de claves


Entonces, comenzamos generando un par de claves que contienen claves privadas y públicas. En la actualidad, en la criptografía rusa hay tres tipos de claves de firma con los correspondientes oid-s
- GOST R 34.10-2001 con una longitud de clave de 256 bits, oid 1.2.643.2.2.19 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x13);
- GOST R 34.10-2012 con una longitud de clave de 256 bits (en adelante GOST R 34.10-12-256), oid 1.2.643.7.1.1.1.1 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x01, 0x01);
- GOST R 34.10-2012 con una longitud de clave de 512 bits (en adelante GOST R 34.10-12-512), oid 1.2.643.7.1.1.1.2 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x01, 0x02).
¡Y noten de inmediato que en términos de matemáticas, algoritmos de generación y su implementación, las claves GOST R 34.10-2001 y GOST R 34.10-12-256 son absolutamente idénticas! Así como algoritmos idénticos para la formación de firmas electrónicas basadas en ellos. ¿Cuál de estas claves en cuestión puede juzgarse solo por información sobre la clave contenida, por ejemplo, en el certificado? Entonces, ¿por qué tomó dos oid diferentes? Solo enfatice el hecho de que al generar una firma electrónica con la clave GOST R 34.10-2001, se debe usar el hash obtenido de acuerdo con GOST R 34.10-94, y cuando se usa la clave GOST R 34.10-12-256, el hash obtenido de acuerdo con GOST R 34.10- 212 con una longitud de 256 bits. Aunque, para enfatizar esto, hay oid-s correspondientes:
- 1.2.643.2.2.3 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x03) - algoritmo de firma GOST R 34.10-2001 con clave 256 con hashing GOST R 34.11-94;
- 1.2.643.7.1.1.3.2 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x03, 0x02) - algoritmo de firma GOST R 34.10-2012 con clave 256 con hashing GOST R 34.11-2012;
- 1.2.643.7.1.1.3.3 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x03, 0x03) - algoritmo de firma GOST R 34.10-2012 con clave 512 con hashing GOST R 34.11-2012.
Por lo tanto, hay algo de clasificación con oid-s, pero es decir, eso es.

Las claves de la familia GOST pertenecen a la familia clave en curvas elípticas. Para generar un par de claves, debe especificar un punto base en una curva elíptica.

El comité técnico para la estandarización “Seguridad de la información criptográfica” ( TC 26 ) recomendó utilizar dos puntos base para las claves GOST R 34.10-2012-512:
- GOST2012-tc26-A (apodo en la terminología de libgcrypt) con oid 1.2.643.7.1.2.1.2.1 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x01, 0x02, 0x01);
- GOST2012-tc26-B con oid 1.2.643.7.1.2.1.2.2 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x01, 0x02, 0x02);
Para claves GOST R 34.10 con una longitud de 256 bits, se recomiendan tres puntos base:
- GOST2001-CryptoPro-A con oid om 1.2.643.2.2.35.1 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01);
- GOST2001-CryptoPro-B con oid 1.2.643.2.2.35.2 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x02);
- GOST2001-CryptoPro-C con oid 1.2.643.2.2.35.3 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x03).
Para las claves GOST R 34.10 con una longitud de 256 bits, se definen dos oid más para los puntos base:
- GOST2001-CryptoPro-XchA con oid 1.2.643.2.2.36.0 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x24, 0x00);
- GOST2001-CryptoPro-XchB con oid 1.2.643.2.2.36.1 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x24, 0x01).
Sin embargo, en realidad, estos oids se refieren a los puntos base de GOST2001-CryptoPro-A con oid 1.2.643.2.2.35.1 y GOST2001-CryptoPro-C con oid 1.2.643.2.2.35.3 respectivamente.
Y esto debe tenerse en cuenta al procesar puntos base con estos oid-s.

Para generar un par de claves, se utiliza la función gcry_pk_genkey de la siguiente forma:

gcry_error_t gcry_pk_genkey (gcry sexp t *key_pair, gcry sexp t key_spec ). 

Los parámetros deben establecerse en la variable parms para generar el par de claves en el formato de la expresión S interna (sexp). Para generar un par de claves de acuerdo con GOST, los parámetros se establecen en la forma de la siguiente expresión S:

 (genkey ( (curve _))),  

ecc determina la generación del par de claves en curvas elípticas, y el punto base debe indicar el punto específico recomendado por el TK-26. Es el punto base especificado que determina qué par de claves se generará. Si especifica, por ejemplo, GOST2012-tc26-A o GOST2012-tc26-B, se generará un par de claves de acuerdo con GOST R 34.10-2012 con una longitud de clave de 512 bits. En lugar del apodo del punto base, puede especificar oid directamente:

 (genkey ( (curve «1.2.643.2.2.35.3»))) 

En el último caso, se supone que el par de claves se generará de acuerdo con GOST R 34.10 con una longitud de clave de 256 bits y un punto base de GOST2001-CryptoPro-C.

A continuación se muestra un ejemplo de programa C que demuestra la generación de claves

GenKey.c
 #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <gcrypt.h> /*  S-*/ static void show_sexp (const char *prefix, gcry_sexp_t a) { char *buf; size_t size; if (prefix) fputs (prefix, stderr); size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0); buf = gcry_xmalloc (size); gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size); fprintf (stderr, "%.*s", (int)size, buf); gcry_free (buf); } int main(int argc, char* argv[]) { gpg_error_t err; gcry_sexp_t key_spec, key_pair, pub_key, sec_key; /* */ char *curve_gost[] = {"GOST2001-CryptoPro-A", "GOST2001-CryptoPro-B", "GOST2001-CryptoPro-C", "GOST2012-tc26-A", "GOST2012-tc26-B", NULL}; /*     */ err = gcry_sexp_build (&key_spec, NULL, "(genkey (ecc (curve %s)))", curve_gost[1]); if (err) { fprintf(stderr, "creating S-expression failed: %s\n", gcry_strerror (err)); exit (1); } err = gcry_pk_genkey (&key_pair, key_spec); if (err){ fprintf(stderr, "creating %s key failed: %s\n", argv[1], gcry_strerror (err)); exit(1); } /* S-  */ show_sexp ("ECC GOST key pair:\n", key_pair); /*  */ pub_key = gcry_sexp_find_token (key_pair, "public-key", 0); if (! pub_key) { fprintf(stderr, "public part missing in key\n"); exit(1); } /* S-  */ show_sexp ("ECC GOST public key:\n", pub_key); /*  */ sec_key = gcry_sexp_find_token (key_pair, "private-key", 0); if (! sec_key){ fprintf(stderr, "private part missing in key\n"); exit(1); } /* S-  */ show_sexp ("ECC GOST private key:\n", sec_key); /* ,   */ gcry_sexp_release (key_pair); /* ,    */ gcry_sexp_release (key_spec); } 


Para difundir el ejemplo, debe ejecutar el comando:

 $cc –o GenKey GenKey.c –lgcrypt $ 

Después de iniciar el módulo GenKey, obtenemos

par de llaves
 ECC GOST key pair: (key-data (public-key (ecc (curve GOST2001-CryptoPro-B) (q #043484CF83F837AAC7ABD4707DE27F5A1F6161120C0D77B63DFFC7D50A7772A12D1E836E6257766E8B83209DD59845F8080BA29E9A86D0A6B6C2D68F44650B3A14#) ) ) (private-key (ecc (curve GOST2001-CryptoPro-B) (q #043484CF83F837AAC7ABD4707DE27F5A1F6161120C0D77B63DFFC7D50A7772A12D1E836E6257766E8B83209DD59845F8080BA29E9A86D0A6B6C2D68F44650B3A14#) (d #1ABB5A62BFF88C97567B467C6F4017242FE344B4F4BC8906CE40A0F9D51CBE48#) ) ) ) ECC GOST public key: (public-key (ecc (curve GOST2001-CryptoPro-B) (q #043484CF83F837AAC7ABD4707DE27F5A1F6161120C0D77B63DFFC7D50A7772A12D1E836E6257766E8B83209DD59845F8080BA29E9A86D0A6B6C2D68F44650B3A14#) ) ) ECC GOST private key: (private-key (ecc (curve GOST2001-CryptoPro-B) (q #043484CF83F837AAC7ABD4707DE27F5A1F6161120C0D77B63DFFC7D50A7772A12D1E836E6257766E8B83209DD59845F8080BA29E9A86D0A6B6C2D68F44650B3A14#) (d #1ABB5A62BFF88C97567B467C6F4017242FE344B4F4BC8906CE40A0F9D51CBE48#) ) ) 

Ahora, con la clave privada en la mano, puede crear una firma electrónica.

Hashing de documentos


La creación de una firma electrónica (ES) de un documento comienza con la obtención de un valor hash del documento que se firma. Para firmar un documento, se puede seleccionar uno de los algoritmos:
- 1.2.643.2.2.3 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x03) - algoritmo de firma GOST R 34.10-2001 con clave 256 con hash según GOST R 34.11-94 con longitud de hash 256 bits;
- 1.2.643.7.1.1.3.2 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x03, 0x02) - algoritmo de firma GOST R 34.10-2012 con una clave de 256 con hash según GOST R 34.11-2012 con una longitud de hash de 256 bits;
- 1.2.643.7.1.1.3.3 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x03, 0x03) - algoritmo de firma GOST R 34.10-2012 con una clave de 512 con hash según GOST R 34.11-2012 con una longitud de hash de 512 bits.
Estos algoritmos determinan no solo el tipo de clave privada que se utilizará para obtener la firma electrónica, sino también el algoritmo de función hash. La biblioteca GCrypt implementa los tres tipos de funciones. El algoritmo hash GOST R 34.11-94 (con el parámetro oid 1.2.643.2.2.30.1 - 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01) se implementa bajo el apodo GOSTR3411_CP, el algoritmo GOST R 34.11-2012 con longitud 256 bits (oid 1.2.43.7.1.1.2.2 - 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02) se implementa bajo el apodo STRIBOG256 y el algoritmo GOST R 34.11-2012 con una longitud de 512 bits (oid 1.2.43.7 .1.1.2.3 - 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03) se implementa bajo el apodo STRIBOG512.

A continuación se muestra el código del módulo en C para calcular el valor hash de un documento almacenado en un archivo

digest_gcrypt.c:
 #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <gcrypt.h> int main(int argc, char* argv[]) { gpg_error_t err; int algo = GCRY_MD_NONE; int i; unsigned char *h; size_t size; gcry_md_hd_t hd; FILE *fp; unsigned char buf[1024]; char *dgst_gost[] = {"GOSTR3411_CP", "STRIBOG256", "STRIBOG512", NULL}; i = 0; /*  -*/ if (argc == 3) algo = gcry_md_map_name (argv[1]); if (algo == GCRY_MD_NONE) { fprintf(stderr, "Usage: digest_gcrypt <nick_name_digest> <file_for_digest>\n"); fprintf(stderr, "<nick_name_digest>="); while (dgst_gost[i] != NULL){ if (i > 0 ) fprintf(stderr, " | ", dgst_gost[i]); fprintf(stderr, "%s", dgst_gost[i]); i++; } fprintf(stderr, "\n"); exit (1); } /*   */ err = gcry_md_open(&hd, algo, 0); if (err) { fprintf (stderr, "LibGCrypt error %s\n", gcry_strerror (err)); exit (1); } /*  */ if (!strcmp (argv[2], "-")) fp = stdin; else fp = fopen (argv[2], "r"); if (fp == NULL) { fprintf(stderr, "Cannot fopen file=%s\n", argv[2]); exit(1); } /*   */ while (!feof (fp)) { size = fread (buf, 1, sizeof(buf), fp); gcry_md_write (hd, buf, size); } /*  */ h = gcry_md_read(hd, 0); /*  */ printf("  %s = %d ( )\n", argv[1], gcry_md_get_algo_dlen (algo)); printf("   %s:\n", argv[2]); for (i = 0; i < gcry_md_get_algo_dlen (algo); i++) printf("%02x", h[i]); printf("\n"); fflush(stdout); /* */ gcry_md_reset(hd); gcry_md_close(hd); } 

Traducimos este módulo y obtenemos la utilidad de cálculo hash:

 $cc -o digest_file digest_file.c -lgcrypt $./digest_file Usage: digest_gcrypt <nick_name_digest> <file_for_digest> <nick_name_digest>=GOSTR3411_CP | STRIBOG256 | STRIBOG512 $./digest_file STRIBOG256 digest_file.c   STRIBOG256 = 32 ( )    digest_file.c: f6818dfb26073747266dc721c332d703eb21f2b17e3433c809e0e23b68443d4a $ 

Formación de firma electrónica y su verificación.


Ahora que tenemos la clave privada y el hash del documento, también podemos generar una firma electrónica del documento:

 gcry_error_t gcry_pk_sign (gcry sexp t *r_sig, gcry sexp t data, gcry sexp t skey ),  

r_sig -sexp-variable en la que se guardará la firma electrónica,
skey : una variable sexp con una clave privada (ver arriba), que se utiliza para generar una firma,
data - sexp-variable, que indica el tipo de firma (en nuestro caso, gost) y el hash del documento firmado.
Debe recordarse que el valor hash proporcionado a la entrada gcry_pk_sign para generar la firma GOST R 34.10 debe estar en formato big-endian, es decir en realidad se puso patas arriba (como dijo un amigo: "La tradición rusa de tratar bytes de resumen en orden little-endian"). Con esto en mente, la preparación de la variable de datos datap es la siguiente:

 ... gcry_sexp_t data; unsigned char c; int len_xy; gcry_mpi_t x; … /*    */ printf("%s\n", *((unsigned char *) &arch) == 0 ? " big-endian" : " little-endian"); len_xy = *((unsigned char *) &arch) == 0 ? 0:gcry_md_get_algo_dlen (algo); for (i = 0; i < (len_xy/2); i++) { c = *(h + i); *(h + i) = *(h + len_xy - i - 1); *(h + len_xy - i - 1) = c; } fprintf(stderr, " =%d\n", gcry_md_get_algo_dlen (algo)); for (i = 0; i < gcry_md_get_algo_dlen (algo); i++) printf("%02X", h[i]); fflush(stdout); /*   mpi-*/ x = gcry_mpi_set_opaque_copy(NULL, h, gcry_md_get_algo_dlen (algo) * 8); /* sexp- data   –   */ err = gcry_sexp_build (&data, NULL, "(data (flags gost) (value %m))", x); /*    data */ show_sexp ("data :\n", data); 
Todo está listo para recibir la firma y verla <source: lang = "cpp"> ...
gcry_sexp_t sig_r, sig_s;
...
/ * Firma el hash * /
err = gcry_pk_sign (& sig, data, sec_key);
si (err) {
fprintf (stderr, "error de firma:% s \ n", gcry_strerror (err));
salida (1);
}
/ * Imprimimos la firma * /
show_sexp ("ECC GOST SIG: \ n", sig);
/ * Seleccione e imprima los componentes de la firma r y s * /
sig_r = gcry_sexp_find_token (sig, "r", 0);
if (! sig_r) {
fprintf (stderr, "falta parte r en sig \ n");
salida (1);
}
show_sexp ("Parte R de ECC GOST Sig: \ n", sig_r);
sig_s = gcry_sexp_find_token (sig, "s", 0);
if (! sig_s) {
fprintf (stderr, "parte que falta en sig \ n");
salida (1);
}
show_sexp ("Parte S de ECC GOST Sig: \ n", sig_s);
... Puede verificar la firma de la siguiente manera:
 … err = gcry_pk_verify (sig, data, pub_key); if (err) { putchar ('\n'); show_sexp ("seckey:\n", sec_key); show_sexp ("data:\n", data); show_sexp ("sig:\n", sig); fprintf(stderr, "verify failed: %s\n", gcry_strerror (err)); exit(1); } … 

Verificación de la firma electrónica de GOST R 34.10 en certificados


Uno de los principales objetos de la infraestructura de clave pública (PKI) son los certificados X509. Las recomendaciones del TC-26 sobre la composición y estructura de los certificados se establecen en el documento "ESPECIFICACIONES TÉCNICAS DE USO DE LOS ALGORITMOS GOST R 34.10, GOST R 34.11 EN EL PERFIL DEL CERTIFICADO Y CERTIFICADO REVISIÓN CRL (CRL) DE LA INFRAESTRUCTURA ABIERTA DE LA CLAVE 9 de la X. Comité de X. protección de la información "(Acta No. 13 del 24/04/2014)". De conformidad con estas recomendaciones, todas las AC acreditadas en el Ministerio de Comunicaciones de Rusia emiten certificados.

Para verificar la firma en el certificado, es necesario (ver arriba) obtener el hash del certificado verificado, su firma y la clave pública del certificado raíz. Solo tenga en cuenta que para un certificado autofirmado, todos estos datos se almacenan en un solo certificado.
Para trabajar con objetos PKI / PKI (certificados, CMS, solicitudes
etc.), como regla general, se utiliza la biblioteca KSBA, que por el momento no es compatible con las recomendaciones de TK-26, aunque existe experiencia de dicho soporte. Básicamente, nada impide agregar soporte para las recomendaciones TK-26 al proyecto ksba.
En esta etapa, la etapa de prueba de GCrypt, es conveniente usar lenguajes de script como Python , Tcl, etc. para trabajar con objetos PKI / PKI (certificados, etc.) con criptografía rusa. Se seleccionó el lenguaje de script Tcl . Es fácil y sencillo escribir un programa prototipo en él, que luego puede transferirse al lenguaje C. Tcl incluye un paquete PKI que contiene procedimientos para analizar objetos PKI, en particular, el procedimiento para analizar certificados :: pki :: x509 :: parse_cert. Basado en el procedimiento parse_cert, se desarrolló el procedimiento parse_gost_cert, que se encuentra en el archivo

parse_cert_gost_oid.tcl
 proc parse_cert_gost {cert} { # parray ::pki::oids #puts "parse_cert_gost=$cert" set cert_seq "" if { [string range $cert 0 9 ] == "-----BEGIN" } { array set parsed_cert [::pki::_parse_pem $cert "-----BEGIN CERTIFICATE-----" "-----END CERTIFICATE-----"] set cert_seq $parsed_cert(data) } else { #FORMAT DER set cert_seq $cert } set finger [::sha1::sha1 $cert_seq] set ret(fingerprint) $finger binary scan $cert_seq H* certdb set ret(certdb) $certdb #puts "CERTDB=$certdb" array set ret [list] # Decode X.509 certificate, which is an ASN.1 sequence ::asn::asnGetSequence cert_seq wholething ::asn::asnGetSequence wholething cert set ret(cert) $cert set ret(cert) [::asn::asnSequence $ret(cert)] if {0} { set ff [open "/tmp/tbs.der" w] fconfigure $ff -translation binary puts -nonewline $ff $ret(cert) close $ff } binary scan $ret(cert) H* ret(cert) ::asn::asnPeekByte cert peek_tag if {$peek_tag != 0x02} { # Version number is optional, if missing assumed to be value of 0 ::asn::asnGetContext cert - asn_version ::asn::asnGetInteger asn_version ret(version) incr ret(version) } else { set ret(version) 1 } ::asn::asnGetBigInteger cert ret(serial_number) ::asn::asnGetSequence cert data_signature_algo_seq ::asn::asnGetObjectIdentifier data_signature_algo_seq ret(data_signature_algo) ::asn::asnGetSequence cert issuer ::asn::asnGetSequence cert validity ::asn::asnGetUTCTime validity ret(notBefore) ::asn::asnGetUTCTime validity ret(notAfter) ::asn::asnGetSequence cert subject ::asn::asnGetSequence cert pubkeyinfo binary scan $pubkeyinfo H* pubkeyinfoG set ret(pubkeyinfo) $pubkeyinfoG ::asn::asnGetSequence pubkeyinfo pubkey_algoid binary scan $pubkey_algoid H* pubkey_algoidG set ret(pubkey_algoid) $pubkey_algoidG ::asn::asnGetObjectIdentifier pubkey_algoid ret(pubkey_algo) ::asn::asnGetBitString pubkeyinfo pubkey set extensions_list [list] while {$cert != ""} { ::asn::asnPeekByte cert peek_tag switch -- [format {0x%02x} $peek_tag] { "0xa1" { ::asn::asnGetContext cert - issuerUniqID } "0xa2" { ::asn::asnGetContext cert - subjectUniqID } "0xa3" { ::asn::asnGetContext cert - extensions_ctx ::asn::asnGetSequence extensions_ctx extensions while {$extensions != ""} { ::asn::asnGetSequence extensions extension ::asn::asnGetObjectIdentifier extension ext_oid ::asn::asnPeekByte extension peek_tag if {$peek_tag == 0x1} { ::asn::asnGetBoolean extension ext_critical } else { set ext_critical false } ::asn::asnGetOctetString extension ext_value_seq set ext_oid [::pki::_oid_number_to_name $ext_oid] set ext_value [list $ext_critical] switch -- $ext_oid { id-ce-basicConstraints { ::asn::asnGetSequence ext_value_seq ext_value_bin if {$ext_value_bin != ""} { ::asn::asnGetBoolean ext_value_bin allowCA } else { set allowCA "false" } if {$ext_value_bin != ""} { ::asn::asnGetInteger ext_value_bin caDepth } else { set caDepth -1 } lappend ext_value $allowCA $caDepth } default { binary scan $ext_value_seq H* ext_value_seq_hex lappend ext_value $ext_value_seq_hex } } lappend extensions_list $ext_oid $ext_value } } } } set ret(extensions) $extensions_list ::asn::asnGetSequence wholething signature_algo_seq ::asn::asnGetObjectIdentifier signature_algo_seq ret(signature_algo) ::asn::asnGetBitString wholething ret(signature) # Convert values from ASN.1 decoder to usable values if needed set ret(notBefore) [::pki::x509::_utctime_to_native $ret(notBefore)] set ret(notAfter) [::pki::x509::_utctime_to_native $ret(notAfter)] set ret(serial_number) [::math::bignum::tostr $ret(serial_number)] set ret(data_signature_algo) [::pki::_oid_number_to_name $ret(data_signature_algo)] set ret(signature_algo) [::pki::_oid_number_to_name $ret(signature_algo)] set ret(pubkey_algo) [::pki::_oid_number_to_name $ret(pubkey_algo)] set ret(issuer) [::pki::x509::_dn_to_string $issuer] set ret(subject) [::pki::x509::_dn_to_string $subject] set ret(signature) [binary format B* $ret(signature)] binary scan $ret(signature) H* ret(signature) # Handle RSA public keys by extracting N and E #puts "PUBKEY_ALGO=$ret(pubkey_algo)" switch -- $ret(pubkey_algo) { "rsaEncryption" { set pubkey [binary format B* $pubkey] binary scan $pubkey H* ret(pubkey) ::asn::asnGetSequence pubkey pubkey_parts ::asn::asnGetBigInteger pubkey_parts ret(n) ::asn::asnGetBigInteger pubkey_parts ret(e) set ret(n) [::math::bignum::tostr $ret(n)] set ret(e) [::math::bignum::tostr $ret(e)] set ret(l) [expr {int([::pki::_bits $ret(n)] / 8.0000 + 0.5) * 8}] set ret(type) rsa } "GostR2012_256" - "GostR2012_512" - "GostR2001" - "1.2.643.2.2.19" - "1.2.643.7.1.1.1.1" - "1.2.643.7.1.1.1.2" { # gost2001, gost2012-256,gost2012-512 set pubkey [binary format B* $pubkey] #puts "LL=[string length $pubkey]" if {[string length $pubkey] < 100} { set pubk [string range $pubkey 2 end] } else { set pubk [string range $pubkey 3 end] } set pubkey_revert [string reverse $pubk] binary scan $pubkey_revert H* ret(pubkey_rev) binary scan $pubkey H* ret(pubkey) set ret(type) gost ::asn::asnGetSequence pubkey_algoid pubalgost #OID -  ::asn::asnGetObjectIdentifier pubalgost ret(paramkey) set ret(paramkey) [::pki::_oid_number_to_name $ret(paramkey)] #OID -   ::asn::asnGetObjectIdentifier pubalgost ret(hashkey) set ret(hashkey) [::pki::_oid_number_to_name $ret(hashkey)] #puts "ret(paramkey)=$ret(paramkey)\n" #puts "ret(hashkey)=$ret(hashkey)\n" } } return [array get ret] } proc set_nick_for_oid {} { # set ::pki::oids(1.2.643.2.2.19) "gost2001pubKey" # set ::pki::oids(1.2.643.2.2.3) "gost2001withGOST3411_94" set ::pki::oids(1.2.643.100.1) "OGRN" set ::pki::oids(1.2.643.100.5) "OGRNIP" set ::pki::oids(1.2.643.3.131.1.1) "INN" set ::pki::oids(1.2.643.100.3) "SNILS" #  set ::pki::oids(1.2.643.2.2.3) "  34.10-2001-256" set ::pki::oids(1.2.643.7.1.1.3.2) "  34.10-2012-256" set ::pki::oids(1.2.643.7.1.1.3.3) "  34.10-2012-512" # set ::pki::oids(1.2.643.2.2.3) "gost" # set ::pki::oids(1.2.643.7.1.1.3.2) "gost" # set ::pki::oids(1.2.643.7.1.1.3.3) "gost" #  # set ::pki::oids(1.2.643.2.2.19) "  34.10-2001" # set ::pki::oids(1.2.643.7.1.1.1.1) "  34.10-2012 256 " # set ::pki::oids(1.2.643.7.1.1.1.2) "  34.10-2012 512 " set ::pki::oids(1.2.643.2.2.19) "GostR2001" set ::pki::oids(1.2.643.7.1.1.1.1) "GostR2012_256" set ::pki::oids(1.2.643.7.1.1.1.2) "GostR2012_512" #Oid-     34.10-2001    34.10-2012-256 set ::pki::oids(1.2.643.2.2.35.0) "GOST2001-test" set ::pki::oids(1.2.643.2.2.35.1) "GOST2001-CryptoPro-A" set ::pki::oids(1.2.643.2.2.35.2) "GOST2001-CryptoPro-B" set ::pki::oids(1.2.643.2.2.35.3) "GOST2001-CryptoPro-C" # { "GOST2001-CryptoPro-A", set ::pki::oids("GOST2001-CryptoPro-XchA" }, # { "GOST2001-CryptoPro-C", set ::pki::oids("GOST2001-CryptoPro-XchB" }, set ::pki::oids(1.2.643.2.2.36.0) "GOST2001-CryptoPro-A" set ::pki::oids(1.2.643.2.2.36.1) "GOST2001-CryptoPro-C" #Oid-     34.10-2012-512 set ::pki::oids(1.2.643.7.1.2.1.2.1) "GOST2012-tc26-A" set ::pki::oids(1.2.643.7.1.2.1.2.2) "GOST2012-tc26-B" #Nick   set ::pki::oids(1.2.643.7.1.1.2.2) "STRIBOG256" set ::pki::oids(1.2.643.7.1.1.2.3) "STRIBOG512" set ::pki::oids(1.2.643.2.2.30.1) "GOSTR3411_CP" } 


El procedimiento parse_gost_cert está diseñado para analizar certificados basados ​​en la criptografía rusa.
Este archivo también contiene el procedimiento para asignar nickname-mov a oid-am de la criptografía rusa, teniendo en cuenta el proyecto GCrypt:
 proc set_nick_for_oid {} { set ::pki::oids(1.2.643.100.1) "OGRN" set ::pki::oids(1.2.643.100.5) "OGRNIP" set ::pki::oids(1.2.643.3.131.1.1) "INN" set ::pki::oids(1.2.643.100.3) "SNILS" #  set ::pki::oids(1.2.643.2.2.3) "  34.10-2001-256" set ::pki::oids(1.2.643.7.1.1.3.2) "  34.10-2012-256" set ::pki::oids(1.2.643.7.1.1.3.3) "  34.10-2012-512" #  set ::pki::oids(1.2.643.2.2.19) "GostR2001" set ::pki::oids(1.2.643.7.1.1.1.1) "GostR2012_256" set ::pki::oids(1.2.643.7.1.1.1.2) "GostR2012_512" #Oid-     34.10-2001    34.10-2012-256 set ::pki::oids(1.2.643.2.2.35.0) "GOST2001-test" set ::pki::oids(1.2.643.2.2.35.1) "GOST2001-CryptoPro-A" set ::pki::oids(1.2.643.2.2.35.2) "GOST2001-CryptoPro-B" set ::pki::oids(1.2.643.2.2.35.3) "GOST2001-CryptoPro-C" set ::pki::oids(1.2.643.2.2.36.0) "GOST2001-CryptoPro-A" set ::pki::oids(1.2.643.2.2.36.1) "GOST2001-CryptoPro-C" #Oid-     34.10-2012-512 set ::pki::oids(1.2.643.7.1.2.1.2.1) "GOST2012-tc26-A" set ::pki::oids(1.2.643.7.1.2.1.2.2) "GOST2012-tc26-B" #Nick   set ::pki::oids(1.2.643.7.1.1.2.2) "STRIBOG256" set ::pki::oids(1.2.643.7.1.1.2.3) "STRIBOG512" set ::pki::oids(1.2.643.2.2.30.1) "GOSTR3411_CP" } 

El procedimiento parse_gost_cert le permite obtener un certificado TBS , firma de certificado, tipo de firma, clave pública. Si estamos considerando un certificado autofirmado, esto es suficiente para verificar la firma. Si verificamos la firma de un certificado firmado (emitido) por otro certificado (el emisor y el sujeto del certificado no coinciden), entonces el procedimiento para obtener información para verificar la firma es el siguiente:

- del certificado verificado extraemos su certificado TBS, tipo de firma y la firma misma;
- extraemos la clave pública del certificado raíz.

El responsable de la preparación de los datos de origen para verificar la firma del certificado es el estricto cumplimiento de las recomendaciones de TK-26. Para un valor de clave pública, suenan así:
La representación de clave pública de GostR3410-2012-256-PublicKey es idéntica a la representación de clave pública de GOST R 34.10-2001 [IETF RFC 4491], y DEBE contener 64 octetos, donde los primeros 32 octetos contienen la coordenada x en la vista little endian y los segundos 32 octetos contienen la coordenada y en la visión little endian.

GostR3410-2012-512-PublicKey La vista de clave pública DEBE contener
128 octetos, donde los primeros 64 octetos contienen la coordenada x en la vista little-endian, y los segundos 64 octetos contienen la coordenada y en la vista little-endian.
Al cargar una firma, debe guiarse por lo siguiente:
El algoritmo de firma GOST R 34.10-2012 con una longitud de código hash de 256 bits se utiliza para generar una firma digital en forma de dos números de 256 bits, r y s. Su representación en forma de cadena de octeto (OCTET STRING) es idéntica a la representación de la firma GOST R 34.10-2001 [IETF RFC 4491] y consta de 64 octetos; los primeros 32 octetos contienen el número s en la representación big-endian (el octeto más alto se escribe primero), y los segundos 32 octetos contienen el número r en la representación big-endian.

El algoritmo de firma GOST R 34.10-2012 con una longitud de código hash de 512 se utiliza para generar una firma digital en forma de dos números de 512 bits, claves públicas de acuerdo con r y s. Su representación como una cadena de octetos (OCTET STRING) consta de 128 octetos; los primeros 64 octetos contienen el número s en la representación big-endian (el octeto más alto se escribe primero), y los segundos 64 octetos contienen el número r en la representación big-endian.
Según estas recomendaciones, el módulo Tcl parse_certs_for_verify_load.tcl prepara los datos de origen para la verificación de la firma del certificado

se ve así:
 #!/usr/bin/tclsh #  PKI package require pki #     -  source parse_cert_gost_oid.tcl if {$argc != 2} { puts "Usage: parse_certs_for_verify_load.tcl < > < >" exit 1 } #  if {[file exists "[lindex $argv 0]"] == 0 } { puts "Usage: parse_certs_for_verify_load.tcl < > < >" puts "  [lindex $argv 0]" exit 1 } #  if {[file exists "[lindex $argv 1]"] == 0 } { puts "Usage: parse_certs_for_verify_load.tcl < > < >" puts "  [lindex $argv 1]" exit 1 } # nick-  - oid- set_nick_for_oid set file [lindex $argv 0] set f [open $file r] set cert [read $f] close $f #READ DER-format if { [string range $cert 0 9 ] != "-----BEGIN" } { set fd [open $file] chan configure $fd -translation binary set cert [read $fd] close $fd } array set cert_user [parse_cert_gost $cert] #  -26 set len_sign [expr [string length $cert_user(signature)] /2] set sign_r [string range $cert_user(signature) $len_sign end] set sign_s [string range $cert_user(signature) 0 [expr $len_sign - 1]] #puts " : $file" set file [lindex $argv 1] set f [open $file r] set cert [read $f] close $f #READ DER if { [string range $cert 0 9 ] != "-----BEGIN" } { set fd [open $file] chan configure $fd -translation binary set cert [read $fd] close $fd } #     array set cert_ca [parse_cert_gost $cert] #  -26 set len_key [expr [string length $cert_ca(pubkey_rev)]/2] set key_pub_left [string range $cert_ca(pubkey_rev) $len_key end] set key_pub_right [string range $cert_ca(pubkey_rev) 0 [expr $len_key - 1]] puts "/* C-:      */" #TBS-  puts "char tbc\[\] = \"[string toupper $cert_user(cert)]\";" # - puts "char hash_type\[\] = \"$cert_ca(hashkey)\";" #    puts "unsigned char pub_key_ca\[\] = \"(public-key \"" puts "\"(ecc \"" puts "\" (curve $cert_ca(paramkey))\"" puts "\" (q #04[string toupper $key_pub_left$key_pub_right]#)\"" puts "\")\"" puts "\")\";" #   puts "unsigned char sig_cert\[\] = \"(sig-val\"" puts "\"($cert_ca(type) \"" puts "\" (r #[string toupper $sign_r]#)\"" puts "\" (s #[string toupper $sign_s]#)\"" puts "\")\"" puts "\")\";" puts "/*    TEST_from_TCL.h*/" puts "/*  TEST_from_Tcl.c: cc -o TEST_from_Tcl TEST_from_Tcl.c -lgcrypt    TEST_from_Tcl*/" 


Para las pruebas, tomamos dos certificados reales:

certificado \ "UTs 1 IS GUTs.pem \"
-----BEGIN CERTIFICATE-----
MIIGrDCCBlugAwIBAgILAOvBBVQAAAAAAFkwCAYGKoUDAgIDMIIBSjEeMBwGCSqG
SIb3DQEJARYPZGl0QG1pbnN2eWF6LnJ1MQswCQYDVQQGEwJSVTEcMBoGA1UECAwT
Nzcg0LMuINCc0L7RgdC60LLQsDEVMBMGA1UEBwwM0JzQvtGB0LrQstCwMT8wPQYD
VQQJDDYxMjUzNzUg0LMuINCc0L7RgdC60LLQsCwg0YPQuy4g0KLQstC10YDRgdC6
0LDRjywg0LQuIDcxLDAqBgNVBAoMI9Cc0LjQvdC60L7QvNGB0LLRj9C30Ywg0KDQ
vtGB0YHQuNC4MRgwFgYFKoUDZAESDTEwNDc3MDIwMjY3MDExGjAYBggqhQMDgQMB
ARIMMDA3NzEwNDc0Mzc1MUEwPwYDVQQDDDjQk9C+0LvQvtCy0L3QvtC5INGD0LTQ
vtGB0YLQvtCy0LXRgNGP0Y7RidC40Lkg0YbQtdC90YLRgDAeFw0xNjAzMTYxMjAy
NTFaFw0yNzA3MTIxMjAyNTFaMIIBITEaMBgGCCqFAwOBAwEBEgwwMDc3MTA0NzQz
NzUxGDAWBgUqhQNkARINMTA0NzcwMjAyNjcwMTEeMBwGCSqGSIb3DQEJARYPZGl0
QG1pbnN2eWF6LnJ1MTwwOgYDVQQJDDMxMjUzNzUg0LMuINCc0L7RgdC60LLQsCDR
g9C7LiDQotCy0LXRgNGB0LrQsNGPINC0LjcxLDAqBgNVBAoMI9Cc0LjQvdC60L7Q
vNGB0LLRj9C30Ywg0KDQvtGB0YHQuNC4MRUwEwYDVQQHDAzQnNC+0YHQutCy0LAx
HDAaBgNVBAgMEzc3INCzLiDQnNC+0YHQutCy0LAxCzAJBgNVBAYTAlJVMRswGQYD
VQQDDBLQo9CmIDEg0JjQoSDQk9Cj0KYwYzAcBgYqhQMCAhMwEgYHKoUDAgIjAQYH
KoUDAgIeAQNDAARAx70Y7WYQ4ODtdiSSx3MJnr1GQBEIExiPO/LWj1TRKES1OcDI
YgtdOBGVYSvbsStl10jkAOG0OpnGsd2by4m+LaOCA0MwggM/MA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFBGIaV7vyOlz23pXNbzSAfMF/qfRMAsGA1UdDwQEAwIB
hjCCAYsGA1UdIwSCAYIwggF+gBSLmDuJGFHo75wCeLjqyNQgslXJXaGCAVKkggFO
MIIBSjEeMBwGCSqGSIb3DQEJARYPZGl0QG1pbnN2eWF6LnJ1MQswCQYDVQQGEwJS
VTEcMBoGA1UECAwTNzcg0LMuINCc0L7RgdC60LLQsDEVMBMGA1UEBwwM0JzQvtGB
0LrQstCwMT8wPQYDVQQJDDYxMjUzNzUg0LMuINCc0L7RgdC60LLQsCwg0YPQuy4g
0KLQstC10YDRgdC60LDRjywg0LQuIDcxLDAqBgNVBAoMI9Cc0LjQvdC60L7QvNGB
0LLRj9C30Ywg0KDQvtGB0YHQuNC4MRgwFgYFKoUDZAESDTEwNDc3MDIwMjY3MDEx
GjAYBggqhQMDgQMBARIMMDA3NzEwNDc0Mzc1MUEwPwYDVQQDDDjQk9C+0LvQvtCy
0L3QvtC5INGD0LTQvtGB0YLQvtCy0LXRgNGP0Y7RidC40Lkg0YbQtdC90YLRgIIQ
NGgeQMtB7zOpoLfIdpKaKTBZBgNVHR8EUjBQMCagJKAihiBodHRwOi8vcm9zdGVs
ZWNvbS5ydS9jZHAvZ3VjLmNybDAmoCSgIoYgaHR0cDovL3JlZXN0ci1wa2kucnUv
Y2RwL2d1Yy5jcmwwJgYFKoUDZG8EHQwb0JrRgNC40L/RgtC+LdCf0YDQviBDU1Ag
My42MCUGA1UdIAQeMBwwCAYGKoUDZHEBMAgGBiqFA2RxAjAGBgRVHSAAMIHGBgUq
hQNkcASBvDCBuQwj0J/QkNCa0JwgwqvQmtGA0LjQv9GC0L7Qn9GA0L4gSFNNwrsM
INCf0JDQmiDCq9CT0L7Qu9C+0LLQvdC+0Lkg0KPQpsK7DDbQl9Cw0LrQu9GO0YfQ
tdC90LjQtSDihJYgMTQ5LzMvMi8yLTk5OSDQvtGCIDA1LjA3LjIwMTIMONCX0LDQ
utC70Y7Rh9C10L3QuNC1IOKEliAxNDkvNy8xLzQvMi02MDMg0L7RgiAwNi4wNy4y
MDEyMAgGBiqFAwICAwNBAKVYokUvb7XAMPJF38ZPKO2BFBldmGEfqsfmsiO35Y52
kTkx512H3YLqWMrOLjIfVMJhc+DTCNeXWY6bhK4/DRU=
-----END CERTIFICATE-----

y su certificado raíz \ "SEC Minkomsvyaz.pem \":
-----BEGIN CERTIFICATE-----
MIIFGTCCBMigAwIBAgIQNGgeQMtB7zOpoLfIdpKaKTAIBgYqhQMCAgMwggFKMR4w
HAYJKoZIhvcNAQkBFg9kaXRAbWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRwwGgYD
VQQIDBM3NyDQsy4g0JzQvtGB0LrQstCwMRUwEwYDVQQHDAzQnNC+0YHQutCy0LAx
PzA9BgNVBAkMNjEyNTM3NSDQsy4g0JzQvtGB0LrQstCwLCDRg9C7LiDQotCy0LXR
gNGB0LrQsNGPLCDQtC4gNzEsMCoGA1UECgwj0JzQuNC90LrQvtC80YHQstGP0LfR
jCDQoNC+0YHRgdC40LgxGDAWBgUqhQNkARINMTA0NzcwMjAyNjcwMTEaMBgGCCqF
AwOBAwEBEgwwMDc3MTA0NzQzNzUxQTA/BgNVBAMMONCT0L7Qu9C+0LLQvdC+0Lkg
0YPQtNC+0YHRgtC+0LLQtdGA0Y/RjtGJ0LjQuSDRhtC10L3RgtGAMB4XDTEyMDcy
MDEyMzExNFoXDTI3MDcxNzEyMzExNFowggFKMR4wHAYJKoZIhvcNAQkBFg9kaXRA
bWluc3Z5YXoucnUxCzAJBgNVBAYTAlJVMRwwGgYDVQQIDBM3NyDQsy4g0JzQvtGB
0LrQstCwMRUwEwYDVQQHDAzQnNC+0YHQutCy0LAxPzA9BgNVBAkMNjEyNTM3NSDQ
sy4g0JzQvtGB0LrQstCwLCDRg9C7LiDQotCy0LXRgNGB0LrQsNGPLCDQtC4gNzEs
MCoGA1UECgwj0JzQuNC90LrQvtC80YHQstGP0LfRjCDQoNC+0YHRgdC40LgxGDAW
BgUqhQNkARINMTA0NzcwMjAyNjcwMTEaMBgGCCqFAwOBAwEBEgwwMDc3MTA0NzQz
NzUxQTA/BgNVBAMMONCT0L7Qu9C+0LLQvdC+0Lkg0YPQtNC+0YHRgtC+0LLQtdGA
0Y/RjtGJ0LjQuSDRhtC10L3RgtGAMGMwHAYGKoUDAgITMBIGByqFAwICIwEGByqF
AwICHgEDQwAEQI+lv3kQI8jWka1kMVdbvpvFioP0Pyn3Knmp+2XD6KgPWnXEIlSR
X8g/IYracDr51YsNc2KE3C7mkH6hA3M3ofujggGCMIIBfjCBxgYFKoUDZHAEgbww
gbkMI9Cf0JDQmtCcIMKr0JrRgNC40L/RgtC+0J/RgNC+IEhTTcK7DCDQn9CQ0Jog
wqvQk9C+0LvQvtCy0L3QvtC5INCj0KbCuww20JfQsNC60LvRjtGH0LXQvdC40LUg
4oSWIDE0OS8zLzIvMi05OTkg0L7RgiAwNS4wNy4yMDEyDDjQl9Cw0LrQu9GO0YfQ
tdC90LjQtSDihJYgMTQ5LzcvMS80LzItNjAzINC+0YIgMDYuMDcuMjAxMjAuBgUq
hQNkbwQlDCPQn9CQ0JrQnCDCq9Ca0YDQuNC/0YLQvtCf0YDQviBIU03CuzBDBgNV
HSAEPDA6MAgGBiqFA2RxATAIBgYqhQNkcQIwCAYGKoUDZHEDMAgGBiqFA2RxBDAI
BgYqhQNkcQUwBgYEVR0gADAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB
/zAdBgNVHQ4EFgQUi5g7iRhR6O+cAni46sjUILJVyV0wCAYGKoUDAgIDA0EA23Re
ec/Y27rpMi+iFbgWCazGY3skBTq5ZGsQKOUxCe4mO7UBDACiWqdA0nvqiQMXeHgq
o//fO9pxuIHtymwyMg==
-----END CERTIFICATE-----

Prepararemos los datos iniciales para verificar el certificado "UTs 1 IS GUTs.pem":

 $ ./parse_certs_for_verify_load.tcl " 1  .pem" " .pem" > TEST_from_TCL.h $echo "  TEST_from_TCL.h" $cat TEST_from_TCL.h /* C-:      */ char tbc[] = "3082065B . . . "; char hash_type[] = "GOSTR3411_CP"; unsigned char pub_key_ca[] = "(public-key " "(ecc " " (curve GOST2001-CryptoPro-A)" " (q #040FA8E8C365FBA9792AF7293FF4838AC59BBE5B573164AD91D6C8231079BFA58FFBA1377303A17E90E62EDC8462730D8BD5F93A70DA8A213FC85F915422C4755A#)" ")" ")"; unsigned char sig_cert[] = "(sig-val" "(gost " " (r #913931E75D87DD82EA58CACE2E321F54C26173E0D308D797598E9B84AE3F0D15#)" " (s #A558A2452F6FB5C030F245DFC64F28ED8114195D98611FAAC7E6B223B7E58E76#)" ")" ")"; /*    TEST_from_TCL.h*/ /*  TEST_from_Tcl.c: cc -o TEST_from_Tcl TEST_from_Tcl.c -lgcrypt    TEST_from_Tcl*/ $ 

La verificación de la firma del certificado es realizada por el módulo:

TEST_from_TCL.c
 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdarg.h> #include <gcrypt.h> #include "TEST_from_TCL.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)) #define xmalloc(a) gcry_xmalloc ((a)) static void show_sexp (const char *prefix, gcry_sexp_t a) { char *buf; size_t size; if (prefix) fputs (prefix, stderr); size = gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, NULL, 0); buf = gcry_xmalloc (size); gcry_sexp_sprint (a, GCRYSEXP_FMT_ADVANCED, buf, size); fprintf (stderr, "%.*s", (int)size, buf); gcry_free (buf); } /* Convert STRING consisting of hex characters into its binary representation and return it as an allocated buffer. The valid length of the buffer is returned at R_LENGTH. The string is delimited by end of string. The function returns NULL on error. */ static void * hex2buffer (const char *string, size_t *r_length) { const char *s; unsigned char *buffer; size_t length; buffer = xmalloc (strlen(string)/2+1); length = 0; for (s=string; *s; s +=2 ) { if (!hexdigitp (s) || !hexdigitp (s+1)) return NULL; /* Invalid hex digits. */ ((unsigned char*)buffer)[length++] = xtoi_2 (s); } *r_length = length; return buffer; } int main(int argc, char* argv[]) { gpg_error_t err; int algo; gcry_md_hd_t hd; unsigned char *tbs_ptr; size_t len_tbs; int i; unsigned char *h; gcry_sexp_t pub_key; gcry_sexp_t data; gcry_sexp_t sig; gcry_mpi_t x; int len_xy; unsigned char c; /*   little-endian  big-endian*/ unsigned short arch = 1; /* 0x0001 */ tbs_ptr = hex2buffer(tbc, &len_tbs); if (tbs_ptr == NULL) { fprintf (stderr, "Bad tbs\n"); exit(1); } algo = gcry_md_map_name (hash_type); if (algo == GCRY_MD_NONE) { fprintf (stderr, "Unknown algorithm '%s'\n", hash_type); exit (1); } err = gcry_md_open(&hd, algo, 0); if (err) { fprintf (stderr, "LibGCrypt error %s/%s\n", gcry_strsource (err), gcry_strerror (err)); exit (1); } gcry_md_write (hd, tbs_ptr, len_tbs); h = gcry_md_read(hd, 0); // len_xy = gcry_md_get_algo_dlen (algo); /*    */ printf("%s\n", *((unsigned char *) &arch) == 0 ? " big-endian" : " little-endian"); len_xy = *((unsigned char *) &arch) == 0 ? 0:gcry_md_get_algo_dlen (algo); for (i = 0; i < (len_xy/2); i++) { c = *(h + i); *(h + i) = *(h + len_xy - i - 1); *(h + len_xy - i - 1) = c; } fprintf(stderr, " =%d\n", gcry_md_get_algo_dlen (algo)); for (i = 0; i < gcry_md_get_algo_dlen (algo); i++) printf("%02X", h[i]); // printf("\n %s\n", tbc); fflush(stdout); /*  */ x = gcry_mpi_set_opaque_copy(NULL, h, gcry_md_get_algo_dlen (algo) * 8); /*  */ gcry_md_reset(hd); gcry_md_close(hd); /* */ err = gcry_sexp_build (&data, NULL, "(data (flags gost) (value %m))", x); show_sexp ("ECC GOST data cert:\n", data); fprintf (stderr, "\nStep 1\n"); /*    */ err = gcry_sexp_sscan (&pub_key, NULL, pub_key_ca, strlen (pub_key_ca)); if (err){ fprintf(stderr, "TEST_SEXP: er gcry_sexp_sscan for pub_key_ca\n"); exit(1); } show_sexp ("ECC GOST public key:\n", pub_key); fprintf (stderr, "Step 2\n"); /*   */ err = gcry_sexp_sscan (&sig, NULL, sig_cert, strlen (sig_cert)); if (err){ fprintf(stderr, "TEST_SEXP: er gcry_sexp_sscan for sig_cert\n"); exit(1); } show_sexp ("ECC GOST sig cert:\n", sig); fprintf (stderr, "Step 3\n"); /*  */ err = gcry_pk_verify (sig, data, pub_key); if (err) { fprintf (stderr, "TEST_SEXP: verify cert failed\n"); exit (1); } fprintf (stderr, "TEST_SEXP: verify cert OK!!\n"); } 

Traducimos y ejecutamos la utilidad TEST_from_TCL:

 $cc –o TEST_from_TCL TEST_from_TCL.c –lgcrypt $./TEST_from_TCL  little-endian  =32 D485903E7E8D60820118329060C558B9C733D53CA608C0C79363ECE7B4C1F799ECC GOST data cert: (data (flags gost) (value #D485903E7E8D60820118329060C558B9C733D53CA608C0C79363ECE7B4C1F799#) ) Step 1 ECC GOST public key: (public-key (ecc (curve GOST2001-CryptoPro-A) (q #040FA8E8C365FBA9792AF7293FF4838AC59BBE5B573164AD91D6C8231079BFA58FFBA1377303A17E90E 62EDC8462730D8BD5F93A70DA8A213FC85F915422C4755A#) ) ) Step 2 ECC GOST sig cert: (sig-val (gost (r #913931E75D87DD82EA58CACE2E321F54C26173E0D308D797598E9B84AE3F0D15#) (s #A558A2452F6FB5C030F245DFC64F28ED8114195D98611FAAC7E6B223B7E58E76#) ) ) Step 3 TEST_SEXP: verify cert OK!! $ 

Como puede ver, la verificación de la firma del certificado "UTs 1 IS GUTs.pem" fue exitosa. Queda por verificar el certificado raíz en sí "GUT Minkomsvyaz.pem". Es simple, solo ejecuta el comando:

 $ parse_certs_for_verify_load.tcl " .pem" " .pem" > TEST_from_TCL.h $  .. 

Si alguien quiere verificar los certificados con otras claves GOST (GOST R 34.10-2012-256 o GOST R 34.10-2012-512), entonces puede usar CAFL63 CA y preparar cualquier certificado:

imagen

Entonces, las investigaciones realizadas mostraron que la biblioteca GCrypt puede solía trabajar con criptografía rusa. La perspectiva inmediata se ve en finalizar la biblioteca KSBA y expandir el paquete pki del lenguaje de script Tcl (o quizás Python) con soporte para la criptografía rusa.

Aquellos que quieran usar la biblioteca GCrypt para el cifrado, les recomiendo que echen un vistazo aquí .

Source: https://habr.com/ru/post/es414249/


All Articles