1. Introdução

O protocolo Kerberos 5 agora é usado ativamente para autenticação. Uma característica deste protocolo é que ele executa autenticação com base em quatro pilares:
- Criptografia simétrica;
- Hashing
- EDS;
- Terceiro Confiável.
A partir da quinta versão, agora é possível usar criptografia assimétrica (para assinatura eletrônica). Não faz sentido insistir na operação do protocolo Kerberos, porque a descrição do algoritmo pode ser encontrada aqui .
Infelizmente, o número de algoritmos de criptografia, hash e assinatura digital que este protocolo usa não é tão grande quanto eu gostaria, portanto, neste artigo, quero mostrar como adicionar fácil e simples algoritmos próprios para a implementação deste protocolo pelo MIT . Adicionaremos nossos algoritmos domésticos: GOST 28147-89 (também conhecido como Magma), GOST R 34.11-2012 (também conhecido como Stribog) e GOST R 34.10-2012 (eu também gostaria de ter o conhecido para ele, mas não sei :(). Pronto uma solução para esses algoritmos pode ser encontrada no meu repositório.No lado do cliente, usaremos implementações de hardware dos algoritmos GOST no Rutoken EDS 2.0 e suas implementações de software no mecanismo GOST para openssl. Mas a opção mais segura para armazenar chaves é quando elas são geradas diretamente no Rutoken e nunca não deixe sua memória durante operações criptográficas Para esta opção, o rtengine é necessário.
Antes de começar a implementar os algoritmos, descrevemos os principais locais onde as alterações serão feitas. Dentro do diretório src / lib / crypto / estão as implementações de todos os algoritmos responsáveis pela criptografia e hash simétricas. Possui 2 implementações desses algoritmos criptográficos: builtin e openssl. Para economizar tempo e espaço, é claro que adicionaremos a implementação de algoritmos usando o openssl, no qual eles já existem (bem, ou quase lá, mas mais sobre isso posteriormente). Para adicionar algoritmos assimétricos, você precisará ajustar o plug-in src / plugins / preauth / pkinit
Se você ainda não configurou o Kerberos, as instruções para sua configuração e operação inicial podem ser encontradas aqui . Além disso, o autor assume que você está trabalhando com o domínio AKTIV-TEST.RU
Adicionando algoritmos de hash e criptografia simétrica
Como foi anunciado anteriormente, não escreveremos algoritmos de criptografia e hash do zero, mas usaremos uma implementação pronta desses algoritmos no openssl. O Openssl diretamente não suporta a implementação de algoritmos domésticos, mas possui mobilidade nesse assunto e permite adicionar novos algoritmos usando o mecanismo GOST do mecanismo para trabalhar com chaves no sistema de arquivos e armazenadas no token - rtengine.
Uma pequena introdução ao mecanismo do mecanismo openssl e à conexão GOST do mecanismo
O mecanismo no openssl é uma pequena biblioteca dinâmica que carrega o openssl no tempo de execução sob demanda. Cada biblioteca deve conter certos símbolos (funções) para carregar os algoritmos necessários. Neste artigo, usaremos o mecanismo gost, que contém todos os algoritmos criptográficos domésticos necessários.
A instalação é a mais simples, por exemplo, e ocorre da seguinte maneira:
Faça o download da implementação desse mecanismo no repositório .
construa uma biblioteca com ele (mkdir build && cd build && cmake ... && make):
mkdir build cd build cmake .. make
No diretório bin (QUE APARECERÁ NO CATÁLOGO DE RAIZ DO PROJETO !!!), haverá uma biblioteca dinâmica gost.so - este é o nosso Engin. Ele precisará ser movido para o diretório em que os mecanismos openssl estão armazenados. Descubra a localização deste diretório usando:
openssl version -a | grep ENGINESDIR
Depende do último - você precisa informar ao openssl onde está o mecanismo especificado e como ele se chama. Você pode fazer isso alterando o arquivo de configuração openssl.cnf. O local pode ser encontrado usando:
openssl version -a | grep OPENSSLDIR
No final deste arquivo, você precisará adicionar o seguinte conteúdo:
Depois disso, esse mecanismo deve aparecer no openssl. Você pode verificar seu desempenho forçando, por exemplo, a gerar uma chave privada em um arquivo de acordo com o GOST R 34.10-2012:
openssl genpkey -engine gost -algorithm gost2012_512 -pkeyopt paramset:A -out client_key.pem
O sinalizador -engine apenas indica qual mecanismo precisa ser carregado antes de iniciar o trabalho, para que o algoritmo de geração de chaves do GOST R 34.10-2012 fique visível.
Implementação do algoritmo Hash
Vamos começar com o mais simples - com a implementação do algoritmo Stribog. O Kerberos possui uma forte conexão entre os algoritmos de hash e criptografia, ou seja, você não pode simplesmente escolher um algoritmo para criptografia e, para o hash outro, precisará integrar o algoritmo de hash e criptografia. A razão para isso não me é conhecida, mas como essas regras existem lá - vamos tentar criar uma combinação do algoritmo Stribog e do AES.
Porque precisamos de confiança na conexão em diferentes locais do nosso programa, primeiro vamos criar uma pequena biblioteca gost_helper que conterá a função de inicialização do mecanismo no openssl, além de algumas funções que retornam contextos de alguns algoritmos de hash por conveniência - isso nos ajudará no futuro. Nomeamos essa biblioteca gost_helper e criamos um cabeçalho e um arquivo de origem apropriados no diretório 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); }
Depois de adicionar a biblioteca auxiliar, você precisará declarar sua existência no Makefile e gravar suas dependências de arquivo. Para fazer isso, adicione o seguinte:
É importante notar que, no futuro, ao conectar esta biblioteca, precisaremos adicionar as dependências de alguns arquivos no cabeçalho desta biblioteca. Isso é feito de maneira muito simples - o objetivo é encontrado, onde você precisa adicionar a dependência no arquivo deps e a dependência em rel / path / to / gost_helper.h é registrada . Por exemplo, em src / lib / crypto / openssl / hash_provider / deps, você precisará adicionar o seguinte:
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 economizar espaço no artigo e esticar seu cérebro, não prestarei mais atenção a ele: tenha cuidado e cuidado!
Agora adicione as implementações da função hash, todas elas estão em src / lib / crypto / openssl / hash_provider / hash_evp.c . Lá você precisará adicionar o seguinte:
// 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 };
Agora precisamos declarar esses contextos em toda a biblioteca. Para fazer isso, crie sua descrição no arquivo 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 o identificador do pacote Stribog e AES, introduzindo macros que chamaremos de CKSUMTYPE_STRIBOG_256_AES256 , CKSUMTYPE_STRIBOG_512_AES256 , ENCTYPE_AES256_CTS_STRIBOG_256 , ENCTYPE_AES256_AES256 . Eles devem ser declarados no modelo de arquivo de cabeçalho src / include / krb5 / krb5.hin . Será algo parecido com isto:
#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 */
Agora você precisa adicionar dois pacotes configuráveis das funções de hash e criptografia e as funções de criptografia e hash. Por que dois, se eles são equivalentes, você pergunta? Resposta: Eu não sei, ou é uma muleta histórica ou uma maneira de otimizar. No entanto, vamos adicionar novas estruturas aos arquivos necessários:
// 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 }, };
Parece que tudo, mas não! Depois, há problemas que serão visíveis apenas durante o processo de depuração, alguns deles poderiam ter sido evitados indicando, por exemplo, outros ponteiros de função nas estruturas acima, mas iremos de uma maneira mais complicada para mostrar o que mais pode ser corrigido ao longo do caminho. Eu aprendi sobre todos esses problemas usando apenas o 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 };
Após todas essas alterações, você pode verificar a operação do algoritmo. Coletaremos tudo o que fizemos.
autoconf ./configure --with-crypto-impl=openssl
Agora vamos começar a checar. Para fazer isso, vamos colocar o uso forçado dos algoritmos que implementamos nos arquivos de configuração do Kerberos. Faça o seguinte:
Pare o krb5kdc:
service krb5-kdc stop
Vamos corrigir o arquivo de configuração do kdc.conf (eu tenho /usr/local/var/krb5kdc/kdc.conf para mim). Defina o hash forçado usando o algoritmo recém-introduzido:
[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 }
Alterações semelhantes no arquivo de configuração de todo o protocolo krb5.conf (eu o tenho em /etc/krb5.conf):
[libdefaults] supported_enctypes = aes256-stribog512:normal default_tgs_enctypes = aes256-stribog512 default_tkt_enctypes = aes256-stribog512 permitted_enctypes = aes256-stribog512 # 256
Em seguida, execute o krb5kdc desde Se master_key for alterado, talvez seja necessário criar o banco de dados de entidades principais novamente usando krb5_newrealm.
Depois disso, criamos todos os princípios necessários e você pode começar a trabalhar. Tente autenticar com o kinit.
Verificaremos se o hash ocorre de acordo com o algoritmo especificado.
usando o klist -e.
Se o serviço não for iniciado, ele poderá ser iniciado na raiz usando src / kdc / krb5kdc . Se tudo começou, correu bem - parabéns! Caso contrário - infelizmente, não ofereço uma panacéia para todos os problemas, mas apenas ofereço uma instrução "pequena" que contém as etapas básicas que você precisará executar para implementar o novo algoritmo no Kerberos. E se nada der certo para você - pegue o gdb e veja onde está o problema. Só posso lhe dar algumas dicas:
- você pode criar um projeto no modo de depuração se o transmitir para ./configure CFLAGS = "-g -O0";
- O krb5kdc pode ser iniciado em segundo plano usando o sinalizador -n;
- Espero que não chegue a isso (embora eu ainda tenha implementado algoritmos assimétricos) - você pode precisar de símbolos de depuração openssl - instale-os no repositório ou instale o openssl da fonte com símbolos de depuração;
- o mesmo vale para o motor gost.
A idéia desse conjunto de dicas deve ser suficiente para você evitar desperdiçar tempo procurando o "desconhecido".
Implementação do algoritmo de criptografia
Nesta seção, mostrarei como você pode adicionar seu próprio algoritmo de criptografia de dados no Kerberos, e tentaremos criar um monte de Magma e Stribog, adicionados na última seção. Aqui já presumo que a pequena biblioteca gost_helper já foi adicionada na última seção. Bem, estique os dedos e prossiga:
Primeiro, descrevemos o algoritmo em nossa biblioteca libk5crypto, descrevendo-o no arquivo de cabeçalho 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; ...
No diretório src / lib / crypto / openssl / enc_provider, adicione o código-fonte gost.c, que contém a implementação de todos os algoritmos necessários (peguei o código-fonte do algoritmo des como base). É importante observar que apenas implementamos o modo de criptografia cbc, portanto, para o autoteste, você pode usar qualquer outro modo de criptografia e adicioná-lo:
#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 };
No modelo src / lib / crypto / openssl / enc_provider / Makefile.in, indicamos que uma nova fonte apareceu:
STLIBOBJS= \ des.o \ ... gost.o OBJS= \ $(OUTPRE)des.$(OBJEXT) \ ... $(OUTPRE)gost$(OBJEXT) SRCS= \ $(srcdir)/des.c \ ... $(srcdir)/gost.c
Não se esqueça de especificar dependências em 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
Inclua os identificadores de pacote configurável em 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 */
Vamos descrever a estrutura dos pacotes de funções de hash e criptografia, como na última seção:
// 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 }, };
Adicione à lista de modos de criptografia padrão no arquivo 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 };
Após essas manipulações, você pode tentar testar o algoritmo recém-introduzido. Os testes são realizados da mesma maneira que na última seção . O nome do pacote configurável pode ser obtido na forma de um alias (por exemplo, gost89-stribog512) ou usando o nome do próprio algoritmo (por exemplo, gost89-cbc-stribog-512). Espero que tudo funcione, caso contrário não se esqueça do que eu disse anteriormente .
Adicionando um algoritmo de assinatura digital
Viva! Prosseguimos para a seção final deste artigo e tentamos adicionar nosso próprio algoritmo de assinatura eletrônica. Não se assuste, é mais fácil adicionar do que qualquer outra coisa, então vamos começar o mais rápido possível ... Embora não, espere um pouco para começar.
A criptografia assimétrica é uma coisa bastante pesada. – : , - . ...
, . , , . . . . , . , . , , — . .
Algo assim. , , . : . , , , , , .
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, , .
, .