13.01.2004 11:14:57"доподпись" сообщения Ответов: 20
Alexandr
как используя Crypto API (Low Level Message Functions) побавить подпись к уже подписанному сообщению, так, чтобы сообщение, полученное в результате, выглядело как подписанное двумя подписями (т.е. не добавляя подпись "поверх" первого)?
 
Ответы:
15.01.2004 11:28:37Седов Роман
Последовательность действий должна быть такая:

1. Вызов CryptMsgOpenToDecode+CryptMsgUpdate для декодирования "доподписываемого" сообщения.
2. Вызов CryptMsgControl с параметром "CMSG_CTRL_ADD_SIGNER".
3. Получившиеся сообщение получаем функцией CryptMsgGetParam с параметром "CMSG_ENCODED_MESSAGE"
4. Все.
15.01.2004 12:11:33Alexandr
спасибо
08.11.2006 11:10:33Андрей Горячев
Есть вот такая функция, которая доподписывает сообщение. Проблема в том, что в Windows 2000 и XP функция работает, а в Win 98 нет, на функции CryptAcquireCertificatePrivateKey пишет ошибку (Операция завершена успешно). В MSDN сказано, что функция присутствует при установленом IE 5 и выше. В Crypt32.dll функция CryptAcquireCertificatePrivateKey присутствует.

В чём может быть проблема?


extern "C" bool __declspec(dllexport) CosignMessage(const BYTE* signedMessage, DWORD signedMessageSize,
BYTE* cosignedMessageBlob, DWORD* cosignedMessageBlobSize,
LPCSTR certHash, DWORD certHashLength)
{
HCRYPTMSG cryptMsg = 0;
HCERTSTORE hStoreHandle = 0;
PCCERT_CONTEXT certContext = 0;
CRYPT_HASH_BLOB blobCert;
HCRYPTPROV hCryptProv = NULL;
DWORD keySpec = 0;
char OID[64] = szOID_CP_GOST_R3411;

BYTE bytes[20];
blobCert.cbData = 20;
blobCert.pbData = bytes;
stringToBinary((char*)certHash, &blobCert);

if (!(hStoreHandle = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_CURRENT_USER, PERSONAL_STORE_NAME)))
{
setLastError();
return false;
}

if(!(certContext = CertFindCertificateInStore(hStoreHandle, MY_ENCODING_TYPE, 0, CERT_FIND_HASH, &blobCert, NULL)))
{
CertCloseStore(hStoreHandle, 0);
setLastError();
return false;
}

if (!CryptAcquireCertificatePrivateKey(certContext, CRYPT_ACQUIRE_CACHE_FLAG, NULL, &hCryptProv, &keySpec, NULL))
{
CertCloseStore(hStoreHandle, 0);
CertFreeCertificateContext(certContext);
setLastError();
return false;
}

if (!(cryptMsg = CryptMsgOpenToDecode(MY_ENCODING_TYPE, 0, 0, NULL, NULL, NULL)))
{
CryptReleaseContext(hCryptProv, 0);
CertCloseStore(hStoreHandle, 0);
CertFreeCertificateContext(certContext);
setLastError();
return false;
}

if(!CryptMsgUpdate(cryptMsg, signedMessage, signedMessageSize, TRUE))
{
CryptReleaseContext(hCryptProv, 0);
CertCloseStore(hStoreHandle, 0);
CertFreeCertificateContext(certContext);
setLastError();
return false;
}

CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
memset(&HashAlgorithm, 0, sizeof(CRYPT_ALGORITHM_IDENTIFIER));
HashAlgorithm.pszObjId = OID;

CMSG_SIGNER_ENCODE_INFO signerInfo;
memset(&signerInfo, 0, sizeof(CMSG_SIGNER_ENCODE_INFO));
signerInfo.cbSize = sizeof(CMSG_SIGNER_ENCODE_INFO);
signerInfo.pCertInfo = certContext->pCertInfo;
signerInfo.hCryptProv = hCryptProv;
signerInfo.cAuthAttr = 0;
signerInfo.cUnauthAttr = 0;
signerInfo.dwKeySpec = keySpec;
signerInfo.HashAlgorithm = HashAlgorithm;
signerInfo.pvHashAuxInfo = NULL;
signerInfo.rgAuthAttr = NULL;
signerInfo.rgUnauthAttr = NULL;

if (!CryptMsgControl(cryptMsg, 0, CMSG_CTRL_ADD_SIGNER, &signerInfo))
{
CryptReleaseContext(hCryptProv, 0);
CertCloseStore(hStoreHandle, 0);
CertFreeCertificateContext(certContext);
setLastError();
return false;
}

// добавим сертификат
CERT_BLOB certInfo;
certInfo.cbData = certContext->cbCertEncoded;
certInfo.pbData = certContext->pbCertEncoded;
if (!CryptMsgControl(cryptMsg, 0, CMSG_CTRL_ADD_CERT, &certInfo))
{
CryptReleaseContext(hCryptProv, 0);
CertCloseStore(hStoreHandle, 0);
CertFreeCertificateContext(certContext);
setLastError();
return false;
}

if (!CryptMsgGetParam(cryptMsg, CMSG_ENCODED_MESSAGE, 0, cosignedMessageBlob, cosignedMessageBlobSize))
{
CryptReleaseContext(hCryptProv, 0);
CertCloseStore(hStoreHandle, 0);
CertFreeCertificateContext(certContext);
setLastError();
return false;
}

CryptReleaseContext(hCryptProv, 0);
CertCloseStore(hStoreHandle, 0);
CertFreeCertificateContext(certContext);
return true;
}
08.11.2006 11:12:49Седов Роман
Программа - "юникодная"?
08.11.2006 11:15:28Андрей Горячев
Сама программа на C# .NET - следовательно юникодная.
А данная функция заключена в C++ Dll (отдельная библиотека), в параметрах dll'ки указано, чтобы не использовать Unicode, так что вроде не должна быть.
08.11.2006 11:19:34Седов Роман
Просто это важно с точки зрения Windows 98.
Чтобы убедится наверняка я бы посоветовал открыть вашу DLL с этим кодом в Microsoft Dependency Walker (depends.exe из состава Visual Studio или Platform SDK).
Там будет явно указан суффикс импортируемых функций - "A" или "W".
08.11.2006 11:29:33Андрей Горячев
Открыл.
Там нет никаких суффиксов и префиксов.
Простоназвания функций, такие, какими я их называл в коде.
08.11.2006 11:41:16Седов Роман
Да, действительно. Используемые функции в единственном экземпляре.
Так, едем дальше: "CryptAcquireCertificatePrivateKey пишет ошибку (Операция завершена успешно)" - как вы получаете код/текст ошибки?
08.11.2006 11:45:42Андрей Горячев
extern "C" void __declspec(dllexport) setLastError()
{
LPCSTR lpMsgBuf;

FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPSTR)&lpMsgBuf,
0, NULL);

lastError = lpMsgBuf;
}

Вот такой вот функцией.
08.11.2006 11:47:51Седов Роман
Но между вызовами CryptAcquireCertificatePrivateKey() и setLastError() вы вызываете CertCloseStore() и CertFreeCertificateContext().
Они сбрасывают "LastError" на 0.
GetLastError() необходимо вызывать сразу после интересующей функции.
08.11.2006 12:16:32Андрей Горячев
вот на этом месте вываливается оказывается:

CMSG_SIGNER_ENCODE_INFO signerInfo;
memset(&signerInfo, 0, sizeof(CMSG_SIGNER_ENCODE_INFO));
signerInfo.cbSize = sizeof(CMSG_SIGNER_ENCODE_INFO);
signerInfo.pCertInfo = certContext->pCertInfo;
signerInfo.hCryptProv = hCryptProv;
signerInfo.cAuthAttr = 0;
signerInfo.cUnauthAttr = 0;
signerInfo.dwKeySpec = keySpec;
signerInfo.HashAlgorithm = HashAlgorithm;
signerInfo.pvHashAuxInfo = NULL;
signerInfo.rgAuthAttr = NULL;
signerInfo.rgUnauthAttr = NULL;

if (!CryptMsgControl(cryptMsg, 0, CMSG_CTRL_ADD_SIGNER, &signerInfo))
{
setLastError();
MessageBox(NULL, lastError, "CryptMsgControl_1", 0);
CryptReleaseContext(hCryptProv, 0);
CertCloseStore(hStoreHandle, 0);
CertFreeCertificateContext(certContext);
return false;
}

при выводе ошибки, пишет: ",ye" :)
Видимо что то со структурой не то, она правильно заполнена?
08.11.2006 12:20:03Седов Роман
Код ошибки-то какой?
08.11.2006 12:29:47Андрей Горячев
Код: -2146893811 = 0x8009000D
Значение: Ключ не существует.

Вот
08.11.2006 12:39:07Андрей Горячев
Может быть всё таки CryptAcquireCertificatePrivateKey не срабатывает? Возвращает что нить не то...

MSDN:

Client Requires: Windows XP, Windows 2000 Professional, Windows NT Workstation 4.0 SP3 and later, or Windows Me.

Server Requires: Windows Server 2003, Windows 2000
Server, or Windows NT Server 4.0 SP3 and later.

Redistributable: Requires Internet Explorer 5 or later on Windows 98 or Windows 95.

Header Declared in: Wincrypt.h.
Library Link to: Crypt32.lib.
DLL Requires: Crypt32.dll.
08.11.2006 15:46:09Андрей Горячев
заменил CryptAcquireCertificatePrivateKey на Вытаскивание keySpec и контекста криптопровайдера из свойств сертификата, выбранного из хранилища.
Проблема такая же, в XP работает, в 98 ошибка: отсутствует набор ключей!
08.11.2006 15:53:50Андрей Горячев
точнее не "отсутствует набор ключей" a "Ключ не существует"
08.11.2006 15:54:51Седов Роман
В общем, ошибка с CryptAcquireCertificatePrivateKey() воспроизводится на примере cryptcp на Windows 98.
Попробую поизучать еще.
08.11.2006 15:58:13Андрей Горячев
Я вот как раз оттуда и взял, что надо открыть сертификат из хранилища и получить из него свойства. И так тоже не работает
09.11.2006 13:59:19Седов Роман
Попробуйте передать в CryptMsgOpenToDecode() hProv
09.11.2006 17:24:19Андрей Горячев
Отлично! Спасибо, заработало.