Пишем ГОСТ криптопровайдер

    рис.1
    Секреты создания CSP для Windows раскрыты в статье Ю.С.Зырянова.

    Российские криптоалгоритмы ГОСТ реализованы в OpenSSL Gost.

    Удивлен, что на просторах Интернета не удалось найти подтверждения, что кем-то был создан интерфейс криптопровайдера ГОСТ под Windows с использованием вышеприведенных инструментов.

    Можно подумать, что эта задача под силу только крупным коммерческим компаниям, имеющим большой опыт в сфере информационной безопасности, к примеру, таким как:Опровергнуть, хотя бы частично, это утверждение и будет задачей данной статьи.

    Для начала ограничимся малым, сделаем возможность проверки целостности ГОСТ сертификатов с помощью стандартных средств Windows, как на рисунке. Для простоты создание ЭЦП не будем рассматривать, не будем затрагивать тему генерации ключей. Во всех местах провайдера, кроме проверки ЭЦП и функции хеширования, будут использованы уже находящиеся в примере от Майкрософт заглушки в виде return TRUE и т.д. Автором планируется написание цикла статей на тему создания криптопровайдера и в дальнейшем все эти недостатки будут постепенно устранены.

    На первом этапе нам нужно сделать не так чтобы много, а именно, сначала выполнить пункты, описанные в статье Ю.С.Зырянова, далее получить список OID из RFC-4357, скомпилировать библиотеку OpenSSL в части реализации ГОСТ криптоалгоритмов, ну и наконец, подыскать реальные корневые сертификаты УЦ, можно и не только корневые, для проведения тестирования того, что у нас получилось.

    Итак, приступим.

    Качаем исходники


    Для начала потребуется скачать Microsoft Cryptographic Service Provider Development Kit, нажав на зеленую кнопку Download Now.

    Прошу простить за непрямую ссылку, но на сайте www.microsoft.com CSPDK найти не удалось. Похоже, что он убран с сайта, а по вышеприведенной ссылке находится устаревший вариант от 2001 года, но даже такой нам вполне подходит. После скачивания и распаковки найдем в исходниках файл csp.c и переименуем его в xyzcsp.c для последующих модификаций, csp.def и csp.rc — соответственно в xyzcsp.def и xyzcsp.rc

    Далее скачаем библиотеку OpenSSL. На момент написания статьи она имеет версию 1.0.0e

    Регистрация криптопровайдера xyzcsp.dll


    Зарегистрируем криптопровайдер в реестре. Для этого выполним (с правами администратора) нижеприведенный файл командой «regedit xyzcsp.reg»

    Файл xyzcsp.reg:
    Windows Registry Editor Version 5.00
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider\XYZ Provider]
    "Image Path"="xyzcsp.dll"
    "Type"=dword:0000007B
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider Types\Type 123]
    "Name"="XYZ Provider"
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptDllFindOIDInfo]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptDllFindOIDInfo\1.2.643.2.2.19!1]
    "Name"="GOST R 34.10-2001"
    "Algid"=dword:00002036
    "ExtraInfo"=hex:00,00,00,00
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptDllFindOIDInfo\1.2.643.2.2.3!2]
    "Name"="GOST R 34.11/34.10-2001"
    "Algid"=dword:00008037
    "ExtraInfo"=hex:36,20,00,00,00,00,00,00
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 1\CryptDllConvertPublicKeyInfo]
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 1\CryptDllConvertPublicKeyInfo\1.2.643.2.2.19]
    "Dll"="xyzcsp.dll"
    "FuncName"="xyz_ConvertPublicKeyInfo"
    
    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 1\CryptDllConvertPublicKeyInfo\1.2.643.2.2.98]
    "Dll"="xyzcsp.dll"
    "FuncName"="xyz_ConvertPublicKeyInfo"
    


    Библиотека OpenSSL


    Сборку OpenSSL из исходников оставим за кадром, заметим только, что это несложно, требуется наличие установленного perl и MS Visual Studio, для компиляции нужно следовать инструкциям в файле Install.W32, после этого получим в директории C:\xyzcsp\openssl-1.0.0e\tmp32dll множество объектных файлов, которые я для простоты собрал в один файл openssl.lib, который и использовал в своем проекте.

    Файл flist.txt:
    bf_buff.obj aes_core.obj aes_wrap.obj ameth_lib.obj asn1_gen.obj asn1_lib.obj asn1_par.obj asn_mime.obj asn_moid.obj asn_pack.obj a_bitstr.obj a_bool.obj a_bytes.obj a_d2i_fp.obj a_digest.obj a_dup.obj a_enum.obj a_gentm.obj a_i2d_fp.obj a_int.obj a_mbstr.obj a_object.obj a_octet.obj a_print.obj a_set.obj a_sign.obj a_strex.obj a_strnid.obj a_time.obj a_type.obj a_utctm.obj a_utf8.obj a_verify.obj bio_asn1.obj bio_b64.obj bio_cb.obj bio_enc.obj bio_err.obj bio_lib.obj bio_md.obj bio_ndef.obj bio_ok.obj bio_pk7.obj bio_ssl.obj bn_add.obj bn_asm.obj bn_blind.obj bn_const.obj bn_ctx.obj bn_depr.obj bn_div.obj bn_err.obj bn_exp.obj bn_exp2.obj bn_gcd.obj bn_gf2m.obj bn_kron.obj bn_lib.obj bn_mod.obj bn_mont.obj bn_mpi.obj bn_mul.obj bn_nist.obj bn_prime.obj bn_print.obj bn_rand.obj bn_recp.obj bn_shift.obj bn_sqr.obj bn_sqrt.obj bn_word.obj bss_dgram.obj bss_fd.obj bss_file.obj bss_log.obj bss_mem.obj bss_null.obj bss_sock.obj buffer.obj buf_err.obj by_dir.obj by_file.obj b_dump.obj b_print.obj b_sock.obj ca.obj camellia.obj cbc128.obj cbc_cksm.obj cbc_enc.obj cfb128.obj cfb64ede.obj cfb64enc.obj cfb_enc.obj ciphers.obj cmll_cbc.obj cmll_cfb.obj cmll_ctr.obj cmll_ecb.obj cmll_misc.obj cmll_ofb.obj cms.obj cms_asn1.obj cms_att.obj cms_cd.obj cms_dd.obj cms_enc.obj cms_env.obj cms_err.obj cms_ess.obj cms_io.obj cms_lib.obj cms_sd.obj cms_smime.obj comp_err.obj comp_lib.obj conf_api.obj conf_def.obj conf_err.obj conf_lib.obj conf_mall.obj conf_mod.obj conf_sap.obj cpt_err.obj crl.obj crl2p7.obj cryptlib.obj ctr128.obj cts128.obj cversion.obj c_all.obj c_allc.obj c_alld.obj c_cfb64.obj c_ecb.obj c_enc.obj c_ofb64.obj c_rle.obj c_skey.obj c_zlib.obj d1_both.obj d1_clnt.obj d1_enc.obj d1_lib.obj d1_meth.obj d1_pkt.obj d1_srvr.obj d2i_pr.obj d2i_pu.obj des_enc.obj des_old.obj des_old2.obj dgst.obj dh.obj dhparam.obj dh_ameth.obj dh_asn1.obj dh_check.obj dh_depr.obj dh_err.obj dh_gen.obj dh_key.obj dh_lib.obj dh_pmeth.obj dh_prn.obj digest.obj dsa.obj dsaparam.obj dsa_ameth.obj dsa_asn1.obj dsa_depr.obj dsa_err.obj dsa_gen.obj dsa_key.obj dsa_lib.obj dsa_ossl.obj dsa_pmeth.obj dsa_prn.obj dsa_sign.obj dsa_vrf.obj dso_beos.obj dso_dl.obj dso_dlfcn.obj dso_err.obj dso_lib.obj dso_null.obj dso_openssl.obj dso_vms.obj dso_win32.obj ebcdic.obj ec.obj ec2_mult.obj ec2_smpl.obj ecb3_enc.obj ecb_enc.obj ech_err.obj ech_key.obj ech_lib.obj ech_ossl.obj eck_prn.obj ecparam.obj ecp_mont.obj ecp_nist.obj ecp_smpl.obj ecs_asn1.obj ecs_err.obj ecs_lib.obj ecs_ossl.obj ecs_sign.obj ecs_vrf.obj ec_ameth.obj ec_asn1.obj ec_check.obj ec_curve.obj ec_cvt.obj ec_err.obj ec_key.obj ec_lib.obj ec_mult.obj ec_pmeth.obj ec_print.obj ede_cbcm_enc.obj enc.obj encode.obj enc_read.obj enc_writ.obj engine.obj eng_all.obj eng_cnf.obj eng_cryptodev.obj eng_ctrl.obj eng_dyn.obj eng_err.obj eng_fat.obj eng_init.obj eng_lib.obj eng_list.obj eng_openssl.obj eng_pkey.obj eng_table.obj err.obj errstr.obj err_all.obj err_prn.obj evp_acnf.obj evp_asn1.obj evp_enc.obj evp_err.obj evp_key.obj evp_lib.obj evp_pbe.obj evp_pkey.obj ex_data.obj e_aes.obj e_bf.obj e_camellia.obj e_cast.obj e_des.obj e_des3.obj e_gost_err.obj e_idea.obj e_null.obj e_old.obj e_rc2.obj e_rc4.obj e_rc5.obj e_seed.obj e_xcbc_d.obj fcrypt.obj fcrypt_b.obj f_enum.obj f_int.obj f_string.obj gendh.obj gendsa.obj genpkey.obj genrsa.obj gost2001.obj gost2001_keyx.obj gost89.obj gost94_keyx.obj gosthash.obj gost_ameth.obj gost_asn1.obj gost_crypt.obj gost_ctl.obj gost_eng.obj gost_keywrap.obj gost_md.obj gost_params.obj gost_pmeth.obj gost_sign.obj hmac.obj hm_ameth.obj hm_pmeth.obj i2d_pr.obj i2d_pu.obj i_cbc.obj i_cfb64.obj i_ecb.obj i_ofb64.obj i_skey.obj krb5_asn.obj kssl.obj lhash.obj lh_stats.obj md4_dgst.obj md4_one.obj md5_dgst.obj md5_one.obj mdc2dgst.obj mdc2_one.obj md_rand.obj mem.obj mem_clr.obj mem_dbg.obj m_dss.obj m_dss1.obj m_ecdsa.obj m_md4.obj m_md5.obj m_mdc2.obj m_null.obj m_ripemd.obj m_sha.obj m_sha1.obj m_sigver.obj m_wp.obj names.obj nseq.obj nsseq.obj n_pkey.obj obj_dat.obj obj_err.obj obj_lib.obj obj_xref.obj ocsp.obj ocsp_asn.obj ocsp_cl.obj ocsp_err.obj ocsp_ext.obj ocsp_ht.obj ocsp_lib.obj ocsp_prn.obj ocsp_srv.obj ocsp_vfy.obj ofb128.obj ofb64ede.obj ofb64enc.obj ofb_enc.obj o_dir.obj o_names.obj o_str.obj o_time.obj p12_add.obj p12_asn.obj p12_attr.obj p12_crpt.obj p12_crt.obj p12_decr.obj p12_init.obj p12_key.obj p12_kiss.obj p12_mutl.obj p12_npas.obj p12_p8d.obj p12_p8e.obj p12_utl.obj p5_crpt.obj p5_crpt2.obj p5_pbe.obj p5_pbev2.obj p8_pkey.obj passwd.obj pcbc_enc.obj pcy_cache.obj pcy_data.obj pcy_lib.obj pcy_map.obj pcy_node.obj pcy_tree.obj pem_all.obj pem_err.obj pem_info.obj pem_lib.obj pem_oth.obj pem_pk8.obj pem_pkey.obj pem_seal.obj pem_sign.obj pem_x509.obj pem_xaux.obj pk12err.obj pk7_asn1.obj pk7_attr.obj pk7_doit.obj pk7_lib.obj pk7_mime.obj pk7_smime.obj pkcs12.obj pkcs7.obj pkcs7err.obj pkcs8.obj pkey.obj pkeyparam.obj pkeyutl.obj pmeth_fn.obj pmeth_gn.obj pmeth_lib.obj pqueue.obj prime.obj pvkfmt.obj p_dec.obj p_enc.obj p_lib.obj p_open.obj p_seal.obj p_sign.obj p_verify.obj qud_cksm.obj rand.obj randfile.obj rand_egd.obj rand_err.obj rand_key.obj rand_lib.obj rand_nw.obj rand_os2.obj rand_unix.obj rand_win.obj rc2cfb64.obj rc2ofb64.obj rc2_cbc.obj rc2_ecb.obj rc2_skey.obj rc4_enc.obj rc4_skey.obj read2pwd.obj req.obj rmd_dgst.obj rmd_one.obj rpc_enc.obj rsa.obj rsautl.obj rsa_ameth.obj rsa_asn1.obj rsa_chk.obj rsa_depr.obj rsa_eay.obj rsa_err.obj rsa_gen.obj rsa_lib.obj rsa_none.obj rsa_null.obj rsa_oaep.obj rsa_pk1.obj rsa_pmeth.obj rsa_prn.obj rsa_pss.obj rsa_saos.obj rsa_sign.obj rsa_ssl.obj rsa_x931.obj s23_clnt.obj s23_lib.obj s23_meth.obj s23_pkt.obj s23_srvr.obj s2_clnt.obj s2_enc.obj s2_lib.obj s2_meth.obj s2_pkt.obj s2_srvr.obj s3_both.obj s3_clnt.obj s3_enc.obj s3_lib.obj s3_meth.obj s3_pkt.obj s3_srvr.obj seed.obj seed_cbc.obj seed_cfb.obj seed_ecb.obj seed_ofb.obj sess_id.obj set_key.obj sha1dgst.obj sha1_one.obj sha256.obj sha512.obj sha_dgst.obj sha_one.obj smime.obj speed.obj spkac.obj ssl_algs.obj ssl_asn1.obj ssl_cert.obj ssl_ciph.obj ssl_err.obj ssl_err2.obj ssl_lib.obj ssl_rsa.obj ssl_sess.obj ssl_stat.obj ssl_txt.obj stack.obj str2key.obj s_cb.obj s_client.obj s_server.obj s_socket.obj s_time.obj t1_clnt.obj t1_enc.obj t1_lib.obj t1_meth.obj t1_reneg.obj t1_srvr.obj tasn_dec.obj tasn_enc.obj tasn_fre.obj tasn_new.obj tasn_prn.obj tasn_typ.obj tasn_utl.obj tb_asnmth.obj tb_cipher.obj tb_dh.obj tb_digest.obj tb_dsa.obj tb_ecdh.obj tb_ecdsa.obj tb_pkmeth.obj tb_rand.obj tb_rsa.obj tb_store.obj ts.obj ts_asn1.obj ts_conf.obj ts_err.obj ts_lib.obj ts_req_print.obj ts_req_utils.obj ts_rsp_print.obj ts_rsp_sign.obj ts_rsp_utils.obj ts_rsp_verify.obj ts_verify_ctx.obj txt_db.obj t_bitst.obj t_crl.obj t_pkey.obj t_req.obj t_spki.obj t_x509.obj t_x509a.obj uid.obj ui_compat.obj ui_err.obj ui_lib.obj ui_openssl.obj ui_util.obj uplink.obj v3err.obj v3_addr.obj v3_akey.obj v3_akeya.obj v3_alt.obj v3_asid.obj v3_bcons.obj v3_bitst.obj v3_conf.obj v3_cpols.obj v3_crld.obj v3_enum.obj v3_extku.obj v3_genn.obj v3_ia5.obj v3_info.obj v3_int.obj v3_lib.obj v3_ncons.obj v3_ocsp.obj v3_pci.obj v3_pcia.obj v3_pcons.obj v3_pku.obj v3_pmaps.obj v3_prn.obj v3_purp.obj v3_skey.obj v3_sxnet.obj v3_utl.obj verify.obj version.obj wp_block.obj wp_dgst.obj x509.obj x509cset.obj x509name.obj x509rset.obj x509spki.obj x509type.obj x509_att.obj x509_cmp.obj x509_d2.obj x509_def.obj x509_err.obj x509_ext.obj x509_lu.obj x509_obj.obj x509_r2x.obj x509_req.obj x509_set.obj x509_trs.obj x509_txt.obj x509_v3.obj x509_vfy.obj x509_vpm.obj xcbc_enc.obj x_algor.obj x_all.obj x_attrib.obj x_bignum.obj x_crl.obj x_exten.obj x_info.obj x_long.obj x_name.obj x_nx509.obj x_pkey.obj x_pubkey.obj x_req.obj x_sig.obj x_spki.obj x_val.obj x_x509.obj x_x509a.obj
    


    Прошу извинить за столь длинный список, но мне неизвестен простой способ, как можно узнать, какие obj файлы из данного lib файла нужны для сборки, а какие нет. Создание map файла не помогает, так что пришлось использовать практически полный список obj файлов из директории tmp32dll.

    Создаем openssl.lib при помощи команды make_lib.bat

    make_lib.bat:
    call "C:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"
    lib /out:openssl.lib @flist.txt

    Приступим к написанию необходимых нам функций хеширования и проверки ЭЦП по ГОСТ.

    Назовем их my_hash_gost() и my_verify_gost() соответственно. Чтобы их получить, были использованы готовые куски из текста OpenSSL, но много времени ушло на отладку, в частности на то, чтобы понять, что когда мы делаем «переворот» данных, меняя порядок следования байт, то хеш переворачивать не нужно, а все остальное, включая публичный ключ и ЭЦП — нужно.

    Немного истории. Изначально функция my_verify_gost тестировалась с хешем, равным константе во всех 32-х байтах. И это меня спасло. Потому что достаточно быстро функция заработала, но когда был вставлен реальный хеш, состоящий из достаточно случайного набора байт, то сразу после этого ЭЦП перестала проверяться. Я долго не мог понять, что константный хеш является зеркальным, поэтому подходит для работы функции и без переворачивания. Мне повезло, если бы тестирование началось с реального хеша, то библиотеку OpenSSL было бы достаточно тяжело настроить для проверки ЭЦП сертификатов, потому что такое «хитрое» поведение хеша достаточно неочевидно.

    Вставим эти строки в файл xyzcsp.c сразу после всех имеющихся там #include
    #include "gosthash.h"
    #include "gost_lcl.h"
    
    static void perevorot_buf(unsigned char *obj, int k)
    {
         char buf[64];
         int i;
         for( i = 0; i < k; i++ ) buf[i] = obj[k-1-i];
         memcpy(obj, buf, k);
    }
    
    static int pkey_gost01_cp_verify(EC_KEY* pub_key, const unsigned char *sig,
    	size_t siglen, unsigned char *tbs, size_t tbs_len)
    {
    	int ok = 0;
    	DSA_SIG *s=unpack_cp_signature(sig,siglen);
    	if (!s) return 0;
    	if (pub_key) ok = gost2001_do_verify(tbs,tbs_len,s,pub_key);
    	DSA_SIG_free(s);
    	return ok;
    }
    
    int my_verify_gost(char *in_hash, const BYTE *in_sign, char *in_pub1, char *in_pub2, int nid)
    {
    	int res, errcode;
    	EC_KEY *eckey = NULL;
    	unsigned char sig[64], tbs[32];
    	int siglen=64, tbs_len=32;
    	BIGNUM *X=NULL,*Y=NULL;
    	char perevorot_pub[32];
    	EC_POINT *pub_key;
    //Волшебные перевороты
    	memcpy(tbs, in_pub1, 32); perevorot_buf(tbs, 32);
    	X= getbnfrombuf((const unsigned char*)tbs,32);
    	memcpy(tbs, in_pub2, 32); perevorot_buf(tbs, 32);
    	Y= getbnfrombuf((const unsigned char*)tbs,32);
    	memcpy(tbs, in_hash, 32); //хеш переворачивать не надо! ранее был perevorot_buf(tbs, 32);
    	memcpy(sig, in_sign, 64); perevorot_buf(sig, 64);
    //Проверка ЭЦП
    	if (!(eckey = EC_KEY_new())) { errcode = 1; goto err_exit; }
    	if (!fill_GOST2001_params(eckey, nid)) { errcode = 2; goto err_exit; }
    	if (!(pub_key = EC_POINT_new(EC_KEY_get0_group(eckey)))) { errcode = 3; goto err_exit; }
    	if (!EC_POINT_set_affine_coordinates_GFp(EC_KEY_get0_group(eckey)
    			,pub_key,X,Y,NULL)) { errcode = 4; goto err_exit; }
    	if (!EC_KEY_set_public_key(eckey,pub_key)) { errcode = 5; goto err_exit; }
    	if (!pkey_gost01_cp_verify(eckey, sig, siglen, tbs, tbs_len)) { errcode = 6; goto err_exit; }
    	else errcode = 0; //success
    err_exit:
    	if (pub_key) EC_POINT_free(pub_key);
    	if (X) BN_free(X);
    	if (Y) BN_free(Y);
    	if (eckey) EC_KEY_free(eckey);
    	return errcode;
    }
    
    void my_hash_gost(const BYTE *buf, int buflen, char *hash_res)
    {
    	gost_subst_block *b=  &GostR3411_94_CryptoProParamSet;
    	gost_hash_ctx ctx;
    	init_gost_hash_ctx(&ctx,b);
    	start_hash(&ctx);
    	hash_block(&ctx,buf,buflen);
    	finish_hash(&ctx,(byte *)hash_res);
    }
    
    //Глобальные переменные для хеша и публичного ключа
    char hash_gost[32];
    char hash_sha1[20];
    char public_key[64];
    


    Провайдер XYZ Provider


    Теперь займемся, наконец, провайдером. Выберем ему имя: «XYZ Provider». Соответственно, основной файл будет называться xyzcsp.c, также нужны файлы xyzcsp.def и xyzcsp.rc

    В исходном образце, который можно найти в CSPDK, в файле csp.c, нас интересуют только функции CPAcquireContext, CPHashData, CPGetHashParam, CPVerifySignature. Легко видеть, что это функции для создания хендла провайдера, функции хеширования и проверки ЭЦП. Заменим эти функции на приведенные ниже.

    Кроме них, напишем одну замечательную функцию, xyz_ConvertPublicKeyInfo, которая будет заниматься конвертацией публичного ключа ЭЦП. Не забываем добавить xyz_ConvertPublicKeyInfo в файл xyzcsp.def, чтобы линкер экспортировал это имя. Конвертация будет заключаться в игнорировании первых двух байт в записи ASN1 нотации публичного ключа, тем самым получая ключ в чистом виде, две половинки по 32 байта.

    Удалим функцию DllMain, а также старые CPAcquireContext, CPHashData, CPGetHashParam, CPVerifySignature из xyzcsp.c, уберем устаревшую команду DESCRIPTION и имена DllRegisterServer и DllUnregisterServer из xyzcsp.def

    Добавим в конец файла xyzcsp.c:
    BOOL WINAPI
    CPAcquireContext(
        OUT HCRYPTPROV *phProv,
        IN  LPCSTR szContainer,
        IN  DWORD dwFlags,
        IN  PVTableProvStruc pVTable)
    {
        *phProv = 123;
        return TRUE;
    }
    
    BOOL WINAPI
    CPHashData(
        IN  HCRYPTPROV hProv,
        IN  HCRYPTHASH hHash,
        IN  CONST BYTE *pbData,
        IN  DWORD cbDataLen,
        IN  DWORD dwFlags)
    {
        my_hash_gost(pbData, cbDataLen, hash_gost);
        SHA1(pbData, cbDataLen, hash_sha1);
        return TRUE;
    }
    
    BOOL WINAPI
    CPGetHashParam(
        IN  HCRYPTPROV hProv,
        IN  HCRYPTHASH hHash,
        IN  DWORD dwParam,
        OUT LPBYTE pbData,
        IN OUT LPDWORD pcbDataLen,
        IN  DWORD dwFlags)
    {
    	switch(dwParam)
    	{
    		case HP_HASHVAL:
    			if(*pcbDataLen == 20) // у нас просят отпечаток sha1
    			{
    				memcpy(pbData, hash_sha1, 20);
    				break;
    			}
    		default:
    			*pcbDataLen = 0;
    			SetLastError(E_INVALIDARG);
    			return FALSE;
    	}
        return TRUE;
    }
    
    BOOL WINAPI
    CPVerifySignature(
        IN  HCRYPTPROV hProv,
        IN  HCRYPTHASH hHash,
        IN  CONST BYTE *pbSignature,
        IN  DWORD cbSigLen,
        IN  HCRYPTKEY hPubKey,
        IN  LPCWSTR szDescription,
        IN  DWORD dwFlags)
    {
    #define NTE_IC_ERROR_PREDEF          0x89900000L
        INT err;
        err = my_verify_gost(hash_gost, pbSignature, public_key, public_key+32, 
                NID_id_GostR3410_2001_CryptoPro_A_ParamSet);
        if ( err ) 
        {
            SetLastError( NTE_IC_ERROR_PREDEF | err );
            return FALSE;
        }
        return TRUE;
    }
    
    BOOL WINAPI xyz_ConvertPublicKeyInfo(
      DWORD dwCertEncodingType,
      VOID *EncodedKeyInfo,
      DWORD dwAlg,
      DWORD dwFlags,
      BYTE** ppStructInfo,
      DWORD* StructLen
    )
    {
        memcpy(public_key, ((CERT_PUBLIC_KEY_INFO*)EncodedKeyInfo)->PublicKey.pbData + 2, 64);
        return TRUE;
    }
    


    Подробно комментировать исходники криптопровайдера, думаю, излишне. Все понятно по тексту.

    Пишем тестовую программу


    Зачем нужна отдельная тестовая программа? Она, кроме запуска тестов, будет патчить систему, чтобы поменять функции SystemFunction035 в ADVAPI32.dll и I_CryptGetDefaultCryptProv в CRYPT32.dll на их «правильный» вариант. Данная программа будет успешно работать как в Windows XP так и в Windows 7.

    В конце статьи показано, как можно пропатчить системные файлы в Windows XP SP3, в этом случае специальная тестовая программа будет не нужна.

    Файл testcsp.cpp:
    #include "stdafx.h"
    #include <windows.h>
    #include <wincrypt.h>
    
    typedef HCRYPTPROV (WINAPI *pI_CryptGetDefaultCryptProv)(ALG_ID algid);
    HCRYPTPROV        hProv = NULL;
    
    typedef int (__stdcall *def_CryptExtOpenCER)(
        HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);
    def_CryptExtOpenCER CryptExtOpenCER;
    
    typedef int (__stdcall *def_MyProc)(void);
    def_MyProc MyProc;
    
    #define PATCH_NUM 2
    char *patch_list[2*PATCH_NUM]={
        "ADVAPI32.dll","SystemFunction035",
        "CRYPT32.dll","I_CryptGetDefaultCryptProv"
    };
    
    void WriteMem(int pos, char *patch, int len)
    {
        DWORD my_id = GetCurrentProcessId();
        HANDLE p_hand = OpenProcess(PROCESS_VM_WRITE | PROCESS_VM_OPERATION, NULL, my_id);
        if (WriteProcessMemory(p_hand, (LPDWORD)pos, patch, len, NULL)==0) {
            printf("Error write to memory\nHint: run from Administrator rigths");
        }
        CloseHandle(p_hand);
    }
    
    HCRYPTPROV PASCAL old_I_CryptGetDefaultCryptProv(int AlgID) //call MS Provider
    {
        __asm mov eax,0; //заглушка, достаточно 10 байт
        __asm mov eax,0;
        return NULL;
    }
    
    HCRYPTPROV PASCAL my_I_CryptGetDefaultCryptProv(int AlgID)
    {
        if (AlgID!=0 && AlgID!=0x2036) 
            return old_I_CryptGetDefaultCryptProv(AlgID); //old MS
        return hProv;
    }
    
    int StartPatch(void)
    {
        BYTE *p;
        HMODULE h_dll;
        char buf[10];
        DWORD new_addr;
        for(int i=0;i<PATCH_NUM;i++)
        {
            h_dll = LoadLibrary(patch_list[i*2]);
            if (h_dll==NULL) 
            {
                printf("Error! Can not LoadLibrary(%s)\n", patch_list[i*2]);
                return 1;
            }
            MyProc = (def_MyProc)GetProcAddress(h_dll, patch_list[i*2+1]);
            if (MyProc==NULL)
            {
                printf("Error! Can not GetProcAddress(%s)\n", patch_list[i*2+1]);
                return 1;
            }
    
            p = (BYTE*)MyProc;
            if (i==1)
            {
                memcpy(buf, p, 5);
    
                buf[5]=0xe9;
                new_addr = (DWORD)p;
                new_addr -= (DWORD)old_I_CryptGetDefaultCryptProv;
                new_addr -= 5;
                memcpy(buf+6, &new_addr, 4);
                WriteMem((DWORD)old_I_CryptGetDefaultCryptProv, buf, 10);
    
                buf[0]=0xe9;
                new_addr = (DWORD)my_I_CryptGetDefaultCryptProv;
                new_addr -= (DWORD)MyProc;
                new_addr -= 5;
                memcpy(buf+1, &new_addr, 4);
                WriteMem((DWORD)MyProc, buf, 5);
            }
            else
            {
                WriteMem((int)p, "\xb8\x01\x00\x00\x00\xC2\x04\x00", 8); //mov ax,1 - ret 4
            }
        }
        return 0;
    }
    
    int RunCert(char *certName)
    {
        HMODULE h_dll;
        h_dll = LoadLibrary("C:\\windows\\system32\\CRYPTEXT.dll");
        if (h_dll==NULL) return 1;
        CryptExtOpenCER = (def_CryptExtOpenCER)GetProcAddress(h_dll, "CryptExtOpenCER");
        if (CryptExtOpenCER==NULL) return 2;
           CryptExtOpenCER(NULL, NULL, certName, SW_SHOW);
        FreeLibrary(h_dll);
        return 0;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        if (StartPatch())
        {
            printf("Error Patch\n");
            return 1;
        }
    
        if (RCRYPT_FAILED(CryptAcquireContext(&hProv, "test", NULL, 123, 0)))
        {
            printf("CryptAcquireConext returned error %x\n", GetLastError());
            printf("FAILED\n");
            return 1;
        }
        printf("SUCCEED\n");
    
        RunCert("gnivc_2006.cer");
        RunCert("rootsber.cer");
        
        return 0;
    }
    


    Компиляция файлов


    Файл компиляции провайдера comp_xyzcsp.bat:
    
    call "C:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"
    cl /I"..\include" /nologo /MT /O2 /c xyzcsp.c
    rc /I"..\include" xyzcsp.rc
    link /SUBSYSTEM:WINDOWS",5.0" /NODEFAULTLIB /DLL /DEF:xyzcsp.def /MACHINE:x86 /OUT:xyzcsp.dll xyzcsp.obj openssl.lib advapi32.lib kernel32.lib msvcrt.lib gdi32.lib user32.lib xyzcsp.res
    copy xyzcsp.dll ..\testcsp\
    rem copy xyzcsp.dll c:\windows\system32
    

    Файл компиляции теста comp_test.bat:
    
    call "C:\Program Files\Microsoft Visual Studio 10.0\VC\vcvarsall.bat"
    cl /I"..\include" testcsp.cpp advapi32.lib
    


    Сертификаты для тестирования


    Файл gnivc_2006.cer, в котором лежит корневой сертификат ФНС:
    -----BEGIN CERTIFICATE-----
    MIIDGjCCAsegAwIBAgIQPx2a1ZtKRIBLiHKukksltTAKBgYqhQMCAgMFADCBwDEe
    MBwGCSqGSIb3DQEJARYPdWNpbmZvQGduaXZjLnJ1MQswCQYDVQQGEwJSVTEVMBMG
    A1UEBwwM0JzQvtGB0LrQstCwMTAwLgYDVQQKDCfQpNCT0KPQnyDQk9Cd0JjQktCm
    INCk0J3QoSDQoNC+0YHRgdC40LgxMDAuBgNVBAsMJ9Cj0LTQvtGB0YLQvtCy0LXR
    gNGP0Y7RidC40Lkg0YbQtdC90YLRgDEWMBQGA1UEAxMNR05JVkMgRk5TIFJVUzAe
    Fw0wNjA5MjcwOTI5NTdaFw0xMjA5MjcwOTM4MjdaMIHAMR4wHAYJKoZIhvcNAQkB
    Fg91Y2luZm9AZ25pdmMucnUxCzAJBgNVBAYTAlJVMRUwEwYDVQQHDAzQnNC+0YHQ
    utCy0LAxMDAuBgNVBAoMJ9Ck0JPQo9CfINCT0J3QmNCS0KYg0KTQndChINCg0L7R
    gdGB0LjQuDEwMC4GA1UECwwn0KPQtNC+0YHRgtC+0LLQtdGA0Y/RjtGJ0LjQuSDR
    htC10L3RgtGAMRYwFAYDVQQDEw1HTklWQyBGTlMgUlVTMGMwHAYGKoUDAgITMBIG
    ByqFAwICIwEGByqFAwICHgEDQwAEQCzY8VGw9ged02ijaj2KWOMXJVvzY1FEcg7G
    xedUtKx0wqyTVti0kmodEmm2cVfAbDkp0xAdBS9/mdDfeIrKXLajgZYwgZMwCwYD
    VR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFBMQt5JPv+eiD7j1
    nYkVJssQ6/RfMBAGCSsGAQQBgjcVAQQDAgEAMEIGCCsGAQUFBwEBBDYwNDAyBggr
    BgEFBQcwAoYmaHR0cDovL3d3dy5nbml2Yy5ydS91Yy9HTklWQ0ZOU1JVUy5jcnQw
    CgYGKoUDAgIDBQADQQDgEyWPI+fdXXiTYMLHdV76v8kVFIxCHCYtastcvZiM3cG1
    wTFhio8fDx6sLgHHriOwQFg0zRUYHIs9nZEptLvM
    -----END CERTIFICATE-----
    

    Файл rootsber.cer, в котором лежит корневой сертификат Сбербанка:
    -----BEGIN CERTIFICATE-----
    MIIDKjCCAtmgAwIBAgIGMDBDQT0HMAgGBiqFAwICAzCBwTELMAkGA1UEBhMCUlUx
    LTArBgNVBAoMJNCh0LHQtdGA0LHQsNC90Log0KDQvtGB0YHQuNC4INCe0JDQnjE4
    MDYGA1UECwwv0JTQtdC/0LDRgNGC0LDQvNC10L3RgiDQsdC10LfQvtC/0LDRgdC9
    0L7RgdGC0LgxJjAkBgNVBAMMHdCh0LHQtdGA0LHQsNC90Log0KDQvtGB0YHQuNC4
    MSEwHwYJKoZIhvcNAQkBFhJjYXNicmZAc2JlcmJhbmsucnUwHhcNMDkwODA1MDAw
    MDAwWhcNMTcwODA1MDAwMDAwWjCBwTELMAkGA1UEBhMCUlUxLTArBgNVBAoMJNCh
    0LHQtdGA0LHQsNC90Log0KDQvtGB0YHQuNC4INCe0JDQnjE4MDYGA1UECwwv0JTQ
    tdC/0LDRgNGC0LDQvNC10L3RgiDQsdC10LfQvtC/0LDRgdC90L7RgdGC0LgxJjAk
    BgNVBAMMHdCh0LHQtdGA0LHQsNC90Log0KDQvtGB0YHQuNC4MSEwHwYJKoZIhvcN
    AQkBFhJjYXNicmZAc2JlcmJhbmsucnUwYzAcBgYqhQMCAhMwEgYHKoUDAgIjAgYH
    KoUDAgIeAQNDAARAaYzyi29YQ9NC5cb/kq//J1kKhOgcvGWqsQu50mldjADTGfrl
    JUVXwu4fMUTHoF9TjY0O1kgrLYWT/kI4jABAWKOBsjCBrzAdBgNVHQ4EFgQUZmHo
    Zo41vw/U74ZlC8k/bcQODuowDAYDVR0TBAUwAwEB/zAzBgNVHR8ELDAqMCigJqAk
    hiJodHRwOi8vd3d3LnNicmYucnUvY2EvMDAwMHg1MDkuY3JsMAsGA1UdDwQEAwIC
    hDA+BgcqhQMDewMBBDMMMTAwQ0ExODUzetCa0L7RgNC90LXQstC+0Lkg0LrQu9GO
    0Ycg0KPQpiDQodCRINCg0KQwCAYGKoUDAgIDA0EAD9Umnh/EZgjgQvpypdVwe0wa
    GnTi+dHhVwoNAX1tquxQNbAptbBs2OKzkRU7/mrBfDD4EdVV5xC1f2DTcH8NAg==
    -----END CERTIFICATE-----
    


    Результаты работы


    Запустим тестовую программу, получим результат:


    Чтобы убрать последнее препятствие, включим сертификат в список доверенных, как рекомендуется. Для этого нажмем кнопку Установить сертификат и несколько раз кнопку Далее. По окончании тестирования нужно удалить тестовый сертификат из хранилища доверенных корневых, чтобы не подвергать возможной опасности свою систему.

    После повторного запуска теста наблюдаем уже иную картину:


    Что и требовалось получить.

    Корневой сертификат Сбербанка


    С сертификатом Сбербанка подобное проделать не получается:


    Это связано с тем, что в Сбербанке используется константа B из RFC 4357, а именно GostR3410_2001_CryptoPro_B_ParamSet.

    Меняем тогда в нашем файле xyzcsp.c в функции CPVerifySignature константу A на B, то есть при вызове my_verify_gost будем использовать следующий параметр: NID_id_GostR3410_2001_CryptoPro_B_ParamSet.

    После компиляции провайдера и запуска теста наблюдаем обратную картину, сертификат ФНС не проверяется, а Сбербанковский — работает отлично. Казалось бы, существенный недостаток, но для простейшего криптопровайдера это простительно.

    Конечно, возможен такой вариант: проверять сразу две ЭЦП и, если хотя бы одна из них сойдется, объявлять что ЭЦП верна, но это в корне неправильно. Нужно смотреть на OID публичного ключа и уже по нему искать требуемые параметры эллиптической кривой.

    В следующей статье, которая сейчас готовится к публикации, мы подробно рассмотрим, как избежать подобной ситуации, а также напишем функцию по созданию ключей ЭЦП с различными параметрами.

    Создание универсального патча


    В заключение несколько слов о создании универсального патча.

    Для Windows XP SP3 достаточно подменить следующие файлы:
    c:\windows\system32\advapi32.dll
    c:\windows\system32\dllcache\advapi32.dll
    c:\windows\system32\crypt32.dll
    c:\windows\system32\dllcache\crypt32.dll

    Патчить нужно, загрузившись с другого диска, чтобы системный был свободным (можно с загрузочного диска Windows в режиме восстановления), далее заменяем эти два файла как в директории system32 так и в system32\dllcache, где хранятся их копии. Патчить на работающей системе не получится, потому что файлы «залочены» и поменять их не удастся.

    После этого не забудьте скопировать файл с криптопровайдером xyzcsp.dll в директорию c:\windows\system32, чтобы система его находила.

    Теперь можно кликать мышью на сертификат и он будет проверяться непосредственно в операционной системе, без запуска специальных программ.

    Заменить файлы нужно на их пропатченный вариант:
    Сравнение файлов advapi32.dll и C:\xyzcsp\PATCH\ADVAPI32.DLL
    00017585: 8B B8
    00017586: FF 01
    00017587: 55 00
    00017588: 8B 00
    00017589: EC 00
    0001758A: 81 C2
    0001758B: EC 04
    0001758C: 50 00
    
    Сравнение файлов crypt32.dll и C:\xyzcsp\PATCH\CRYPT32.DLL
    00008F66: 8B E9
    00008F67: FF 47
    00008F68: 55 09
    00008F69: 8B 00
    00008F6A: EC 00
    000098B2: 90 55
    000098B3: 90 8B
    000098B4: 90 EC
    000098B5: 90 8B
    000098B6: 90 45
    000098B7: 90 08
    000098B8: 53 83
    000098B9: 00 F8
    000098BA: 6F 00
    000098BB: 00 74
    000098BC: 66 21
    000098BD: 00 3D
    000098BE: 74 36
    000098BF: 00 20
    000098C0: 77 00
    000098C2: 61 74
    000098C3: 00 1A
    000098C4: 72 3D
    000098C5: 00 35
    000098C6: 65 66
    000098C8: 5C 00
    000098C9: 00 74
    000098CA: 50 13
    000098CB: 00 3D
    000098CC: 6F 37
    000098CD: 00 80
    000098CE: 6C 00
    000098D0: 69 74
    000098D1: 00 0C
    000098D2: 63 3D
    000098D3: 00 38
    000098D4: 69 AA
    000098D6: 65 00
    000098D7: 00 74
    000098D8: 73 05
    000098D9: 00 E9
    000098DA: 5C 8D
    000098DB: 00 F6
    000098DC: 4D FF
    000098DD: 00 FF
    000098DE: 69 6A
    000098E0: 63 68
    000098E1: 00 7B
    000098E2: 72 00
    000098E4: 6F 00
    000098E5: 00 6A
    000098E6: 73 00
    000098E7: 00 8D
    000098E8: 6F 05
    000098E9: 00 40
    000098EA: 66 A5
    000098EB: 00 A7
    000098EC: 74 77
    000098ED: 00 50
    000098EE: 5C 8D
    000098EF: 00 45
    000098F0: 53 08
    000098F1: 00 50
    000098F2: 79 FF
    000098F3: 00 15
    000098F4: 73 00
    000098F5: 00 10
    000098F6: 74 A7
    000098F7: 00 77
    000098F8: 65 83
    000098F9: 00 F8
    000098FA: 6D 00
    000098FB: 00 74
    000098FC: 43 DC
    000098FD: 00 8B
    000098FE: 65 45
    000098FF: 00 08
    00009900: 72 C9
    00009901: 00 C2
    00009902: 74 04
    


    Для Windows 7 также можно создать универсальный патч, желающие могут попробовать сделать это самостоятельно.

    В реальной системе, в которой установлен ГОСТ криптопровайдер, проблемы с патчем обычно решаются за счет установки специального драйвера PatchEngine, который наблюдает за загрузкой системных DLL и патчит их «на лету».

    Все файлы одним архивом


    В заключение приведу ссылку, по которой все файлы из этой статьи можно скачать одним архивом:
    files.mail.ru/1OVVDB
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 41

      +2
      Какова сейчас ситуация с продвижением ГОСТа в качестве мирового промышленного стандарта? Зарубили окончательно или еще нет?
        +3
        Судьба ГОСТа сейчас под вопросом даже в пределах нашей страны.
          +2
          Расскажите подробности?
            +5
            15/06/2011 Сообщения о взломе блочного шифра ГОСТ 28147-89 в разгар усилий по его международной стандартизации

            16/06/2011 Показан первый концептуальный взлом ГОСТ 28147-89 путём дифференциального криптоанализа

            12/10/2011 Улучшенная атака на полнораундовый ГОСТ 28147-89

            22/11/2011 Полная публикация множественных атак на ГОСТ 28147-89 от Николя Куртуа

            12/12/2011 Медведев поручил Путину открыть дорогу иностранной криптографии
              +4
              >12/12/2011 Медведев поручил Путину открыть дорогу иностранной криптографии
              А вот это очень даже хорошая новость.
                0
                На сколько мне известно, на сегодняшний день, единственным недостатком ГОСТа 28147 является его производительность. Если бы не этот фактор, то никто бы и не суетился.
                Лучшей реальной атакой является атака предложенная Takanori Isobe на FSE 2011, сложность которой составляет 2^{225}. Однако 2^{225} достаточно для того, чтобы использовать шифр ещё до 2030 года и дальше (согласно NIST SP800-57 Part1 May2011 (Draft)).

                Но сточки зрения теории ГОСТ был взломан, с этим я согласен.
                  +1
                  Небольшое уточнение, на данный момент всё же лучшей атакой является 2^{192} (ссылка).
                  Ну а кто в коалиции с Nicolas T. Courtois, то с их точки зрения лучшая атака 2^{185} (ссылка).
                    +1
                    А что означает сложность атаки 2^{225}? Мне нужно в худшем случае перебрать 2^{225} вариантов? То есть там ключ длиной 256 бит, а с помощью атаки его, как будто, уменьшили до 225?
                      0
                      Ага, именно так.
                    0
                    Куртуа вообще довольно странный парень. Его публикациям я бы лично не стал абсолютно доверять.
              +6
              Меня всегда поражало, что практически все гос.структуры, у которых есть порталы с https используют самопальные сертификаты, на которые орут все браузеры.
                +1
                Просто сложно объяснить начальству, что такое сертификат,
                и почему он должен быть подписан корневым УЦ :-).
                Столкнулся недавно с одной штукой на таких HTTPS соединениях:
                Firefox начиная с 4-й версии отказывается на таких соединениях ставить плагины, начинает проверять сертификаты по цепочке и ругается. Задолбался объяснять, и в итоге пришлось ставить плагин через обычный HTTP.
                  +1
                  Ещё сложнее объяснить, почему «крутая госконтора» должна платить деньги «каким-то буржуям» за подписывание сертификатов, и почему сертификату «каких-то буржуев» верят больше, чем сертификату «крутой госконторы» =))
                  А единой государственной системы сертификации вроде пока ещё не сделали. Хотят, но пока не замечал особых успехов.
                0
                Интересно :-). Сам периодически сталкиваюсь с криптографией.
                Для полноты картины неплохо добавить в провайдер собственно саму подпись.
                А если вы сможете сами реализовывать не обычную ЭЦП, а что нить типа CAdES BES, а еще лучше CAdES X_LONG_TYPE_1 как у Крипто Про, то цены не будет вашему провайдеру.
                Собственно из-за усовершенствованной подписи и имеет смысл пользовать Крипто Про и другие платные решения, иначе можно взять випнет и радоваться жизни.
                  +1
                  Какая гадость этот випнет…
                  +9
                  Суть в том, что ваш криптопровайдер не сертифицирован, это и есть основная проблема, как правило.
                    +1
                    Всегда думал, а почему бы всем миром не скинуться по копейке и сертифицировать бесплатное открытое криптосредство на ГОСТах..? Если поделить расходы на всех заинтересованных получится реально по копейке.
                      +3
                      Сертификация подразумевает не столько деньги, сколько выполнение каких-то требований. Иногда эти требования муторные или мутные, не всякий разберётся и не каждый захочет этой рутиной заниматься.
                        0
                        Я не питаю иллюзий по поводу процедур сертификации в этой стране, но согласитесь, что в конечном итоге все упирается в деньги. Были бы деньги, а разработать и сертифицировать можно что угодно. Сейчас компании тратят просто гигантские суммы только на закупку лицензий таких средств, все ради бумажки — сертификата. Львиная доля из них, кстати, госслужбы, которые тратят деньги налогоплательщиков, таких как мы с вами :(
                          0
                          Мы с Вами живем в одном и той же реальности? — первая же сертификация бесплатного открытого криптосредства уничтожит институт сертификации изнутри.
                            0
                            Вы тоже думаете что обязательная-сертификация один из честных способов отъема денег у населения организаций?
                            +1
                            Сертификацией должно заниматься юрлицо, имеющее лицензии на работу с криптографией, для этого, как минимум, нужно иметь несколько специалистов с профильным образованием в штате. То есть просто скинуться одноразово не получится, даже при наличии готового продукта, нужна постоянная «подпитка» хотя бы этим специалистам по МРОТу чтоб платить.
                          0
                          А зачем?

                          Ну вот сертифицируете вы CSP для Windows — все равно при каждом встраивании придется получать отдельный сертификат.

                            0
                            Как минимум потому, что сертификаты выдаются только на бинарник, а в случае выхода новой версии, всю процедуру сертификации придётся запускать сначала.

                            С год назад мне говорили, что стоимость сертификации криптопровайдера порядка 500.000 рублей, а по срокам может затянуться на многие месяцы, при этом gost из openssl не пройдёт этой сертификации, потому что кроме самой реализации алгоритмов надо выполнить ещё кучу требований навроде защиты от вытаскивания ключей из пямяти процесса.
                            0
                            В точку. В виду текущих требований в гос. проектах (например, проект УЭК) все же используют наши ГОСТовые алгоритмы, более того, используют не OpenSSL, про который писали выше, а именно решения от ифнотекса или крипто про, так как они сертифицированы. А это, в общем-то, создает некоторые трудности.
                            +4
                            Ну то есть сам ГОСТ-то вы не написали, а взяли чужую реализацию. Так что это не «написать криптопровайдер», а «подключить реализацию OpenSSL».

                            Во-вторых, зачем что-то патчить (учитывая, что это нарушение целостности системы, и будет убито произвольным обновлением), когда инфраструктура CSP специально сделана расширяемой? И даже в статье с RSDN, на которую вы ссылаетесь, это описано.

                            (ну и про сертификацию все правильно сказали, несертифицированное встраивание ГОСТа никому не нужно)
                              0
                              Насчет «подключить реализацию OpenSSL» — это не проблема, можно использовать любое другое криптоядро, просто был выбран наиболее простой способ для демонстрации криптопровайдера «в исходниках». Интересно, есть ли ГОСТ криптофункции в исходниках где-либо, кроме как в OpenSSL?

                              Насчет патчить или не патчить, в статье на RSDN в разделе Функция I_CryptGetDefaultCryptProvider из crypt32.dll, есть такие слова: «Необходимо заменить эту функцию таким образом, чтобы при получении нулевого параметра algid она возвращала наш провайдер», а как ее можно заменить, кроме как пропатчить crypt32.dll?

                              Тут же предлагается использовать для этого библиотеку Microsoft Detours, мной был выбран способ попроще — через WriteProcessMemory, благо патчить нужно свой собственный процесс, загрузив предварительно в пространство процесса библиотеку crypt32.dll через LoadLibrary.

                              Насчет сертификации — да, это проблема. Я не специалист по сертификации, ничего не знаю конкретно, но думаю, что был бы продукт, а получить для него сертификат — дело десятое.

                              И кстати, какие гарантии дает наличие сертификата, но отсутствие исходных текстов у некоего абстрактного криптопровайдера, может быть, там где-то сбоку встроен ключ ФСБ, как в Windows встроен ключ АНБ, по крайней мере такие слухи периодически появляются?

                              Но такие слухи лишены основания, насколько я знаю, ключ АНБ в Windows действительно есть, но это не более, чем один из корневых сертификатов, чтобы АНБ могло использовать в Windows свои собственные криптопровайдеры, не подписывая их в Microsoft.

                              Кстати, проблема подписывания криптопровайдера в Microsoft не освещена ни в статье на RSDN, ни в данной статье просто потому, что раз мы патчим crypt32.dll, то заодно и пропатчим в advapi32.dll функцию SystemFunction035, которая занимается проверкой наличия подписи в криптопровайдере. По крайней мере, когда эта функция вызывается, то ей на вход идет полный путь DLL-ки с криптопровайдером.

                              Если кому-то удастся решить проблему с недокументированной I_CryptGetDefaultCryptProvider, например, через реестр и отпадет необходимость патчить crypt32.dll, тогда можно задуматься и о сертификации в Microsoft, чтобы получить от них подписанный криптопровайдер и прекратить патчить SystemFunction035.

                              Кстати, если установить провайдер Крипто-Про и запустить поиск руткитов GMER, то он найдет кучу пропатченных DLL, не только crypt32.dll и advapi32.dll. Интересно, зачем такое большое количество патчей, неужели все настолько плохо?

                              Кстати, на форуме Крипто-Про предлагают патчить функцию SystemFunction035, чтобы она возвращала eax=0. Но оказалось, что это неправильно, нужно возвращать eax=1. Интересно, это нарочно сделано, или простая описка?
                                0
                                «Необходимо заменить эту функцию таким образом, чтобы при получении нулевого параметра algid она возвращала наш провайдер»
                                Это нужно только для запроса сертификата, а не для всех криптоопераций.

                                «Я не специалист по сертификации, ничего не знаю конкретно, но думаю, что был бы продукт, а получить для него сертификат — дело десятое.»
                                Не специалист. Cертифицировать продукт намного сложнее, чем написать. А для использования в соответствующих местах (а зачем вам еще ГОСТ?) сертификация необходима.
                                  0
                                  >Это нужно только для запроса сертификата, а не для всех криптоопераций.

                                  Не знаю, является ли проверка сертификата криптооперацией, но если перед этим функцию I_CryptGetDefaultCryptProvider хорошенько не пропатчить, ничего работать не будет.
                                +1
                                Насчёт никому не нужно я бы поспорил. Есть области использования ЭЦП где обязательной сертификации не требуется, но хотелось бы иметь именно ГОСТовскую реализацию, пускай и не сертифицированную. При юридических разборках это будет полезней, чем какой-нибудь иностранный алгоритм. При экспертизе возьмут документ с подписью, введут его в сертифицированный продукт, установят соответствующие сертификаты, и он покажет (или нет) валидность подписи. Как эта подпись сформирована — дело десятое, чем её проверял контрагент — тем более.
                                  0
                                  " При экспертизе возьмут документ с подписью, введут его в сертифицированный продукт, установят соответствующие сертификаты, и он покажет (или нет) валидность подписи. Как эта подпись сформирована — дело десятое, чем её проверял контрагент — тем более."
                                  Ах если бы. Если подпись сформирована несертифицированным средством, подписант может отказаться от подписанного документа, ссылаясь на то, что при подписании он видел другой документ. И доказать обратное невозможно.
                                    +1
                                    Вот так штука! Получается, даже от валидной ЭЦП можно отказаться?
                                      +1
                                      Получается, что можно. Потому что описанная мной атака — это даже не атака, а просто детский лепет, и реализуется в пять минут.

                                      Сертификация — она не просто так.
                                        0
                                        А если подпись сформирована сертифицированным средством, но оно исполняется в недоверенной среде, например, в Windows поселился троян, подменяющий документы перед подписанием?

                                        Тогда тоже можно отказаться от валидной ЭЦП, если доказать наличие трояна?
                                          0
                                          Вообще, в сертифицированном средстве это невозможно. Сертификация, в числе прочего, проверяет (должна, по крайней мере), что пользователь подписывает именно тот документ, который видит.
                                      0
                                      Вот пришло ко мне мыло, во вложении два файла — letter.odt и signup.gost :) Подпись соответствует документу. Скажет, что не только подписывал, но и отправлял другой документ, а почтовик подсунул что-то левое, причем не просто левое, но договорился с сайнером о том, то именно?
                                        0
                                        Для detached-подписи несколько сложнее, потому что документ можно просмотреть отдельно. А вот в случае attached, когда результирующий файл ничем не открывается — можно.

                                        Да и тут, на самом деле, можно сказать, что открыл в программе документ, сказал «подписать и отправить», а уже она сама сформировала письмо, а что в том письме — ни сном, ни духом.
                                  0
                                  блин, нельзя так пугать!
                                  первый скриншот появился посреди экрана и я долго пытался окошко закрыть!!! 8(
                                    +9
                                    Какой-то кривокод. И вообще, вы (или разработчики виндоус) наркоман какой-то, зачем патчить ситемные библиотеки Windows? Тем более драйвером, на ходу? Не объясняется. Может, вы дорогу троянам таким образом открываете. И вносите возможные нарушения в систему, а также потенциальные проблемы при обновлении.

                                    Установку библиотеки лучше бы сделать через regserver или как там это в виндоус принято. OpenSSL стоило бы собирать в виде dll, а не намертво прилинковывать.

                                    Используются глобальные переменные — а в многопоточной среде такой код будет нормально работать?

                                    И вообще, какой-то код кривой, неаккуратный, куча оговорок, куча непонятных мест, например, откуда берутся эти цифры и имена ключей:

                                    > CryptDllFindOIDInfo\1.2.643.2.2.19!1

                                    Неужели в виндоус нет функции типа registerCryptoProvider()? Ведь при первом же изменении в ключах реестра ваш провайдер отвалится.

                                    Вместо того, чтобы нормально собрать OpenSSL, опять какие-то собственные костыли делаете. Это у вас Windows программистов так принято что ли? Все делать по-кривому? Как не увижу код под винду, там всегда какие-то костыли, подчеркивания в именах переменных и прочий треш. И патч системных библиотек драйвером. Да у меня просто слов нет выразить мое возмущение.

                                    А возвращаясь к вопросу, сложно ли написать криптопровайдер — ну да, вполне реально, достаточно 1 разработчика + затраты на сертификацию, просто это никому не нужно, ну не нужно, покупайте платные и не жалуйтесь.

                                      +4
                                      Удивлен, что на просторах Интернета не удалось найти подтверждения, что кем-то был создан интерфейс криптопровайдера ГОСТ под Windows с использованием вышеприведенных инструментов.

                                      ГОСТ нужен только тем, кому нужна сертифицированность СКЗИ.
                                      А тем кому нужна сертифицированность СКЗИ не нужны самодельные криптопровайдеры.
                                      Вот и вся мотивация.
                                        +2
                                        Если кому интересно: OpenSSL + сертифицированное СКЗИ Рутокен ЭЦП — forum.rutoken.ru/topic/1639/

                                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                        Самое читаемое