09.03.2004 15:37:30Неправильная работа с памятью при экспорте ключа Ответов: 8
Ivan Ravin
Уважаемые разработчики! Жизнь заставляет отказываться от провайдеров MS и переходить на сертифицированные. Насколько я понимаю, ваш CSP должен соответствовать требованиям Crypto API 2.0. В моем приложениии все проще - используются вызовы Crypto API 1, и все это успешно было опробовано и работает на различных типах провайдеров. Но не Крипто ПРО.
Как оказалось, при экспорте открытого ключа, буфер, отведенный под этот ключ, становится невозможно освободить. Более того, при последующей попытке освободить контекст, происходит аварийное завершение приложения. Вот упрощенный кусок кода:

function ExportKeyToBuff(hProv: HCRYPTPROV;hKey:HCRYPTKEY;
isPrivate:boolean; Pass:string;
FromPoint:pointer):dword;
const
hExpKey: HCRYPTKEY = 0;
dwAlg: DWORD = 0;
dwBlobType: DWORD = 0;
hHash: HCRYPTHASH = 0;
htempKey:HCRYPTKEY = 0;
begin
result:=0;
hExpKey:= 0;
dwAlg:= 0;
dwBlobType:= 0;
hHash:= 0;
try
if isPrivate then begin
dwBlobType:=PRIVATEKEYBLOB;
...
end
else
dwBlobType:=PUBLICKEYBLOB;
result:=$3FFF;
if not CryptExportKey(hKey, hExpKey, dwBlobType, 0, FromPoint, @result)
then
raise Exception.CreateFmt(’Cant export. Error %x’,[getlasterror]);

finally
if hHash<>0 then
CryptDestroyHash(hHash);
if hExpKey<>0 then
CryptDestroyKey(hExpKey);
end;
end;

function ExportKeyToFile(hProv: HCRYPTPROV;hKey:HCRYPTKEY;
isPrivate:boolean; Pass,FileName:string):dword;
// я оставил код для isPrivate = true
var
hFile,hFileMapping:THandle;
FileMap:pointer;
temp:pointer;
begin
result:=0;
// узнаем необходимую длину буфера
result:=ExportKeyToBuff(hProv,hKey,isPrivate,Pass,nil);
// выделяем память
temp:=AllocMem(result);
try
// экспортируем ключ в буфер
result:=ExportKeyToBuff(hProv,hKey,isPrivate,Pass,temp);
...
finally
...
// освобождаем память
FreeMem(temp);
end;
end;

При попытке освободить память (FreeMem(temp)) получаем invalid pointer operation. При последующем освобождении контекста происходит цепочка исключений, приводящих к падению приложения (если оно было скомпилировано в Delphi5), либо к невозможности дальнейшей работы с провайдером (скомпилировано в Delphi7).
Повторю, что провайдеры MS в приведенном куске кода работают абсолютно корректно.
Обнаруженная ошибка происходит если включена опция "хранить ключи в памяти приложений", т.е. установка по умолчанию.
Надеюсь, приведенная информация поможет вам исправить работу провайдера
 
Ответы:
10.03.2004 13:32:19Serge3leo
Здравствуйте,

Нижеследующий кусок кода вызывает сомнения:

dwBlobType:=PUBLICKEYBLOB;
result:=$3FFF;
if not CryptExportKey(hKey, hExpKey, dwBlobType, 0, FromPoint, @result)
10.03.2004 17:00:28Ivan Ravin
Действительно, если подать на вход точное значение кол-ва выделенных байт, ошибки не происходит. Очевидно, csp крипто про при экспорте открытого ключа "пишет" не столько, сколько требуется для клоюча ключ, а сколько задали на входе.
В отличие от других провайдеров (да и от того же крипто про в режиме использования службы хранения ключей).
Возьмем на заметку. Спасибо.
10.03.2004 18:19:14Serge3leo
> Действительно, если подать на вход точное значение кол-ва выделенных байт, ошибки не происходит. Очевидно, csp крипто про при экспорте открытого ключа "пишет" не столько, сколько требуется для клоюча ключ, а сколько задали на входе.

Имеет право, у MS черным по анлийски написано, что "value that specifies the size, in bytes, of the buffer pointed to by the pbData parameter"

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

Вот это странность, на мой взгляд Вы должны эту проблемы при работе с сервисом хранения ключей.
03.08.2004 14:15:45xyz
По нашему мнению, Ваш код неправильно работает с указателями, поскольку ни на одной версии провайдера мы такой ошибки не обнаружили.
24.08.2004 16:00:41Павел
У MS английским по белому написано, что "when the function returns, the DWORD value contains the number of bytes stored in the buffer". Так что на входе pdwDataLen определяет только максимальный размер переданного буфера. Если его недостаточно, Ваша задача вернуть ERROR_MORE_DATA.
24.08.2004 16:41:10Serge3leo
Ошибка ERROR_MORE_DATA должна выдаваться в случае, если CSP требуется буфер размером больше чем *pdwDataLen (в Вашем случае >16383).

Вы же сообщили CSP, что буфер имеет размер 0x3FFF и он может ими пользоваться. Он и пользуется, и размера ему тоже хватает.

pdwDataLen
[in, out] Pointer to a DWORD value that specifies the size, in bytes, of the buffer pointed to by the pbData parameter...

У MS поведение функции "Retrieving Data of Unknown Length" неопределено в случае если, и указатель не нулевой, и длина некорректная.

Что и получается. Я всё правильно понял?
24.08.2004 16:45:21Павел
Все верно, за исключением того, что в MSDN в этом же абзаце написано, что по выходе из функции в переменную по указателю pdwDataLen созраняется длина данных, скопированных в буфер. В данном случае это явно указано и ссылка на "Retrieving Data of Unknown Length" не корректна.
24.08.2004 17:04:02Serge3leo
Согласно приведённому коду по адресу
@result, перед каждым вызовом CryptExportKey сначала записывается 0, а потом 16383.