Infrastructure à clé publique: bibliothèque GCrypt comme alternative à OpenSSL avec prise en charge de la cryptographie russe

Le second semestre 2018 approche et la «2000e année» de l' ICP basée sur la cryptographie russe devrait bientôt arriver. Cela est dû au fait que
L'utilisation du schéma de signature GOST R 34.10-2001 pour générer une signature après le 31 décembre 2018 n'est pas autorisée!
Aujourd'hui, cela n'a aucun sens de recevoir des certificats avec une signature conformément à GOST R 34.10-2001.
Dans le même temps, de nombreux services ou applications sont développés sur la base d'OpenSSL, qui a soutenu le travail avec GOST R 34.10-2001.

Mais aujourd'hui, dans la version standard d'OpenSL, il n'y a pas de support pour GOST R 34.11-2012 et GOST R 34.10-2012. De plus, dans la version 1.1, la prise en charge de la cryptographie GOST est exclue de la distribution standard («Le moteur GOST était obsolète et a donc été supprimé.»).

Tout cela nous amène à chercher des façons alternatives de travailler avec des certificats, avec la signature électronique («messages au format CMS») et d'autres objets PKI basés sur la nouvelle cryptographie russe.

Une telle façon possible est d'utiliser la bibliothèque GCrypt . Cette bibliothèque prend en charge les nouveaux algorithmes GOST R 34.11-2012 (algorithmes de hachage) et GOST R 34.10-2012 (algorithmes de signature).

Génération de paires de clés


Nous commençons donc par générer une paire de clés contenant à la fois des clés privées et publiques. À l'heure actuelle, dans la cryptographie russe, il existe trois types de clés de signature avec les oid-s correspondants
- GOST R 34.10-2001 avec une longueur de clé de 256 bits, oid 1.2.643.2.2.19 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x13);
- GOST R 34.10-2012 avec une longueur de clé de 256 bits (ci-après 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 avec une longueur de clé de 512 bits (ci-après GOST R 34.10-12-512), oid 1.2.643.7.1.1.1.2 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x01, 0x02).
Et notez immédiatement qu'en termes de mathématiques, d'algorithmes de génération et de leur implémentation, les clés de GOST R 34.10-2001 et GOST R 34.10-12-256 sont absolument identiques! Ainsi que des algorithmes identiques pour la formation de signatures électroniques basées sur eux. Laquelle de ces clés en question ne peut être jugée que par des informations sur la clé contenue, par exemple, dans le certificat. Alors, pourquoi at-il fallu deux oid différents? Soulignez uniquement le fait que lors de la génération d'une signature électronique avec la clé GOST R 34.10-2001, le hachage obtenu conformément à GOST R 34.10-94 doit être utilisé, et lors de l'utilisation de la clé GOST R 34.10-12-256, le hachage obtenu conformément à GOST R 34.10- 212 avec une longueur de 256 bits. Bien que, pour le souligner, il existe des oid-s correspondants:
- 1.2.643.2.2.3 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x03) - algorithme de signature GOST R 34.10-2001 avec clé 256 avec hachage GOST R 34.11-94;
- 1.2.643.7.1.1.3.2 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x03, 0x02) - algorithme de signature GOST R 34.10-2012 avec clé 256 avec hachage GOST R 34.11-2012;
- 1.2.643.7.1.1.3.3 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x03, 0x03) - algorithme de signature GOST R 34.10-2012 avec clé 512 avec hachage GOST R 34.11-2012.
Ainsi, il y a un tri avec oid-s, mais c'est-Ă -dire.

Les clés de la famille GOST appartiennent à la famille de clés sur les courbes elliptiques. Pour générer une paire de clés, vous devez spécifier un point de base sur une courbe elliptique.

Le comité technique de normalisation «Sécurité des informations cryptographiques» ( TC 26 ) a recommandé d'utiliser deux points de base pour les clés GOST R 34.10-2012-512:
- GOST2012-tc26-A (surnom dans la terminologie libgcrypt) avec oid 1.2.643.7.1.2.1.2.1 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x01, 0x02, 0x01);
- GOST2012-tc26-B avec oid 1.2.643.7.1.2.1.2.2 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x02, 0x01, 0x02, 0x02);
Pour les clés GOST R 34.10 d'une longueur de 256 bits, trois points de base sont recommandés:
- GOST2001-CryptoPro-A avec oid om 1.2.643.2.2.35.1 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x01);
- GOST2001-CryptoPro-B avec oid 1.2.643.2.2.35.2 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x02);
- GOST2001-CryptoPro-C avec oid 1.2.643.2.2.35.3 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x23, 0x03).
Pour les clés GOST R 34.10 d'une longueur de 256 bits, deux autres OID sont définis pour les points de base:
- GOST2001-CryptoPro-XchA avec oid 1.2.643.2.2.36.0 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x24, 0x00);
- GOST2001-CryptoPro-XchB avec oid 1.2.643.2.2.36.1 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x24, 0x01).
Cependant, en réalité, ces oids font référence aux points de base de GOST2001-CryptoPro-A avec oid 1.2.643.2.2.35.1 et GOST2001-CryptoPro-C avec oid 1.2.643.2.2.35.3 respectivement.
Et cela doit ĂŞtre pris en compte lors du traitement des points de base avec ces OID.

Pour générer une paire de clés, la fonction gcry_pk_genkey de la forme suivante est utilisée:

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

Les paramètres doivent être définis dans la variable parms pour générer la paire de clés au format de l' expression S interne (sexp). Pour générer une paire de clés selon GOST, les paramètres sont définis sous la forme de l'expression S suivante:

 (genkey ( (curve _))),  

ecc détermine la génération de la paire de clés sur les courbes elliptiques, et le point de base doit indiquer le point spécifique recommandé par le TK-26. C'est le point de base spécifié qui détermine quelle paire de clés sera générée. Si vous spécifiez, par exemple, GOST2012-tc26-A ou GOST2012-tc26-B, une paire de clés sera générée conformément à GOST R 34.10-2012 avec une longueur de clé de 512 bits. Au lieu du surnom du point de base, vous pouvez spécifier directement oid:

 (genkey ( (curve «1.2.643.2.2.35.3»))) 

Dans ce dernier cas, on suppose que la paire de clés est générée conformément à GOST R 34.10 avec une longueur de clé de 256 bits et un point de base GOST2001-CryptoPro-C.

Voici un exemple de programme C qui illustre la génération de clés

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


Pour diffuser l'exemple, vous devez exécuter la commande:

 $cc –o GenKey GenKey.c –lgcrypt $ 

Après avoir démarré le module GenKey, nous obtenons

paire de clés
 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#) ) ) 

Maintenant, en ayant la clé privée en main, vous pouvez créer une signature électronique.

Hachage de documents


La création d'une signature électronique (ES) d'un document commence par l'obtention d'une valeur de hachage à partir du document en cours de signature. Pour signer un document, l'un des algorithmes peut être sélectionné:
- 1.2.643.2.2.3 (0x2a, 0x85, 0x03, 0x02, 0x02, 0x03) - algorithme de signature GOST R 34.10-2001 avec clé 256 avec hachage selon GOST R 34.11-94 avec longueur de hachage 256 bits;
- 1.2.643.7.1.1.3.2 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x03, 0x02) - algorithme de signature GOST R 34.10-2012 avec une clé de 256 avec hachage selon GOST R 34.11-2012 avec une longueur de hachage de 256 bits;
- 1.2.643.7.1.1.3.3 (0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x03, 0x03) - algorithme de signature GOST R 34.10-2012 avec une clé de 512 avec hachage selon GOST R 34.11-2012 avec une longueur de hachage de 512 bits.
Ces algorithmes déterminent non seulement le type de clé privée qui sera utilisée pour obtenir la signature électronique, mais également l'algorithme de la fonction de hachage. La bibliothèque GCrypt implémente les trois types de fonctions. L'algorithme de hachage GOST R 34.11-94 (avec le paramètre oid 1.2.643.2.2.30.1 - 0x2a, 0x85, 0x03, 0x02, 0x02, 0x1e, 0x01) est implémenté sous le surnom GOSTR3411_CP, l'algorithme GOST R 34.11-2012 de longueur 256 bits (oid 1.2.43.7.1.1.2.2 - 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x02) sont implémentés sous l'alias STRIBOG256 et GOST R 34.11-2012 avec une longueur de 512 bits (oid 1.2.43.7 .1.1.2.3 - 0x2a, 0x85, 0x03, 0x07, 0x01, 0x01, 0x02, 0x03) est implémenté sous le surnom STRIBOG512.

Ci-dessous le code du module C pour calculer la valeur de hachage à partir d'un document stocké dans un fichier

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

Nous traduisons ce module et obtenons l'utilitaire de calcul de hachage:

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

Formation de la signature électronique et sa vérification


Maintenant que nous avons la clé privée et le hachage du document, nous pouvons également générer une signature électronique du document:

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

r_sig -sexp-variable dans laquelle la signature électronique sera enregistrée,
skey - une variable sexp avec une clé privée (voir ci-dessus), qui est utilisée pour générer une signature,
data - variable sexp, qui indique le type de signature (dans notre cas, gost) et le hachage du document signé.
Il faut se rappeler que la valeur de hachage fournie à l'entrée gcry_pk_sign pour générer la signature GOST R 34.10 doit être au format big-endian, c'est-à-dire en fait bouleversé (comme l'a dit un ami: «La tradition russe de traiter les octets de digestion en ordre peu-endien»). Dans cet esprit, la préparation de la variable sexe du datap est la suivante:

 ... 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); 
Tout est prĂŞt Ă  recevoir la signature et Ă  la visualiser <source: lang = "cpp"> ...
gcry_sexp_t sig_r, sig_s;
...
/ * Signez le hachage * /
err = gcry_pk_sign (& sig, data, sec_key);
si (err) {
fprintf (stderr, "échec de la signature:% s \ n", gcry_strerror (err));
sortie (1);
}
/ * Nous imprimons la signature * /
show_sexp ("ECC GOST SIG: \ n", sig);
/ * Sélectionnez et imprimez les composants des signatures r et s * /
sig_r = gcry_sexp_find_token (sig, "r", 0);
si (! sig_r) {
fprintf (stderr, "r part missing in sig \ n");
sortie (1);
}
show_sexp ("ECC GOST Sig R-part: \ n", sig_r);
sig_s = gcry_sexp_find_token (sig, "s", 0);
si (! sig_s) {
fprintf (stderr, "s part missing in sig \ n");
sortie (1);
}
show_sexp ("ECC GOST Sig S-part: \ n", sig_s);
... Vous pouvez vérifier la signature comme suit:
 … 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); } … 

Vérification de la signature électronique de GOST R 34.10 dans les certificats


L'un des principaux objets de l'infrastructure à clé publique (PKI) est les certificats X509. Les recommandations du TC-26 sur la composition et la structure des certificats sont énoncées dans le document «SPÉCIFICATIONS TECHNIQUES D'UTILISATION DES ALGORITHMES GOST R 34.10, GOST R 34.11 DANS LE PROFIL DU CERTIFICAT ET CRL (CRL) D'EXAMEN DE L'INFRASTRUCTURE OUVERTE DE LA CLÉ 9 DU X. Comité de X. protection de l'information »(procès-verbal n ° 13 du 24/04/2014).» C'est conformément à ces recommandations que toutes les autorités de certification accréditées auprès du ministère des Communications de Russie délivrent des certificats.

Pour vérifier la signature dans le certificat, il est nécessaire (voir ci-dessus) d'obtenir le hachage du certificat vérifié, sa signature et la clé publique du certificat racine. Notez simplement que pour un certificat auto-signé, toutes ces données sont stockées dans un seul certificat.
Pour travailler avec des objets PKI / PKI (certificats, CMS, requĂŞtes
etc.), en règle générale, la bibliothèque KSBA est utilisée, ce qui pour le moment ne prend pas en charge les recommandations du TK-26, bien qu'il existe une expérience de ce type de support. Fondamentalement, rien n'empêche d'ajouter la prise en charge des recommandations TK-26 au projet ksba.
À ce stade, l'étape de test GCrypt, il est pratique d'utiliser des langages de script tels que Python , Tcl, etc. pour travailler avec des objets PKI / PKI (certificats, etc.) avec la cryptographie russe. Le langage de script Tcl a été sélectionné. Il est facile et simple d'y écrire un programme prototype, qui peut ensuite être transféré dans le langage C. Tcl comprend un package PKI qui contient des procédures pour analyser les objets PKI, en particulier, la procédure pour analyser les certificats :: pki :: x509 :: parse_cert. Basé sur la procédure parse_cert, la procédure parse_gost_cert a été développée, qui se trouve dans le fichier

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


La procédure parse_gost_cert est conçue pour analyser les certificats basés sur la cryptographie russe.
Ce fichier contient également la procédure d'attribution des pseudonymes aux oid-s de la cryptographie russe, en tenant compte du projet 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" } 

La procédure parse_gost_cert vous permet d'obtenir un certificat TBS , une signature de certificat, un type de signature, une clé publique. Si nous envisageons un certificat auto-signé, cela suffit pour vérifier la signature. Si nous vérifions la signature d'un certificat signé (délivré) par un autre certificat (l'émetteur et le sujet du certificat ne correspondent pas), la procédure d'obtention des informations pour vérifier la signature est la suivante:

- du certificat vérifié, nous extrayons son certificat TBS, le type de signature et la signature elle-même;
- nous extrayons la clé publique du certificat racine.

Le plus responsable de la préparation des données source pour vérifier la signature du certificat est le strict respect des recommandations du TK-26. Pour une valeur de clé publique, ils sonnent comme ceci:
La représentation de la clé publique de GostR3410-2012-256-PublicKey est identique à la représentation de la clé publique de GOST R 34.10-2001 [IETF RFC 4491], et DOIT contenir 64 octets, où les 32 premiers octets contiennent la coordonnée x dans la vue en petit bout et les 32 seconds octets contiennent les coordonnées. y dans la vue peu endienne.

La vue de clé publique GostR3410-2012-512-PublicKey DOIT contenir
128 octets, où les 64 premiers octets contiennent la coordonnée x dans la vue en petit bout, et les 64 derniers octets contiennent la coordonnée y dans la vue en petit bout.
Lors du téléchargement d'une signature, vous devez être guidé par les éléments suivants:
L'algorithme de signature GOST R 34.10-2012 avec une longueur de code de hachage de 256 bits est utilisé pour générer une signature numérique sous la forme de deux nombres de 256 bits, r et s. Sa représentation sous la forme d'une chaîne d'octets (OCTET STRING) est identique à la représentation de la signature GOST R 34.10-2001 [IETF RFC 4491] et se compose de 64 octets; les 32 premiers octets contiennent le nombre s dans la représentation big-endian (l'octet le plus élevé est écrit en premier), et les 32 derniers octets contiennent le nombre r dans la représentation big-endian.

L'algorithme de signature GOST R 34.10-2012 avec une longueur de code de hachage 512 est utilisé pour générer une signature numérique sous la forme de deux nombres de 512 bits, des clés publiques selon r et s. Sa représentation sous la forme d'une chaîne d'octets (OCTET STRING) se compose de 128 octets; les 64 premiers octets contiennent le nombre s dans la représentation big-endian (l'octet le plus élevé est écrit en premier), et les 64 derniers octets contiennent le nombre r dans la représentation big-endian.
Sur la base de ces recommandations, le module Tcl parse_certs_for_verify_load.tcl prépare les données source pour la vérification de la signature du certificat

ressemble Ă  ceci:
 #!/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*/" 


Pour les tests, nous prenons deux vrais certificats:

certificat \ "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-----

et son certificat racine \ "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-----

Nous préparerons les données initiales pour vérifier le certificat "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 vérification de la signature du certificat est effectuée par le module:

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

Nous traduisons et exécutons l'utilitaire 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!! $ 

Comme vous pouvez le constater, la vérification de la signature du certificat "UTs 1 IS GUTs.pem" a réussi. Il reste à vérifier le certificat racine lui-même "GUT Minkomsvyaz.pem". C'est simple, il suffit d'exécuter la commande:

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

Si quelqu'un veut vérifier les certificats avec d'autres clés GOST (GOST R 34.10-2012-256 ou GOST R 34.10-2012-512), alors il peut utiliser CAFL63 CA et préparer des certificats:

image

Ainsi, les recherches effectuées ont montré que la bibliothèque GCrypt pourrait bien utilisé pour travailler avec la cryptographie russe. La perspective la plus proche consiste à finaliser la bibliothèque KSBA et à étendre le package pki du langage de script Tcl (ou peut-être Python) avec la prise en charge de la cryptographie russe.

Ceux qui veulent utiliser la bibliothèque GCrypt pour le cryptage, je recommande un coup d'oeil ici .

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


All Articles