Introduccion

El protocolo Kerberos 5 ahora se usa activamente para la autenticación. Una característica de este protocolo es que realiza una autenticación basada en cuatro pilares:
- Cifrado simétrico;
- Hashing
- EDS;
- Tercera parte de confianza.
A partir de la quinta versión, ahora es posible usar cifrado asimétrico (para firma electrónica). No tiene sentido detenerse en el funcionamiento del protocolo Kerberos, porque la descripción del algoritmo se puede encontrar aquí .
Desafortunadamente, la cantidad de algoritmos de cifrado, hash y firma digital que utiliza este protocolo no es tan grande como me gustaría, por lo que en este artículo quiero mostrar cómo agregar fácil y sencillo algoritmos propios para la implementación de este protocolo por el MIT . Agregaremos nuestros algoritmos domésticos: GOST 28147-89 (también conocido como Magma), GOST R 34.11-2012 (también conocido como Stribog) y GOST R 34.10-2012 (También me gustaría tener aka para eso, pero no sé :(). Listo se puede encontrar una solución para estos algoritmos en mi repositorio . En el lado del cliente, utilizaremos implementaciones de hardware de algoritmos GOST en Rutoken EDS 2.0 y sus implementaciones de software en el motor GOST para openssl. Pero la opción más segura para almacenar claves es cuando se generan directamente en Rutoken y nunca no deje su memoria durante las operaciones criptográficas Para esta opción, se requiere rtengine.
Antes de comenzar a implementar los algoritmos, describimos los principales lugares donde se realizarán los cambios. Dentro del directorio src / lib / crypto / se encuentran las implementaciones de todos los algoritmos responsables de la criptografía simétrica y el hash. Tiene 2 implementaciones de estos algoritmos criptográficos: incorporado y openssl. Para ahorrar tiempo y espacio, nosotros, por supuesto, agregaremos la implementación de algoritmos usando openssl, en el que ya existen (bueno, o casi allí, pero más sobre eso más adelante). Para agregar algoritmos asimétricos, deberá ajustar el complemento src / plugins / preauth / pkinit
Si aún no ha configurado Kerberos, puede encontrar las instrucciones para su configuración inicial y operación aquí . Además, el autor supone que está trabajando con el dominio AKTIV-TEST.RU
Agregar algoritmos hash y cifrado simétrico
Como se anunció anteriormente, no escribiremos algoritmos de cifrado y hash desde cero, sino que utilizaremos una implementación lista de estos algoritmos en openssl. Openssl directamente no admite la implementación de algoritmos domésticos, pero tiene movilidad en este asunto y le permite agregar nuevos algoritmos utilizando el mecanismo GOST del motor para trabajar con claves en el sistema de archivos y almacenados en el token - rtengine.
Una pequeña introducción al motor del motor openssl y la conexión GOST del motor
Engine in openssl es una pequeña biblioteca dinámica que abre cargas en tiempo de ejecución bajo demanda. Cada biblioteca debe contener ciertos símbolos (funciones) para cargar los algoritmos necesarios. En este documento, utilizaremos el motor Gost, que contiene todos los algoritmos criptográficos domésticos necesarios.
La instalación es lo más sencilla posible, por ejemplo, y procede de la siguiente manera:
Descargue la implementación de este motor desde el repositorio .
construir una biblioteca con él (mkdir build && cd build && cmake ... && make):
mkdir build cd build cmake .. make
En el directorio bin (¡QUE APARECERÁ EN EL CATÁLOGO RAÍZ DEL PROYECTO!) Habrá una biblioteca dinámica gost.so - este es nuestro Engin. Será necesario moverlo al directorio donde se almacenan los motores openssl. Descubra la ubicación de este directorio usando:
openssl version -a | grep ENGINESDIR
Depende de esto último: debe decirle a openssl dónde está el motor dado y cómo se llama. Puede hacer esto cambiando el archivo de configuración openssl.cnf. La ubicación de la cual se puede encontrar usando:
openssl version -a | grep OPENSSLDIR
Al final de este archivo deberá agregar los siguientes contenidos:
Después de eso, este motor debería aparecer en openssl. Puede verificar su rendimiento forzando, por ejemplo, a generar una clave privada en un archivo de acuerdo con GOST R 34.10-2012:
openssl genpkey -engine gost -algorithm gost2012_512 -pkeyopt paramset:A -out client_key.pem
El indicador -engine solo indica qué motor necesita cargarse antes de comenzar a trabajar para que el algoritmo de generación de claves para GOST R 34.10-2012 se vuelva visible.
Implementación de algoritmo hash
Comencemos con lo más simple: con la implementación del algoritmo Stribog. Kerberos tiene una fuerte conexión entre los algoritmos de cifrado y hash, es decir, no puede simplemente elegir un algoritmo para el cifrado, y para el hash de otro, deberá integrar el algoritmo de cifrado y hash. No conozco la razón de esto, pero dado que existen tales reglas allí, intentemos crear una combinación del algoritmo de Stribog y AES.
Porque necesitamos confianza en la conexión en diferentes lugares de nuestro programa, primero creemos una pequeña biblioteca gost_helper que contendrá la función de inicialización del motor en openssl, así como varias funciones que devuelven los contextos de algunos algoritmos de hash por conveniencia; esto nos ayudará en el futuro. Llamamos a esta biblioteca gost_helper y creamos un encabezado y un archivo fuente apropiados para él en el directorio src / lib / crypto / openssl / :
// gost_helper.h #include <openssl/evp.h> // GOST void krb5int_init_gost(); // , const EVP_MD * EVP_gostR3411_2012_256(); const EVP_MD * EVP_gostR3411_2012_512(); // gost_helper.c #include "gost_helper.h" #include <openssl/engine.h> // static ENGINE *eng = NULL; void krb5int_init_gost() { // , if (eng) return; OPENSSL_add_all_algorithms_conf(); ERR_load_crypto_strings(); if (!(eng = ENGINE_by_id("gost"))) { printf("Engine gost doesn't exist"); return; } ENGINE_init(eng); ENGINE_set_default(eng, ENGINE_METHOD_ALL); } const EVP_MD * EVP_gostR3411_2012_256() { krb5int_init_gost(); return EVP_get_digestbynid(NID_id_GostR3411_2012_256); } const EVP_MD * EVP_gostR3411_2012_512() { krb5int_init_gost(); return EVP_get_digestbynid(NID_id_GostR3411_2012_512); }
Después de agregar la biblioteca auxiliar, deberá declarar su existencia en el Makefile y escribir sus dependencias de archivo. Para hacer esto, agregue lo siguiente:
Vale la pena señalar que en el futuro, al conectar esta biblioteca, tendremos que agregar algunas dependencias de archivo en el encabezado de esta biblioteca. Esto se hace de manera muy simple: se encuentra el objetivo, donde debe agregar la dependencia en el archivo deps y se registra la dependencia de rel / path / to / gost_helper.h . Por ejemplo, en src / lib / crypto / openssl / hash_provider / deps necesitaría agregar lo siguiente:
hash_evp.so hash_evp.po $(OUTPRE)hash_evp.$(OBJEXT): \ $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \ ... $(srcdir)/../gost_helper.h hash_evp.c
Para ahorrar espacio en el artículo y estirar sus cerebros, ya no le prestaré más atención: ¡tenga cuidado y cuidado!
Ahora agregue las implementaciones de la función hash, todas sus implementaciones están en src / lib / crypto / openssl / hash_provider / hash_evp.c . Allí deberá agregar lo siguiente:
// deps !!! #include "crypto_int.h" #include "gost_helper.h" #include <openssl/evp.h> ... static krb5_error_code hash_sha384(const krb5_crypto_iov *data, size_t num_data, krb5_data *output) { return hash_evp(EVP_sha384(), data, num_data, output); } static krb5_error_code hash_stribog256(const krb5_crypto_iov *data, size_t num_data, krb5_data *output) { return hash_evp(EVP_gostR3411_2012_256(), data, num_data, output); } static krb5_error_code hash_stribog512(const krb5_crypto_iov *data, size_t num_data, krb5_data *output) { return hash_evp(EVP_gostR3411_2012_512(), data, num_data, output); } // /* -- -- -- -- */ const struct krb5_hash_provider krb5int_hash_sha384 = { "SHA-384", 48, 128, hash_sha384 }; const struct krb5_hash_provider krb5int_hash_stribog256 = { "GOSTR34.11-2012-256", 32, 64, hash_stribog256 }; const struct krb5_hash_provider krb5int_hash_stribog512 = { "GOSTR34.11-2012-512", 64, 64, hash_stribog512 };
Ahora necesitamos declarar estos contextos en toda la biblioteca. Para hacer esto, cree su descripción en el archivo src / lib / crypto / krb / crypto_int.h .
// , , , krb5_hash_provider - : ... struct krb5_hash_provider { char hash_name[32]; // 8 size_t hashsize, blocksize; krb5_error_code (*hash)(const krb5_crypto_iov *data, size_t num_data, krb5_data *output); }; ... extern const struct krb5_hash_provider krb5int_hash_sha384; extern const struct krb5_hash_provider krb5int_hash_stribog256; extern const struct krb5_hash_provider krb5int_hash_stribog512; ...
Declararemos el identificador del paquete Stribog y AES, presentando macros que llamaremos CKSUMTYPE_STRIBOG_256_AES256 , CKSUMTYPE_STRIBOG_512_AES256 , ENCTYPE_AES256_CTS_STRIBOG_256 , ENCTYPE_AESTRIB_C . Deben declararse en la plantilla de archivo de encabezado src / include / krb5 / krb5.hin . Se verá más o menos así:
#define ENCTYPE_ARCFOUR_HMAC_EXP 0x0018 /**< RFC 4757 */ #define ENCTYPE_CAMELLIA128_CTS_CMAC 0x0019 /**< RFC 6803 */ #define ENCTYPE_CAMELLIA256_CTS_CMAC 0x001a /**< RFC 6803 */ #define ENCTYPE_AES256_CTS_STRIBOG_256 0x001b /**< NO RFC */ #define ENCTYPE_AES256_CTS_STRIBOG_512 0x001c /**< NO RFC */ #define ENCTYPE_UNKNOWN 0x01ff ... #define CKSUMTYPE_CMAC_CAMELLIA256 0x0012 /**< RFC 6803 */ #define CKSUMTYPE_MD5_HMAC_ARCFOUR -137 /* Microsoft netlogon */ #define CKSUMTYPE_HMAC_MD5_ARCFOUR -138 /**< RFC 4757 */ #define CKSUMTYPE_STRIBOG_256_AES256 -139 /**< NO RFC */ #define CKSUMTYPE_STRIBOG_512_AES256 -140 /**< NO RFC */
Ahora necesita agregar dos paquetes de las funciones de cifrado y hashing y las funciones de cifrado y hashing. ¿Por qué dos, si son equivalentes, preguntas? Respuesta: No lo sé, o es una muleta histórica o una forma de optimizar. Sin embargo, agreguemos nuevas estructuras a los archivos necesarios:
// src/lib/crypto/krb/cksumtypes.c /* -- -- -- -- -- -- -- " " -- -- -- */ const struct krb5_cksumtypes krb5int_cksumtypes_list[] = { ... { CKSUMTYPE_STRIBOG_256_AES256, "stribog-256-aes256", { 0 }, "STRIBOG256 AES256 key", &krb5int_enc_aes256, &krb5int_hash_stribog256, krb5int_etm_checksum, NULL, 64, 32, 0 }, { CKSUMTYPE_STRIBOG_512_AES256, "stribog-512-aes256", { 0 }, "STRIBOG512 AES256 key", &krb5int_enc_aes256, &krb5int_hash_stribog512, krb5int_etm_checksum, NULL, 64, 64, 0 }, }; // src/lib/crypto/krb/etypes.c : /* -- -- -- -- -- -- -- -- , -- , -- , -- -- -- -- */ const struct krb5_keytypes krb5int_enctypes_list[] = { ... { ENCTYPE_AES256_CTS_STRIBOG_256, "aes256-cts-stribog-256", { "aes256-stribog256" }, "AES-256 CTS mode with 256-bit stribog", &krb5int_enc_aes256, &krb5int_hash_stribog256, 16, krb5int_aes2_crypto_length, krb5int_etm_encrypt, krb5int_etm_decrypt, krb5int_aes2_string_to_key, k5_rand2key_direct, krb5int_aes2_prf, CKSUMTYPE_STRIBOG_256_AES256, 0 /*flags*/, 256 }, { ENCTYPE_AES256_CTS_STRIBOG_512, "aes256-cts-stribog-512", { "aes256-stribog512" }, "AES-256 CTS mode with 512-bit stribog", &krb5int_enc_aes256, &krb5int_hash_stribog512, 16, krb5int_aes2_crypto_length, krb5int_etm_encrypt, krb5int_etm_decrypt, krb5int_aes2_string_to_key, k5_rand2key_direct, krb5int_aes2_prf, CKSUMTYPE_STRIBOG_512_AES256, 0 /*flags*/, 256 }, };
Parecería que todo, pero no! Luego, hay problemas que serán visibles solo durante el proceso de depuración, algunos de ellos podrían evitarse indicando, por ejemplo, otros punteros a las funciones en las estructuras anteriores, pero iremos de una manera más complicada para mostrar qué más puede tener que solucionar en el camino. Aprendí sobre todos estos problemas solo usando el depurador:
// src/lib/crypto/openssl/hmac.c map_digest -- - . . : #include "crypto_int.h" #include "gost_helper.h" #include <openssl/hmac.h> #include <openssl/evp.h> static const EVP_MD * map_digest(const struct krb5_hash_provider *hash) { if (!strncmp(hash->hash_name, "SHA1",4)) return EVP_sha1(); ... else if (!strncmp(hash->hash_name, "GOSTR34.11-2012-256", 19)) return EVP_gostR3411_2012_256(); else if (!strncmp(hash->hash_name, "GOSTR34.11-2012-512", 19)) return EVP_gostR3411_2012_512(); else return NULL; } // src/lib/crypto/openssl/pbkdf2.c krb5int_pbkdf2_hmac, : krb5_error_code krb5int_pbkdf2_hmac(const struct krb5_hash_provider *hash, const krb5_data *out, unsigned long count, const krb5_data *pass, const krb5_data *salt) { const EVP_MD *md = NULL; /* Get the message digest handle corresponding to the hash. */ if (hash == &krb5int_hash_sha1) md = EVP_sha1(); ... else if (hash == &krb5int_hash_stribog256) md = EVP_gostR3411_2012_256(); else if (hash == &krb5int_hash_stribog512) md = EVP_gostR3411_2012_512(); ... return 0; } // src/lib/krb5/krb/init_ctx.c , : static krb5_enctype default_enctype_list[] = { ... ENCTYPE_AES256_CTS_STRIBOG_256, ENCTYPE_AES256_CTS_STRIBOG_512, 0 };
Después de todos estos cambios, puede verificar el funcionamiento del algoritmo. Recogeremos todo lo que hemos hecho.
autoconf ./configure --with-crypto-impl=openssl
Ahora comencemos a verificar. Para hacer esto, pongamos el uso forzado de los algoritmos que hemos implementado en los archivos de configuración de Kerberos. Haz lo siguiente:
Detener krb5kdc:
service krb5-kdc stop
Arreglaremos el archivo de configuración kdc.conf (tengo /usr/local/var/krb5kdc/kdc.conf para mí). Establezca el hash forzado utilizando el algoritmo recientemente introducido:
[realms] AKTIV-TEST.RU = { master_key_type = aes256-stribog512 supported_enctypes = aes256-stribog512:normal default_tgs_enctypes = aes256-stribog512 default_tkt_enctypes = aes256-stribog512 permitted_enctypes = aes256-stribog512 }
Cambios similares en el archivo de configuración de todo el protocolo krb5.conf (lo tengo en /etc/krb5.conf):
[libdefaults] supported_enctypes = aes256-stribog512:normal default_tgs_enctypes = aes256-stribog512 default_tkt_enctypes = aes256-stribog512 permitted_enctypes = aes256-stribog512 # 256
A continuación, ejecute krb5kdc desde Si master_key cambia, es posible que deba volver a crear la base de datos de principales utilizando krb5_newrealm.
Después de eso, creamos todos los principios necesarios y puede comenzar a trabajar. Intenta autenticarte con kinit.
Verificaremos que el hash ocurra de acuerdo con el algoritmo especificado.
usando klist -e.
Si el servicio no se inicia, puede iniciarse desde debajo de la raíz utilizando src / kdc / krb5kdc . Si todo comenzó, todo salió bien, ¡felicidades! De lo contrario, por desgracia, no ofrezco una panacea para todos los problemas, sino solo una "pequeña" instrucción que contiene los pasos básicos que deberá seguir para implementar el nuevo algoritmo en Kerberos. Y si nada funciona para usted, elija gdb y vea dónde está lo que sale mal. Solo puedo darte algunos consejos:
- puede construir un proyecto en modo de depuración si lo pasa a ./configure CFLAGS = "-g -O0";
- krb5kdc se puede iniciar en segundo plano con el indicador -n;
- Espero que no llegue a esto (aunque todavía se me ocurrió la implementación de algoritmos asimétricos); es posible que necesite símbolos de depuración de openssl, ya sea instalarlos desde el repositorio o instalar openssl desde la fuente con símbolos de depuración;
- Lo mismo ocurre con el motor Gost.
La idea de este conjunto de consejos debería ser suficiente para evitar ahorrar tiempo buscando lo "desconocido".
Implementación del algoritmo de encriptación
En esta sección, mostraré cómo puede agregar su propio algoritmo de cifrado de datos en Kerberos, e intentaremos crear un montón de Magma y Stribog, que se agregaron en la última sección. Aquí ya supongo que la pequeña biblioteca gost_helper ya se ha agregado en la última sección. Bueno, estira los dedos y procede:
Primero, describimos el algoritmo en nuestra biblioteca libk5crypto describiéndolos en el archivo de encabezado src / lib / crypto / krb / crypto_int.h .
... extern const struct krb5_enc_provider krb5int_enc_camellia256; extern const struct krb5_enc_provider krb5int_enc_gost89; ...
En el directorio src / lib / crypto / openssl / enc_provider agregue la fuente gost.c, que contiene la implementación de todos los algoritmos necesarios (tomé la fuente del algoritmo des como base). Es importante tener en cuenta que solo implementamos el modo de cifrado cbc, por lo que para la autoevaluación puede tomar cualquier otro modo de cifrado y agregarlo:
#include "crypto_int.h" #include "gost_helper.h" #include <openssl/evp.h> #define BLOCK_SIZE 8 static krb5_error_code krb5int_gost_encrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { int ret, olen = BLOCK_SIZE; unsigned char iblock[BLOCK_SIZE], oblock[BLOCK_SIZE]; struct iov_cursor cursor; EVP_CIPHER_CTX *ctx; // , krb5int_gost_encrypt, : krb5int_init_gost(); ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) return ENOMEM; ret = EVP_EncryptInit_ex(ctx, EVP_get_cipherbynid(NID_gost89_cbc), NULL, key->keyblock.contents, (ivec) ? (unsigned char*)ivec->data : NULL); if (!ret) { EVP_CIPHER_CTX_free(ctx); return KRB5_CRYPTO_INTERNAL; } EVP_CIPHER_CTX_set_padding(ctx,0); k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE); while (k5_iov_cursor_get(&cursor, iblock)) { ret = EVP_EncryptUpdate(ctx, oblock, &olen, iblock, BLOCK_SIZE); if (!ret) break; k5_iov_cursor_put(&cursor, oblock); } if (ivec != NULL) memcpy(ivec->data, oblock, BLOCK_SIZE); EVP_CIPHER_CTX_free(ctx); zap(iblock, sizeof(iblock)); zap(oblock, sizeof(oblock)); if (ret != 1) return KRB5_CRYPTO_INTERNAL; return 0; } static krb5_error_code krb5int_gost_decrypt(krb5_key key, const krb5_data *ivec, krb5_crypto_iov *data, size_t num_data) { int ret, olen = BLOCK_SIZE; unsigned char iblock[BLOCK_SIZE], oblock[BLOCK_SIZE]; struct iov_cursor cursor; EVP_CIPHER_CTX *ctx; krb5int_init_gost(); ctx = EVP_CIPHER_CTX_new(); if (ctx == NULL) return ENOMEM; ret = EVP_DecryptInit_ex(ctx, EVP_get_cipherbynid(NID_gost89_cbc), NULL, key->keyblock.contents, (ivec) ? (unsigned char*)ivec->data : NULL); if (!ret) { EVP_CIPHER_CTX_free(ctx); return KRB5_CRYPTO_INTERNAL; } EVP_CIPHER_CTX_set_padding(ctx,0); k5_iov_cursor_init(&cursor, data, num_data, BLOCK_SIZE, FALSE); while (k5_iov_cursor_get(&cursor, iblock)) { ret = EVP_DecryptUpdate(ctx, oblock, &olen, (unsigned char *)iblock, BLOCK_SIZE); if (!ret) break; k5_iov_cursor_put(&cursor, oblock); } if (ivec != NULL) memcpy(ivec->data, iblock, BLOCK_SIZE); EVP_CIPHER_CTX_free(ctx); zap(iblock, sizeof(iblock)); zap(oblock, sizeof(oblock)); if (ret != 1) return KRB5_CRYPTO_INTERNAL; return 0; } static krb5_error_code krb5int_gost_init_state (const krb5_keyblock *key, krb5_keyusage usage, krb5_data *state) { state->length = 8; state->data = (void *) malloc(8); if (state->data == NULL) return ENOMEM; memset(state->data, 0, state->length); return 0; } static void krb5int_gost_free_state(krb5_data *state) { free(state->data); *state = empty_data(); } /* -- , -- - -- -- -- -- cbc-mac checksum, , .. -- -- */ const struct krb5_enc_provider krb5int_enc_gost89 = { BLOCK_SIZE, 32, 32, krb5int_gost_encrypt, krb5int_gost_decrypt, NULL, krb5int_gost_init_state, krb5int_gost_free_state };
En la plantilla src / lib / crypto / openssl / enc_provider / Makefile.in, indicamos que ha aparecido una nueva fuente:
STLIBOBJS= \ des.o \ ... gost.o OBJS= \ $(OUTPRE)des.$(OBJEXT) \ ... $(OUTPRE)gost$(OBJEXT) SRCS= \ $(srcdir)/des.c \ ... $(srcdir)/gost.c
No se olvide de especificar dependencias en src / lib / crypto / openssl / enc_provider / deps :
gost.so gost.po $(OUTPRE)gost.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(srcdir)/../../krb/crypto_int.h \ $(srcdir)/../crypto_mod.h $(top_srcdir)/include/k5-buf.h \ $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \ $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \ $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \ $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \ $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \ $(top_srcdir)/include/socket-utils.h $(srcdir)/../gost_helper.h gost.c
Agregue los identificadores de paquete a src / include / krb5 / krb5.hin :
... #define ENCTYPE_AES256_CTS_STRIBOG_256 0x001b /**< NO RFC */ #define ENCTYPE_AES256_CTS_STRIBOG_512 0x001c /**< NO RFC */ #define ENCTYPE_GOST89_CBC_STRIBOG_256 0x001d /**< SOME GOST */ #define ENCTYPE_GOST89_CBC_STRIBOG_512 0x001e /**< SOME GOST */ #define ENCTYPE_UNKNOWN 0x01ff ... #define CKSUMTYPE_STRIBOG_256_AES256 -139 /**< NO RFC */ #define CKSUMTYPE_STRIBOG_512_AES256 -140 /**< NO RFC */ #define CKSUMTYPE_STRIBOG_256_GOST89 -141 /**< SOME GOST */ #define CKSUMTYPE_STRIBOG_512_GOST89 -142 /**< SOME GOST */
Describamos la estructura de los paquetes de funciones de cifrado y hash, como en la última sección:
// src/lib/crypto/krb/cksumtypes.c const struct krb5_cksumtypes krb5int_cksumtypes_list[] = { ... { CKSUMTYPE_STRIBOG_256_GOST89, "stribog-256-gost89", { 0 }, "STRIBOG256 GOST89 key", &krb5int_enc_gost89, &krb5int_hash_stribog256, krb5int_dk_checksum, NULL, 64, 32, 0 }, { CKSUMTYPE_STRIBOG_512_GOST89, "stribog-512-gost89", { 0 }, "STRIBOG512 GOST89 key", &krb5int_enc_gost89, &krb5int_hash_stribog512, krb5int_dk_checksum, NULL, 64, 64, 0 }, }; // src/lib/crypto/krb/etypes.c // , , , aes const struct krb5_keytypes krb5int_enctypes_list[] = { ... { ENCTYPE_GOST89_CBC_STRIBOG_256, "gost89-cbc-stribog-256", { "gost89-stribog256" }, "GOST 28147-89 CBC mode with 256-bit stribog", &krb5int_enc_gost89, &krb5int_hash_stribog256, 16, krb5int_dk_crypto_length, krb5int_dk_encrypt, krb5int_dk_decrypt, krb5int_dk_string_to_key, k5_rand2key_direct, krb5int_dk_prf, CKSUMTYPE_STRIBOG_256_GOST89, 0 /*flags*/, 256 }, { ENCTYPE_GOST89_CBC_STRIBOG_512, "gost89-cbc-stribog-512", { "gost89-stribog512" }, "GOST 28147-89 CBC mode with 512-bit stribog", &krb5int_enc_gost89, &krb5int_hash_stribog512, 16, krb5int_dk_crypto_length, krb5int_dk_encrypt, krb5int_dk_decrypt, krb5int_dk_string_to_key, k5_rand2key_direct, krb5int_dk_prf, CKSUMTYPE_STRIBOG_512_GOST89, 0 /*flags*/, 256 }, };
Agregue a la lista de modos de cifrado predeterminados en el archivo src / lib / krb5 / krb / init_ctx.c :
static krb5_enctype default_enctype_list[] = { ... ENCTYPE_AES256_CTS_STRIBOG_256, ENCTYPE_AES256_CTS_STRIBOG_512, ENCTYPE_GOST89_CBC_STRIBOG_256, ENCTYPE_GOST89_CBC_STRIBOG_512, 0 };
Después de estas manipulaciones, puede intentar probar el algoritmo recién introducido. Las pruebas se realizan de la misma manera que en la última sección . El nombre del paquete se puede tomar como un alias (por ejemplo, gost89-stribog512) o usando el nombre del algoritmo en sí (por ejemplo, gost89-cbc-stribog-512). Espero que todo funcione, de lo contrario no te olvides de lo que dije antes .
Agregar un algoritmo de firma digital
¡Hurra! Pasamos a la sección final de este artículo e intentamos agregar nuestro propio algoritmo de firma electrónica. No se asuste, agregarlo es más fácil que cualquier otra cosa, así que comencemos lo antes posible ... Aunque no, espere un pequeño comentario para comenzar.
El cifrado asimétrico es algo bastante pesado. – : , - . ...
, . , , . . . . , . , . , , — . .
Algo asi. , , . : . , , , , , .
openssl ( )
, -, openssl rtengine. GOST, , .
SDK rutoken sdk/openssl/rtengine/bin/ engine.
librtpkcs11ecp.so.
master OpenSC 8cf1e6f
, openssl.cnf:
engine .
, KDC
kerberos . , .
, , , KDC:
openssl genpkey -engine gost -algorithm gost2012_256 -pkeyopt paramset:B -out CA_key.pem
kdc, , :
[kdcdefaults] ... pkinit_identity = FILE:/var/lib/krb5kdc/KDC.pem,/var/lib/krb5kdc/KDC_key.pem pkinit_anchors = FILE:/var/lib/krb5kdc/CA_cert.pem
[libdefaults]
spake_preauth_groups = edwards25519
3. : ```bash sudo kadmin.local kadmin.local$: modprinc +requires_preauth user
. , , : :
, KDC:
openssl genpkey -engine gost -algorithm gost2012_256 -pkeyopt paramset:B -out client_key.pem
:
pkcs11-tool --module /path/to/module/librtpkcs11ecp.so --keypairgen --key-type GOSTR3410-2012-256:B -l --id 45
:
REALM=AKTIV-TEST.RU; export REALM
: , – :
sudo cp ./client_key.pem client.pem /etc/krb5
( /etc/krb5.conf):
[libdefaults] ... pkinit_anchors = FILE:/var/lib/krb5kdc/CA_cert.pem
, . ! .
, – - 2 ! src/plugins/preauth/pkinit/pkcs11.h src/plugins/preauth/pkinit/pkinit_crypto_openssl.c
pkcs11.h . – , , . ( ). sdk/pkcs11/include/rtpkcs11t.h . :
... #define CKK_TWOFISH (0x21) #define CKK_GOSTR3410 (0x30) #define CKK_GOSTR3411 (0x31) #define CKK_GOST28147 (0x32) #define CKK_VENDOR_DEFINED (1UL << 31) // A mask for new GOST algorithms. // For details visit https://tc26.ru/standarts/perevody/guidelines-the-pkcs-11-extensions-for-implementing-the-gost-r-34-10-2012-and-gost-r-34-11-2012-russian-standards-.html #define NSSCK_VENDOR_PKCS11_RU_TEAM (CKK_VENDOR_DEFINED | 0x54321000) #define CK_VENDOR_PKCS11_RU_TEAM_TK26 NSSCK_VENDOR_PKCS11_RU_TEAM #define CKK_GOSTR3410_512 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x003) ... #define CKM_AES_MAC_GENERAL (0x1084) #define CKM_AES_CBC_PAD (0x1085) #define CKM_GOSTR3410_KEY_PAIR_GEN (0x1200UL) #define CKM_GOSTR3410 (0x1201UL) #define CKM_GOSTR3410_WITH_GOSTR3411 (0x1202UL) #define CKM_GOSTR3410_KEY_WRAP (0x1203UL) #define CKM_GOSTR3410_DERIVE (0x1204UL) #define CKM_GOSTR3410_512_KEY_PAIR_GEN (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x005) #define CKM_GOSTR3410_512 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x006) #define CKM_GOSTR3410_12_DERIVE (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x007) #define CKM_GOSTR3410_WITH_GOSTR3411_12_256 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x008) #define CKM_GOSTR3410_WITH_GOSTR3411_12_512 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x009) #define CKM_GOSTR3411 (0x1210UL) #define CKM_GOSTR3411_HMAC (0x1211UL) #define CKM_GOSTR3411_12_256 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x012) #define CKM_GOSTR3411_12_512 (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x013) #define CKM_GOSTR3411_12_256_HMAC (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x014) #define CKM_GOSTR3411_12_512_HMAC (CK_VENDOR_PKCS11_RU_TEAM_TK26 | 0x015) #define CKM_GOST28147_KEY_GEN (0x1220UL) #define CKM_GOST28147_ECB (0x1221UL) #define CKM_GOST28147 (0x1222UL) #define CKM_GOST28147_MAC (0x1223UL) #define CKM_GOST28147_KEY_WRAP (0x1224UL)
, .
pkinit_crypto_openssl.c , , . get_key, .. - :
#include <dirent.h> #include <arpa/inet.h> #include <openssl/engine.h> static ENGINE *eng = NULL; krb5int_init_engines() { if (eng) return; OPENSSL_add_all_algorithms_conf(); ERR_load_crypto_strings(); if (!(eng = ENGINE_by_id("rtengine"))) { printf("Engine rtengine doesn't exist"); return; } ENGINE_init(eng); ENGINE_set_default(eng, ENGINE_METHOD_ALL); if (!(eng = ENGINE_by_id("gost"))) { printf("Engine gost doesn't exist"); return; } ENGINE_init(eng); ENGINE_set_default(eng, ENGINE_METHOD_ALL); } ... get_key(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, char *filename, const char *fsname, EVP_PKEY **retkey, const char *password) { ... krb5_error_code retval; krb5int_init_engines(); ... } ... int pkinit_openssl_init() { /* Initialize OpenSSL. */ ERR_load_crypto_strings(); OpenSSL_add_all_algorithms(); krb5int_init_engines(); return 0; }
. – RSA , sha1. , . , , . , .. RSA :
// krb5_error_code pkinit_find_private_key(pkinit_identity_crypto_context id_cryptoctx, CK_ATTRIBUTE_TYPE usage, CK_OBJECT_HANDLE *objp) { ... true_false = TRUE; attrs[nattrs].type = usage; attrs[nattrs].pValue = &true_false; attrs[nattrs].ulValueLen = sizeof true_false; nattrs++; #endif // keytype = CKK_RSA; // attrs[nattrs].type = CKA_KEY_TYPE; // attrs[nattrs].pValue = &keytype; // attrs[nattrs].ulValueLen = sizeof keytype; // nattrs++; ... } // : static int ckk_key_to_nid(CK_KEY_TYPE type) { switch(type){ case CKK_GOSTR3410: return NID_id_GostR3410_2012_256; case CKK_GOSTR3410_512: return NID_id_GostR3410_2012_512; default: return NID_rsa; } } // , , : static int pkinit_get_pkey_type(krb5_context context, pkinit_identity_crypto_context id_cryptoctx) { CK_OBJECT_HANDLE obj; CK_ATTRIBUTE attrs[1]; CK_KEY_TYPE key_type; int r; // : if (pkinit_open_session(context, id_cryptoctx)) { pkiDebug("can't open pkcs11 session\n"); return NID_rsa; } // : if (pkinit_find_private_key(id_cryptoctx, CKA_SIGN, &obj)) { return NID_rsa; } // : attrs[0].type = CKA_KEY_TYPE; attrs[0].pValue = &key_type; attrs[0].ulValueLen = sizeof (key_type); if ((r = id_cryptoctx->p11->C_GetAttributeValue(id_cryptoctx->session, obj, attrs, 1)) != CKR_OK) { pkiDebug("C_GetAttributeValue: %s\n Used RSA\n", pkinit_pkcs11_code_to_text(r)); return NID_rsa; } // : return ckk_key_to_nid(key_type); } // , , : static int pkey_to_digest_nid(const EVP_PKEY* const pkey) { switch (EVP_PKEY_id(pkey)) { case NID_id_GostR3410_2012_256: return NID_id_GostR3411_2012_256; case NID_id_GostR3410_2012_512: return NID_id_GostR3411_2012_512; case NID_id_GostR3410_2001: return NID_id_GostR3411_2012_256; default: return NID_sha1; } } // , : static int get_digest_nid(krb5_context context, const pkinit_identity_crypto_context id_cryptctx) { int nid; // ( ), NID , if (id_cryptctx->my_key) { nid = EVP_PKEY_id(id_cryptctx->my_key); } else { nid = pkinit_get_pkey_type(context, id_cryptctx); } switch (nid) { case NID_id_GostR3410_2012_256: return NID_id_GostR3411_2012_256; case NID_id_GostR3410_2012_512: return NID_id_GostR3411_2012_512; case NID_id_GostR3410_2001: return NID_id_GostR3411_2012_256; default: return NID_sha1; } } // , : static int get_alg_nid(krb5_context context, const pkinit_identity_crypto_context id_cryptctx) { int nid; if (id_cryptctx->my_key) { nid = EVP_PKEY_id(id_cryptctx->my_key); } else { nid = pkinit_get_pkey_type(context, id_cryptctx); } switch (nid) { case NID_id_GostR3410_2012_256: return NID_id_tc26_signwithdigest_gost3410_2012_256; case NID_id_GostR3410_2012_512: return NID_id_tc26_signwithdigest_gost3410_2012_512; case NID_id_GostR3410_2001: return NID_id_tc26_signwithdigest_gost3410_2012_256; default: return NID_sha1WithRSAEncryption; } } // : static CK_MECHANISM_TYPE get_mech_type(krb5_context context, const pkinit_identity_crypto_context id_cryptctx) { int nid; if (id_cryptctx->my_key) { nid = EVP_PKEY_id(id_cryptctx->my_key); } else { nid = pkinit_get_pkey_type(context, id_cryptctx); } switch (nid) { case NID_id_GostR3410_2012_256: return CKM_GOSTR3410_WITH_GOSTR3411_12_256; case NID_id_GostR3410_2012_512: return CKM_GOSTR3410_WITH_GOSTR3411_12_512; case NID_id_GostR3410_2001: return CKM_GOSTR3410_WITH_GOSTR3411_12_256; default: return CKM_RSA_PKCS; } }
cms_signeddata_create create_signature :
krb5_error_code cms_signeddata_create(krb5_context context, pkinit_plg_crypto_context plg_cryptoctx, pkinit_req_crypto_context req_cryptoctx, pkinit_identity_crypto_context id_cryptoctx, int cms_msg_type, int include_certchain, unsigned char *data, unsigned int data_len, unsigned char **signed_data, unsigned int *signed_data_len) { ... /* Set digest algs */ p7si->digest_alg->algorithm = OBJ_nid2obj( get_digest_nid(context, id_cryptoctx)); ... p7si->digest_enc_alg->algorithm = OBJ_nid2obj(get_alg_nid(context, id_cryptoctx)); ... EVP_DigestInit_ex(ctx, EVP_get_digestbynid(get_digest_nid(context, id_cryptoctx)), NULL); ... alen = (unsigned int )ASN1_item_i2d((ASN1_VALUE *) sk, &abuf, ASN1_ITEM_rptr(PKCS7_ATTR_SIGN)); ... // , ( ): if (id_cryptoctx->pkcs11_method == 1 && get_digest_nid(context, id_cryptoctx) == NID_sha1) { } static krb5_error_code create_signature(unsigned char **sig, unsigned int *sig_len, unsigned char *data, unsigned int data_len, EVP_PKEY *pkey) { ... EVP_SignInit(ctx, EVP_get_digestbynid(pkey_to_digest_nid(pkey))); ... } // : static krb5_error_code pkinit_sign_data_pkcs11(krb5_context context, pkinit_identity_crypto_context id_cryptoctx, unsigned char *data, unsigned int data_len, unsigned char **sig, unsigned int *sig_len) { ... mech.mechanism = get_mech_type(context, id_cryptoctx); mech.pParameter = NULL; mech.ulParameterLen = 0; ... }
, ( , , ):
sudo kinit user
user, , .
, .