Ключевое слово в защите информации
КЛЮЧЕВОЕ СЛОВО
в защите информации
Получить ГОСТ TLS-сертификат для домена (SSL-сертификат)
Добро пожаловать, Гость! Чтобы использовать все возможности Вход или Регистрация.

Уведомление

Icon
Error

Опции
К последнему сообщению К первому непрочитанному
Offline trunovsergey  
#1 Оставлено : 29 марта 2021 г. 20:27:42(UTC)
trunovsergey

Статус: Участник

Группы: Участники
Зарегистрирован: 19.09.2019(UTC)
Сообщений: 13
Российская Федерация
Откуда: Москва

Сказал(а) «Спасибо»: 4 раз
Коллеги, доброго времени суток.

Реализовали установку ПИН через последовательность вызовов:
1
Код:
CertOpenSystemStore(0, strStore)

2
Код:
pCert = CertFindCertificateInStore

3
Код:
CertCloseStore(hCertStore, CERT_CLOSE_STORE_CHECK_FLAG);

После этого контекст сертификата должен становиться не персистентным и любые изменения к нему не должны сохраняться?
4
Код:
CertGetCertificateContextProperty(pCert,
	                                      CERT_KEY_PROV_INFO_PROP_ID,
	                                      pCryptKeyProvInfo,
	                                      &szData))

5
Код:
DWORD keyType = pCryptKeyProvInfo->dwKeySpec==AT_SIGNATURE ? PP_SIGNATURE_PIN: PP_KEYEXCHANGE_PIN;

	CRYPT_KEY_PROV_PARAM key_prov_param;
	memset(&key_prov_param, 0, sizeof(CRYPT_KEY_PROV_PARAM));
	key_prov_param.dwFlags = 0;
	key_prov_param.dwParam = keyType;
	key_prov_param.cbData  = static_cast<DWORD>(pin.size()+1);
	key_prov_param.pbData  = (BYTE *)pin.c_str();

	pCryptKeyProvInfo->cProvParam = 1;
	pCryptKeyProvInfo->rgProvParam = &key_prov_param;

6
Код:
CertSetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID, 0, pCryptKeyProvInfo)) 


7 Насколько я понимаю опциональный шаг
Код:
BOOL isHProvNeedToFree = false;
  HCRYPTPROV hProv = NULL;
  BOOST_SCOPE_EXIT(&hProv, &isHProvNeedToFree)
	{
		if (hProv && isHProvNeedToFree)
		{
			::CryptReleaseContext(hProv, 0);
		}
	}
  BOOST_SCOPE_EXIT_END;

  CryptAcquireCertificatePrivateKey(pCert, CRYPT_ACQUIRE_SILENT_FLAG,
	                                       NULL,
	                                       &hProv,
	                                       &pCryptKeyProvInfo->dwKeySpec,
	                                       &isHProvNeedToFree))


После этого пароль успешно устанавливается и операция подписи происходит успешно без окна запроса ПИН. При этом кэш ПИН в панели управления CryptoPro не досупен для отчистки.
8
Код:
CRYPT_SIGN_MESSAGE_PARA SigParams;
SigParams.pSigningCert      = pCert;
SigParams.dwFlags           = CRYPT_MESSAGE_SILENT_KEYSET_FLAG;
...
CryptSignMessage(&SigParams,...)

...
::CertFreeCertificateContext(pCert);


Однако, ПИН похоже каким-то образом кэшируется и не совсем понятно как? После перезагрузки PC или приложения (даже без флага CRYPT_ACQUIRE_CACHE_FLAG в CryptAcquireCertificatePrivateKey), при повторном вызове (1-3)(8) ПИН не запрашивается.
В документации на CryptSignMessage сказано, что он дергает CryptAcquireContext используя CRYPT_KEY_PROV_INFO из pCert сертификата, и должен кэшировать хэндл провайдера только при передаче в функцию флагов CERT_SET_KEY_CONTEXT_PROP_ID, CERT_SET_KEY_PROV_HANDLE_PROP_ID.
При выставлении флага CRYPT_ACQUIRE_CACHE_FLAG в CryptAcquireCertificatePrivateKey в вызове перед CryptSignMessage хэндл должен кэшироваться только до момента освобождения CertFreeCertificateContext, однако этого тоже не проиходит. Следующий вызов CryptSignMessage после CertFreeCertificateContext и затем (1-3) также не требует ПИН.
По результатам экспериментов кэш ПИН можно сбросить вызовом (1-7) с неправильным pin.

Закрытая часть хранится в реестре, если это важно.

Подскажите, где этот кэш ПИН в итоге хранится? Его нельзя сбросить через панель КриптоПро, он по идее не должен сохраниться после CertSetCertificateContextProperty т.к. контекст не персистентен после CertCloseStore (3) ? Остается, что только CryptSignMessage может его куда-то сохранить? Куда?
Что можно сделать, чтобы заставить CryptSignMessage не использовать закэшированный ПИН на вызовах после первого?
Этот процесс работы с кэш ПИН можно регулировать через панель управления КриптоПро или например через комбинацию каких-то других флагов в процессе (1-8)?

Отредактировано пользователем 30 марта 2021 г. 11:32:46(UTC)  | Причина: Не указана

Offline two_oceans  
#2 Оставлено : 1 апреля 2021 г. 11:25:46(UTC)
two_oceans

Статус: Эксперт

Группы: Участники
Зарегистрирован: 05.03.2015(UTC)
Сообщений: 1,602
Российская Федерация
Откуда: Иркутская область

Сказал(а) «Спасибо»: 110 раз
Поблагодарили: 393 раз в 366 постах
Автор: trunovsergey Перейти к цитате
по идее не должен сохраниться после CertSetCertificateContextProperty т.к. контекст не персистентен после CertCloseStore (3)?
Добрый день. Если кратко, то флаг в CertCloseStore для такого сценария нужен нулевой.

Отредактировано пользователем 1 апреля 2021 г. 11:44:35(UTC)  | Причина: Не указана

thanks 1 пользователь поблагодарил two_oceans за этот пост.
trunovsergey оставлено 02.04.2021(UTC)
Offline trunovsergey  
#3 Оставлено : 1 апреля 2021 г. 19:28:50(UTC)
trunovsergey

Статус: Участник

Группы: Участники
Зарегистрирован: 19.09.2019(UTC)
Сообщений: 13
Российская Федерация
Откуда: Москва

Сказал(а) «Спасибо»: 4 раз
Спасибо, похоже придется делать копию контекста сертификата.
CertCloseStore с флагом 0 имеет такой же эффект как и с CERT_CLOSE_STORE_CHECK_FLAG. И это странно т.к. в документации для флага 0:
Цитата:
By default, memory used to store contexts with reference count greater than zero is not freed when a certificate store is closed. References to those contexts remain valid; however, this can cause memory leaks. Also, any changes made to the properties of a context after the store has been closed are not persisted.

Для CERT_CLOSE_STORE_CHECK_FLAG также кажется, что подразумевается то же самое:
Цитата:
When this flag is set, and all certificate, CRL, or CTL contexts have not been released, the function returns FALSE and GetLastError returns CRYPT_E_PENDING_CLOSE. Note that the store is still closed when FALSE is returned and the memory for any active contexts is not freed

т.е. store должен все равно закрыться, но возможно это произойдет не сразу? С учетом того, что в контексте сертификата также хранится указатель на store, видимо полное закрытие возможно, когда все контексты будут закрыты? Иначе выходит нарушается
Цитата:
Also, any changes made to the properties of a context after the store has been closed are not persisted.
Offline two_oceans  
#4 Оставлено : 2 апреля 2021 г. 7:19:27(UTC)
two_oceans

Статус: Эксперт

Группы: Участники
Зарегистрирован: 05.03.2015(UTC)
Сообщений: 1,602
Российская Федерация
Откуда: Иркутская область

Сказал(а) «Спасибо»: 110 раз
Поблагодарили: 393 раз в 366 постах
Автор: trunovsergey Перейти к цитате
Спасибо, похоже придется делать копию контекста сертификата.
Слово "пересоздание" подходит наверно больше для алгоритма "закрыть-создать", так как еще можно "копировать" вызовом CertDuplicateContext. Вот только реально копирования в CertDuplicateContext не происходит, просто увеличивается счетчик ссылок на контекст сертификата и возвращается тот же указатель, никаких изменений связи с хранилищем не происходит.
Автор: trunovsergey Перейти к цитате
Для CERT_CLOSE_STORE_CHECK_FLAG... store должен все равно закрыться, но возможно это произойдет не сразу? ... видимо полное закрытие возможно, когда все контексты будут закрыты?
Похоже что так. Я понимаю так (пусть меня поправят если ошибаюсь): хэндл самого хранилища HSTORE закроется для текущего процесса (CRYPT_E_PENDING_CLOSE... Note that the store is still closed when FALSE is returned), но и сам контекст "жив" и связь контекста сертификата с хранилищем сохраняется (то есть "где-то за кадром" хранилище все еще не выгружено из памяти, хотя и недоступно напрямую).
Offline trunovsergey  
#5 Оставлено : 17 мая 2021 г. 15:43:17(UTC)
trunovsergey

Статус: Участник

Группы: Участники
Зарегистрирован: 19.09.2019(UTC)
Сообщений: 13
Российская Федерация
Откуда: Москва

Сказал(а) «Спасибо»: 4 раз
Коллеги, похоже тут есть нюансы такого подхода с рутокенами. При пересоздании контекста pCert без привязки к хранилищу:
Цитата:
получить данные сертификата (это поля pbCertEncoded/cbCertEncoded в памяти, на которую указывает контекст pCert); данные ссылки на контейнер по идее получены в шаге 4; закрыть контекст CertFreeCertificateContext (что вызовет реальное выполнение отложенного на шаге 3 закрытия хранилища CertCloseStore). Далее создать новый контекст из данных сертификата CertCreateCertificateContext; проставить ссылку на контейнер (что по идее сделает шаг 6).


Пин все также кэшируется и при тестировании закрытой части по сертификату запрос на ввод ПИН отсутствует, при этом при работе с закрытыми частями в реестре все работает.

Пересоздаем контекст сертификата так:

Код:
CertGetCertificateContextProperty(pCert,
		                                      CERT_KEY_PROV_INFO_PROP_ID,
		                                      pCryptKeyProvInfo,
		                                      &szData)

		newPcert = CertCreateCertificateContext(PKCS_7_ASN_ENCODING | X509_ASN_ENCODING ,
		                                        pCert->pbCertEncoded, pCert->cbCertEncoded);
		                                   
               CertFreeCertificateContext(pCert);


Далее устанавливаем ПИН в новый контекст сертификата:
Код:
DWORD keyType = pCryptKeyProvInfo->dwKeySpec==AT_SIGNATURE ? PP_SIGNATURE_PIN: PP_KEYEXCHANGE_PIN;

		CRYPT_KEY_PROV_PARAM key_prov_param;
		memset(&key_prov_param, 0, sizeof(CRYPT_KEY_PROV_PARAM));
		key_prov_param.dwFlags = 0;
		key_prov_param.dwParam = keyType;
		key_prov_param.cbData  = static_cast<DWORD>(pin.size()+1);
		key_prov_param.pbData  = (BYTE *)pin.c_str();

		pCryptKeyProvInfo->cProvParam = 1;
		pCryptKeyProvInfo->rgProvParam = &key_prov_param;

		CertSetCertificateContextProperty(newPcert, CERT_KEY_PROV_INFO_PROP_ID,
		                                  0, pCryptKeyProvInfo);
		


Пересозданный контекст сертификата newPcert явно не добавляется в хранилище и после использования только освобождается.
Пробовал вызывать CertSetCertificateContextProperty с CERT_SET_PROPERTY_INHIBIT_PERSIST_FLAG, что по идее явно говорит провайдеру не кэшировать изменения контекста.
"If CERT_SET_PROPERTY_INHIBIT_PERSIST_FLAG is set, any context property set is not persisted.", однако, эффект такой же - ПИН кэшируется.

Проверил как происходит тот же процесс, только через панель управления КриптоПро 5.0.11635 КС1:
1. Сбросил ПИН через утилиту рутокена.
2. В панели КриптоПро при тестировании контейнера указал ПИН и согласился сохранить в системе.
3. Сбросил ПИН в панели КриптоПро Сервис->Удалить сохраненные пароли
4. Последующее тестирование контейнера не требует ПИН

Не совсем понятно, почему при той же последовательности действий работа с ПИН закрытой части на рутокене отличается от работы с закрытой закрытой частью в реестре и через панель КриптоПро и через наш код?
Является ли поведение со сбросом ПИН через панель КриптоПро при работе с рутокеном ошибкой?

Отредактировано пользователем 17 мая 2021 г. 19:15:03(UTC)  | Причина: Не указана

Offline two_oceans  
#6 Оставлено : 18 мая 2021 г. 6:25:55(UTC)
two_oceans

Статус: Эксперт

Группы: Участники
Зарегистрирован: 05.03.2015(UTC)
Сообщений: 1,602
Российская Федерация
Откуда: Иркутская область

Сказал(а) «Спасибо»: 110 раз
Поблагодарили: 393 раз в 366 постах
Добрый день.
Код вроде бы верный, разве что я бы все же (на всякий случай) выгрузил данные сертификата в отдельную переменную, а не "прикуривал один от другого". Хотя... так наверно удобнее сравнивать не указывают ли они на один адрес. С контейнером в реестре теперь спрашивает пин-код? Странное поведение, выскажу свои предположения.
Цитата:
Коллеги, похоже тут есть нюансы такого подхода с рутокенами.
Вспоминается, что с какой-то версии КриптоПро пробует ввести стандартные пароли (12345678 1234567890 9876543210 и т.д.) на токен автоматически. На токене пин-код ставится как правило на весь токен, а не конкретно на контейнер. Сначала пробуется стандартный, если он подошел, то у пользователя не спрашивает пин-код. Поэтому токены со стандартным пин-кодом не являются показателем кэшируется ли пин-код. Интересно как именно было реализовано "Сбросил ПИН через утилиту рутокена" (поменяли на нестандартный?)

С какой целью вообще сохраняли пароль при тестировании? Или с реестром сохранение-удаление пин-кода не вызывает проблемы? Есть ли зависимость от времени между удалением сохраненного пин-кода и проверкой запросит ли пин-код? Что указано на вкладке Безопасность? Использовать службу хранения ключей? Включить кэширование?


Еще предположение, что какое-то время сохраняется сессия связи с токеном, а без установки новой сессии не запрашивается пин-код повторно? Суть в том, что канал связи с токеном (в отличие от флешки/реестра) защищается специальным алгоритмом от вмешательства извне в передаваемые токену и от токена данные (конечно если есть техническая возможность для модели токенов). Если токен вытащить, некоторое время подождать (перебрать разный интервал от 5 секунд до получаса, например) и снова подключить - все равно не спросит пин-код?
Offline trunovsergey  
#7 Оставлено : 19 мая 2021 г. 12:33:30(UTC)
trunovsergey

Статус: Участник

Группы: Участники
Зарегистрирован: 19.09.2019(UTC)
Сообщений: 13
Российская Федерация
Откуда: Москва

Сказал(а) «Спасибо»: 4 раз
Цитата:
С контейнером в реестре теперь спрашивает пин-код?

Да, в случае с реестром пароль запрашивается каждый раз, к чему и стремимся со всеми видами контейнеров.

Цитата:
Вспоминается, что с какой-то версии КриптоПро пробует ввести стандартные пароли (12345678 1234567890 9876543210 и т.д.) на токен автоматически.

Очень интересное поведение... В рутокенах есть юзер пароль (не стандартный), а есть администратора (стандартный). При тестировании контейнера через панель КриптоПро первый раз пароль все-таки запрашивается. Значит все-таки попытка подстановки стандартного пароля, если таковая производится не прошла.

Цитата:
Интересно как именно было реализовано "Сбросил ПИН через утилиту рутокена" (поменяли на нестандартный?)

Да, меняем, например, на 111111 с 1111 юзер пароль.

Цитата:
С какой целью вообще сохраняли пароль при тестировании?

Пытались проверить как сам КриптоПро делает операцию сохранения ПИН для рутокена и сравнить с поведением реализуемым нашим кодом. Поведение показалось идентичным. ПИН первый раз устанавливается и далее не запрашивается, даже если его удалить через панель КриптоПро.

Цитата:
Или с реестром сохранение-удаление пин-кода не вызывает проблемы?

С реестром все работает ожидаемо. ПИН сбрасывается.

Цитата:
Что указано на вкладке Безопасность? Использовать службу хранения ключей? Включить кэширование?

Нет, мы службу хранения и кэширование явно не используем. Стоят галки "Хранить ключи в приложении" и "Усиленый контроль использования ключей". Приложение для тестирования, перезапускалось после установки ПИН и не должно было бы сохранить ПИН в кэш. Во всяком случае, этот же код для контейнеров в реестре не сохраняет ПИН нигде.

Основная проблема в том, что один и тот же код для реестра и для рутокенов работает по-разному. А хотелось бы, чтобы работало одинаково (ПИН не сохранять нигде). Решение перевтыкать токен конечно потенциально тоже решение (можно пробовать даже делать это программно и как-то автоматизировать), но если есть какое-то решение только в рамках CAPI (на первый взгляд можно только явно сбрасывать ПИН в handle провайдера для рутокенов?)
Код:
SetProvParam(hProv, PP_DELETE_SAVED_PASSWD, 0, 0); 
Но опять же, если панель КриптоПро делает так же и ждать сброса некое время - не очень чистое решение, мягко говоря.

Отредактировано пользователем 19 мая 2021 г. 15:47:54(UTC)  | Причина: Не указана

Offline trunovsergey  
#8 Оставлено : 19 мая 2021 г. 13:22:34(UTC)
trunovsergey

Статус: Участник

Группы: Участники
Зарегистрирован: 19.09.2019(UTC)
Сообщений: 13
Российская Федерация
Откуда: Москва

Сказал(а) «Спасибо»: 4 раз
UPD: при тестировании контейнера с Рутокена через панель КриптоПро, после первого ввода ПИН, даже без галки "Сохранить пароль в системе" пароль все-равно сохраняется. Дальнейшие тестирование этого контейнера пароль больше не требует! Удалить пароль через панель КриптоПро нельзя, спустя 2ч. пароль также не требуется.
Как это вообще может быть?

Отредактировано пользователем 19 мая 2021 г. 15:41:31(UTC)  | Причина: Не указана

Offline two_oceans  
#9 Оставлено : 20 мая 2021 г. 5:51:56(UTC)
two_oceans

Статус: Эксперт

Группы: Участники
Зарегистрирован: 05.03.2015(UTC)
Сообщений: 1,602
Российская Федерация
Откуда: Иркутская область

Сказал(а) «Спасибо»: 110 раз
Поблагодарили: 393 раз в 366 постах
Цитата:
Основная проблема в том, что один и тот же код для реестра и для рутокенов работает по-разному.
Интересно, значит и правда дело в токене.
Цитата:
Решение перевтыкать токен конечно потенциально тоже решение (можно пробовать даже делать это программно и как-то автоматизировать), но если есть какое-то решение только в рамках CAPI ...
Выше я предложил "для отладки" проверить сработает ли как требуется если "перевтыкать" токен. Если "внезапно" работает, то высока вероятность что дело не в самом API, а именно во взаимодействии между криптопровайдером и токеном. По такой узкой теме лучше спрашивать достоверные пояснения или у отдела по работе с ФКН КриптоПро (на форуме Агафьин Сергей) или у производителей рутокена.

Еще наверно поможет указать версию КриптоПро, операционную систему (windows или *nix), модель рутокена, версию и дату драйверов рутокена. Работа с токенами менялась в последних версиях криптопровайдера, в частности, было упоминание на форуме, что в новейшей версии теперь полноценная работа через PKCS#11 (с Рутокеном ЭЦП 2.0).

Вообще я не вижу как на windows само извлечение токена можно сделать программно - при вызове функции безопасного излечения USB устройство переходит в состояние "ожидает извлечения" пока его физически не отсоединят-присоединят, поиск нового оборудования его игнорирует. Где-то говорилось, что можно убрать устройство (не отключая физически) из списка смарт-карт (из-за ограничения windows на количество перечисляемых смарт-карт при большом количестве токенов видны не все), потом вернуть, но не знаю применимо ли для разрыва связи.
thanks 1 пользователь поблагодарил two_oceans за этот пост.
trunovsergey оставлено 21.05.2021(UTC)
RSS Лента  Atom Лента
Пользователи, просматривающие эту тему
Быстрый переход  
Вы не можете создавать новые темы в этом форуме.
Вы не можете отвечать в этом форуме.
Вы не можете удалять Ваши сообщения в этом форуме.
Вы не можете редактировать Ваши сообщения в этом форуме.
Вы не можете создавать опросы в этом форуме.
Вы не можете голосовать в этом форуме.