Речь пойдет о файлах primary.key, masks.key и header.key, которые лежат в директории ххххх.000 на флешке. Данные файлы входят в состав криптоконтейнера закрытого ключа электронной подписи криптопровайдера КриптоПро, формат которого нигде не опубликован. Целью данной статьи является чтение контейнера и преобразование закрытого ключа в формат, который может быть прочитан в библиотеке OpenSSL. Долгое время было распространено ошибочное суждение, что достаточно сделать нечто вида (primary_key XOR masks_key) и мы получим закрытый ключ в чистом (raw) виде, однако забегая вперед, можно утверждать, что в КриптоПро было применено более сложное преобразование, в худшем случае состоящее из более чем 2000 (двух тысяч) операций хеширования.

Стоит упомянуть о существовании утилиты P12FromGostCSP которая позволяет конвертировать ключ в формат P12, доступный для работы с OpenSSL, но утилита имеет следующие существенные недостатки:
  • Читает контейнер не напрямую, а через криптопровайдер, поэтому там, где кроме OpenSSL ничего нет, не работает.
  • Если в свойствах ключа не отмечено, что ключ «экспортируемый», то конвертировать его невозможно.
  • В демо версии не формирует файл с ключом, эта возможность присутствует только в платной версии.

Файл primary.key


Содержит 32 байта ключа в формате Asn1. Это только половина ключа, полный ключ получается при делении этого числа по модулю Q на маску. Поле, хранящее модуль Q в библиотеке OpenSSL имеет название order. Маска лежит в файле masks.key:

primary.key

Файл masks.key


Содержит 32 байта маски ключа в формате Asn1, зашифрованного на ключе хранения pwd_key. Далее 12 байт «затравочной» информации для генерации ключа хранения pwd_key, если криптоконтейнер защищен паролем, то пароль также участвует в генерации ключа хранения.

Далее контрольная сумма (имитозащита) 4 байта. Контрольной информацией для простоты мы пользоваться не будем, общий контроль будет осуществляться путем генерации открытого ключа и сравнения первых 8 байт полученного ключа с соответствующим полем из файла header.key:

masks.key

Файл header.key


Из этого файла нам потребуется параметры электронной подписи CryptoProParamSet (подчеркнуто красным).
  • GostR3410_2001_CryptoPro_A_ParamSet — 1.2.643.2.2.35.1
  • GostR3410_2001_CryptoPro_B_ParamSet — 1.2.643.2.2.35.2
  • GostR3410_2001_CryptoPro_C_ParamSet — 1.2.643.2.2.35.3
  • GostR3410_2001_CryptoPro_XchA_ParamSet — 1.2.643.2.2.36.0
  • GostR3410_2001_CryptoPro_XchB_ParamSet — 1.2.643.2.2.36.1

А также первые 8 байт открытого ключа (подчеркнуто) для контроля правильности чтения закрытого.

header.key

Читаем закрытый ключ и конвертируем


Файл privkey.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <openssl/pem.h>
#include <openssl/cms.h>
#include <openssl/err.h>
#include "gost_lcl.h"

/* Convert little-endian byte array into bignum */
BIGNUM *reverse32bn(char *b, BN_CTX *ctx)
{
	BIGNUM *res;
	char buf[32];
	BUF_reverse(buf, b, 32);
	res = BN_bin2bn(buf, 32, BN_CTX_get(ctx));
	OPENSSL_cleanse(buf, sizeof(buf));
	return res;
}

void xor_material(char *buf36, char *buf5C, char *src)
{
	int i;
	for(i = 0; i < 32; i++)
	{
		buf36[i] = src[i] ^ 0x36;
		buf5C[i] = src[i] ^ 0x5C;
	}
}

int make_pwd_key(char *result_key, char *start12, int start12_len, char *passw)
{
	int result;
	int i;
	char pincode4[1024];
	int pin_len;
	char current[32];
	char material36[32];
	char material5C[32];
	char hash_result[32];
	gost_hash_ctx ctx;
	init_gost_hash_ctx(&ctx, &GostR3411_94_CryptoProParamSet);
	memset(pincode4, 0, sizeof(pincode4));
	pin_len = strlen(passw);
	if (pin_len*4 > sizeof(pincode4)) {	result = 1;	goto err; }
	for(i = 0; i < pin_len; i++)
		pincode4[i*4] = passw[i];

	start_hash(&ctx);
	hash_block(&ctx, start12, start12_len);
	if (pin_len) 
		hash_block(&ctx, pincode4, pin_len * 4);
	finish_hash(&ctx, hash_result);

	memcpy(current, (char*)"DENEFH028.760246785.IUEFHWUIO.EF", 32);

	for(i = 0; i < (pin_len?2000:2); i++)
	{
		xor_material(material36, material5C, current);
		start_hash(&ctx);
		hash_block(&ctx, material36, 32);
		hash_block(&ctx, hash_result, 32);
		hash_block(&ctx, material5C, 32);
		hash_block(&ctx, hash_result, 32);
		finish_hash(&ctx, current);
	}

	xor_material(material36, material5C, current);

	start_hash(&ctx);
	hash_block(&ctx, material36, 32);
	hash_block(&ctx, start12, start12_len);
	hash_block(&ctx, material5C, 32);
	if (pin_len) 
		hash_block(&ctx, pincode4, pin_len * 4);
	finish_hash(&ctx, current);

	start_hash(&ctx);
	hash_block(&ctx, current, 32);
	finish_hash(&ctx, result_key);

	result = 0; //ok
err:
	return result;
}

BIGNUM *decode_primary_key(char *pwd_key, char *primary_key, BN_CTX *bn_ctx)
{
	BIGNUM *res;
	char buf[32];
	gost_ctx ctx;
	gost_init(&ctx, gost_cipher_list->sblock);
	gost_key(&ctx, pwd_key);
	gost_dec(&ctx, primary_key, buf, 4);
	res = reverse32bn(buf, bn_ctx);
	OPENSSL_cleanse(buf, sizeof(buf));
	return res;
}

BIGNUM *remove_mask_and_check_public(char *oid_param_set8, BIGNUM *key_with_mask, BIGNUM *mask, char *public8, BN_CTX *ctx)
{
	int result;
	EC_KEY *eckey = NULL;
	const EC_POINT *pubkey;
	const EC_GROUP *group;
	BIGNUM *X, *Y, *order, *raw_secret, *mask_inv;
	char outbuf[32], public_X[32];
	ASN1_OBJECT *obj;
	int nid;

	order = BN_CTX_get(ctx);
	mask_inv = BN_CTX_get(ctx);
	raw_secret = BN_CTX_get(ctx);
	X = BN_CTX_get(ctx);
	Y = BN_CTX_get(ctx);
	if (!order || !mask_inv || !raw_secret || !X || !Y) { result = 1; goto err; }

	obj = ASN1_OBJECT_create(0, oid_param_set8+1, *oid_param_set8, NULL, NULL);
	nid = OBJ_obj2nid(obj);
	ASN1_OBJECT_free(obj);

	if (!(eckey = EC_KEY_new())) { result = 1; goto err; }
	if (!fill_GOST2001_params(eckey, nid)) { result = 1; goto err; }
	if (!(group = EC_KEY_get0_group(eckey))) { result = 1; goto err; }
	if (!EC_GROUP_get_order(group, order, ctx)) { result = 1; goto err; }

	if (!BN_mod_inverse(mask_inv, mask, order, ctx)) { result = 1; goto err; }
	if (!BN_mod_mul(raw_secret, key_with_mask, mask_inv, order, ctx)) { result = 1; goto err; }

	if (!EC_KEY_set_private_key(eckey, raw_secret)) { result = 1; goto err; }
	if (!gost2001_compute_public(eckey)) { result = 1; goto err; }
	if (!(pubkey = EC_KEY_get0_public_key(eckey))) { result = 1; goto err; }
	if (!EC_POINT_get_affine_coordinates_GFp(group, pubkey, X, Y, ctx)) { result = 1; goto err; }

	store_bignum(X, outbuf, sizeof(outbuf));
	BUF_reverse(public_X, outbuf, sizeof(outbuf));
	if (memcmp(public_X, public8, 8) != 0) { result = 1; goto err; }

	result = 0; //ok
err:
	if (eckey) EC_KEY_free(eckey);
	if (result == 0) return raw_secret;
	return NULL;
}

int file_length(char *fname)
{
	int len;
	FILE *f = fopen(fname, "rb");
	if (f == NULL) return -1;
	fseek(f, 0, SEEK_END);
	len = ftell(f);
	fclose(f);
	return len;
}

int read_file(char *fname, int start_pos, char *buf, int len)
{
	int read_len;
	FILE *f = fopen(fname, "rb");
	if (f == NULL) return 1;
	if (start_pos) fseek(f, start_pos, SEEK_SET);
	read_len = fread(buf, 1, len, f);
	fclose(f);
	if (read_len != len) return 1;
	return 0; //ok
}

int get_asn1_len(unsigned char *buf, int *size_hdr)
{
	int n, i, res;
	int pos = 0;
	if ((buf[pos]&0x80) == 0) {
		*size_hdr = 1;
		return buf[pos];
	}
	n = buf[pos++]&0x7f;
	res = 0;
	for(i = 0; i < n; i++) {
		res = res*256 + buf[pos++];
	}
	*size_hdr = n+1;
	return res;
}

#define MAX_HEADER 20000
int read_container(char *fpath, int flag2, char *salt12, char *primary_key, char *masks_key, char *public8, char *oid_param_set8)
{
	int result;
	char primary_path[1024+30];
	char masks_path[1024+30];
	char header_path[1024+30];
	char header_buf[MAX_HEADER];
	int header_len;
	int i, len, pos, size_hdr;

	if (strlen(fpath)>1024) { result = 1; goto err; }

	sprintf(header_path, "%s/header.key", fpath);
	if (flag2 == 0)
	{
		sprintf(primary_path, "%s/primary.key", fpath);
		sprintf(masks_path, "%s/masks.key", fpath);
	}
	else
	{
		sprintf(primary_path, "%s/primary2.key", fpath);
		sprintf(masks_path, "%s/masks2.key", fpath);
	}

	if (read_file(primary_path, 4, primary_key, 32)) { result = 1; goto err; }
	if (read_file(masks_path, 4, masks_key, 32)) { result = 1; goto err; }
	if (read_file(masks_path, 0x26, salt12, 12)) { result = 1; goto err; }

	header_len = file_length(header_path);
	if (header_len < 0x42 || header_len > MAX_HEADER) { result = 1; goto err; }
	if (read_file(header_path, 0, header_buf, header_len)) { result = 1; goto err; }

//------------- skip certificate ---------------------------
	pos = 0;
	for(i = 0; i < 2; i++)
	{
		get_asn1_len(header_buf+pos+1, &size_hdr);
		pos += size_hdr+1;
		if (pos > header_len-8) { result = 2; goto err; }
	}

//------------------ get oid_param_set8 -----------------------
#define PARAM_SET_POS 34
	if (memcmp(header_buf+pos+PARAM_SET_POS, "\x6\x7", 2) != 0) { result = 2; goto err; }
	memcpy(oid_param_set8, header_buf+pos+PARAM_SET_POS+1, 8);

//------------------ get public8 -----------------------
	result = 2; //not found
	pos += 52;
	for(i = 0; i < 3; i++)
	{
		len = get_asn1_len(header_buf+pos+1, &size_hdr);
		if (len == 8 && memcmp(header_buf+pos, "\x8a\x8", 2) == 0)
		{
			memcpy(public8,header_buf+pos+2,8);
			result = 0; //ok
			break;
		}
		pos += len+size_hdr+1;
		if (pos > header_len-8) { result = 2; goto err; }
	}
err:
	OPENSSL_cleanse(header_buf, sizeof(header_buf));
	return result;
}

#define START_OID 0x12
#define START_KEY 0x28
unsigned char asn1_private_key[72] = {
	0x30,0x46,2,1,0,0x30,0x1c,6,6,0x2a,0x85,3,2,2,0x13,0x30,0x12,6,7,0x11,
	0x11,0x11,0x11,0x11,0x11,0x11,6,7,0x2a,0x85,3,2,2,0x1e,1,4,0x23,2,0x21,0
};

int main(int argc, char **argv)
{
	int result;
	char *container_path;
	char *passw;
	char salt12[12];
	char primary_key[32];
	char masks_key[32];
	char public8[8];
	char oid_param_set8[8];
	BN_CTX *ctx;
	BIGNUM *key_with_mask;
	BIGNUM *mask;
	BIGNUM *raw_key;
	char pwd_key[32];
	char outbuf[32];

	ctx = BN_CTX_new();

	if (argc == 2)
	{
		container_path = argv[1];
		passw = "";
	}
	else
	if (argc == 3)
	{
		container_path = argv[1];
		passw = argv[2];
	}
	else
	{
		printf("get_private container_path [passw]\n");
		result = 1;
		goto err;
	}

	if (read_container(container_path, 0, salt12, primary_key, masks_key, public8, oid_param_set8) != 0 &&
		read_container(container_path, 1, salt12, primary_key, masks_key, public8, oid_param_set8) != 0)
	{
		printf("can not read container from %s\n", container_path);
		result = 2;
		goto err;
	}

	make_pwd_key(pwd_key, salt12, 12, passw);
	key_with_mask = decode_primary_key(pwd_key, primary_key, ctx);
	OPENSSL_cleanse(pwd_key, sizeof(pwd_key));
	mask = reverse32bn(masks_key, ctx);
	raw_key = remove_mask_and_check_public(oid_param_set8, key_with_mask, mask, public8, ctx);

	if (raw_key)
	{
		BIO *bio;
		store_bignum(raw_key, outbuf, sizeof(outbuf));
		memcpy(asn1_private_key+START_OID, oid_param_set8, 8);
		memcpy(asn1_private_key+START_KEY, outbuf, 32);
		//bio = BIO_new_file("private.key", "w");
		bio = BIO_new_fp(stdout, BIO_NOCLOSE | BIO_FP_TEXT);
		PEM_write_bio(bio, "PRIVATE KEY", "", asn1_private_key, sizeof(asn1_private_key));
		BIO_free(bio);
		OPENSSL_cleanse(outbuf, sizeof(outbuf));
		OPENSSL_cleanse(asn1_private_key, sizeof(asn1_private_key));
		result = 0; //ok
	}
	else
	{
		printf("Error check public key\n");
		result = 3;
	}

err:
	BN_CTX_free(ctx);
	OPENSSL_cleanse(salt12, sizeof(salt12));
	OPENSSL_cleanse(primary_key, sizeof(primary_key));
	OPENSSL_cleanse(masks_key, sizeof(masks_key));
	return result;
}


Небольшой комментарий.

Основную работу выполняют следующие 3 функции:

1. Создаем ключ хранения исходя из 12-ти байтовой «соли» и пароля.
make_pwd_key(pwd_key, salt12, 12, passw);

2. Расшифровываем основной ключ на ключе хранения.
key_with_mask = decode_primary_key(pwd_key, primary_key, ctx);

3. Делим ключ с маской на маску.
raw_key = remove_mask_and_check_public(oid_param_set8, key_with_mask, mask, public8, ctx);

Но так как в библиотеке OpenSLL операция деления по модулю традиционно отсутствует, пользуемся операцией взятия обратного числа и умножением.
if (!BN_mod_inverse(mask_inv, mask, order, ctx)) { result = 1; goto err; }
if (!BN_mod_mul(raw_secret, key_with_mask, mask_inv, order, ctx)) { result = 1; goto err; }


Сборка утилиты конвертирования ключа


Далее сборка исходников описана для Linux версии.

Версию для Windows можно скачать отсюда там же есть сертификаты и закрытый ключ для тестирования, для сборки потребуется бесплатный компилятор Borland C++ 5.5

Компиляция OpenSSL библиотеки


После скачивания и распаковки исходных текстов openssl в целевой директории выполняем команды:

./config
make

Получаем готовую библиотеку libcrypto.a в текущей директории.
Также потребуются заголовочные файлы из директорий engines/ccgost и include.

Компиляция privkey.c


gcc -o privkey -Iengines/ccgost -Iinclude privkey.c libcrypto.a -pthread -ldl

Формирование файла закрытого ключа private.key


./privkey /mnt/usbflash/lp-9a0fe.000

Тестовый закрытый ключ в криптоконтейнере lp-9a0fe.000, сертификат открытого ключа signer.cer и другие файлы для тестирования можно взять отсюда

Получаем результат работы:
-----BEGIN PRIVATE KEY-----
MEYCAQAwHAYGKoUDAgITMBIGByqFAwICJAAGByqFAwICHgEEIwIhAKzsrv/l1Uwk
uzph/LQN9mux0Jz0yaW21kOYEFv0Xyut
-----END PRIVATE KEY-----

Cохраняем в private.key

Пользуемся закрытым ключом private.key для подписывания файла file.txt


openssl cms -sign -inkey private.key -in file.txt -CAfile CA.cer -signer signer.cer -engine gost -out test.sign -outform DER -noattr -binary

Проверяем подпись


openssl cms -verify -content file.txt -in test.sign -CAfile CA.cer -signer signer.cer -engine gost -inform DER -noattr -binary

Все работает просто замечательно!

Спасибо за внимание. Это была моя первая статья на хабре.

Комментарии (29)


  1. erlyvideo
    16.01.2016 00:37
    +1

    очень круто!


  1. Cosmonaut
    16.01.2016 09:13
    +3

    Достойный ответ проприентарному криптопровайдеру!


  1. mva
    16.01.2016 09:23
    +1

    Но остаётся открытым вопрос о том, как без «проприетарного криптопровайдера» (как сказали выше) вытащить со «свистка» криптоконтейнер (потому что далеко не все из предоставляемых «свистков» (если честно, я вообще ни с одним таким не сталкивался) монтируются как флешки, чтобы была возможность просто взять и добраться до /mnt/bla/*.000


    1. vsb
      16.01.2016 13:21

      Вообще в нормальном криптотокене ключи лежат в защищённом хранилище, специально спроектированном так, чтобы вытащить их было нельзя, а все криптооперации выполняются самим токеном. Если у вас такой — за разумные деньги никак не вытащить.


      1. Maysoft
        16.01.2016 16:54
        -4

        Позвольте не согласиться: вытащить можно и даже нужно (ну хотя бы для резервной копии). Делается это на машине с установленным КриптоПро CSP + драйвера токена:
        запускаем КриптоПро CSP (в Windows cpconfig.cpl);
        на вкладке Сервис выбираем кнопку Скопировать...;
        источник токен, приемник флешка (могут быть проблемы, если не установлен считыватель «Все съемные диски»).
        С флешки можно спокойно пересылать контейнер хоть по мылу.


        1. grossws
          16.01.2016 17:56
          +4

          Если используется реальный токен (хоть тот же рутокен эцп или etoken), а не поделие на основе флешки, то вытащить оттуда приватный ключ — крайне сложная деструктивная операция (микросхема, на которой хранится ключ, будет физически разрушена).


          1. Maysoft
            16.01.2016 18:04
            -6

            Боюсь, что вы плохо представляете, что любое обращение к ключу — это чтение даже из самого супер защищенного хранилища, операция копирования требует только чтения. Дальше додумаетесь сами?


            1. grossws
              16.01.2016 18:09
              +5

              Криптотокен часто не предоставляет операции чтения. Он предоставляет операции типа хэширования, подписи и дешифрования (всё это выполняется на токене). Плюс управление приватными ключами и сертификатами, которое в случае ключей часто сводится к «сгенерировать ключ», «записать ключ» (с хоста на токен). Посмотрите на интерфейс pkcs11 или pcsc.


            1. Kolyuchkin
              16.01.2016 19:25
              +3

              Я разрабатывал подобные «крипто-свистки» и архитектура была спроектирована таким образом, что исключалась возможность чтения ключей ЭП даже инвазивными методами, не говоря уж о программных. Это закладывалось в топологию микроконтроллера. Вот) И запросы библиотеки криптоинтерфейсов анализировались строжайшим образом перед их выполнением) Компания «Крипто-Про» и ее партнеры заботятся о своих ключах не хуже (в серьезных продуктах, конечно). Так что, на мой взгляд, легче «купить» владельца или прибегнуть к «термо-криптоанализу», чем пытаться вытащить ключ из серьезного крипто-свистка.


              1. erlyvideo
                17.01.2016 11:06

                исключается возможность или делается очень дорогой?


                1. Kolyuchkin
                  17.01.2016 12:30

                  делает возможность настолько дорогой, что «овчинка выделки не стоит»)


                  1. dmitrmax
                    18.01.2016 19:52

                    Неужели нет секретной команды «выдай мне все ключи»? Неужели ФСБ такое сертифицирует? )


                    1. Kolyuchkin
                      19.01.2016 01:55

                      «Нет», конечно. О чем Вы говорите?))



            1. dmitrmax
              17.01.2016 01:37
              +3

              Боюсь, что вы плохо разбираетесь в предмете и осмеливаетесь других в этом обвинять. Вам не приходило в голову, что можно закачать в токен информацию, которую надо зашифровать или подписать, и получить из токена зашифрованную инфу или подпись? и тогда ключу нет нужды покидать пределы носителя. Так работают нормальные токены и смарт-карты. Потому они и смарт.


        1. Greyushko
          16.01.2016 17:56

          Существуют разные способы хранения ключей на токене (в том числе и среди продуктов КриптоПро). В предыдущем комментарии речь шла о неизвлекаемых ключах.


        1. palmich
          16.01.2016 19:00

          там еще есть галочка, при создании ключа «возможность тиражирования» или как то так. Если правильно помню — на то, чтобы поставить такую галочку при генерации ЭЦП для электронных торгов с меня хотели еще +1000 рублей. Без этой галочки ключ с рутокена просто не копируется.


    1. Greyushko
      16.01.2016 17:58
      +2

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


      1. dmitrmax
        17.01.2016 01:32
        -1

        Зачем? Дерзните сделать гостовую подпись в браузере под линуксом с помощью такого хардварного токена.


        1. khim
          17.01.2016 05:11
          +2

          Токена у меня нет, но вроде как на форуме вполне себе инструкции приводятся.


          1. dmitrmax
            18.01.2016 19:51

            Есть некоторая беда с этими токенами. Заключается она в том, что поддержка этих токенов должна быть сделана на сайте с помощью плагина, коих сейчас уже небольшой зоопарк. Сделать функционал ЭП по ГОСТу с помощью стандартных механизмов браузера у таких токенов увы нельзя. С плагинами есть другая беда: большая часть плагинов используют NP API, который выпилен из Хрома и вот-вот выпилят из Firefox.

            Под виндой работа с данными токенами тоже не самый удобный вариант, так как при работе с ними через Crypto Pro или другое популярное СКЗИ данный токен превращается в тыкву флэшку. Работа Microsoft Crypto API поддержана только для RSA части этого токена. А вот функционал ГОСТовских функций по аппаратной подписи надо тянуть через PKSC#11, но программ, которые поддерживают такое под виндой в дополнение к MS Crypto API днём с огнём.


    1. lovecraft
      16.01.2016 19:07

      Как флешка монтируется обычная флешка с контейнером в виде отдельной папки. В общем-то это самый популярный и самый доступный тип токена в случае с Крипто-ПРО. И с него-то вытащить ключи сложнее всего, потому что формат хранения -проприетарный.

      С настоящими токенами гораздо легче — есть pcsk11-tool, есть, в конце концов, утилиты от производителей токенов. Если ключ сгенерирован на токене как неэкспортируемый, то, естественно, его ничем не вытащишь, но, как правило, ключ делают выгружаемым, чтобы можно было сделать копию, потому что вероятность использования ключа злоумышленниками намного ниже, чем вероятность его, например, утраты или поломки.

      Так что автору — респект.


      1. khim
        17.01.2016 05:23
        +2

        как правило, ключ делают выгружаемым, чтобы можно было сделать копию
        Ужас какой. Кто и где делает ключ выгружаемым? В нормальных компаниях с «утратой или поломкой» борются просто: упрощением регистрации нового ключа. В идеале — это должен иметь возможность сделать сам клиент без участия службы поддержки — примерно так.

        Самое дебильное — что ведь кто-то что-то там ведь проверяет, сертифицирует, разбирает. Куча денег вертится. Но при этом самое элементарное — похоже никто не проверяет. Зачем тогда всё это? То есть я понимаю, что для большинства людей «безопасность — это такая галочка, без которой клиенты не придут/договор не подпишут», а как оно там реально защищает ваши данные — неважно, но, чёрт побери, всему же есть предел!

        Обнаружение экспортируемого ключа в продакшене — это же ЧП. Кто-то что-то настолько не понимает, что «с дуба рухнул» и решил дискредитировать всю идею аппаратных токенов… А тут оказыватся, что «как правило ключ делают выгружаемым»… грустно это…


        1. palmich
          17.01.2016 09:09

          это вы просто с работой со СМЭВ не сталкивались…


          1. khim
            17.01.2016 14:15
            +5

            Не сталкивался. И проводку у себя в доме не я монтировал. И даже систему гидроусиления руля я не чинил. Но что это, собственно, меняет?..

            Обратная сторона российской «смекалки»: если кто-то разработал систему, в которой что-то делать неудобно, элементарная операция регистрации дополнительного ключа (которая должна быть элементарной и бесплатной) требует кучу времени и сил и/или денег, то мы это как-нибудь обойдём. «Жучок» поставим чтобы пробки не горели, датчик кувалдой забьём, если он на свое место не лезет, ключи экспортируемыми оставим. Всё — из самых лучших побуждений.

            А потом — дома горяд, ракеты падают, деньги пропадают.

            Смысл существования криптотокена — в неизвлекаемости помещённых в него ключей. Это, собственно, единственное оправдание его сущесвования. Использовать экспортируемые ключи вне разработки — должно быть как минимум поводом для увольнения за халатность, а как максимум — и для привлечения к отвественности (в зависимости от того кому и сколько таких ключей было выдано).

            А то над паролями, записанными на бумажечке и приклеенными к монитору — все смеются. А когда ключи выдаются с PINом 12345678 и с экспортируемыми ключами — это как будто «так и надо»…


        1. dmitrmax
          18.01.2016 21:10
          +1

          Я вам даже больше скажу. На сегодняшний день большая часть УЦ использует в работе софт КриптоПро, который позволяет создавать пары ключей только на убогих токенах. Более того, подавляющая часть УЦ не даёт (своим регламентом) клиентам возможности получить сертификат по запросу сертификата.

          Таким образом, всё гораздо хуже, потому что приватный ключ генерирует УЦ. Причем он может его скопировать себе в этот момент.

          Если вы действительно знаете, что такое безопасность, то, конечно, потратите время и найдете УЦ, который работает с запросами. Но большинство УЦ, а стало быть и их клиентов живут вот так вот.


      1. vsb
        17.01.2016 10:06
        +1

        Для чего вообще нужно платить деньги за такой ключ? Купить самую дешёвую флешку и всё. В Казахстане, например, так и есть. Ты получаешь сертификат от государства для получения госуслуг. При получении можно либо принести свою флешку, либо дать удостоверение, в котором встроен криптотокен (но нужен специальный ридер), либо заплатить деньги и получить USB-токен. Покупать втридорога флешку — странно ведь. Или это обусловлено этим софтом, который читает ключи только с этой брендовой флешки?


        1. khim
          17.01.2016 14:40

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

          У меня, скажем, подобный ключик вставлен раз и навсегда в ноут, в котором и ездит. Пока он находится в моём распоряжении — всё, что с его помощью производится — это моя ответственность и мои проблемы (если я его утеряю или, скажем, таможенники заберут ноут и отправят «на проверку» — я должен немедленно об этом сообщить, чтобы ключ аннулировали).

          Главное: так как ключ скопировать нельзя, то никакие «отмазки» и «происки конкурентов» во внимание не принимаются: ключ был у меня в руках — значит я за то, что произошло с его помощью и отвечаю.

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


          1. grossws
            17.01.2016 17:30

            У меня, скажем, подобный ключик вставлен раз и навсегда в ноут, в котором и ездит.
            Я такого избегаю, вставляю только когда надо что-нибудь подписать. У yubikey был security bug в openpgp-апплете, который позволял производить операции без пина.