Como configurar o Linux para entrar no domínio usando os algoritmos GOST

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:


  1. Criptografia simétrica;
  2. Hashing
  3. EDS;
  4. 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:


  1. Faça o download da implementação desse mecanismo no repositório .


  2. construa uma biblioteca com ele (mkdir build && cd build && cmake ... && make):


    mkdir build cd build cmake .. make 

  3. 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 

  4. 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:


     #     openssl_conf = openssl_def ... #    # OpenSSL default section [openssl_def] engines = engine_section # Engine section [engine_section] gost = gost_section # Engine gost section [gost_section] engine_id = gost dynamic_path = /path/to/engines/dir/with/gost.so default_algorithms = ALL init = 0 


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.


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

  2. 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:


     #    src/lib/crypto/openssl/Makefile.in      : STLIBOBJS=\ hmac.o \ ... stubs.o \ gost_helper.o OBJS=\ $(OUTPRE)hmac.$(OBJEXT) \ ... $(OUTPRE)stubs.$(OBJEXT) \ $(OUTPRE)gost_helper$(OBJEXT) SRCS=\ $(srcdir)/hmac.c \ ... $(srcdir)/stubs.c \ $(srcdir)/gost_helper.c #    src/lib/crypto/openssl/deps ,         : gost_helper.so gost_helper.po $(OUTPRE)gost_helper.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(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 \ gost_helper.c gost_helper.h 

  3. É 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!


  4. 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 }; 

  5. 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; ... 

  6. 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 */ 

  7. 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 }, }; 

  8. 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 #     openssl make sudo make install 

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:


  1. Pare o krb5kdc:


     service krb5-kdc stop 

  2. 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 } #       256  

  3. 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  

  4. 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.


  5. Depois disso, criamos todos os princípios necessários e você pode começar a trabalhar. Tente autenticar com o kinit.


  6. 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:


  1. você pode criar um projeto no modo de depuração se o transmitir para ./configure CFLAGS = "-g -O0";
  2. O krb5kdc pode ser iniciado em segundo plano usando o sinalizador -n;
  3. 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;
  4. 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:


  1. 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; ... 

  2. 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 }; 

  3. 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 

  4. 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 

  5. 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 */ 

  6. 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 }, }; 

  7. 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, , .


  1. SDK rutoken sdk/openssl/rtengine/bin/ engine.


  2. librtpkcs11ecp.so.


  3. master OpenSC 8cf1e6f


  4. , openssl.cnf:


     #     openssl_conf = openssl_def ... #    # OpenSSL default section [openssl_def] engines = engine_section # Engine section [engine_section] gost = gost_section rtengine = rtengine_section # Engine gost section [gost_section] engine_id = gost dynamic_path = /usr/lib/x86_64-linux-gnu/engines-1.1/gost.so default_algorithms = ALL # Engine rtengine section [rtengine_section] engine_id = rtengine dynamic_path = /path/to/engine/librtengine.so MODULE_PATH = /path/to/module/librtpkcs11ecp.so RAND_TOKEN = pkcs11:manufacturer=Aktiv%20Co.;model=Rutoken%20ECP default_algorithms = CIPHERS, DIGEST, PKEY, RAND 


engine .


, KDC


kerberos . , .


  1. , , , KDC:


     openssl genpkey -engine gost -algorithm gost2012_256 -pkeyopt paramset:B -out CA_key.pem #    openssl req -engine gost -key CA_key.pem -new -x509 -out CA_cert.pem #    openssl genpkey -engine gost -algorithm gost2012_256 -pkeyopt paramset:B -out KDC_key.pem #   KDC openssl req -engine gost -new -out KDC.req -key ./KDC_key.pem #      KDC # !!!     pkinit_extensions    REALM=AKTIV-TEST.RU; export REALM #   KDC CLIENT=127.0.0.1; export CLIENT #   ,     (    KDC).    ,   localhost openssl x509 -engine gost -req -in ./KDC.req -CAkey ./CA_key.pem -CA ./CA_cert.pem -out ./KDC.pem -extfile ./pkinit_extensions -extensions kdc_cert -CAcreateserial #    KDC. sudo cp ./KDC.pem ./KDC_key.pem ./CA_cert.pem /var/lib/krb5kdc #        kdc. 

  2. 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 

  1. . , , : :


    1. , KDC:


       openssl genpkey -engine gost -algorithm gost2012_256 -pkeyopt paramset:B -out client_key.pem #    openssl req -engine gost -new -out client.req -key ./client_key.pem #       

    2. :


       pkcs11-tool --module /path/to/module/librtpkcs11ecp.so --keypairgen --key-type GOSTR3410-2012-256:B -l --id 45 #             id=45 openssl req -engine rtengine -new -key="pkcs11:id=E" -keyform engine -out client.req #         . E -- ascii  45 


  2. :


     REALM=AKTIV-TEST.RU; export REALM #   CLIENT=user; export CLIENT #  ,     openssl x509 -engine gost -CAkey ./CA_key.pem -CA ./CA_cert.pem -req -in ./client.req -extensions client_cert -extfile ./pkinit_extensions -out client.pem openssl x509 -engine gost -in client.pem -out client.crt -outform DER #     PEM   CRT 

  3. : , – :


     sudo cp ./client_key.pem client.pem /etc/krb5 #   pkcs11-tool --module /usr/lib/librtpkcs11ecp.so -l -y cert -w ./client.crt --id 45 #   (     id,   ) 

  4. ( /etc/krb5.conf):


     [libdefaults] ... pkinit_anchors = FILE:/var/lib/krb5kdc/CA_cert.pem #     pkinit_identities = FILE:/etc/krb5/client.pem,/etc/krb5/client_key.pem #     #pkinit_identities = PKCS11:/usr/lib/librtpkcs11ecp.so 


, . ! .



, – - 2 ! src/plugins/preauth/pkinit/pkcs11.h src/plugins/preauth/pkinit/pkinit_crypto_openssl.c


  1. 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) 

    , .


  2. 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; } 

  3. . – 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; } } 

  4. 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, , .


, .

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


All Articles