| ||||
| ||||
Доброго времени суток! Вопрос у меня как я понимаю избитый до боли..Однако ответ на него мной так и не найден. Вызов 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....Пока почему-то такое ощущение, что границы массива сообщений интерпретируются неправильно... Вот такая беда... | ||||
Ответы: | ||||
| ||||
Несколько вариантов может быть 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; | ||||
| ||||
Неа, не помогает..ни предварительное обнуление структуры (zeromemory(@sm_para,sizeof(CRYPT_SIGN_MESSAGE_PARA));) ни изменение прототипа... Может у кого-нибудь есть работающий паскалевский исходник..дайте посмотреть, а? | ||||
| ||||
А чему у Вас равен sizeof(CRYPT_SIGN_MESSAGE_PARA)? | ||||
| ||||
68..Это верно? | ||||
| ||||
Верно, если в описании структуры не определены поля HashEncryptionAlgorithm и pvHashEncryptionAuxInfo; | ||||
| ||||
Они были не определены, потом были определены жестко, потом были определены условно (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 :) Ничего понять не могу. Номер ошибки не меняется при изменении каких-либо аргументов функции, найти описание этой ошибки не могу...Чего еще сделать можно? | ||||
| ||||
Ага, значит все-таки прототип был кривой. Это вполне разумная ошибка 0x8009200B - "Не удается найти сертификат и закрытый ключ для расшифровки.". У сертификата подписчика есть ссылка секретный ключ то? | ||||
| ||||
если я правильно вопрос понимаю, то контейнер к которому подсоединились должен содержать ключевую пару, соответствующую сертификату..ну он и содержит...по крайней мере экспортируется отттуда без проблем. Или я вопрос не правильно понимаю? А где Вы описание ошибки берете? А если это wincrypt.h не могли ли Вы его мне намылить? :) | ||||
| ||||
Ошибка скорее всего в кривом 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; | ||||
| ||||
>>Ошибка скорее всего в кривом 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 :-) | ||||
| ||||
И все-таки кривой Wincrypt2.pas...Код с кривым выделением памяти - это все лишь один из многочисленных вариантов одного и того же...Были у меня и динамические массивы, и статические массивы и вовсе даже не массивы :) По всякому было...Однако AV происходил всегда...пока не исправила прототип на приблизительно такой же как и у Mick'a. На данный момент файл подписывается под таким прототипом. Без Access Violation... | ||||
| ||||
для тех, у кого все еще появляется ошибка #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))!!! | ||||