http://www.delphijs.net/Article_Show.asp?ArticleID=489 function CryptAcquireContext ( phPROV: PHCRYPTPROV; pszContainer: LPCTSTR; pszProvider: LPCTSTR; dwProvType: DWORD; dwFlags: DWORD): BOOL; stdcall;function CryptAcquireContext; external ‘advapi32.dll’ name 'CryptAcquireContextA'; if not CryptGetProvParam(hProv, PP_VERSION, (@vers), @DataLen, 0) then begin case int64(GetLastError) of ERROR_INVALID_HANDLE: err := 'ERROR_INVALID_HANDLE'; ERROR_INVALID_PARAMETER: err := 'ERROR_INVALID_PARAMETER'; ERROR_MORE_DATA: err := 'ERROR_MORE_DATA'; ERROR_NO_MORE_ITEMS: err := 'ERROR_NO_MORE_ITEMS'; NTE_BAD_FLAGS: err := 'NTE_BAD_FLAGS'; NTE_BAD_TYPE: err := 'NTE_BAD_TYPE'; NTE_BAD_UID: err := 'NTE_BAD_UID'; else err := 'Unknown error'; end; MessageDlg('Error of CryptGetProvParam: ' + err, mtError, [mbOK], 0); exit end;type algInfo = record algID: ALG_ID; dwBits: DWORD; dwNameLen: DWORD; szName: array[0..100] of char; end; {вспомогательная функция, преобразующая тип провайдера в строку} function ProvTypeToStr(provType: DWORD): string; begin case provType of PROV_RSA_FULL: ProvTypeToStr := 'RSA full provider'; PROV_RSA_SIG: ProvTypeToStr := 'RSA signature provider'; PROV_DSS: ProvTypeToStr := 'DSS provider'; PROV_DSS_DH: ProvTypeToStr := 'DSS and Diffie-Hellman provider'; PROV_FORTEZZA: ProvTypeToStr := 'Fortezza provider'; PROV_MS_EXCHANGE: ProvTypeToStr := 'MS Exchange provider'; PROV_RSA_SCHANNEL: ProvTypeToStr := 'RSA secure channel provider'; PROV_SSL: ProvTypeToStr := 'SSL provider'; else ProvTypeToStr := 'Unknown provider'; end; end; {вспомогательная функция, преобразующая тип реализации в строку} function ImpTypeToStr(it: DWORD): string; begin case it of CRYPT_IMPL_HARDWARE: ImpTypeToStr := 'аппаратный'; CRYPT_IMPL_SOFTWARE: ImpTypeToStr := 'программный'; CRYPT_IMPL_MIXED: ImpTypeToStr := 'смешанный'; CRYPT_IMPL_UNKNOWN: ImpTypeToStr := 'неизвестен'; else ImpTypeToStr := 'неверное значение'; end; end; {процедура вывода информации о криптопровайдерах} procedure TMainForm.InfoItemClick(Sender: TObject); var i: DWORD; dwProvType, cbName, DataLen: DWORD; provName: array[0..200] of char; vers: array[0..3] of byte; impType: DWORD; ai: algInfo; err: string; begin i:= 0; FileMemo.Clear; while (CryptEnumProviders(i, nil, 0, {проверяем наличие еще одного} @dwProvType, nil, @cbName)) do begin if CryptEnumProviders(i, nil, 0, {получаем имя CSP} @dwProvType, @provName, @cbName) then begin FileMemo.Lines.Add('Криптопровайдер: '+provName); FileMemo.Lines.Add('Тип: '+IntToStr(dwProvType)+' - '+ ProvTypeToStr(dwProvType)); if not CryptAcquireContext(@hProv, nil, provName, dwProvType, CRYPT_VERIFYCONTEXT) then begin {обработка ошибок} end; DataLen := 4; if not CryptGetProvParam(hProv, PP_VERSION, (@vers), @DataLen, 0) then begin {обработка ошибок} end; FileMemo.Lines.Add('Версия: ' + chr(vers[1]+) + '.' + chr(vers[0]+)); if not CryptGetProvParam(hProv, PP_IMPTYPE, @impType, @DataLen, 0) then begin {обработка ошибок} end; FileMemo.Lines.Add('Тип реализации: '+ImpTypeToStr(impType)); FileMemo.Lines.Add('Поддерживает алгоритмы:'); DataLen := sizeof(ai); if not CryptGetProvParam(hProv, PP_ENUMALGS, @ai, @DataLen, CRYPT_FIRST) then begin {обработка ошибок} end; with ai do FileMemo.Lines.Add(szName+#9+'длина ключа - '+IntToStr(dwBits)+ ' бит' +#9+ 'ID: '+IntToStr(AlgID)); DataLen := sizeof(ai); while CryptGetProvParam(hProv, PP_ENUMALGS, @ai, @DataLen, 0) do begin with ai do FileMemo.Lines.Add(szName+#9+'длина ключа - ' +IntToStr(dwBits)+' бит'+#9+'ID: '+IntToStr(AlgID)); DataLen := sizeof(ai); end; FileMemo.Lines.Add(''); CryptReleaseContext(hProv, 0); end; inc(i); end; end; hProv: HCRYPTPROV; hash: HCRYPTHASH; password: string; key: HCRYPTKEY; plaintext, ciphertext: string; inFile, outFile: file; data: PByte; l: DWORD;{получаем контекст криптопровайдера} CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT); {создаем хеш-объект} CryptCreateHash(hProv, CALG_SHA, 0, 0, @hash); {хешируем пароль} CryptHashData(hash, @password[1], length(password), 0); {создаем ключ на основании пароля для потокового шифра RC4} CryptDeriveKey(hProv, CALG_RC4, hash, 0, @key); {уничтожаем хеш-объект} CryptDestroyHash(hash); {открываем файлы} AssignFile(inFile, plaintext); AssignFile(outFile, ciphertext); reset(inFile, 1); rewrite(outFile, 1); {выделяем место для буфера} GetMem(data, 512); {шифруем данные} while not eof(inFile) do begin BlockRead(inFile, data^, 512, l); CryptEncrypt(key, 0, eof(inFile), 0, data, @l, l); BlockWrite(outFile, data^, l); end; {освобождаем место и закрываем файлы} FreeMem(data, 512); CloseFile(inFile); CloseFile(outFile); {освобождаем контекст криптопровайдера} CryptReleaseContext(hProv, 0);
procedure TGenerateForm.OKBtnClick(Sender: TObject); var cont: PChar; err: string; hProv: HCRYPTPROV; KeyExchKey, SignKey: HCRYPTKEY; flag, keyLen: DWORD; begin {если ни один ключ не выбран - выход} if not (KEKCheckBox.Checked or SKCheckBox.Checked) then exit; {"считываем" имя контейнера} if length(ContainerEdit.Text) = 0 then cont := nil else begin err := ContainerEdit.Text; cont := StrAlloc(length(err) + 1); StrPCopy(cont, err); end; CryptAcquireContext(@hProv, cont, nil, PROV_RSA_FULL, 0); {генерация ключа обмена ключами (Key Exchange Key)} if KEKCheckBox.Checked then begin {"считываем" длину ключа и помещаем ее в старшее слово параметра ФЛАГИ} keyLen := strtoint(KeyExchLenEdit.text); flag := keyLen shl 16; if not CryptGenKey(hProv, AT_KEYEXCHANGE, flag, @KeyExchKey) then begin јобработка ошибокј end else begin ReportMemo.Lines.Add(''); ReportMemo.Lines.Add('Создан ключ обмена ключами:'); flag := 4; if not CryptGetKeyParam(KeyExchKey, KP_KEYLEN, @keyLen, @flag, 0) then begin јобработка ошибокј end else ReportMemo.Lines.Add(' длина ключа - ' + inttostr(keyLen)); flag := 4; if not CryptGetKeyParam(KeyExchKey, KP_ALGID, @keyLen, @flag, 0) then begin јобработка ошибокј end else ReportMemo.Lines.Add(' алгоритм - ' + algIDtostr(keyLen)); {функция algIDtostr здесь не приводится. Она состоит из единственного оператора case, отображающего целый идентификатор алгоритма в строку} end; end; {генерация ключа подписи (Signature Key)} if SKCheckBox.Checked then begin јвыполняется аналогично генерации ключа обмена ключамиј end; CryptReleaseContext(hProv, 0); end;procedure TExportForm.OKBtnClick(Sender: TObject); var cont: PChar; err: string; hProv: HCRYPTPROV; key, expKey: HCRYPTKEY; pbuf: PBYTE; buflen: DWORD; f: file; hash: HCRYPTHASH; begin {если ни один ключ не выбран - выход} if not (KEKCheckBox.Checked or SKCheckBox.Checked) then exit; {если нужен пароль, т.е. экспортируется ключевая пара целиком} if PasswEdit.Enabled and (PasswEdit.Text <> Passw2Edit.Text) then begin MessageDlg('Ошибка при вводе пароля! Повторите ввод.', mtError, [mbOK], 0); exit; end; … "считываем" имя контейнера и подключаемся к криптопровайдеру … если нужен ключ шифрования - создаем его на основании пароля … {ключ обмена ключами} if KEKCheckBox.Checked then repeat {получаем дескриптор ключа} CryptGetUserKey(hProv, AT_KEYEXCHANGE, @key); {пытаемся определить размер буфера для экспорта ключа} if (WhatRadioGroup.ItemIndex = 0) then CryptExportKey(key, 0, PUBLICKEYBLOB, 0, nil, @bufLen) else CryptExportKey(key, expKey, PRIVATEKEYBLOB, 0, nil, @bufLen); GetMem(pbuf, bufLen); {экспортируем данные} if (WhatRadioGroup.ItemIndex = 0) then CryptExportKey(key, 0, PUBLICKEYBLOB, 0, pbuf, @bufLen) else CryptExportKey(key, expKey, PRIVATEKEYBLOB, 0, pbuf, @bufLen); {освобождаем дескриптор ключа обмена ключами (сам ключ при этом не уничтожается)} CryptDestroyKey(key); SaveDialog1.Title := 'Укажите файл для сохранения ключа обмена ключами'; if SaveDialog1.Execute then begin AssignFile(f, SaveDialog1.FileName); rewrite(f, 1); BlockWrite(f, pbuf^, bufLen); CloseFile(f); MessageDlg('Ключ обмена ключами успешно сохранен', mtInformation, [mbOK], 0); end; until true; {KeyExchange} {ключ подписи} if SKCheckBox.Checked then repeat јаналогично ключу обмена ключамиј until true; {Signature} … если создавался ключ на основании пароля - уничтожаем его, после чего освобождаем контекст криптопровайдера … end;procedure TSigningForm.SignBtnClick(Sender: TObject); var cont: PChar; err: string; hProv: HCRYPTPROV; key: HCRYPTKEY; alg: ALG_ID; hash: HCRYPTHASH; infile, outfile: file; size: DWORD; buf: array [0..511] of byte; signature: PBYTE; begin {проверка существования выбранного файла} if not FileExists(DataNameEdit.Text) then begin MessageDlg('Неверное имя файла!', mtError, [mbOK], 0); exit; end; AssignFile(infile, DataNameEdit.Text); … "считываем" имя контейнера и подключаемся к нему … case HashRadioGroup.ItemIndex of 0: alg := CALG_MD5; 1: alg := CALG_SHA; end; CryptCreateHash(hProv, alg, 0, 0, @hash); SaveDialog1.Title := 'Задайте имя файла для хранения подписанных данных'; if SaveDialog1.Execute then begin AssignFile(outfile, SaveDialog1.FileName); rewrite(outfile, 1); {записываем в файл идентификатор алгоритма хеширования} BlockWrite(outfile, alg, 4); reset(infile, 1); size := FileSize(infile); {записываем размер подписываемых данных} BlockWrite(outfile, size, 4); {пишем сами данные и вычисляем хеш:} while not eof(infile) do begin BlockRead(infile, buf, 512, size); BlockWrite(outFile, buf, size); CryptHashData(hash, @buf, size, 0); end; CloseFile(infile); {выясняем размер подписи} CryptSignHash(hash, AT_SIGNATURE, nil, 0, nil, @size); {создаем подпись} GetMem(signature, size); CryptSignHash(hash, AT_SIGNATURE, nil, 0, signature, @size); BlockWrite(outfile, size, 4); BlockWrite(outfile, signature^, size); CloseFile(outfile); end; … уничтожаем хеш-объект и освобождаем контекст … end;
应用他们的函数 应该不难 我们直接用就是....
function CryptAcquireContext (
phPROV: PHCRYPTPROV;
pszContainer: LPCTSTR;
pszProvider: LPCTSTR;
dwProvType: DWORD;
dwFlags: DWORD): BOOL; stdcall;function CryptAcquireContext; external ‘advapi32.dll’
name 'CryptAcquireContextA'; if not CryptGetProvParam(hProv, PP_VERSION, (@vers), @DataLen, 0)
then
begin
case int64(GetLastError) of
ERROR_INVALID_HANDLE: err := 'ERROR_INVALID_HANDLE';
ERROR_INVALID_PARAMETER: err := 'ERROR_INVALID_PARAMETER';
ERROR_MORE_DATA: err := 'ERROR_MORE_DATA';
ERROR_NO_MORE_ITEMS: err := 'ERROR_NO_MORE_ITEMS';
NTE_BAD_FLAGS: err := 'NTE_BAD_FLAGS';
NTE_BAD_TYPE: err := 'NTE_BAD_TYPE';
NTE_BAD_UID: err := 'NTE_BAD_UID';
else err := 'Unknown error';
end;
MessageDlg('Error of CryptGetProvParam: ' + err, mtError, [mbOK], 0);
exit
end;type algInfo = record
algID: ALG_ID;
dwBits: DWORD;
dwNameLen: DWORD;
szName: array[0..100] of char;
end;
{вспомогательная функция, преобразующая тип провайдера в строку}
function ProvTypeToStr(provType: DWORD): string;
begin
case provType of
PROV_RSA_FULL: ProvTypeToStr := 'RSA full provider';
PROV_RSA_SIG: ProvTypeToStr := 'RSA signature provider';
PROV_DSS: ProvTypeToStr := 'DSS provider';
PROV_DSS_DH: ProvTypeToStr := 'DSS and Diffie-Hellman provider';
PROV_FORTEZZA: ProvTypeToStr := 'Fortezza provider';
PROV_MS_EXCHANGE: ProvTypeToStr := 'MS Exchange provider';
PROV_RSA_SCHANNEL: ProvTypeToStr := 'RSA secure channel provider';
PROV_SSL: ProvTypeToStr := 'SSL provider';
else ProvTypeToStr := 'Unknown provider';
end;
end;
{вспомогательная функция, преобразующая тип реализации в строку}
function ImpTypeToStr(it: DWORD): string;
begin
case it of
CRYPT_IMPL_HARDWARE: ImpTypeToStr := 'аппаратный';
CRYPT_IMPL_SOFTWARE: ImpTypeToStr := 'программный';
CRYPT_IMPL_MIXED: ImpTypeToStr := 'смешанный';
CRYPT_IMPL_UNKNOWN: ImpTypeToStr := 'неизвестен';
else ImpTypeToStr := 'неверное значение';
end;
end;
{процедура вывода информации о криптопровайдерах}
procedure TMainForm.InfoItemClick(Sender: TObject);
var i: DWORD;
dwProvType, cbName, DataLen: DWORD;
provName: array[0..200] of char;
vers: array[0..3] of byte;
impType: DWORD;
ai: algInfo;
err: string;
begin
i:= 0;
FileMemo.Clear;
while (CryptEnumProviders(i, nil, 0, {проверяем наличие еще одного}
@dwProvType, nil, @cbName)) do
begin
if CryptEnumProviders(i, nil, 0, {получаем имя CSP}
@dwProvType, @provName, @cbName) then
begin
FileMemo.Lines.Add('Криптопровайдер: '+provName);
FileMemo.Lines.Add('Тип: '+IntToStr(dwProvType)+' - '+
ProvTypeToStr(dwProvType));
if not CryptAcquireContext(@hProv, nil, provName, dwProvType,
CRYPT_VERIFYCONTEXT)
then
begin
{обработка ошибок}
end;
DataLen := 4;
if not CryptGetProvParam(hProv, PP_VERSION, (@vers), @DataLen, 0)
then
begin
{обработка ошибок}
end;
FileMemo.Lines.Add('Версия: ' + chr(vers[1]+) + '.' + chr(vers[0]+));
if not CryptGetProvParam(hProv, PP_IMPTYPE, @impType, @DataLen, 0)
then
begin
{обработка ошибок}
end;
FileMemo.Lines.Add('Тип реализации: '+ImpTypeToStr(impType));
FileMemo.Lines.Add('Поддерживает алгоритмы:');
DataLen := sizeof(ai);
if not CryptGetProvParam(hProv, PP_ENUMALGS, @ai, @DataLen, CRYPT_FIRST)
then
begin
{обработка ошибок}
end;
with ai do
FileMemo.Lines.Add(szName+#9+'длина ключа - '+IntToStr(dwBits)+
' бит' +#9+ 'ID: '+IntToStr(AlgID));
DataLen := sizeof(ai);
while CryptGetProvParam(hProv, PP_ENUMALGS, @ai, @DataLen, 0) do
begin
with ai do FileMemo.Lines.Add(szName+#9+'длина ключа - '
+IntToStr(dwBits)+' бит'+#9+'ID: '+IntToStr(AlgID));
DataLen := sizeof(ai);
end;
FileMemo.Lines.Add('');
CryptReleaseContext(hProv, 0);
end;
inc(i);
end;
end;
hProv: HCRYPTPROV;
hash: HCRYPTHASH;
password: string;
key: HCRYPTKEY;
plaintext, ciphertext: string;
inFile, outFile: file;
data: PByte;
l: DWORD;{получаем контекст криптопровайдера}
CryptAcquireContext(@hProv, nil, nil, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
{создаем хеш-объект}
CryptCreateHash(hProv, CALG_SHA, 0, 0, @hash);
{хешируем пароль}
CryptHashData(hash, @password[1], length(password), 0);
{создаем ключ на основании пароля для потокового шифра RC4}
CryptDeriveKey(hProv, CALG_RC4, hash, 0, @key);
{уничтожаем хеш-объект}
CryptDestroyHash(hash);
{открываем файлы}
AssignFile(inFile, plaintext);
AssignFile(outFile, ciphertext);
reset(inFile, 1);
rewrite(outFile, 1);
{выделяем место для буфера}
GetMem(data, 512);
{шифруем данные}
while not eof(inFile) do
begin
BlockRead(inFile, data^, 512, l);
CryptEncrypt(key, 0, eof(inFile), 0, data, @l, l);
BlockWrite(outFile, data^, l);
end;
{освобождаем место и закрываем файлы}
FreeMem(data, 512);
CloseFile(inFile);
CloseFile(outFile);
{освобождаем контекст криптопровайдера}
CryptReleaseContext(hProv, 0);
var cont: PChar;
err: string;
hProv: HCRYPTPROV;
KeyExchKey, SignKey: HCRYPTKEY;
flag, keyLen: DWORD;
begin
{если ни один ключ не выбран - выход}
if not (KEKCheckBox.Checked or SKCheckBox.Checked) then exit;
{"считываем" имя контейнера}
if length(ContainerEdit.Text) = 0
then cont := nil
else
begin
err := ContainerEdit.Text;
cont := StrAlloc(length(err) + 1);
StrPCopy(cont, err);
end;
CryptAcquireContext(@hProv, cont, nil, PROV_RSA_FULL, 0);
{генерация ключа обмена ключами (Key Exchange Key)}
if KEKCheckBox.Checked then
begin
{"считываем" длину ключа и помещаем ее в
старшее слово параметра ФЛАГИ}
keyLen := strtoint(KeyExchLenEdit.text);
flag := keyLen shl 16;
if not CryptGenKey(hProv, AT_KEYEXCHANGE, flag, @KeyExchKey) then
begin
јобработка ошибокј
end
else
begin
ReportMemo.Lines.Add('');
ReportMemo.Lines.Add('Создан ключ обмена ключами:');
flag := 4;
if not CryptGetKeyParam(KeyExchKey, KP_KEYLEN, @keyLen, @flag, 0) then
begin
јобработка ошибокј
end
else ReportMemo.Lines.Add(' длина ключа - ' + inttostr(keyLen));
flag := 4;
if not CryptGetKeyParam(KeyExchKey, KP_ALGID, @keyLen, @flag, 0) then
begin
јобработка ошибокј
end
else ReportMemo.Lines.Add(' алгоритм - ' + algIDtostr(keyLen));
{функция algIDtostr здесь не приводится. Она состоит из единственного
оператора case, отображающего целый идентификатор алгоритма в строку}
end;
end;
{генерация ключа подписи (Signature Key)}
if SKCheckBox.Checked then
begin
јвыполняется аналогично генерации ключа обмена ключамиј
end;
CryptReleaseContext(hProv, 0);
end;procedure TExportForm.OKBtnClick(Sender: TObject);
var cont: PChar;
err: string;
hProv: HCRYPTPROV;
key, expKey: HCRYPTKEY;
pbuf: PBYTE;
buflen: DWORD;
f: file;
hash: HCRYPTHASH;
begin
{если ни один ключ не выбран - выход}
if not (KEKCheckBox.Checked or SKCheckBox.Checked) then exit;
{если нужен пароль, т.е. экспортируется ключевая пара целиком}
if PasswEdit.Enabled and (PasswEdit.Text <> Passw2Edit.Text) then
begin
MessageDlg('Ошибка при вводе пароля! Повторите ввод.', mtError, [mbOK], 0);
exit;
end;
…
"считываем" имя контейнера и подключаемся к криптопровайдеру
…
если нужен ключ шифрования - создаем его на основании пароля
…
{ключ обмена ключами}
if KEKCheckBox.Checked then
repeat
{получаем дескриптор ключа}
CryptGetUserKey(hProv, AT_KEYEXCHANGE, @key);
{пытаемся определить размер буфера для экспорта ключа}
if (WhatRadioGroup.ItemIndex = 0) then
CryptExportKey(key, 0, PUBLICKEYBLOB, 0, nil, @bufLen)
else CryptExportKey(key, expKey, PRIVATEKEYBLOB, 0, nil, @bufLen);
GetMem(pbuf, bufLen);
{экспортируем данные}
if (WhatRadioGroup.ItemIndex = 0) then
CryptExportKey(key, 0, PUBLICKEYBLOB, 0, pbuf, @bufLen)
else CryptExportKey(key, expKey, PRIVATEKEYBLOB, 0, pbuf, @bufLen);
{освобождаем дескриптор ключа обмена ключами
(сам ключ при этом не уничтожается)}
CryptDestroyKey(key);
SaveDialog1.Title := 'Укажите файл для сохранения ключа обмена ключами';
if SaveDialog1.Execute then
begin
AssignFile(f, SaveDialog1.FileName);
rewrite(f, 1);
BlockWrite(f, pbuf^, bufLen);
CloseFile(f);
MessageDlg('Ключ обмена ключами успешно сохранен', mtInformation, [mbOK], 0);
end;
until true; {KeyExchange}
{ключ подписи}
if SKCheckBox.Checked then
repeat
јаналогично ключу обмена ключамиј
until true; {Signature}
…
если создавался ключ на основании пароля - уничтожаем его,
после чего освобождаем контекст криптопровайдера
…
end;procedure TSigningForm.SignBtnClick(Sender: TObject);
var cont: PChar;
err: string;
hProv: HCRYPTPROV;
key: HCRYPTKEY;
alg: ALG_ID;
hash: HCRYPTHASH;
infile, outfile: file;
size: DWORD;
buf: array [0..511] of byte;
signature: PBYTE;
begin
{проверка существования выбранного файла}
if not FileExists(DataNameEdit.Text) then
begin
MessageDlg('Неверное имя файла!', mtError, [mbOK], 0);
exit;
end;
AssignFile(infile, DataNameEdit.Text);
…
"считываем" имя контейнера и подключаемся к нему
…
case HashRadioGroup.ItemIndex of
0: alg := CALG_MD5;
1: alg := CALG_SHA;
end;
CryptCreateHash(hProv, alg, 0, 0, @hash);
SaveDialog1.Title := 'Задайте имя файла для хранения подписанных данных';
if SaveDialog1.Execute then
begin
AssignFile(outfile, SaveDialog1.FileName);
rewrite(outfile, 1);
{записываем в файл идентификатор алгоритма хеширования}
BlockWrite(outfile, alg, 4);
reset(infile, 1);
size := FileSize(infile);
{записываем размер подписываемых данных}
BlockWrite(outfile, size, 4);
{пишем сами данные и вычисляем хеш:}
while not eof(infile) do
begin
BlockRead(infile, buf, 512, size);
BlockWrite(outFile, buf, size);
CryptHashData(hash, @buf, size, 0);
end;
CloseFile(infile);
{выясняем размер подписи}
CryptSignHash(hash, AT_SIGNATURE, nil, 0, nil, @size);
{создаем подпись}
GetMem(signature, size);
CryptSignHash(hash, AT_SIGNATURE, nil, 0, signature, @size);
BlockWrite(outfile, size, 4);
BlockWrite(outfile, signature^, size);
CloseFile(outfile);
end;
…
уничтожаем хеш-объект и освобождаем контекст
…
end;
发送到服务器上去很急!!!!!!!!!
谢谢!