16.03.2006 19:55:46CryptSignMessage - Access Violation Ответов: 12
tranquilla
Доброго времени суток!
Вопрос у меня как я понимаю избитый до боли..Однако ответ на него мной так и не найден.
Вызов CryptSignMessage (DELPHI) приводит к Access Violation по адресу 000000 (или 000001) :(
Исходник:
var
sm_para: CRYPT_SIGN_MESSAGE_PARA;
cert: PCCERT_CONTEXT;
cert1: PCCERT_CONTEXT;
buffer1: array of pbyte;
size_buffer1:array of dword;
sign_blob_size1: dword;
sign_blob: pbyte;
store: HCERTSTORE;
hprov: HCRYPTPROV;
str: string;
str_p: PWideChar;
f1:file;

encType:= X509_ASN_ENCODING or PKCS_7_ASN_ENCODING;
if form1.OpenDialog1.Execute then
begin
setlength(buffer1,1);
setlength(size_buffer1,1);
AssignFile (f1, OpenDialog1.FileName);
reset (f1, 1);
size_buffer1[0]:=filesize(f1);
GetMem(buffer1[0], size_buffer1[0]);
BlockRead (f1, buffer1[0]^, size_buffer1[0]);
CloseFile (f1);
str:='new_sub';
GetMem(str_p,2*length(str)+1);
StringToWideChar(str,str_p,2*length(str)+1);
if not
CryptAcquireContext(@hprov,'new_sub',nil,PROV_RSA_FULL,0)
then
begin
MessageDlg('Error1',mtError,[mbOK],0);
exit;
end;
store:=CertOpenSystemStore(hprov,'OTHER');
if store=nil
then
begin
MessageDlg('Error2',mtError,[mbOK],0);
exit;
end;
cert:=CertFindCertificateInStore(store,encType,0,CERT_FIND_SUBJECT_STR,str_p,nil);
if cert=nil then
begin
case int64(GetLastError()) of
ERROR_INVALID_PARAMETER:err:='INVALID_PARAMETER';
E_INVALIDARG: err:='E_INVALIDARG';
else
err:='Unknown!';
end;
MessageDlg('Error3'+err,mtError,[mbOK],0);
exit;
end;
cert1:=CertDuplicateCertificateContext(cert);
sm_para.dwMsgEncodingType:=encType;
sm_para.HashAlgorithm.pszObjId:=szOID_RSA_MD2;
sm_para.HashAlgorithm.Parameters.cbData:=0;
sm_para.HashAlgorithm.Parameters.pbData:=nil;
sm_para.pvHashAuxInfo:=nil;
sm_para.cMsgCert:=0;
sm_para.rgpMsgCert:=nil;
sm_para.cMsgCrl:=0;
sm_para.rgpMsgCrl:=nil;
sm_para.cAuthAttr:=0;
sm_para.rgAuthAttr:=nil;
sm_para.cUnauthAttr:=0;
sm_para.rgUnauthAttr:=nil;
sm_para.dwFlags:=0;
sm_para.dwInnerContentType:=0;
sm_para.pSigningCert:=cert1;
sm_para.cbSize:=sizeof(CRYPT_SIGN_MESSAGE_PARA);


if not
CryptSignMessage(@sm_para,false,1,buffer1,size_buffer1,nil,@sign_blob_size1)

then
begin
MessageDlg('Error4',mtError,[mbOK],0);
exit;
end;
GetMem(sign_blob,sign_blob_size1);
if not
CryptSignMessage(@sm_para,false,1,buffer1,size_buffer1,sign_blob,@sign_blob_size1)
then
begin
MessageDlg('Error5',mtError,[mbOK],0);
exit
end;
CertCloseStore(store,0);
end;

Ошибка возникает при первом вызове CryptSignMessage....Пока почему-то такое ощущение, что границы массива сообщений интерпретируются неправильно...
Вот такая беда...
 
Ответы:
17.03.2006 9:40:51Kirill Sobolev
Несколько вариантов может быть
1)Вы не обнуляете структуру перед вызовом, соответственно у Вас допустим поле HashEncryptionAlgorithm не инициализировано.
Вот здесь тоже самое http://www.cryptopro.ru/cryptopro/forum/view.asp?q=1220
2)Возможно, проблема кроется в прототипе функции и описании структуры CRYPT_SIGN_MESSAGE_PARA.
Тут вот похожая проблема http://www.cryptopro.ru/cryptopro/forum/view.asp?q=2188
Рекомендуют сравнить прототип
в wincrypt.h это

BOOL
WINAPI
CryptSignMessage(
IN PCRYPT_SIGN_MESSAGE_PARA pSignPara,
IN BOOL fDetachedSignature,
IN DWORD cToBeSigned,
IN const BYTE *rgpbToBeSigned[],
IN DWORD rgcbToBeSigned[],
OUT BYTE *pbSignedBlob,
IN OUT DWORD *pcbSignedBlob
);


typedef struct _CRYPT_SIGN_MESSAGE_PARA {
DWORD cbSize;
DWORD dwMsgEncodingType;
PCCERT_CONTEXT pSigningCert;
CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
void *pvHashAuxInfo;
DWORD cMsgCert;
PCCERT_CONTEXT *rgpMsgCert;
DWORD cMsgCrl;
PCCRL_CONTEXT *rgpMsgCrl;
DWORD cAuthAttr;
PCRYPT_ATTRIBUTE rgAuthAttr;
DWORD cUnauthAttr;
PCRYPT_ATTRIBUTE rgUnauthAttr;
DWORD dwFlags;
DWORD dwInnerContentType;

#ifdef CRYPT_SIGN_MESSAGE_PARA_HAS_CMS_FIELDS
CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;
void *pvHashEncryptionAuxInfo;
#endif
} CRYPT_SIGN_MESSAGE_PARA, *PCRYPT_SIGN_MESSAGE_PARA;
17.03.2006 14:54:35tranquilla
Неа, не помогает..ни предварительное обнуление структуры (zeromemory(@sm_para,sizeof(CRYPT_SIGN_MESSAGE_PARA));)
ни изменение прототипа...
Может у кого-нибудь есть работающий паскалевский исходник..дайте посмотреть, а?
17.03.2006 14:57:17Kirill Sobolev
А чему у Вас равен sizeof(CRYPT_SIGN_MESSAGE_PARA)?
17.03.2006 16:44:30tranquilla
68..Это верно?
17.03.2006 16:53:56Kirill Sobolev
Верно, если в описании структуры не определены поля HashEncryptionAlgorithm и pvHashEncryptionAuxInfo;
17.03.2006 17:38:51tranquilla
Они были не определены, потом были определены жестко, потом были определены условно (ifdef)...Ничего не меняется. Кстати была попытка изменить прототип CryptSignMessage на
function CryptSignMessage(pSignPara :PCRYPT_SIGN_MESSAGE_PARA;
fDetachedSignature :BOOL;
cToBeSigned :DWORD;
const rgpbToBeSigned :PBYTE;
rgcbToBeSigned :DWORD;
pbSignedBlob :PBYTE;
pcbSignedBlob :PDWORD):BOOL ; stdcall;
В оригинале она выглядит так:
function CryptSignMessage(pSignPara :PCRYPT_SIGN_MESSAGE_PARA;
fDetachedSignature :BOOL;
cToBeSigned :DWORD;
const rgpbToBeSigned :array of PBYTE;
rgcbToBeSigned :array of DWORD;
pbSignedBlob :PBYTE;
pcbSignedBlob :PDWORD):BOOL ; stdcall;
После изменения этого прототипа AV перестал возникать, зато возникла ошибка под номером 2148081675 :) Ничего понять не могу. Номер ошибки не меняется при изменении каких-либо аргументов функции, найти описание этой ошибки не могу...Чего еще сделать можно?
17.03.2006 17:47:16Kirill Sobolev
Ага, значит все-таки прототип был кривой.
Это вполне разумная ошибка 0x8009200B - "Не удается найти сертификат и закрытый ключ для расшифровки.". У сертификата подписчика есть ссылка секретный ключ то?
17.03.2006 18:23:06tranquilla
если я правильно вопрос понимаю, то контейнер к которому подсоединились должен содержать ключевую пару, соответствующую сертификату..ну он и содержит...по крайней мере экспортируется отттуда без проблем.
Или я вопрос не правильно понимаю?
А где Вы описание ошибки берете? А если это wincrypt.h не могли ли Вы его мне намылить? :)
21.03.2006 16:59:07Mick
Ошибка скорее всего в кривом WinCrypt2.pas скачанном с джедаев. Там ошибочно используются открытые массивы в параметрах. Причем не только в CryptSignMessage.
Вот мой исправленный вариант.


function CryptSignMessageMy(pSignPara :PCRYPT_SIGN_MESSAGE_PARA;
fDetachedSignature :BOOL;
cToBeSigned :DWORD;
const rgpbToBeSigned : Pointer;//array of PBYTE;
rgcbToBeSigned : Pointer;//array of DWORD;
pbSignedBlob :PBYTE;
pcbSignedBlob :PDWORD):BOOL ; stdcall;
23.03.2006 15:37:35Yanka
>>Ошибка скорее всего в кривом WinCrypt2.pas

Ошибка была не в кривом WinCrypt2.pas, как вы пишете, а в неправильном выделении памяти под массивы buffer1 и size_buffer1.

Ошибка вот в этих строчках вашей программы:
setlength(buffer1,1);
setlength(size_buffer1,1);

Вы под каждый из массивов выделили всего по одному байту памяти, тогда как элементами массива являются не байты, а PByte в первом случае и DWORD во втором.

Надо написать:

SetLength(buffer1, 1*sizeof(PByte));
SetLength(size_buffer1, 1*sizeof(DWORD));

И никаких AccessViolation :-)
23.03.2006 17:20:55tranquilla
И все-таки кривой Wincrypt2.pas...Код с кривым выделением памяти - это все лишь один из многочисленных вариантов одного и того же...Были у меня и динамические массивы, и статические массивы и вовсе даже не массивы :) По всякому было...Однако AV происходил всегда...пока не исправила прототип на приблизительно такой же как и у Mick'a. На данный момент файл подписывается под таким прототипом. Без Access Violation...
12.06.2006 16:04:52NOTORIOUS
для тех, у кого все еще появляется ошибка #2148081675
добавьте в переменные
var keyContext : CERT_KEY_CONTEXT;
и keyContext.hCryptProv := hProv;
keyContext.dwKeySpec := AT_SIGNATURE;
keyContext.cbSize := sizeof(CERT_KEY_CONTEXT);
CertSetCertificateContextProperty( сert1, СERT_KEY_CONTEXT_PROP_ID,
CERT_STORE_NO_CRYPT_RELEASE_FLAG,
@keyContext);
после вызова CertDuplicateContext(), см. пост#1
тем самым вы получите доступ к private signature key
и еще... SetLength(buffer1,1) нужно вызывать все-таки так, не путать с GetMem(buffer1,1*sizeof(PByte))!!!