03.04.2006 14:53:46Проблемы с CryptVerifyDetachedMessageSignature Ответов: 26
Мария
Уважаемые Знающие...

Попытка провести верификацию detached-подписи...
...
var VerifyPara : CRYPT_VERIFY_MESSAGE_PARA;
sign, data : array [0..0] of PByte;
signsize, datasize : array [0..0] of DWORD;
tmpSign, tmpData, tmpCert : PByte;
RecCert : PCCERT_CONTEXT;
//
function my_PFN_CRYPT_GET_SIGNER_CERTIFICATE_function (pvGetArg :PVOID; dwCertEncodingType :DWORD; pSignerId :PCERT_INFO; hMsgCertStore :HCERTSTORE):PCCERT_CONTEXT;
begin
Result:=RecCert;//PCCERT_CONTEXT(pvGetArg);
end;
...
ZeroMemory(@VerifyPara,sizeof(CRYPT_VERIFY_MESSAGE_PARA));
VerifyPara.cbSize:=sizeof(CRYPT_VERIFY_MESSAGE_PARA);
VerifyPara.dwMsgAndCertEncodingType:=X509_ASN_ENCODING or PKCS_7_ASN_ENCODING;
VerifyPara.hCryptProv:=hProv;
VerifyPara.pfnGetSignerCertificate:=@my_PFN_CRYPT_GET_SIGNER_CERTIFICATE_function;
VerifyPara.pvGetArg:=RecCert;
...
GetMem(tmpSign,GetBase64DecodedSize(SignedMsg));
Base64Decode(SignedMsg,tmpSign);
sign[0]:=tmpSign;
...
GetMem(tmpData,Length(Content));
System.Move(Pointer(Content)^,tmpData^,Length(Content));
data[0]:=tmpData;
datasize[0]:=Length(Content);

if not CryptVerifyDetachedMessageSignature(@VerifyPara,0,@sign[0],GetBase64DecodedSize(SignedMsg),1,data,datasize,@RecCert)
then begin
Result:=false;
ShowMessage(IntToStr(GetLastError));
end else begin
Result:=True;
end;

GetLastError дает ошибку 2148086027
Я так понимаю это значит что где то неправильно что то передано в качестве параметров, но где не знаю...
 
Ответы:
03.04.2006 15:57:58Kirill Sobolev
Это ошибка "Встречено неверное значение тега ASN1", т.е. передаваемая подпись имеет неверный формат.
Возможно ошибка в преобразовании из base64
03.04.2006 16:00:39Kirill Sobolev
Это ошибка "Встречено неверное значение тега ASN1", т.е. передаваемая подпись имеет неверный формат.
Возможно ошибка в преобразовании из base64
03.04.2006 16:05:46Kirill Sobolev
Это ошибка "Встречено неверное значение тега ASN1", т.е. передаваемая подпись имеет неверный формат.
Возможно ошибка в преобразовании из base64
03.04.2006 16:10:01Мария
Спасибо за ответ
Не могли бы вы мне подсказать где можно посмотреть описание ошибок этих ? я искала на форуме, не нашла
А то приходится каждый раз спрашивать что за ошибки такие...

Что касается кодировки Base64
У меня изначально подпись была реализована при помощи объекта CAPICOM, потом пришлось переделывать на CryptoAPI
Заметила что у меня ни подпись, ни сертификат теперь не содержат переносов строк... теперь это просто длинная строчка.. Тут наверное где собака и зарыта... На примере сертификата смотрю, что данные точно все те же самые, не хватает только переносов
Не подскажет ли кто нибудь каким образом можно это обработать ?
03.04.2006 16:19:13Kirill Sobolev
А чем Вы делаете преобразование в/из base64?
03.04.2006 16:35:40Мария
Да, я извиняюсь что сразу не написала, у нас тут проблемы с интернетом... пока пробилась...

Использую функции следующие для кодирования в Base64


function Base64DecodeChar(Ch : Char) : Byte;
begin
case Ch of
'A'..'Z': Result := Ord(Ch) - Ord('A');
'a'..'z': Result := Ord(Ch) - Ord('a') + 26;
'0'..'9': Result := Ord(Ch) - Ord('0') + 52;
'+': Result := 62;
'/': Result := 63
else Result := 64;
end;
end;

function GetBase64DecodedSize(const CodedString : string) : Integer;
var
Len : Integer;
begin
Len := Length(CodedString);
Result := Len div 4 * 3;
if (Len > 0) and (CodedString[Len] = '=') then Dec(Result);
if (Len > 1) and (CodedString[Len - 1] = '=') then Dec(Result);
end;

procedure Base64Decode(const CodedString : string; Res : PByte);
var
I, Len : Integer;
CodedBuf : array [0..3] of Byte;
P : PByte;
begin
Len := Length(CodedString);
I := 1;
P := Res;
while I + 3 <= Len do
begin
CodedBuf[0] := Base64DecodeChar(CodedString[I]);
CodedBuf[1] := Base64DecodeChar(CodedString[I + 1]);
CodedBuf[2] := Base64DecodeChar(CodedString[I + 2]);
CodedBuf[3] := Base64DecodeChar(CodedString[I + 3]);
P^ := (CodedBuf[0] shl 2) + ((CodedBuf[1] and $30) shr 4);
Inc(P);
if CodedBuf[2] < 64 then begin
P^ := ((CodedBuf[1] and $0F) shl 4) + ((CodedBuf[2] and $3C) shr 2);
Inc(P);
if CodedBuf[3] < 64 then begin
P^ := ((CodedBuf[2] and $03) shl 6) + CodedBuf[3];
Inc(P);
end;
end;
Inc(I, 4);
end;
end;

03.04.2006 16:36:50Мария
и обратно...

function Base64EncodeByte(B : Byte) : Char;
begin
Result := #0;
case B of
0..25 : Result := Char(B + Ord('A'));
26..51 : Result := Char(B + Ord('a') - 26);
52..61 : Result := Char(B + Ord('0') - 52);
62 : Result := '+';
63 : Result := '/';
end;
end;

function Base64Encode(Data : PByte; DataSize : Cardinal) : string;
var
P : PChar;
I : Integer;
begin
P := PChar(Data);
I := 1;
SetLength(Result, Ceil(DataSize / 3) * 4);
while (cardinal(P - PChar(Data)) + 3 <= DataSize) do
begin
Result[I] := Base64EncodeByte(PByte(P)^ shr 2);
Result[I + 1] := Base64EncodeByte((PByte(P)^ and $03) shl 4 + PByte(P + 1)^ shr 4);
Result[I + 2] := Base64EncodeByte((PByte(P + 1)^ and $0F) shl 2 + PByte(P + 2)^ shr 6);
Result[I + 3] := Base64EncodeByte(PByte(P + 2)^ and $3F);
Inc(P, 3);
Inc(I, 4);
end;
if (cardinal(P - PChar(Data)) = DataSize - 2) then begin
Result[I] := Base64EncodeByte(PByte(P)^ shr 2);
Result[I + 1] := Base64EncodeByte((PByte(P)^ and $03) shl 4 + PByte(P + 1)^ shr 4);
Result[I + 2] := Base64EncodeByte((PByte(P + 1)^ and $0F) shl 2);
Result[I + 3] := '=';
end
else if (cardinal(P - PChar(Data)) = DataSize - 1) then begin
Result[I] := Base64EncodeByte(PByte(P)^ shr 2);
Result[I + 1] := Base64EncodeByte((PByte(P)^ and $03) shl 4);
Result[I + 2] := '=';
Result[I + 3] := '=';
end
end;
03.04.2006 16:36:53Мария
и обратно...

function Base64EncodeByte(B : Byte) : Char;
begin
Result := #0;
case B of
0..25 : Result := Char(B + Ord('A'));
26..51 : Result := Char(B + Ord('a') - 26);
52..61 : Result := Char(B + Ord('0') - 52);
62 : Result := '+';
63 : Result := '/';
end;
end;

function Base64Encode(Data : PByte; DataSize : Cardinal) : string;
var
P : PChar;
I : Integer;
begin
P := PChar(Data);
I := 1;
SetLength(Result, Ceil(DataSize / 3) * 4);
while (cardinal(P - PChar(Data)) + 3 <= DataSize) do
begin
Result[I] := Base64EncodeByte(PByte(P)^ shr 2);
Result[I + 1] := Base64EncodeByte((PByte(P)^ and $03) shl 4 + PByte(P + 1)^ shr 4);
Result[I + 2] := Base64EncodeByte((PByte(P + 1)^ and $0F) shl 2 + PByte(P + 2)^ shr 6);
Result[I + 3] := Base64EncodeByte(PByte(P + 2)^ and $3F);
Inc(P, 3);
Inc(I, 4);
end;
if (cardinal(P - PChar(Data)) = DataSize - 2) then begin
Result[I] := Base64EncodeByte(PByte(P)^ shr 2);
Result[I + 1] := Base64EncodeByte((PByte(P)^ and $03) shl 4 + PByte(P + 1)^ shr 4);
Result[I + 2] := Base64EncodeByte((PByte(P + 1)^ and $0F) shl 2);
Result[I + 3] := '=';
end
else if (cardinal(P - PChar(Data)) = DataSize - 1) then begin
Result[I] := Base64EncodeByte(PByte(P)^ shr 2);
Result[I + 1] := Base64EncodeByte((PByte(P)^ and $03) shl 4);
Result[I + 2] := '=';
Result[I + 3] := '=';
end
end;
03.04.2006 17:02:33Kirill Sobolev
Я не это имел ввиду :-D
Эти функции работают нормально? Т.е. если взять бинарный файл подписи, преобразовать его в base64, потом обратно - получится файл идентичный натуральному?
03.04.2006 17:16:01Мария
Да, я проверяла
Формирую бинарную подпись, кодирую её в Base64, сохраняю в файле
Потом беру из файла строку, раскодирую её, тот же бинарный результат и получается
03.04.2006 17:26:15Kirill Sobolev
Киньте мне само сообщение, подпись в base64 и сертификат, если он не в подписи.
03.04.2006 17:44:50Мария
Подписываемое сообщение :

<SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><SignatureMethod/><Reference URI="#KeyInfo"><DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"/><DigestValue>ypZQkwANbZxtPV5GVTtGGOFOZCrZ//28qzWzdAQyIkg=</DigestValue></Reference><Reference URI="#InputData"><Transforms><Transform Algorithm="urn:xml-dsig:normalization:v1.0"/><Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#gostr3411"/><DigestValue>6ubx7zbKOQ0ZCEHonhyxGyEzxL1TQSUIoqVQelw447s=</DigestValue></Reference></SignedInfo>

Полученная подпись :

MIIBMgYJKoZIhvcNAQcCoIIBIzCCAR8CAQExDDAKBgYqhQMCAgkFADALBgkqhkiG9w0BBwExgf4wgfsCAQEwgZswgYwxJjAkBgkqhkiG9w0BCQEWF2RkdW5hZXZAbWFpbC5jdXN0b21zLnJ1MQswCQYDVQQGEwJSVTEPMA0GA1UEBxMGTW9zY293MQwwCgYDVQQKEwNHVEsxIjAgBgNVBAsTGUluZm9ybWF0aW9uIFNlY3VyaXR5IERlcC4xEjAQBgNVBAMTCUN1c3RvbSBDQQIKEWzSiAAAAAACmjAKBgYqhQMCAgkFADAKBgYqhQMCAhMFAARA000/aWQuBPRWyUFJfnDySj1szv9mMj9ZTrf0sgozTYVbQggLkJXmaigP6Iiho4vcVwAJuSdXbKklROr+zGO78Q==

Сертификат :

MIIDpTCCA1KgAwIBAgIKEWzSiAAAAAACmjAKBgYqhQMCAgMFADCBjDEmMCQGCSqGSIb3DQEJARYXZGR1bmFldkBtYWlsLmN1c3RvbXMucnUxCzAJBgNVBAYTAlJVMQ8wDQYDVQQHEwZNb3Njb3cxDDAKBgNVBAoTA0dUSzEiMCAGA1UECxMZSW5mb3JtYXRpb24gU2VjdXJpdHkgRGVwLjESMBAGA1UEAxMJQ3VzdG9tIENBMB4XDTA1MDUyMzEwMjkwMFoXDTA2MDUyMzEwMzgwMFowgfQxJDAiBgkqhkiG9w0BCQEWFW5hc3R5YTdAc3Z4bm9yZG9zdC5ydTELMAkGA1UEBhMCUlUxFTATBgNVBAgeDAQcBD4EQQQ6BDIEMDEVMBMGA1UEBx4MBBwEPgRBBDoEMgQwMSkwJwYDVQQKHiAEFwQQBB4AIAQhBBIEJQAgBB0EHgQgBBQALQQeBCEEIjEbMBkGA1UECx4SBBQENQQ6BDsEMARABDAEPQRCMUkwRwYDVQQDHkAEEQQwBEEEQQQwBEAENQQyBDAAIAQQBD0EMARBBEIEMARBBDgETwAgBBIEOwQwBDQEOAQ8BDgEQAQ+BDIEPQQwMGMwHAYGKoUDAgITMBIGByqFAwICJAAGByqFAwICHgEDQwAEQGKI4MKUepopvaMBq9PtL6W+QySku6VBCdTOGgq8Js4mmG/03EugABiJoqEfwdn5mkIpzI2gK5jgNpHNlwkocjKjggEmMIIBIjAOBgNVHQ8BAf8EBAMCBPAwJgYDVR0lBB8wHQYHKoUDAgIiBgYIKwYBBQUHAwIGCCsGAQUFBwMEMB0GA1UdDgQWBBQdlWty6ZIPmhjx0P8dLht5mLESujCByAYDVR0jBIHAMIG9gBRIO7tuaF11IZc9/EQS7EIvBuk0dqGBkqSBjzCBjDEmMCQGCSqGSIb3DQEJARYXZGR1bmFldkBtYWlsLmN1c3RvbXMucnUxCzAJBgNVBAYTAlJVMQ8wDQYDVQQHEwZNb3Njb3cxDDAKBgNVBAoTA0dUSzEiMCAGA1UECxMZSW5mb3JtYXRpb24gU2VjdXJpdHkgRGVwLjESMBAGA1UEAxMJQ3VzdG9tIENBghA8FnqZ0oPnhkRr3Kg8xtSBMAoGBiqFAwICAwUAA0EATJhtlEu2nxx8yP06E6SElyr321W1fKBTmgXLxmTEThAzcq0hfz0tyPbgr1rA5x63JHM0emwuxFTQVvJ/xN4MBQ==
03.04.2006 18:18:51Kirill Sobolev
Нормально все, т.е. и сертификат и подпись не битые
Я вот что подумал:
У Вас sign : array [0..0] of PByte
Соответственно в функцию Вы передаете @sign[0] - это будет указатель на указатель, а там нужен просто PByte.
04.04.2006 9:42:01Мария
Если я пишу таким образом
...
tmpSign : PByte;
...
GetMem(tmpSign,GetBase64DecodedSize(SignedMsg));
Base64Decode(SignedMsg,tmpSign);
...
CryptVerifyDetachedMessageSignature(@VerifyPara,0,tmpSign,GetBase64DecodedSize(SignedMsg),1,data,datasize,@RecCert);

у меня возникает AV по адресу 0000000 при вызове CryptVerifyDetachedMessageSignature
хотя в tmpSign именно то, что мне надо
05.04.2006 11:58:40Мария
У меня есть полученная мной ЭЦП и ЭЦП полученная другими товарищами.
При проверке чужой ЭЦП AV не возникает, то есть допустим что моя ЭЦП не правильно получена.
Но ошибка "Встречено неверное значение тега ASN1." все равно вызникает на CryptVerifyDetachedMessageSignature
05.04.2006 12:01:01Yanka
Добрый день, Маша!

А вы пробовали создать подпись и включить туда сертификат?

Я создаю подпись (правда сертификат включаю в подпись), проверяю (CryptVerifyDetachedMessageSignature) все замечательно работает.

Если вы скините код вашей функции my_PFN_CRYPT_GET_SIGNER_CERTIFICATE_function, то могу проверить на вашем примере
05.04.2006 12:04:15Yanka
Добрый день, Маша!

А вы не пробовали создавать подпись с включенным в нее сертификатом?

Я создаю подпись (правда сертификат включаю в состав подписи), проверяю CryptVerifyDetachedMessageSignature - все замечательно работает.

Если вы скините код вашей функции _CRYPT_GET_SIGNER_CERTIFICATE_function, то я могу проверить на вашем примере.
05.04.2006 12:13:50Мария
Добрый день Yanka )
Моя проблема как раз и заключается в том, что мне нужна подпись БЕЗ сертификата

а функция у меня такая
function my_PFN_CRYPT_GET_SIGNER_CERTIFICATE_function (pvGetArg :PVOID; dwCertEncodingType :DWORD; pSignerId :PCERT_INFO; hMsgCertStore :HCERTSTORE):PCCERT_CONTEXT;
begin
Result:=PCCERT_CONTEXT(pvGetArg);
end;
05.04.2006 12:38:18Yanka
Маша!

Я поняла, что вам нужна подпись без сертификата. Я предлагаю вам для проверки создать подпись с включенным в нее сертификатом и провести проверку CryptVerifyDetachedMessageSignature но уже без использования my_PFN_CRYPT_GET_SIGNER_CERTIFICATE_function.
Если ошибки не будет, значит проблема в my_PFN_CRYPT_GET_SIGNER_CERTIFICATE_function, а если будет, то в другом.
Вам же для этого требуется совсем немного изменить свою программу.
05.04.2006 12:40:28Мария
Ок, спасибо за предложение, сейчас попробую
05.04.2006 12:54:10Мария
Сделала следующее

это при получении ЭЦП :
SignPara.cMsgCert:=1;
SignPara.rgpMsgCert:=@FCertificate;

это при проверке :
VerifyPara.pfnGetSignerCertificate:=nil;
VerifyPara.pvGetArg:=nil;

Ошибки все те же, то есть на мое подписи AV, на чужой - "неправильное значение ТЕГА"

Значит ошибка в другом месте..
05.04.2006 13:34:36Yanka
Маша!

Ошибка в использовании CryptVerifyDetachedMessageSignature.

Исправь для начала прототипы функций CryptSignMessage и CryptVerifyDetachedMessageSignature в модуле wcrypt2.pas


type
pacarPByte = packed array of PByte;
pacarDWORD = packed array of DWORD;


function CryptSignMessage(pSignPara: PCRYPT_SIGN_MESSAGE_PARA;
fDetachedSignature: BOOL;
cToBeSigned: DWORD;
const rgpbToBeSigned: pacarPByte;
rgcbToBeSigned: pacarDWORD;
pbSignedBlob: PBYTE;
pcbSignedBlob: PDWORD): BOOL ; stdcall;

function CryptVerifyDetachedMessageSignature(pVerifyPara :PCRYPT_VERIFY_MESSAGE_PARA;
dwSignerIndex :DWORD;
const pbDetachedSignBlob :PBYTE;
cbDetachedSignBlob :DWORD;
cToBeSigned :DWORD;
const rgpbToBeSigned :pacarPbyte; //array of PBYTE;
rgcbToBeSigned :pacarDWORD; //rray of DWORD;
ppSignerCert :PPCCERT_CONTEXT):BOOL ; stdcall;



Теперь, как я создаю подпись. Правда я включаю сертификат в подпись, но это ты уже сама исправишь.

var
Data_array_pointer: pacarPByte;
Size_array_pointer: pacarDWORD;
Data_blob_str: string; // подписываемые данные
sign_blob_str: string; // строка с подписью
sign_blob_size: DWORD; // размер создаваемой подписи
........................................
.........................................


var
sm_para : CRYPT_SIGN_MESSAGE_PARA;
pSignerCert: PCCERT_CONTEXT; // Мой сертификат, которым подписываю сообщение

begin
........................................
.........................................
FillChar(sm_para, sizeof(CRYPT_SIGN_MESSAGE_PARA), #0);
sm_para.cbSize:= sizeof(CRYPT_SIGN_MESSAGE_PARA);
sm_para.dwMsgEncodingType:= X509_ASN_ENCODING OR PKCS_7_ASN_ENCODING;
sm_para.HashAlgorithm.pszObjId:= szOID_CP_GOST_R3411;
sm_para.pSigningCert:= pSignerCert;
sm_para.cMsgCert:= 1;
sm_para.rgpMsgCert:= @pSignerCert;


SetLength(Data_array_pointer, sizeof(PByte));
SetLength(Size_array_pointer, sizeof(DWORD));

Data_array_pointer[0]:= @Data_blob_str[1];
Size_array_pointer[0]:= length(Data_blob_str);

if CryptSignMessage(@sm_para,
TRUE,
1,
Data_array_pointer,
Size_array_pointer,
nil,
@sign_blob_size) then
begin
ShowMessage('CryptSignMessage (определения размера буффера) - OK!!!');
SetLength(sign_blob_str, sign_blob_size);
if CryptSignMessage(@sm_para, TRUE, 1, Data_array_pointer,
Size_array_pointer, @sign_blob_str[1], @sign_blob_size) then
begin
ShowMessage('CryptSignMessage - OK!!!');
SetLength(sign_blob_str, sign_blob_size);
end else
ShowMessage('Error CryptSignMessage. ErrCode = '+inttostr(GetLastError));
end else
ShowMessage('Error CryptSignMessage (Ошибка определения размера). ErrCode = '+inttostr(GetLastError));

........................................
.........................................
end;


Теперь, как я проверяю подпись

var
VerifyParams : CRYPT_VERIFY_MESSAGE_PARA;
pRecCert: PCCERT_CONTEXT;
begin
FillChar(VerifyParams, sizeof(CRYPT_VERIFY_MESSAGE_PARA), 0);
VerifyParams.cbSize:= sizeof(CRYPT_VERIFY_MESSAGE_PARA);
VerifyParams.dwMsgAndCertEncodingType:= X509_ASN_ENCODING OR PKCS_7_ASN_ENCODING;
VerifyParams.hCryptProv:= 0;
VerifyParams.pfnGetSignerCertificate:= nil;
VerifyParams.pvGetArg:= nil;

if CryptVerifyDetachedMessageSignature(@VerifyParams,
0,
@sign_blob_str[1],
length(sign_blob_str),
1,
Data_array_pointer,
Size_array_pointer,
@pRecCert) then

begin
ShowMessage('CryptVerifyDetachedMessageSignature - OK!!!');
end else
ShowMessage('CryptVerifyDetachedMessageSignature - ERROR. ErrCode = '+inttostr(GetLastError));

........................................
.........................................
end;
05.04.2006 13:56:19Yanka
Небольшое уточнение...

В модуле wcrypt2.pas можно написать просто

pacarPByte = array of PByte;
pacarDWORD = array of DWORD;

(без "packed"). Проверила - все работает

06.04.2006 13:05:31Мария
Сделала все, как написала Yanka, спасибо ей большое, заработало, но для включенного в подпись сертификата.. Мне нужна подпись без сертификата.
Переделываю следующее :

для получения ЭЦП
...
SignPara.cMsgCert:=0;
SignPara.rgpMsgCert:=nil;
...

для проверки ЭЦП
...
RecCert : PCCERT_CONTEXT;
...
VerifyPara.pfnGetSignerCertificate:= @my_PFN_CRYPT_GET_SIGNER_CERTIFICATE_function;
VerifyPara.pvGetArg:=RecCert;
...
function my_PFN_CRYPT_GET_SIGNER_CERTIFICATE_function (pvGetArg :PVOID; dwCertEncodingType :DWORD; pSignerId :PCERT_INFO; hMsgCertStore :HCERTSTORE):PCCERT_CONTEXT;
begin
Result:=PCCERT_CONTEXT(pvGetArg);
end;
...

При вызове CryptVerifyDetachedMessageSignature возникает AV. Что я опять делаю не так ? ) Тут вроде и ошибиться особо не где...
06.04.2006 13:06:49Мария
Сертификат который передаю в CallBack-функцию проверяла, сохраняла в файл, все нормально открывается.
06.04.2006 15:10:58Мария
Ошибка была в том, что я забыла указать STDCALL;

function my_PFN_CRYPT_GET_SIGNER_CERTIFICATE_function (pvGetArg :PVOID; dwCertEncodingType :DWORD; pSignerId :PCERT_INFO; hMsgCertStore :HCERTSTORE):PCCERT_CONTEXT; stdcall;
begin
Result:=PCCERT_CONTEXT(pvGetArg);
end;