
Continuamos
hablando sobre la alternativa a openssl y hablaremos sobre la biblioteca
libksba , que es parte de GnuPG. La biblioteca libksba proporciona una interfaz de alto nivel para trabajar con objetos de infraestructura de clave pública como certificados, solicitudes de certificados, firma electrónica (CMS / PKCS # 7). Sin embargo, a diferencia de la biblioteca GCrypt, que admite algoritmos criptográficos rusos, libksba carece de la implementación de
las recomendaciones
TK-26 sobre el uso de algoritmos GOST R 34.10-2001 / 2012, GOST R 34.11-94 / 2012 en objetos PKI como certificados, solicitudes de certificados, objetos PKCS # 7 / CMS (documentos firmados y / o encriptados, etc.).

Comencemos con los oids que pueden (o deberían) ser parte de los atributos distinguidos DN (nombre distinguido) del emisor (editor) y el sujeto (propietario) del certificado. En la biblioteca libksba, los oid s se escriben en la estructura oid_name_tbl [] (archivo dn.c). La estructura oid_name_tbl [] no contiene
oid s de TIN, BIN, OGRNIP y SNILS
recomendados por TK-26 para un certificado calificado:
18. Los atributos adicionales del nombre, cuya necesidad se establece de conformidad con la Ley Federal, incluyen:
1) OGRN.
El valor del atributo OGRN es una cadena de 13 dígitos que representa el OGRN del titular de un certificado calificado, una entidad legal. El identificador de objeto del tipo de atributo OGRN tiene la forma 1.2.643.100.1, el tipo de atributo OGRN se describe de la siguiente manera: OGRN :: = TAMAÑO DE LA CADENA NUMÉRICA 13;
2) SNILS (SNILS).
El valor del atributo SNILS es una cadena de 11 dígitos que representa SNILS del propietario de un certificado calificado: un individuo. El identificador de objeto del tipo de atributo SNILS es 1.2.643.100.3, el tipo de atributo SNILS se describe a continuación: SNILS :: = TAMAÑO DE CADENA NUMÉRICA 11;
3) INN.
El valor del atributo INN es una cadena de 12 dígitos que representa el TIN del titular del certificado calificado. El identificador de objeto del tipo de atributo INN es 1.2.643.3.131.1.1, el tipo de atributo INN se describe de la siguiente manera: INN :: = TAMAÑO DE LA CADENA NUMÉRICA 12.
Naturalmente, debe agregar estos 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 } };
Lo que es notable en esta estructura es la presencia del campo fuente, que indica quién ingresó en un campo en particular, por ejemplo, 1 (unidad) indica que el campo está definido en rfc2253, y 4 (cuatro) agregados aquí serán indicar que el campo es recomendado por el comité técnico de TK-26. De acuerdo con las recomendaciones de TK-26, los atributos TIN, OGRN, OGRNIP y SNILS son del tipo NUMERIC STRING (ver arriba). El procesamiento de los oids incluidos en el DN en libksba se proporciona en la función append_atv (archivo dn.c), sin embargo, carece de procesamiento del tipo TYPE_NUMERIC_STRING, y debe agregarlo:
. . . 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: ….
El parche para el archivo dn.c está aquí: Guárdelo en el archivo diff_dn.patch.
La biblioteca libksba se puede descargar
aquí . Descomprima el archivo, ingrese el directorio src y aplique el parche diff_dn.patch al archivo dn.c:
$patch dn.c < diff_dn.patch $
Además de los oid s de los atributos DN, también es necesario agregar oid s de las claves GOST a la estructura struct pk_algo_table [] (archivo 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 manera similar, en la estructura sig_algo_table [] (archivo keyihfo.c) agregamos oid-s asociados con la firma electrónica de acuerdo con 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} };
Y, finalmente, complementamos la estructura de enc_algo_table [] con oids de claves GOST que pueden participar en el cifrado asimétrico (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} };
Ahora que hemos decidido el oid, pasemos a la estructura de almacenamiento de la clave pública en el certificado. En general, la estructura SubjectPublicKeyInfo en un certificado tiene la siguiente forma:
SubjectPublicKeyInfo ::= SEQUENCE { algorithm OBJECT IDENTIFIER GostR3410-2001/2012-PublicKeyParameters ::= SEQUENCE { publicKeyParamSet OBJECT IDENTIFIER, digestParamSet OBJECT IDENTIFIER, encryptionParamSet OBJECT IDENTIFIER } subjectKey BIT STRING }
El parámetro publicKeyParamSet especifica el oid del punto de la curva elíptica. Los puntos OID de los puntos permitidos se registran en la estructura struct curve_aliases [] en el archivo ecc_curves.c de la biblioteca libgcrypt. En la biblioteca libksba, estos oid-s se registran en la estructura struct curve_names [] en el archivo keyinfo.c. En esta estructura, se omiten los oids "1.2.643.2.2.36.0" (GOST2001-CryptoPro-XchA) y "1.2.643.2.2.36.1" (GOST2001-CryptoPro-XchB). Agregamos estos oids, pero teniendo en cuenta el hecho de que el oid "1.2.643.2.2.36.0" es en realidad un punto con el oid "1.2.643.2.2.35.1", y "1.2.643.2.2.36.1" corresponde a 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" },
El parámetro encryptionParamSet generalmente se omite. El parámetro digestParamSet también se puede omitir: el tipo de la tecla GOST determina de forma exclusiva oid digestParamSet.
El código que extrae la clave pública GOST del certificado, la analiza y la empaqueta en una variable S se agrega a la función _ksba_keyinfo_to_sexp (archivo keyinfo.c), y el código responsable de empaquetar la firma GOST en una variable S se agrega a la función crypt_val_to_sexp. El código se proporciona con comentarios y no requiere más explicaciones. Hablamos sobre las características de almacenar la clave pública y la firma en el certificado en un artículo
anterior .
El parche para el archivo keyinfo.c está aquí: Guárdelo en el archivo diff_keyinfo.patch y aplíquelo al archivo keyinfo.c:
$patch keyinfo.c < diff_keyinfo.patch $
Después de agregar estos parches, puede construir la biblioteca. Si no va a instalar la biblioteca ensamblada libksba.so.8.11.6 en el sistema, pero la usará solo para pruebas, es conveniente copiarla al directorio en el que se recopilarán los casos de prueba. Estos parches son suficientes para analizar certificados con claves GOST, así como documentos firmados con claves GOST en formato PKCS # 7, y extraer y verificar la validez matemática de la firma electrónica del certificado X509 o la firma electrónica del documento PKCS # 7 / CMS.
Para probar esto, considere dos módulos de software. El primer módulo check_cert verifica que el certificado esté firmado correctamente, y el segundo módulo check_cms_signed verifica la integridad del documento firmado (CMS / PKCS # 7) y la corrección de la firma del documento. Recuerde que la validez del certificado está determinada no solo por la exactitud de su firma, sino también por su validez (no recuerdo) en el momento en que se verifica. Del mismo modo, la validez de una firma bajo un documento está determinada no solo por la confiabilidad matemática de la firma, sino también por la validez del certificado del firmante.

Para crear un documento firmado en formato PKCS # 7, puede usar la utilidad
guinss.exe escrita en Python con Tkinter, y usar tokens PKCS # 11 con soporte de criptografía rusa como medio de protección de información criptográfica (SKZI):

También observamos que el formato PKCS # 7 bajo consideración de la firma electrónica del documento implica la inclusión de un certificado de firmante en la firma. Esto se hace para que el ejemplo no se vuelva inmenso.
El código fuente de la utilidad de verificación de firma de certificados check_cert.c se proporciona aquí: #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]); } }
Guardamos el código en el archivo check_cert.c, lo traducimos y lo ejecutamos:
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$
Entonces, para verificar el certificado, debe tener el certificado en sí y el certificado de CA (por ejemplo, puede tomarlo del artículo anterior) en el que se emitió. Los certificados deben estar (por ahora) codificados con 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
Para verificar un certificado autofirmado, este último se indica como un certificado verificado y como raíz:
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$
Entonces tenemos una sencilla utilidad para verificar la firma de los certificados.
Para verificar la firma electrónica de documentos en el formato PKCS # 7 desarrollado
utilidad de prueba 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); }
Para simplificar el ejemplo, está escrito bajo el supuesto de que el documento solo tiene una firma (una firma) y su certificado se almacena en la firma. Guardamos la utilidad check_cms_signed.c, la traducimos y la ejecutamos: 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$
Y ahora comprobamos cómo funciona la utilidad: 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 reemplazamos / cambiamos el documento a firmar, obtenemos: 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$
Tras una verificación exitosa, el certificado del firmante se guardará en <Dónde guardar el certificado del firmante> (en el ejemplo, este es el archivo sace_cert.der).El resultado no fue una mala utilidad, que se puede utilizar tanto en la práctica como con fines educativos.Pero solo se ha cubierto una cuarta parte del camino hacia el objetivo final. Fuera de nuestra conversación, trabajar con documentos encriptados (PKCS # 7, VKO, KEK), firmar documentos, crear solicitudes de certificados ha permanecido. El trabajo continúa.