14.02.2005 15:35:00ПРОВЕРКА ЭЦП Ответов: 9
Дробязко Алексей
Добрый день. С подписыванием файлов я более-менее разобрался. Теперь приступил к проверке подписи.
Подпись файла идет отдельно от файла.
В подпись включен сертификат пользователя.
Не совсем понятна такая ситуация. Иду так сказать с конца...Для того, чтобы проверить подпись, мне необходим сертификат пользователя(в сертификате кроме всего прочего указан тип провайдера, с которым он работает, я так понимаю). Чтобы извлечь сертификат из ЭЦП, мне необходимо использовать ф-ию CryptMsgGetParam(hMsg,...). Чтобы использовать эту ф-ию я делжен открыть сообщение для декодирования и проапдейтить его содержимым файла подписи(p7s) и содержимым файла, который был подписан.
!! Но для того, чтобы использовать ф-ию CryptMsgOpenToDecode(..., hProv,...) я должен указать также дескриптор криптопровайдера!
!! А как же я его узнаю, если еще не получил информацию о сертификате пользователя. Т.е. получается какой-то замкнутый круг. Как в таком случае быть? Можно ли работать с ф-ями CryptMsgOpenToDecode, CryptMsgUpdate, CryptMsgGetParam и т.д. без получения дескриптора криптопровайдера.
И вот еще один вопрос. Если я в ЦС получил сертификат с использованием какого-то определенного криптопровайдера(напр, Schlumberger Cryptographic Service Provider), значит ли это, что при написании крипто-прог. я должен в программе получать контекст именно этого криптопровайдера?
Спасибо.
 
Ответы:
14.02.2005 15:58:56Юрий
Диагноз: хронический НЕДОЧИТ документации :)
Рецепт: еще почитать про функцию CryptMsgOpenToDecode.
15.02.2005 12:57:26Дробязко Алексей
Добрый день. Извините за мою назойливость, но очень хочется "добить" эту тему, а знаний и сообразительности маловато.
Почитал про CryptMsgOpenToDecode, решил отказаться от стриминга. Делаю все так, как в Вашем примере lowsign.c.
---------------------------------------
Var
cbSignerCertInfo : DWORD;
pSignerCertInfo : PCERT_INFO;
pSignerCertContext : PCCERT_CONTEXT;
pSignerCertificateInfo : PCERT_INFO;
hStoreHandle : HCERTSTORE;
flags : DWORD;
Begin
flags := CMSG_DETACHED_FLAG;

// --------------------------------------------------------------------
// Откроем сообщение для декодирования

hMsg := CryptMsgOpenToDecode(
TYPE_DER, // Encoding type
flags, // Flags (CMSG_DETACHED_FLAG)
0, // Use the default message type
0, // Cryptographic provider
NIL, // Recipient information
NIL // Stream information (not used)
);

// --------------------------------------------------------------------
// Поместим в сообщение данные ЭЦП

CryptMsgUpdate(hMsg, pbyte(buffer), iFileLength, True);

// --------------------------------------------------------------------
// Поместим в сообщение файл, который подписывали

CryptMsgUpdate(hMsg, pbyte(SourceBuffer), iSourceFileLength, True);

CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_SIGNER_CERT_INFO_PARAM, // Parameter type
0, // Index
NIL, // Pointer to the blob
@cbSignerCertInfo); // Size of the blob

CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_SIGNER_CERT_INFO_PARAM,// Parameter type
0, // Index
pSignerCertInfo, // Pointer to the blob
@cbSignerCertInfo); // Size of the blob

End;
---------------------------------------
В результате все отработало успешно: cbSignerCertInfo=272, а вот в pSignerCertInfo как был NIL, так и остался...
Не подскажите, что может быть?
Спасибо.
15.02.2005 14:00:17Юрий
Ну а кто под pSignerCertInfo память выделять будет? :) Это же просто POINTER, на что ему указывать?
15.02.2005 14:34:13Дробязко Алексей
:) Ламер я, ламер...:) Но проблема не исчезла.
Выделил память:
new(pSignerCertInfo);
Обнулил все, что можно в структуре:

NULL_DATA.cbData := 0;
NULL_DATA.pbData := NIL;

with pSignerCertInfo^ Do
Begin
dwVersion := 0;
SerialNumber := NULL_DATA;

SignatureAlgorithm.pszObjId := NIL;
SignatureAlgorithm.Parameters := NULL_DATA;

Issuer := NULL_DATA;

NotBefore.dwLowDateTime := 0;
NotBefore.dwHighDateTime := 0;
NotAfter.dwLowDateTime := 0;
NotAfter.dwHighDateTime := 0;

SubjectPublicKeyInfo.Algorithm.pszObjId := NIL;
SubjectPublicKeyInfo.Algorithm.Parameters := NULL_DATA;
SubjectPublicKeyInfo.PublicKey.cbData := 0;
SubjectPublicKeyInfo.PublicKey.pbData := NIL;
SubjectPublicKeyInfo.PublicKey.cUnusedBits := 0;

IssuerUniqueId.cbData := 0;
IssuerUniqueId.pbData := NIL;
IssuerUniqueId.cUnusedBits := 0;

SubjectUniqueId.cbData := 0;
SubjectUniqueId.pbData := NIL;
SubjectUniqueId.cUnusedBits := 0;

cExtension := 0;
rgExtension := NIL;
End;

Но вызов:
CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_SIGNER_CERT_INFO_PARAM,// Parameter type
0, // Index
pSignerCertInfo, // Pointer to the blob
@cbSignerCertInfo); // Size of the blob

Вернул в pSignerCertInfo^ как бы НЕ ВСЮ информацию о сертификате. Нет ни subject, ни NotBefore, ни NotAfter, и т.д(т.е. соответствующие поля содержат только нули и NIL-ы).
Есть только Issuer. И то, в нем "нормальная" информация(соответсвующая реальному значению этого поля в сертификате) перемешана с каким-то мусором(например,’0‡1*0( *†H†÷
 alex_drobazko@ukrpatent.org1 0 UUA1
0 UKyiv10U
 UKRPATENT10 U SARIT10UeFilingKeyCentre’)
15.02.2005 14:48:28Дробязко Алексей
P.S. Забыл заметить, что если перед получением информации о сертификате подписчика, попробывать получить размер подписанных данных и сами данные:

CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_CONTENT_PARAM, // Parameter type
0, // Index
NIL, // Pointer to the blob
@cbDecoded); // Size of the blob

new(pbDecoded);

CryptMsgGetParam(
hMsg, // Handle to the message
CMSG_CONTENT_PARAM, // Parameter type
0, // Index
pbDecoded, // Pointer to the blob
@cbDecoded); // Size of the blob

...то результат подобный: cbDecoded=0, pbDecoded=какая-то чушь...
15.02.2005 15:15:44Юрий
На С++:
if(!CryptMsgGetParam(...,NULL,&dwSize))
{
// something wrong, write ERROR!
}

BYTE* pbSignerInfo=(BYTE*)malloc(dwSize);
ZeroMemory(pbSignerInfo,dwSize);

if(!CryptMsgGetParam(...,pbSignerInfo,&dwSize))
{
// something wrong, write ERROR!
}

То есть ошибки еще нужно проверять и оператор типа "new(pSignerInfo)" - это что? pSignerInfo это класс что-ли какой-то? ПАМЯТЬ (блок памяти) нужно выделить.
16.02.2005 13:12:28Дробязко Алексей
Добрый день. Ну вроде получилось с проверкой подписи...вроде проверяет :).
Теперь возник вопрос, как вытащить из контекста сертификата серийный номер, срок его действия, используемый алгоритм хеширования(т.е., например, у меня есть его ID=’1.2.840.113549.1.1.5’, а я хотел бы получить SHA1), подписи(1.2.840.113549.1.1.1 -> RSA).
Спасибо.
16.02.2005 13:44:13Юрий
...Все, хватит потворствовать "недочиту документации"...
Знаю как, но лучше почитать самому.
16.02.2005 18:32:40Дробязко Алексей
Большое спасибо за науку! Разобрался. Даже прицепил к подписи время ее создания. :))
Еще раз прошу прощения за назойливость.