23.10.2007 16:47:03Получение открытого ключа из контейнера Ответов: 20
KDA
возможно ли без знания пин-кода? CryptExportKey(... PUBLICKEYBLOB ...)
и CryptExportPublicKeyInfo его требуют.
 
Ответы:
24.10.2007 17:53:39Василий
Если Вы используете "КриптоПро CSP" версии 3.0 или выше, то ПИН для доступа к открытому ключу не нужен.
24.10.2007 18:14:37KDA
Спасибо. По информации из панели управления - CSP KC1 3.0.3300.2. Пин-код при вызовах этих функций на свежесозданные контейнеры запрашивается. Может, где-то что-то застряло от предыдущих установок CSP 2.0. Как посмотреть, где и что не так?
24.10.2007 19:10:10Василий
Ваша правда, неправильно я написал.
Без ПИН-кода можно добыть сертификат, если он есть в ключевом контейнере (CryptGetKeyParam, KP_CERTIFICATE). А уже потом из него взять открытый ключ.
Вот пример:

HCRYPTPROV hCertContainer = 0;
HCRYPTKEY hKey, hPubKey = 0;
LPBYTE pbUserCert;

DWORD dwCertLen;
PCCERT_CONTEXT pUserCert=0;
DWORD dwUserCertLength=0;




if(!CryptAcquireContext(&hCertContainer,
"Cont1",
NULL,
75,
0
))
HandleError("CryptAcquireContext");

if(!CryptGetUserKey(
hCertContainer,
AT_KEYEXCHANGE,
&hKey))

HandleError("CryptGetUserKey");

/* Получить сертификат.*/
if (!CryptGetKeyParam (hKey, KP_CERTIFICATE, NULL,
&dwUserCertLength, 0)) {
HandleError ("Error during GetKeyParam.\n");

}
pbUserCert = malloc (dwUserCertLength);
if (pbUserCert == NULL) {
HandleError ("Error during malloc.\n");

}
if (!CryptGetKeyParam (hKey, KP_CERTIFICATE, pbUserCert,
&dwUserCertLength, 0)) {
HandleError ("Error during GetKeyParam.\n");

}
/* Декодировать сертификат */
pUserCert = CertCreateCertificateContext (
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pbUserCert,
dwUserCertLength);
if (pUserCert == NULL) {
HandleError ("Error during CertCreateCertificateContext.\n");

}
/* получаем хендл открытого ключа */
if (!CryptImportPublicKeyInfoEx (
hCertContainer,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
&(pUserCert->pCertInfo->SubjectPublicKeyInfo),
0,
0,
NULL,
&hPubKey))
HandleError ("Error during CryptImportPublicKeyInfoEx.\n");

...

CryptReleaseContext(hCertContainer,0);
24.10.2007 19:18:02KDA
К сожалению, это несколько не то: стоит обратная задача - по сертификату найти соотвествующий ему контейнер (а физически сертификат в контейнер не помещается).
Да и сертификат, как показывает практика, в контейнер может быть установлен любой.
24.10.2007 21:16:46Василий
> Да и сертификат, как показывает практика, в контейнер может быть установлен любой.
Штатными средствами это невозможно, только прямым вызовом CryptSetKeyParam с параметром KP_CERTIFICATE.

По существу Вашей задачи - непонятно, сертификат берётся из хранилища или нет. Если первое - то там есть ссылка на контейнер, т.е. имя контейнера получить можно. Если второе - вряд ли вообще существует решение, т.к. даже при переборе всех контейнеров можно не найти нужный, поскольку он может быть на неподключенном съёмном носителе.






24.10.2007 22:40:11maxdm
Эта задача частично решена в продукте КриптоПро Winlogon и полностью решена в КриптоПро CSP, открытый ключ в этом случае берется из сертификата в контейнере (если последний существует). Соответствие закрытому ключу контролируется по контрольной сумме. Открытого ключа в чистом виде как не было в контейнере, так и нет, поскольку его хранение избыточно.
25.10.2007 10:09:10KDA
>По существу Вашей задачи - непонятно, сертификат берётся из хранилища или нет.

Хранилище есть, но свое (общая для всех пользователей БД). Имя контейнера значения не имеет, поскольку это вполне может быть и копия оригинального контейнера с другим именем. Посему вопрос остается открытым.
25.10.2007 10:37:39Kirill Sobolev
Можно узнать поподробнее про функционирование системы? Возможно, есть какие-то обходные пути.
Если хранилище общее для всех пользователей - то ключи у пользователей на съемных носителях?
25.10.2007 12:57:38Василий
Поддерживаю вопрос Кирилла.

Давайте уточним саму постановку задачи.

Вот есть сертификат. Можно делать все операции, не требующие закрытого ключа (например, проверять ЭЦП документа). Вы формулируете такую задачу - узнать, какой ключевой контейнер соответствует этому сертификату, причём, без ввода пароля (ПИН-кода). Собственно, вопрос - а зачем? Если для того, чтобы в дальнейшем использовать секретный ключ из этого контейнера - то по-любому нужно будет вводить пароль или ПИН.
Допустим, Вы рассматриваете ситуацию, когда нельзя запоминать имя контейнера по причине того, что может быть сделана копия контейнера. Тогда к контейнеру по имени обращаться нельзя и вопрос в диалоге для пользователя будет звучать так: "Вставьте какой-нибудь носитель с каким-нибудь контейнером ключа, вдруг ключ в нём соответствует данному сертификату" - всё это очень странно.
25.10.2007 14:55:39KDA
Вводить пин-код, действительно, надо будет. Но тогда, когда нужно и только к нужному контейнеру. Требование заказчика, увы. Пользователь заходит в систему по обычному логину-паролю. Когда требуется операция, требующая персонального ключа - запускается поиск "подходящего" контейнера (сертификаты в БД имеют привязку к пользователю). Если не нашли, то отклонили текущую операцию, если нашли - спросили пин на конкретный контейнер и воспользвались им. Кажется, такая схема вполне имеет право на существования. Ключевое, повторюсь: имя контейнера неизвестно, сертификата в контейнере нет, каждый контейнер на носителе может иметь свой собственный пин(пароль). Ну и минимум запросов к пользователю.
25.10.2007 15:39:03Kirill Sobolev
Вы хотите быть хитрее чем MS :) У них большая часть криптографии построена на том, что сертификат, установленный в системе, имеет ссылку на секретный ключ либо не имеет этого ключа вообще. А Вы хотите это выкинуть, не потеряв при этом в функциональности. Устанавливайте сертификат в контейнер и Ваша схема будет как-то работать. Кстати о таком моменте Вы подумали - по сертификату нельзя узнать ни имя провайдера, ни тип, ни тип ключа - как Вы это будете получать? При каждом обращении перебирать все установленные в системе провайдеры?
25.10.2007 16:13:56KDA
Нет, я всего лишь хочу как-то получить открытый ключ, который, несмотря на то, что он открытый, все равно требует пин-кода :)
И какой функциональности идет речь? О невозможности без установленного в системе сертификата подписать или проверить подпись? Так это все реализовано и работает. Большего и не надо. Так что выкидывать, по сути, нечего. А по поводу установки сертификата - ранее я написал, что сертификат физически не помещается. Размер больше 2К, носитель - DS 1995.
25.10.2007 16:44:56Kirill Sobolev
Только Вы его хотите получить оттуда, где у Вас места нет для его хранения.
При этом хранить базу сертификатов как делает MS, с ссылками на контейнер, Вы тоже не хотите. Очень сильные ограничения на решение получаются.
25.10.2007 17:09:37KDA
>Только Вы его хотите получить оттуда, где у Вас места нет для его хранения.
Простите, контейнер-то там помещается, и даже два, так что места именно под то, что я хочу получить, там все-таки есть :)
>При этом хранить базу сертификатов как делает MS, с ссылками на контейнер, Вы тоже не хотите.
Скорее, не можем, система уже работает.
К вопросу об ограничениях - невозможность без лишних телодвижений использовать на одном компьютере контейнеры с одинаковыми именами но с разных носителей (и копия с тем же именем на другой носитель) - это тоже оно :)
А гарантий уникальности имени нет никаких.
25.10.2007 17:41:28Kirill Sobolev
Вы недавно писали, что места физически нет - а теперь оно есть?
Без сертификата ОК в контейнере смысла не имеет, это действительно избыточная информация.
25.10.2007 18:26:00KDA
Давайте не будем играть словами: я писал, что места нет под сертификат, а не под контейнер. Наверное, это все-таки разные вещи.
>Без сертификата ОК в контейнере смысла не имеет, это действительно избыточная информация.
Запрос сертификата ОК без "сертификата смысла не имеет"? И предназначенные, в частности, для этого, функции КриптоАпи из первого поста, реализованные в вашем продукте и работающие без сертификата, тоже? Наверное, все-таки, не более избыточная, чем сам сертификат. И базовые операции, вроде проверки подписи или шифрования сертификата все-таки не требуют :)
Спасибо за консультацию.
25.10.2007 21:52:44Василий
Я предлагаю такой вариант.
Раз Вы не хотите использовать имена контейнеров хотя все остальные используют, и не жалуются :-) и не хотите, чтобы спрашивались пароли на контейнеры - то и не ставьте пароли на контейнеры.
Тогда достаточно будет самого факта предъявления таблетки в нужный момент.
26.10.2007 9:40:25KDA
Спасибо, такой вариант рассматривался. Однаку у пользователя есть возможность создания контейнера самостоятельно (непосредственно из программы).
Каким образом можно программно создать контейнер с заданным (или пустым) паролем? Не показывая запрос пароля пользователю?
26.10.2007 11:07:27Василий
Программно - легко.
Достаточно установить пароль в явном виде, тогда он не будет спрашиваться у пользователя.
Ф-я CryptSetProvParam, параметр PP_KEYEXCHANGE_PIN:

HCRYPTPROV hProv=0;
HCRYPTKEY hKey=0;
char* pass = "";

CryptAcquireContext( &hProv,
"Cont1",
NULL,
75,
CRYPT_NEWKEYSET );

CryptSetProvParam(hProv, PP_KEYEXCHANGE_PIN,
(BYTE*)pass, 0);

CryptGenKey( hProv, AT_KEYEXCHANGE, CRYPT_EXPORTABLE, &hKey); //тут пароль уже не спросят
26.10.2007 12:00:56Kirill Sobolev
Пожалуйста.
>> Давайте не будем играть словами: я писал, что места нет под >> сертификат, а не под контейнер. Наверное, это все-
>> таки ,разные вещи.
Я не очень хорошо знаком с устройством DS 1995, там место под сертификаты и под контейнеры физически разное? Если же нет и оно общее - то туда куда влезет 2 контейнера точно влезет контейнер и сертификат (сертификат на 2001 ГОСТЕ в DER занимает 300 байт или меньше).
>> Запрос сертификата ОК без "сертификата смысла не имеет"?
Вопрос не понял. Что значит запрос сертификата без сертификата?
А все функции CryptoAPI, в т.ч. и реализованные в нашем продукте, спроектированы и функционируют так, что для операций, выполняющихся только с участием ОК контейнер не требуется - провайдер инициализируется со специальным флагом CRYPT_VERIFYCONTEXT.
Информация избыточная вот почему - имея контейнер с ключем, Вы никак не получите сертификат, который этому контейнеру соответствует, но легко можете вычислить ОК. Поэтому ОК в контейнере и не хранят - собственно, пользоватеь свой ОК редко использует, разве что ведет секретный архив.