
Nous continuons à
parler de l'alternative à openssl et nous parlerons de la bibliothèque
libksba , qui fait partie de GnuPG. La bibliothèque libksba fournit une interface de haut niveau pour travailler avec des objets d'infrastructure à clé publique tels que des certificats, des demandes de certificats, des signatures électroniques (CMS / PKCS # 7). Cependant, contrairement à la bibliothèque GCrypt, qui prend en charge les algorithmes cryptographiques russes, libksba n'a pas la mise en œuvre des recommandations
TK-26 sur l'utilisation des algorithmes GOST R 34.10-2001 / 2012, GOST R 34.11-94 / 2012 dans les objets PKI tels que les certificats, demandes de certificats, objets PKCS # 7 / CMS (documents signés et / ou cryptés, etc.).

Commençons par les oids qui peuvent (ou devraient) faire partie des attributs distingués DN (Distinguished Name) de l'émetteur (éditeur) et du sujet (propriétaire) du certificat. Dans la bibliothèque libksba, les oid sont écrits dans la structure oid_name_tbl [] (fichier dn.c). La structure oid_name_tbl [] ne contient pas d'
oid s de TIN, BIN, OGRNIP et SNILS
recommandés par TK-26 pour un certificat qualifié:
18. Les autres attributs du nom, dont l'utilisation est établie conformément à la loi fédérale, comprennent:
1) OGRN.
La valeur de l'attribut OGRN est une chaîne de 13 chiffres représentant l'ORGRN du titulaire d'un certificat qualifié - une entité juridique. L'identifiant d'objet de type attribut OGRN a la forme 1.2.643.100.1, le type d'attribut OGRN est décrit comme suit: OGRN :: = NUMERIC STRING SIZE 13;
2) SNILS (SNILS).
La valeur de l'attribut SNILS est une chaîne de 11 chiffres représentant SNILS du propriétaire d'un certificat qualifié - un individu. L'identifiant d'objet du type d'attribut SNILS est 1.2.643.100.3, le type d'attribut SNILS est décrit comme suit: SNILS :: = NUMERIC STRING SIZE 11;
3) INN.
La valeur de l'attribut INN est une chaîne de 12 chiffres représentant le NIF du titulaire du certificat qualifié. L'identifiant d'objet du type d'attribut INN est 1.2.643.3.131.1.1, le type d'attribut INN est décrit comme suit: INN :: = NUMERIC STRING SIZE 12.
Naturellement, vous devez ajouter ces oid-s:
static const struct { const char *name; int source; const char *description; size_t oidlen; const unsigned char *oid; const char *oidstr; } oid_name_tbl[] = { {"CN", 1, "CommonName", 3, "\x55\x04\x03", "2.5.4.3" }, {"SN", 2, "Surname", 3, "\x55\x04\x04", "2.5.4.4" }, {"SERIALNUMBER", 2, "SerialNumber",3, "\x55\x04\x05", "2.5.4.5" }, {"C", 1, "CountryName", 3, "\x55\x04\x06", "2.5.4.6" }, {"L" , 1, "LocalityName", 3, "\x55\x04\x07", "2.5.4.7" }, {"ST", 1, "StateOrProvince", 3, "\x55\x04\x08", "2.5.4.8" }, {"STREET", 1, "StreetAddress", 3, "\x55\x04\x09", "2.5.4.9" }, {"O", 1, "OrganizationName", 3, "\x55\x04\x0a", "2.5.4.10" }, {"OU", 1, "OrganizationalUnit", 3, "\x55\x04\x0b", "2.5.4.11" }, {"T", 2, "Title", 3, "\x55\x04\x0c", "2.5.4.12" }, {"D", 3, "Description", 3, "\x55\x04\x0d", "2.5.4.13" }, {"BC", 3, "BusinessCategory", 3, "\x55\x04\x0f", "2.5.4.15" }, {"ADDR", 2, "PostalAddress", 3, "\x55\x04\x11", "2.5.4.16" }, {"POSTALCODE" , 0, "PostalCode", 3, "\x55\x04\x11", "2.5.4.17" }, {"GN", 2, "GivenName", 3, "\x55\x04\x2a", "2.5.4.42" }, {"PSEUDO", 2, "Pseudonym", 3, "\x55\x04\x41", "2.5.4.65" }, {"DC", 1, "domainComponent", 10, "\x09\x92\x26\x89\x93\xF2\x2C\x64\x01\x19", "0.9.2342.19200300.100.1.25" }, {"UID", 1, "userid", 10, "\x09\x92\x26\x89\x93\xF2\x2C\x64\x01\x01", "0.9.2342.19200300.100.1.1 " }, {"E", 1, "emailAddress", 9, "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x01", "1.2.840.113549.1.9.1" }, {"OGRN", 4, "OGRN", 5, "\x2a\x85\x03\x64\x01", "1.2.643.100.1" }, {"INN", 4, "INN", 8, "\x2a\x85\x03\x03\x81\x03\x01\x01", "1.2.643.3.131.1.1" }, {"SNILS", 4, "SNILS", 5, "\x2a\x85\x03\x64\x03", "1.2.643.100.3" }, {"OGRNIP", 4, "OGRNIP", 5, "\x2a\x85\x03\x64\x05", "1.2.643.100.5" }, { NULL } };
Ce qui est remarquable dans cette structure est la présence du champ source, qui indique qui a entré un champ particulier, par exemple, 1 (unité) indique que le champ est défini dans rfc2253, et 4 (quatre) ajoutés ici seront indiquer que le domaine est recommandé par le comité technique du TK-26. Conformément aux recommandations du TK-26, les attributs TIN, OGRN, OGRNIP et SNILS sont de type NUMERIC STRING (voir ci-dessus). Le traitement des oids inclus dans le DN dans libksba est fourni dans la fonction append_atv (fichier dn.c), cependant, il manque un traitement de type TYPE_NUMERIC_STRING, et vous devez l'ajouter:
. . . switch (use_hex? 0 : node->type) { case TYPE_UTF8_STRING: append_utf8_value (image+node->off+node->nhdr, node->len, sb); break; case TYPE_NUMERIC_STRING: case TYPE_PRINTABLE_STRING: ….
Le patch pour le fichier dn.c est ici: Enregistrez-le dans le fichier diff_dn.patch.
La bibliothèque libksba peut être téléchargée
ici . Décompressez l'archive, entrez dans le répertoire src et appliquez le patch diff_dn.patch au fichier dn.c:
$patch dn.c < diff_dn.patch $
En plus des oid s des attributs DN, il est également nécessaire d'ajouter des oid s des clés GOST à la structure struct pk_algo_table [] (fichier keyihfo.c):
static const struct algo_table_s pk_algo_table[] = { { "1.2.840.113549.1.1.1", "\x2a\x86\x48\x86\xf7\x0d\x01\x01\x01", 9, 1, PKALGO_RSA, "rsa", "-ne", "\x30\x02\x02" }, . . . { "1.2.643.2.2.19", "\x2a\x85\x03\x02\x02\x13", 6, 1, PKALGO_ECC, "ecc", "q", "\x80" }, { "1.2.643.7.1.1.1.1", "\x2a\x85\x03\x07\x01\x01\x01\x01", 8, 1, PKALGO_ECC, "ecc", "q", "\x80" }, { "1.2.643.7.1.1.1.2", "\x2a\x85\x03\x07\x01\x01\x01\x02", 8, 1, PKALGO_ECC, "ecc", "q", "\x80" }, {NULL} };
De même, dans la structure sig_algo_table [] (fichier keyihfo.c) nous ajoutons des oid-s associés à la signature électronique selon GOST:
static const struct algo_table_s sig_algo_table[] = { { "1.2.840.113549.1.1.5", "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x05", 9, 1, PKALGO_RSA, "rsa", "s", "\x82", NULL, NULL, "sha1" }, . . . { "1.2.643.2.2.19", "\x2a\x85\x03\x02\x02\x13", 6, 1, PKALGO_ECC, "gost", "s", "\x80", NULL, NULL, "gostr3411_CP" }, { "1.2.643.7.1.1.1.1", "\x2a\x85\x03\x07\x01\x01\x01\x01", 8, 1, PKALGO_ECC, "gost", "s", "\x80", NULL, NULL, "stribog256"}, { "1.2.643.7.1.1.1.2", "\x2a\x85\x03\x07\x01\x01\x01\x02", 8, 1, PKALGO_ECC, "gost", "s", "\x80", NULL, NULL, "stribog512"}, { "1.2.643.7.1.1.3.2", "\x2a\x85\x03\x07\x01\x01\x03\x02", 8, 1, PKALGO_ECC, "gost", "s", "\x80", NULL, NULL, "stribog256" }, { "1.2.643.7.1.1.3.3", "\x2a\x85\x03\x07\x01\x01\x03\x03", 8, 1, PKALGO_ECC, "gost", "s", "\x80", NULL, NULL, "stribog512" }, { "1.2.643.2.2.3", "\x2a\x85\x03\x02\x02\x03", 6, 1, PKALGO_ECC, "gost", "s", "\x80", NULL, NULL, "gostr3411_CP" }, {NULL} };
Et, enfin, nous complétons la structure de enc_algo_table [] avec des oids de clés GOST qui peuvent participer au chiffrement asymétrique (PKCS # 7):
static const struct algo_table_s enc_algo_table[] = { { "1.2.840.113549.1.1.1", "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01", 9, 1, PKALGO_RSA, "rsa", "a", "\x82" }, { "1.2.643.2.2.19", "\x2A\x85\x03\x02\x02\x13", 6, 1, PKALGO_ECC, "ecc", "a", "\x80" }, { "1.2.643.7.1.1.1.1", "\x2A\x85\x03\x07\x01\x01\x01\x01", 8, 1, PKALGO_ECC, "ecc", "a", "\x80" }, { "1.2.643.7.1.1.1.2", "\x2A\x85\x03\x07\x01\x01\x01\x02", 8, 1, PKALGO_ECC, "ecc", "a", "\x80" }, {NULL} };
Maintenant que nous avons décidé de l'oid, passons à la structure de stockage de la clé publique dans le certificat. En général, la structure SubjectPublicKeyInfo dans un certificat a la forme suivante:
SubjectPublicKeyInfo ::= SEQUENCE { algorithm OBJECT IDENTIFIER GostR3410-2001/2012-PublicKeyParameters ::= SEQUENCE { publicKeyParamSet OBJECT IDENTIFIER, digestParamSet OBJECT IDENTIFIER, encryptionParamSet OBJECT IDENTIFIER } subjectKey BIT STRING }
Le paramètre publicKeyParamSet spécifie l'oid du point de la courbe elliptique. Les points OID des points autorisés sont enregistrés dans la structure struct curve_aliases [] du fichier ecc_curves.c de la bibliothèque libgcrypt. Dans la bibliothèque libksba, ces oid-s sont enregistrés dans la structure struct curve_names [] du fichier keyinfo.c. Dans cette structure, les «1.2.643.2.2.36.0» (GOST2001-CryptoPro-XchA) et «1.2.643.2.2.36.1» (GOST2001-CryptoPro-XchB) sont omis. Nous ajoutons ces oids, mais en tenant compte du fait que l'oid "1.2.643.2.2.36.0" est en fait un point avec l'oid "1.2.643.2.2.35.1", et "1.2.643.2.2.36.1" correspond à oid "1.2.643.2.2.35.3":
static const struct { const char *oid; const char *name; } curve_names[] = { { "1.3.6.1.4.1.3029.1.5.1", "Curve25519" }, . . . { "1.2.643.2.2.35.3", "GOST2001-CryptoPro-C" },
Le paramètre encryptionParamSet est généralement omis. Le paramètre digestParamSet peut également être omis: le type de la clé GOST détermine uniquement l'oid digestParamSet.
Le code qui extrait la clé publique GOST du certificat, l'analyse et l'intègre dans une variable S est ajouté à la fonction _ksba_keyinfo_to_sexp (fichier keyinfo.c), et le code chargé de compresser la signature GOST dans une variable S est ajouté à la fonction crypt_val_to_sexp. Le code est fourni avec des commentaires et ne nécessite pas d'explications supplémentaires. Nous avons parlé des fonctionnalités de stockage de la clé publique et de la signature dans le certificat dans un article
précédent .
Le patch pour le fichier keyinfo.c est ici: Enregistrez-le dans le fichier diff_keyinfo.patch et appliquez-le au fichier keyinfo.c:
$patch keyinfo.c < diff_keyinfo.patch $
Après avoir ajouté ces correctifs, vous pouvez créer la bibliothèque. Si vous n'allez pas installer la bibliothèque libksba.so.8.11.6 assemblée dans le système, mais que vous ne l'utiliserez qu'à des fins de test, il est pratique de la copier dans le répertoire dans lequel les cas de test seront collectés. Ces correctifs sont suffisants pour analyser des certificats avec des clés GOST, ainsi que des documents signés avec des clés GOST au format PKCS # 7, et extraire et vérifier la validité mathématique de la signature électronique du certificat X509 ou de la signature électronique du document PKCS # 7 / CMS.
Pour tester cela, considérez deux modules logiciels. Le premier module check_cert vérifie que le certificat est signé correctement et le deuxième module check_cms_signed vérifie l'intégrité du document signé (CMS / PKCS # 7) et l'exactitude de la signature du document. Rappelons que la validité du certificat est déterminée non seulement par l'exactitude de sa signature, mais également par sa validité (non-rappel) au moment de la vérification. De la même manière, la validité d'une signature sous un document est déterminée non seulement par la fiabilité mathématique de la signature, mais également par la validité du certificat signataire.

Pour créer un document signé au format PKCS # 7, vous pouvez utiliser l'utilitaire
guinss.exe écrit en Python avec Tkinter et utiliser des jetons PKCS # 11 avec prise en charge de la cryptographie russe comme moyen de protection des informations cryptographiques (SKZI):

Nous notons également que le format PKCS # 7 envisagé pour la signature électronique du document implique l'inclusion d'un signataire dans la signature. Ceci est fait pour que l'exemple ne devienne pas immense.
Le code source de l'utilitaire de vérification de signature de certificat check_cert.c est donné ici: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <errno.h> #include <ctype.h> #include <gcrypt.h> #include <ksba.h> /* Hash function used with libksba. */ #define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write) /* 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); } /* */ static gpg_error_t check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) { gpg_error_t err; const char *algoid; gcry_md_hd_t md; int i, algo; ksba_sexp_t p; size_t n; /* : S_PKEY - S- . S_SIG - S- S_HASH - S- tbs- */ gcry_sexp_t s_sig, s_hash, s_pkey; const char *s; char algo_name[16+1]; /* hash algorithm name converted to lower case. */ int digestlen; unsigned char *digest; int gost_key; /* Hash the target certificate using the algorithm from that certificate. */ algoid = ksba_cert_get_digest_algo (cert); algo = gcry_md_map_name (algoid); if (!algo) { fprintf(stderr, "unknown hash algorithm `%s'\n", algoid? algoid:"?"); return (-1); } /* */ gost_key = !memcmp(algoid, "1.2.643", 7); s = gcry_md_algo_name (algo); for (i=0; *s && i < sizeof algo_name - 1; s++, i++) algo_name[i] = tolower (*s); algo_name[i] = 0; err = gcry_md_open (&md, algo, 0); if (err) { fprintf(stderr, "md_open failed: %s\n", algoid); return err; } err = ksba_cert_hash (cert, 1, HASH_FNC, md); if (err) { fprintf(stderr, "ksba_cert_hash failed: %s\n", algoid); gcry_md_close (md); return err; } gcry_md_final (md); /* */ p = ksba_cert_get_sig_val (cert); /* */ n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) { gcry_md_close (md); ksba_free (p); return (-1); } /* S- libgcrypt*/ err = gcry_sexp_sscan ( &s_sig, NULL, p, n); ksba_free (p); if (err) { fprintf(stderr, "gcry_sexp_scan failed: %s\n", "Beda"); gcry_md_close (md); return err; } /* S- */ show_sexp ("Sig value:\n", s_sig); /* */ p = ksba_cert_get_public_key (issuer_cert); n = gcry_sexp_canon_len (p, 0, NULL, NULL); if (!n) { fprintf(stderr, "libksba did not return a proper S-Exp\n"); gcry_md_close (md); ksba_free (p); gcry_sexp_release (s_sig); return (-1); } err = gcry_sexp_sscan ( &s_pkey, NULL, p, n); ksba_free (p); if (err) { fprintf(stderr, "gcry_sexp_scan failed: %s\n", "pubkey"); gcry_md_close (md); gcry_sexp_release (s_sig); return err; } /* S- */ show_sexp ("s_pkey:\n", s_pkey); digestlen = gcry_md_get_algo_dlen (algo); digest = gcry_md_read (md, algo); if (gost_key){ unsigned char *h; unsigned char c; int len_xy; /* littlt-endian big-endian*/ unsigned short arch = 1; /* 0x0001 */ h = digest; 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; } } switch (gost_key) { case 0: if ( gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", algo_name, (int)digestlen, digest) ) { exit (1); } break; case 1: if ( gcry_sexp_build (&s_hash, NULL, "(data(flags gost)(value %b))", (int)digestlen, digest) ) { exit (1); } break; default: exit (1); } /* S- tbs-*/ show_sexp ("s_hash:\n", s_hash); /* */ err = gcry_pk_verify (s_sig, s_hash, s_pkey); /* */ gcry_md_close (md); gcry_sexp_release (s_sig); gcry_sexp_release (s_hash); gcry_sexp_release (s_pkey); return err; } int main (int argc, unsigned char *argv[]) { FILE *fp; ksba_reader_t r; ksba_cert_t cert; FILE *fp_ca; ksba_reader_t r_ca; ksba_cert_t cert_ca; gpg_error_t err; unsigned char *sub_dn; if(argc != 3) { fprintf(stderr, "Usage: check_cert < > < >\n"); exit(1); } fp = fopen (argv[1], "rb"); if (!fp) { fprintf (stderr, "check_cert: can't open `%s'\n", argv[1]); exit (1); } err = ksba_reader_new (&r); if (err) { fprintf(stderr, "ksba_reader_new error\n"); exit(1); } err = ksba_reader_set_file (r, fp); if (err) { fprintf(stderr, "ksba_reader_set error\n"); exit(1); } err = ksba_cert_new (&cert); if (err){ fprintf(stderr, "ksba_cert_new error\n"); exit(1); } err = ksba_cert_read_der (cert, r); if (err){ fprintf(stderr, "ksba_cert_read_der error\n"); exit(1); } fp_ca = fopen (argv[2], "rb"); if (!fp_ca) { fprintf (stderr, "check_cert: can't open `%s'\n", argv[2]); exit (1); } err = ksba_reader_new (&r_ca); if (err) { fprintf(stderr, "ksba_reader_new error\n"); exit(1); } err = ksba_reader_set_file (r_ca, fp_ca); if (err) { fprintf(stderr, "ksba_reader_set error\n"); exit(1); } err = ksba_cert_new (&cert_ca); if (err){ fprintf(stderr, "ksba_cert_new error\n"); exit(1); } err = ksba_cert_read_der (cert_ca, r_ca); if (err){ fprintf(stderr, "ksba_cert_read_der error\n"); exit(1); } sub_dn = ksba_cert_get_subject (cert, 0); fprintf(stderr, "check_cert: Verify %s\n", sub_dn); err = check_cert_sig (cert_ca, cert); if (err) { fprintf(stderr, "check_cert: verify %s error\n", argv[1]); } else { fprintf(stderr, "check_cert: verify %s Ok\n", argv[1]); } }
Nous enregistrons le code dans le fichier check_cert.c, le traduisons et l'exécutons:
bash-4.3$ln –s libksba.so.8 lgcrypt libksba.so.8.11.6 bash-4.3$ cc -o check_cert check_cert.c -lgcrypt libksba.so.8.11.6 bash-4.3$ ./check_cert Usage: check_cert < > < > bash-4.3$
Ainsi, afin de vérifier le certificat, il est nécessaire d'avoir le certificat lui-même et le certificat CA (par exemple, vous pouvez le prendre de l'article précédent) sur lequel il a été émis. Les certificats doivent être (pour l'instant) encodés en DER:
bash-4.3$ ./check_cert CERT_CMS_KSBA.der CAcert_NEWCA.der check_cert: Verify SNILS=22222222222,INN=123456789012,O=CMS,STREET=PKCS7,L= ,ST=50 ,C=RU,GN=KSBA,SN=GCrypt,CN= GCrypt and KSBA,E=test@test.ru Sig value: (sig-val (gost (r
Pour vérifier un certificat auto-signé, ce dernier est indiqué à la fois comme certificat vérifié et comme certificat racine:
bash-4.3$ ./check_cert CAcert_NEWCA.der CAcert_NEWCA.der check_cert: Verify E=info@ooo.ru,CN= ,OU= 2,O= ,C=RU,ST= ,L=. . . . check_cert: verify CAcert_NEWCA.der Ok bash-4.3$
Nous avons donc un utilitaire simple pour vérifier la signature des certificats.
Vérifier la signature électronique des documents au format PKCS # 7 développé
utilitaire de test check_cms_signed.c: #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <time.h> #include <errno.h> #include <ksba.h> //#include "t-common.h" #include <gcrypt.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <unistd.h> /* Hash function used with libksba. */ #define HASH_FNC ((void (*)(void *, const void*,size_t))gcry_md_write) void dump_isotime (const ksba_isotime_t t) { if (!t || !*t) fprintf (stderr, "[none]"); else fprintf (stderr,"%.4s-%.2s-%.2s %.2s:%.2s:%s", t, t+4, t+6, t+9, t+11, t+13); } 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); } void dummy_hash_fnc (void *arg, const void *buffer, size_t length) { (void)arg; (void)buffer; (void)length; } static int dummy_writer_cb (void *cb_value, const void *buffer, size_t count) { (void)cb_value; (void)buffer; (void)count; return 0; } /* */ unsigned char *dgst_file (char *oid, char* fcont, int *len_h) { gcry_md_hd_t hd; gcry_error_t err; int algo; FILE *fp; unsigned char buf[1024]; size_t size; int i; unsigned char *h; unsigned char *ret_h; algo = gcry_md_map_name (oid); if (algo == GCRY_MD_NONE) return NULL; err = gcry_md_open(&hd, algo, 0); if (err) return NULL; fp = fopen (fcont, "r"); if (fp == NULL) return NULL; while (!feof (fp)) { size = fread (buf, 1, sizeof(buf), fp); gcry_md_write (hd, buf, size); } h = gcry_md_read(hd, 0); *len_h = gcry_md_get_algo_dlen (algo); ret_h = malloc(*len_h); memcpy(ret_h, h, *len_h); gcry_md_reset(hd); gcry_md_close(hd); return ret_h; } gcry_error_t one_file (const char *fname, char *fcontent, char *fcert) { gcry_error_t err; FILE *fp; ksba_reader_t r; ksba_writer_t w; ksba_cms_t cms; int i; const char *algoid; ksba_stop_reason_t stopreason; const char *s = NULL; size_t n; ksba_sexp_t p; char *dn; int idx; ksba_cert_t cert; int ii; unsigned char *cert_der; int cert_der_len; int f_der; int rez; ksba_sexp_t p1; size_t n1; gcry_sexp_t s_sig, s_hash, s_pkey, s_num; int gost_key; gcry_md_hd_t md; int rc; int digestlen; unsigned char *digest; gcry_md_hd_t data_md = NULL; ksba_isotime_t sigtime; unsigned char *sub_dn; //dn - char *is_dn; //dn - ksba_sexp_t sub_p; // fprintf (stderr, "\n*** checking `%s' ***\n", fname); fp = fopen (fname, "r"); if (!fp) { fprintf (stderr, "can't open `%s'\n", fname); exit (1); } err = ksba_reader_new (&r); if (err) { fprintf (stderr, "can't reader `%s'\n", fname); exit (1); } err = ksba_reader_set_file (r, fp); if (err) { fprintf (stderr, "can't reader set `%s'\n", fname); exit (1); } /* Also create a writer so that cms.c won't return an error when writing processed content. */ err = ksba_writer_new (&w); if (err) { exit (1); } err = ksba_writer_set_cb (w, dummy_writer_cb, NULL); if (err) { exit (1); } switch (ksba_cms_identify (r)) { case KSBA_CT_SIGNED_DATA: s = "signed data"; break; case KSBA_CT_DATA: s = "data"; case KSBA_CT_ENVELOPED_DATA: if (s == NULL) s = "enveloped data"; case KSBA_CT_DIGESTED_DATA: if (s == NULL) s = "digested data"; case KSBA_CT_ENCRYPTED_DATA: if (s == NULL) s = "encrypted data"; case KSBA_CT_AUTH_DATA: if (s == NULL) s = "auth data"; default: if (s == NULL) s = "unknown"; printf ("identified as: %s\n", s); exit(1); } err = ksba_cms_new (&cms); if (err) { exit(1); } err = ksba_cms_set_reader_writer (cms, r, w); if (err) { exit(1); } rc = gcry_md_open (&data_md, 0, 0); if (rc) { fprintf (stderr, "md_open failed: \n"); exit(1);; } err = ksba_cms_parse (cms, &stopreason); if (err) { fprintf (stderr, "ksba_cms_parse: cannot parse %s\n", fname); exit(1); } ksba_cms_set_hash_function (cms, dummy_hash_fnc, NULL); do { err = ksba_cms_parse (cms, &stopreason); if (err) { fprintf (stderr, "ksba_cms_parse: cannot parse %s\n", fname); exit(1); } } while (stopreason != KSBA_SR_READY); /* PKCS7 */ cert_der_len = 0; for (ii=0; (cert=ksba_cms_get_cert (cms, ii)); ii++) { cert_der = (unsigned char*)ksba_cert_get_image(cert, (size_t *)&cert_der_len); f_der = open(fcert, O_RDWR|O_TRUNC|O_CREAT, 0666); if (f_der == -1) { fprintf(stderr, "Bad open file=%s for cert\n", cert_der_len, fcert); exit(1); } /* */ rez = write(f_der, cert_der, cert_der_len); close(f_der); /* */ p1 = ksba_cert_get_public_key (cert); n1 = gcry_sexp_canon_len (p1, 0, NULL, NULL); if (!n1) { fprintf(stderr, "libksba did not return a proper S-Exp\n"); exit(1); } err = gcry_sexp_sscan ( &s_pkey, NULL, p1, n1); ksba_free (p1); if (err) { fprintf(stderr, "gcry_sexp_scan failed: %s\n", "pubkey"); exit(1); } /* DN */ sub_dn = ksba_cert_get_subject (cert, 0); ksba_cert_release (cert); } for (idx=0; idx < 1; idx++) { int algo; int info_pkalgo; unsigned char *s; int s_len; int len_h; unsigned char *dgst_h; /* /PKCS#7 DN is_dn sub_p*/ /* */ /* , PKCS#7*/ err = ksba_cms_get_issuer_serial (cms, idx, &is_dn, &sub_p); if (err) { fprintf (stderr, "ksba_cms_get_issuer_serial cannot %s\n", fname); exit(1); } /* */ algoid = ksba_cms_get_digest_algo (cms, idx); err = ksba_cms_get_sigattr_oids (cms, 0, "1.2.840.113549.1.7.1",&dn); if (err && err != -1) { fprintf (stderr, "ksba_cms_get_sigattr_oids cannot %s err=%d\n", fname, err); exit(1); } algo = gcry_md_map_name (algoid); /* - (1) */ gost_key = !memcmp(algoid, "1.2.643", 7); gcry_md_enable (data_md, algo); rc = gcry_md_open (&md, algo, 0); if (rc) { fprintf(stderr, "md_open failed:\n"); exit(1); } /* -*/ ksba_cms_set_hash_function (cms, HASH_FNC, data_md); /* */ rc = ksba_cms_hash_signed_attrs (cms, idx); if (rc) { fprintf(stderr, "hashing signed attrs failed: \n"); gcry_md_close (md); continue; } gcry_md_final (md); digestlen = gcry_md_get_algo_dlen (algo); /* */ digest = gcry_md_read (data_md, algo); gcry_md_close (md); if (gost_key){ /* */ unsigned char *h; unsigned char c; int len_xy; /* littlt-endian big-endian*/ unsigned short arch = 1; /* 0x0001 */ h = digest; 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; } } switch (gost_key) { case 0: if ( gcry_sexp_build (&s_hash, NULL, "(data(flags pkcs1)(hash %s %b))", algoid, (int)digestlen, digest) ) { fprintf(stderr, "BUG\n"); exit (1); } break; case 1: /* , S- */ if ( gcry_sexp_build (&s_hash, NULL, "(data(flags gost)(value %b))", (int)digestlen, digest) ) { exit (1); } break; default: exit (1); } /* */ rc = ksba_cms_get_signing_time (cms, idx, sigtime); if (rc) { fprintf(stderr, "error getting signing time\n"); // *sigtime = 0; /* (we can't encode an error in the time string.) */ exit(1); } /* PKCS#7 */ err = ksba_cms_get_message_digest (cms, idx, &dn, &n); if (err) { fprintf (stderr, "ksba_cms_get_message_digest cannot %s\n", fname); exit(1); } /* */ dgst_h = dgst_file ((char *)algoid, (char *)fcontent, &len_h); if (dgst_h == NULL) { fprintf(stderr, "\nBad digest %s\n", fcontent); exit (1); } if (memcmp(dgst_h, dn, n)) { /* : , , ..*/ fprintf(stderr, "\nBad content %s\n", fcontent); exit (1); } ksba_free (dn); putchar ('\n'); dn = ksba_cms_get_sig_val (cms, idx); if (!dn) { printf ("signer %d - signature not found\n", idx); exit(1); } n = gcry_sexp_canon_len ((ksba_sexp_t)dn, 0, NULL, NULL); if (!n){ fprintf(stderr, "libksba did not return a proper S-Exp\n"); exit(1); } err = gcry_sexp_sscan ( &s_sig, NULL, dn, n); if (err){ fprintf(stderr, "gcry_sexp_scan failed: %s\n", "Beda"); exit(1); } ksba_free (dn); } fprintf(stderr, "\n===========================================================\n"); if (*sigtime){ fprintf (stderr, " :\n"); dump_isotime (sigtime); fprintf (stderr, "\n"); } else fprintf (stderr, "[date not given]\n"); fprintf(stderr, "\n : %s\n", sub_dn); fprintf(stderr, "\n : %s\n", is_dn); n = gcry_sexp_canon_len ((ksba_sexp_t)sub_p, 0, NULL, NULL); if (!n){ fprintf(stderr, "libksba did not return a proper S-Exp NUM\n"); exit(1); } err = gcry_sexp_sscan ( &s_num, NULL, sub_p, n); show_sexp ("\n :\n", s_num); fprintf(stderr, "\n===========================================================\n"); /* */ err = gcry_pk_verify (s_sig, s_hash, s_pkey); // if(err) { show_sexp ("s_pkey:\n", s_pkey); show_sexp ("s_sig:\n", s_sig); show_sexp ("s_hash:\n", s_hash); // } ksba_cms_release (cms); ksba_reader_release (r); fclose (fp); return (err); } int main (int argc, char **argv) { gcry_error_t err; unsigned char *h; int *len_h; if(argc != 4) { fprintf(stderr, "Usage: check_cms < > < > < >\n"); exit(1); } err = one_file (argv[1], argv[2], argv[3]); if (err) { fprintf(stderr, "check_cms: verify %s error\n", argv[1]); } else { fprintf(stderr, "check_cms: verify %s Ok\n", argv[1]); fprintf(stderr, "check_cms: %s\n", argv[3]); } exit (0); }
Afin de simplifier l'exemple, il est écrit en supposant que le document n'a qu'une seule signature (une signature) et son certificat est stocké dans la signature. Nous enregistrons l'utilitaire check_cms_signed.c, le traduisons et l'exécutons: bash-4.3$ cc -o check_cms_signed check_cms_signed.c -lgcrypt libksba.so.8.11.6 bash-4.3$ ./check_cms_signed Usage: check_cms < > < > < > bash-4.3$
Et maintenant, nous vérifions le fonctionnement de l'utilitaire: bash-4.3$ ./check_cms_signed test_cms_ksba.txt.p7s test_cms_ksba.txt save_cert.der *** checking `test_cms_ksba.txt.p7s' *** =========================================================== : 2018-06-25 15:57:12 : SNILS=22222222222,INN=123456789012,O=CMS,STREET=PKCS7,L= ,ST=50 ,C=RU,GN=KSBA,SN=GCrypt,CN= GCrypt and KSBA,E=test@test.ru : E=info@lissi.ru,CN= ,OU= 2,O= ,C=RU,ST= ,L=. ,STREET=. .4 .7,OGRN=1234567890123,INN=123456789012 : (#73102E931BF9EA1369BA#) =========================================================== s_pkey: (public-key (ecc (curve "1.2.643.7.1.2.1.2.2") (hash "1.2.643.7.1.1.2.3") (q #044840A283684ECB537989536B9F080F7B914F3E6C153BC23F8A9212E303BF5B13905D29D0689CA5F2D0715D13FDC0FD387650193A7B46BE20C266776FAE36483750FE52A4C4E35EFB37EA64B48CB50ED5151289F59793574BD5FA59A2048A97FD94A1E5BB8DBF616B776D70C25774C1AC11CD6B6791D15850C37F7F176F49DBB6#) ) ) s_sig: (sig-val (gost (r #430C6EE1C0126F217B58EF5FB2E25055B2DF64AF0A1D769F2E7402145322ACD77B7D537B985AD7F3E3EDE94F9521D2F1E039B6F818B88D1CD709BE7BA97FE5E7#) (s #6A75146760E21BF6FA4CEDB41D37D938D5988DE048F9171796E764EC0E90891A7E02CA7F855C2468E11D217DB5C28DFAB1E31FF45793029FCDD666BE589F646A#) ) (hash stribog512) ) s_hash: (data (flags gost) (value #AD66AC68C832442C9520718AF9F67A87518CFE23098886C075F09DD19AC626DA65D7E39F6E0F81F5CF19C7DC5C3C9CC75FAD26A6C450ADD4C02FD31B49BA7CF1#) ) check_cms: verify test_cms_ksba.txt.p7s Ok check_cms: save_cert.der bash-4.3$
Si nous remplaçons / modifions le document à signer, nous obtenons: bash-4.3$ ./check_cms_signed test_cms_ksba.txt.p7s test_cms_ksba_Change.txt save_cert.der *** checking `test_cms_ksba.txt.p7s' *** Bad content test_cms_ksba_Change.txt bash-4.3$
Une fois la vérification réussie, le certificat du signataire sera enregistré dans <Où enregistrer le certificat du signataire> (dans l'exemple, il s'agit du fichier sace_cert.der).Le résultat n'était pas du tout une mauvaise utilité, qui peut être utilisé à la fois dans la pratique et à des fins éducatives.Mais seulement un quart du chemin vers le but final a été parcouru. En dehors de notre conversation, le travail avec des documents cryptés (PKCS # 7, VKO, KEK), la signature de documents, la création de demandes de certificats est resté. Le travail continue.