如何使用GOST算法将Linux配置为进入域

引言



Kerberos 5协议现已被主动用于身份验证。 该协议的一个特征是它基于四个支柱执行身份验证:


  1. 对称加密;
  2. 散列
  3. EDS;
  4. 第三受托方。

从第五版开始,现在可以使用非对称加密(用于电子签名)。 不再关注Kerberos协议的操作,因为可以在此处找到算法的描述。


不幸的是,该协议使用的加密,哈希和数字签名算法的数量没有我想要的那么大,因此在本文中,我想展示如何添加 简单容易 麻省理工学院自己的算法,用于实现该协议 。 我们将添加我们的国内算法:GOST 28147-89(又名岩浆),GOST R 34.11-2012(又名Stribog)和GOST R 34.10-2012(我也想使用aka,但我不知道:()。在客户端,我们将在Rutoken EDS 2.0中使用GOST算法的硬件实现,并在openssl引擎中使用GOST算法的软件实现。在加密操作期间不要离开他的记忆 对于此选项,需要rtengine。


在开始实施算法之前,我们描述了将进行更改的主要位置。 在src / lib / crypto /目录内是负责对称加密和散列的所有算法的实现。 它具有以下两种加密算法的实现:内建和openssl。 为了节省时间和空间,我们当然会使用openssl添加算法的实现,而opensl已经存在(或者差不多已经存在,但稍后会介绍)。 要添加非对称算法,您将需要调整src / plugins / preauth / pkinit插件


如果尚未配置Kerberos,则可以在此处找到有关其初始配置和操作的说明。 此外,作者假设您正在使用AKTIV-TEST.RU域


添加哈希算法和对称加密


正如之前宣布的那样,我们不会从头开始编写加密和哈希算法,而是将在openssl中使用这些算法的现成实现。 Openssl直接不支持家用算法的实现,但是它在此方面具有移动性,并且允许您使用引擎GOST机制添加新算法来处理文件系统中的密钥并存储在令牌中-rtengine。


简单介绍openssl引擎引擎并连接引擎GOST


openssl中的Engine是一个小型动态库,可按需将openssl加载到运行时。 每个此类库必须包含某些符号(函数)以加载必要的算法。 在本文中,我们将使用引擎gost,该引擎包含所有必需的国内加密算法。


例如,安装是最简单的,并且进行如下操作:


  1. 存储库下载此引擎实现。


  2. 用它构建一个库(mkdir build && cd build && cmake ... && make):


    mkdir build cd build cmake .. make 

  3. 在bin目录中(将出现在项目的根目录中!!),将有一个动态库gost.so-这是我们的Engin。 它将需要移至openssl引擎存储的目录。 使用以下命令查找此目录的位置:


     openssl version -a | grep ENGINESDIR 

  4. 取决于后者-您需要告诉openssl给定引擎在哪里以及它叫什么。 您可以通过更改openssl.cnf配置文件来实现。 可以使用以下方法找到其位置:


     openssl version -a | grep OPENSSLDIR 

    在此文件的末尾,您将需要添加以下内容:


     #     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 


之后,此引擎应出现在openssl中。 您可以通过强制(例如)根据GOST R 34.10-2012在文件中生成私钥来检查其性能:


 openssl genpkey -engine gost -algorithm gost2012_512 -pkeyopt paramset:A -out client_key.pem 

-engine标志仅指示开始工作之前需要加载哪个引擎,以便GOST R 34.10-2012的密钥生成算法可见。


哈希算法的实现


让我们从最简单的步骤开始-从实现Stribog算法开始。 Kerberos在哈希算法和加密算法之间有很强的联系,也就是说,您不能只选择一种加密算法,而对于另一种哈希算法,则需要集成哈希算法和加密算法。 我之所以不知道其原因,但是因为那里存在这样的规则-让我们尝试创建Stribog算法和AES的组合。


  1. 因为 我们需要对程序不同位置的连接有信心,让我们首先创建一个小的gost_helper库,该库将包含openssl中引擎的初始化函数,以及一些为方便起见返回一些哈希算法的上下文的函数-这将在将来对我们有所帮助。 我们将此库命名为gost_helper,并在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. 添加辅助库后,您将需要在Makefile中声明其存在并写出其文件依赖性。 为此,请添加以下内容:


     #    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. 值得注意的是,将来在连接该库时,我们将需要在该库的标头上添加一些文件的依赖项。 这非常简单地完成-找到目标,您需要在deps文件中添加依赖项,并记录rel / path /到/ gost_helper.h的依赖项。 例如,在src / lib / crypto / openssl / hash_provider / deps中,您需要添加以下内容:


     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 

    为了节省文章的空间并扩展您的大脑,我将不再关注它:小心并小心!


  4. 现在添加哈希函数实现,它们的所有实现都在src / lib / crypto / openssl / hash_provider / hash_evp.c中 。 在那里,您需要添加以下内容:


     //     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. 现在我们需要在整个库中声明这些上下文。 为此,请在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. 我们将声明Stribog和AES捆绑包的标识符,并介绍称为CKSUMTYPE_STRIBOG_256_AES256CKSUMTYPE_STRIBOG_512_AES256ENCTYPE_AES256_CTS_STRIBOG_256ENCTYPE_AES256_CTS_STRIBOG的宏 。 必须在头文件模板src / include / krb5 / krb5.hin中声明它们 。 它看起来像这样:


     #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. 现在,您需要添加两个捆绑的哈希和加密功能以及加密和哈希功能。 为什么要问两个,如果相等的话? 答:我不知道,或者这是历史性的拐杖还是优化的方法。 不过,让我们向必要的文件中添加新的结构:


     //   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. 似乎全部,但没有! 然后存在一些仅在调试过程中可见的问题,例如可以通过指示上述结构中的函数的其他指针来避免某些问题,但是我们将采用一种更为复杂的方式来说明您在此过程中还需要解决的问题。 我仅使用调试器了解了所有这些问题:


     //   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 }; 


完成所有这些更改后,您可以检查算法的操作。 我们将收集我们所做的一切。


 autoconf ./configure --with-crypto-impl=openssl #     openssl make sudo make install 

现在开始检查。 为此,让我们将已实现的算法强制使用到Kerberos配置文件中。 请执行以下操作:


  1. 停止krb5kdc:


     service krb5-kdc stop 

  2. 我们将修复kdc.conf配置文件(我拥有/usr/local/var/krb5kdc/kdc.conf)。 使用新引入的算法设置强制哈希:


     [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. 整个krb5.conf协议的配置文件中有类似的更改(我在/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. 接下来,运行krb5kdc,因为 如果master_key更改,则可能必须使用krb5_newrealm重新创建主体数据库。


  5. 之后,我们将创建所有必要的主体,您可以开始工作。 尝试使用kinit进行身份验证。


  6. 我们将验证是否根据指定算法进行了哈希处理。
    使用klist -e。



如果服务未启动,则可以使用src / kdc / krb5kdc从根目录下启动该服务。 如果一切开始,一切顺利-恭喜! 否则-las,我没有为所有问题提供灵丹妙药,而只是提供了一条“小”指令,其中包含了在Kerberos中实现新算法所必须采取的基本步骤。 如果没有任何结果适合您-请选择gdb,看看出了什么问题。 我只能给您一些提示:


  1. 如果将项目传递给./configure CFLAGS =“ -g -O0”,则可以在调试模式下构建项目;
  2. 可以使用-n标志在后台启动krb5kdc;
  3. 我希望这不会成为现实(尽管我仍然想出非对称算法的实现)-您可能需要openssl调试符号-从存储库安装它们或从源代码安装带有调试符号的openssl;
  4. Gost引擎也是如此。

这套技巧的想法应该足以让您避免浪费时间寻找“未知”对象。


加密算法的实现


在这一部分中,我将展示如何在Kerberos中添加自己的数据加密算法,并且我们将尝试创建在最后部分中添加的一堆Magma和Stribog。 在这里,我已经假定小的gost_helper库已经在上一节中添加了。 好吧,伸展手指并继续:


  1. 首先,我们通过在头文件src / lib / crypto / krb / crypto_int.h中描述它们,在库libk5crypto中描述该算法。


     ... extern const struct krb5_enc_provider krb5int_enc_camellia256; extern const struct krb5_enc_provider krb5int_enc_gost89; ... 

  2. src / lib / crypto / openssl / enc_provider目录中,添加源代码gost.c,其中包含所有必需算法的实现(我以des算法的源代码为基础)。 重要的是要注意,我们仅实现cbc加密模式,因此对于自检,您可以采用任何其他加密模式并将其添加:


     #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. src / lib / crypto / openssl / enc_provider / Makefile.in模板中,我们指示出现了一个新来源:


     STLIBOBJS= \ des.o \ ... gost.o OBJS= \ $(OUTPRE)des.$(OBJEXT) \ ... $(OUTPRE)gost$(OBJEXT) SRCS= \ $(srcdir)/des.c \ ... $(srcdir)/gost.c 

  4. 不要忘记在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. 将包标识符添加到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. 让我们描述哈希和加密功能包的结构,如上一节所示:


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


经过这些操作后,您可以尝试测试新引入的算法。 测试的方式与上一节相同 。 捆绑软件的名称可以采用别名的形式(例如gost89-stribog512)或使用算法本身的名称(例如gost89-cbc-stribog-512)来获取。 我希望一切正常,否则不要忘记我之前说的话


添加数字签名算法


万岁! 我们继续进行本文的最后一节,并尝试添加我们自己的电子签名算法。 不要害怕,添加它比其他任何事情都容易,因此让我们尽快上手...尽管不行,但请稍等一下。


非对称加密是一件非常重量级的事情。 – : , - . ...


, . , , . . . . , . , . , , — . .

这样的东西。 , , . : . , , , , , .


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/zh-CN467707/


All Articles