02.03.2006 14:41:47Проблемы с перечислением контейнеров Ответов: 9
Николай
Добрый день!

Столкнулся со следующей проблемой - не могу получить список имен всех контейнеров для smart card.

Вот код:
LPCSTR ProvName = "Schlumberger Cryptographic Service Provider";

// получаем криптопровайдер
if( !CryptAcquireContext(&hCryptProv, NULL, ProvName, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) )
{
exit(1);
}

// перебираем все контейнеры в нем
DWORD size;
DWORD fParam = CRYPT_FIRST;
while ( CryptGetProvParam(hCryptProv, PP_ENUMCONTAINERS, NULL, &size, fParam) )
{
BYTE* ContName = new BYTE[size];
CryptGetProvParam(hCryptProv, PP_ENUMCONTAINERS, ContName, &size, fParam);
printf( "Name: %s\n", (char*) ContName );
delete[] ContName;
fParam = 0;
}

int err = GetLastError();

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

Вопрос - в чем проблема и как это исправить?
 
Ответы:
02.03.2006 15:20:33maxdm
Читаем документацию
If PP_ENUMCONTAINERS is set, the first call to the function returns the size of the maximum key-container allowed by the current provider. This is in contrast to other possible behaviors, like returning the length of the longest existing container, or the length of the current container. Subsequent enumerating calls will not change the dwLen parameter. For each enumerated container, the caller can determine the length of the null-terminated string programmatically, if desired. If one of the enumeration values is read and the pbData parameter is NULL, the CRYPT_FIRST flag must be specified for the size information to be correctly retrieved.

Кстати у MS тоже ошибка в перечислителе в wlnotify.dll - получают каждый второй контейнер (как и у Вас). И они не торопятся ее исправлять.
02.03.2006 15:56:11Николай
maxdm
Документацию-то я читал - однако не понял причем тут моя проблема. Первый получаю с флагом CRYPT_FIRST, дальнейшие просто вытаскиваем...

Что-то тут не то. Решение проблемы должно быть.
02.03.2006 16:29:58maxdm
Читаем документацию внимательнее.
Запущено два цикла перечислителя
while ( CryptGetProvParam(hCryptProv, PP_ENUMCONTAINERS, NULL, &size, fParam) ) - первый
{
BYTE* ContName = new BYTE[size];
CryptGetProvParam(hCryptProv, PP_ENUMCONTAINERS, ContName, &size, fParam); - второй
printf( "Name: %s\n", (char*) ContName );
delete[] ContName;
fParam = 0;
}

должно быть так
fParam = CRYPT_FIRST;
CryptGetProvParam(hCryptProv, PP_ENUMCONTAINERS, NULL, &size, fParam);
BYTE* ContName = new BYTE[size];
while ( CryptGetProvParam(hCryptProv, PP_ENUMCONTAINERS, ContName, &size, fParam) )
{
printf( "Name: %s\n", (char*) ContName );
fParam = 0;
}
02.03.2006 16:41:16Николай
maxdm Вы не правы :)
В первом случае - получаем размер, который необходимо выделить под строку для названия криптопровайдера, поотм выделяем память, во втором вызове считываем имя, выводим на экран и удаляем...
02.03.2006 17:48:36Николай
Итак, после недолгих проб получил потрясающий результат. Поехали....

Используя пример из MSDN получаю список всех криптопровайдеров. Далее, для каждого из полученных криптопровайдеров, перечисляю их контейнеры. Код привожу ниже (без проверки на ошибки и т.п. - чтобы места меньше занимало):

while( CryptEnumProviders(dwIndex, NULL, 0, &dwType, NULL, &cbName) )
{
CryptEnumProviders(dwIndex++, NULL, 0, &dwType, (LPSTR)provName, &cbName);
printf ("Provider Name: %s\n Name list for available container:\n", (char*)provName );

// получаем криптопровайдер
if( !CryptAcquireContext(&hCryptProv, NULL, (char*) provName, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT) )
continue;

// перебираем все контейнеры в нем
DWORD size = 1000;
BYTE ContName[1000];
DWORD fParam = CRYPT_FIRST;
DWORD cnt = 0;

while ( CryptGetProvParam(hCryptProv, PP_ENUMCONTAINERS, ContName, &size, fParam) )
{
fParam = 0;
printf( " container name: %s\n", (char*) ContName );
cnt++;
}

printf("Total containers: %d.\n", cnt);

CryptReleaseContext(hCryptProv,0);
printf("\n\n\n");
} // End while loop.

Получилось, что для еТокена я вижу 4 контейнера, для всех микрософтовских контейнеров - 53 штуки и для смарт карты - 1.

Меняем алгоритм перечисления контейнеров:
while ( CryptGetProvParam(hCryptProv, PP_ENUMCONTAINERS, NULL, &size, fParam) )
{
// можно динамически выделить память под имя контейнера равную size
CryptGetProvParam(hCryptProv, PP_ENUMCONTAINERS, ContName, &size, fParam);
fParam = 0;
...

Получаю такой результат:
еТокен - 7 контейнеров
Микрософтовых - 27 контейнеров
смарт-карта - 1 контейнер...

Мне точно известно, что контейнеров на еТокене 7 (смотрим через утилиту еТокена). Микрософтовых мне думается все-таки реально 53 стоит. А вот как понять сколько реально на смарт карте - я не знаю. Их там точно минимум два (могу выполнять операции проставления ЭЦП двумя разными ключами с нее).

Похоже, что счетчики количества перечисляемых контейнеров в каждом криптопровайдере работают по-своему. Я не смог найти в Интернете нормальной базы знаний по таким вопросам. Буду рад любой помощи.

Задача все та же - правильно получить список всех контейнеров с микрософтового, еТокеновского и смарт-карточного (Шлембурже) криптопровайдеров.
02.03.2006 18:54:46maxdm
Я на это уже натыкался - и писал в MS - безрезультатно. Наш провайдер реализует первый вариант, чтобы быть похожим на MS.
03.03.2006 11:47:23Василий
А почему в
CryptAcquireContext(&hCryptProv, NULL, (char*) provName, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)
используется константа PROV_RSA_FULL, а не dwType, полученный на предыдущем шаге?
Что будет, если поменять?
03.03.2006 12:00:15Николай
Добрый день!

Поменял - теперь корректно стало отрабатываться для типов криптопровайдеров не PROV_RSA_FULL. Раньше при выполнении функции CryptAcquireContext выдавалась ошибка.

Но по сути - это никак не повлияло на стоящую передо мной проблему. Как работало криво с разными криптопровайдерами, так и работает. Количество контейнеров, демонстрируемое при работе приложения, все также различается в зависимости от метода получения контейнеров при перечислении (с двумя вызовами или с одним).

Какие еще есть предложения по решению данного вопроса?
03.03.2006 13:07:31Василий
По поводу eToken CSP - обратитесь с вопросом к производителю этого оборудования и ПО. Это компания "Аладдин", http://www.aladdin.ru
Интересно услышать их мнение.