| ||||
| ||||
Добрый день! Возникла проблема, вкратце описать которую не получается, поэтому опишу подробно :) Необходимо проверять ЭЦП и подписывать документы (на одной и той же машине). Документы очень большого объема, сертификаты (в виде блобов) извлекаются из базы данных, в системе установлен КриптоПро. Реализован примерно такой алгоритм: ПРИ ПОДПИСИ CryptAcquireContext(&csp,сontainerName, cspName, DWPROVTYPE, 0) ); .... CryptCreateHash(csp, HASHFLAG, 0, 0, &hash); .... (в цикле вычисляем хеш для кусков документа) for (int i=0; i<iCycles; ++i) { .... CryptHashData(hash, message, size, 0); ... } .... CryptSignHash(hash, KEYSPEC, NULL, 0, NULL, &cbSignature); .... CryptSignHash(hash, KEYSPEC, NULL, 0, signature, &cbSignature); ПРИ ПРОВЕРКЕ CryptAcquireContext(&csp, сontainerName, cspName, DWPROVTYPE, 0); .... CryptImportPublicKeyInfo(csp, MY_ENCODING_TYPE, &(pContext->pCertInfo->SubjectPublicKeyInfo), &pubKey) .... CryptCreateHash(csp, HASHFLAG, 0, 0, &hash); .... (в цикле вычисляем хеш для кусков документа) for (int i=0; i<iCycles; ++i) { .... CryptHashData(hash, message, size, 0); ... } .... CryptVerifySignature(hash, sign, cbSign, pubKey, NULL, 0); При использовании Microsoft Enhanced Cryptographic Provider v1.0 и подписи хеша с флагом AT_SIGNATURE все в порядке. Как только перехожу на Crypto-Pro GOST R 34.10-94 Cryptographic Service Provider и AT_KEYEXCHANGE функция CryptVerifySignature возвращает NTE_BAD_SIGNATURE. Документы точно приходят одни и те же (при подписывании/проверке), никаких нарушений в структуре ЭЦП тоже не происходит, на вход проверке подается ровно то, что получилось на выходе подписывания. Что я не учитываю, не понимаю, делаю не так? | ||||
Ответы: | ||||
| ||||
проверка - вместо CryptAcquireContext(&csp, сontainerName, cspName, DWPROVTYPE, 0), попробуйте CryptAcquireContext(&csp, NULL, cspName, DWPROVTYPE, CRYPT_VERIFYCONTEXT) | ||||
| ||||
Кирилл, спасибо за совет. Попробовал, не помогло :) Хотя, конечно, флаг CRYPT_VERIFYCONTEXT и должен там находится по логике вещей. Скачал примеры (исходники) с этого сайта и возник еще один вопрос. В программе определяется параметр HP_OID, выделяется для него память, в хеш (почему-то функцией CryptGetHashParam а не CryptSetHashParam) передается эта память. Что это? Может, именно из-за "неопределения" этого параметра мои подписи некорректны? | ||||
| ||||
Вы csptest скачали? Где именно там определяется параметр HP_OID? | ||||
| ||||
Файл SigningHash.c Куски кода: При подписывании /-------------------------------------------------------------------- // Определение размера BLOBа и распределение памяти. if(CryptGetHashParam(hHash, HP_OID, NULL, &cbHash, 0)) { printf("Size of the BLOB determined. \n"); } else { HandleError("Error computing BLOB length."); } pbHash = (BYTE*)malloc(cbHash); if(pbHash) { printf("Memory has been allocated for the pbHash. \n"); } else { HandleError("Out of memory. \n"); } //-------------------------------------------------------------------- // Копирование параметра HP_OID в pbHash. if(CryptGetHashParam(hHash, HP_OID, pbHash, &cbHash, 0)) { printf("Parameters have been written to the pbHash. \n"); } else { HandleError("Error during CryptGetHashParam."); } При проверке //-------------------------------------------------------------------- // Установка параметра HP_OID. // По умолчанию провайдер работает на наборе параметров 1.2.643.2.2.30.1. // Без установки параметра HP_OID программа будет неверно работать, // если параметры хеширования не будут являться параметрами // по умолчанию. if(CryptSetHashParam( hHash, HP_OID, pbHash, 0)) { printf("The parameters have been set. \n"); } else { HandleError("Error during SetHashParam."); } | ||||
| ||||
Ага, просто невнимательно посмотрел с самого начала. Это просто параметры хеша передаются для корректной проверки. Однако, вопрос остается открытым: что я делаю не так, почему NTE_BAD_SIGNATURE? Добавлю - только что попробовал получать контекст криптопровайдера (при подписывании) через CryptAcquireCertificatePrivateKey(pContext, 0, NULL, &csp, &dwKeySpec, &fCallerFreeProv); Получил ошибку CRYPT_E_NO_KEY_PROPERTY Ерунда какая-то. Сертификат точно существует и связан с ключами, он вообще единственный. | ||||
| ||||
Видимо, для начала неплохо бы убедиться в том, что а) вычисленные хеш-значения при подписи и при проверке подписи совпадают, б) при экспорте ключа подписи в PUBLICKEYBLOB его значение совпадает с тем, что в сертификате. | ||||
| ||||
Василий! > вычисленные хеш-значения при подписи и при проверке подписи совпадают Проверить не знаю как, но они совпадают (почему пришел к такому выводу объясню ниже) > при экспорте ключа подписи в PUBLICKEYBLOB его значение совпадает с тем, что в сертификате Вообще я изначально не планировал экспортировать открытый ключ. Мне виделось, что вместе с ЭЦП лучше хранить отпечаток сертификата (20 байт), а не открытый ключ, соответствующий подписи (100 байт). По отпечатку я хочу получить сертификат и извлечь из него ключ посредством CryptImportPublicKeyInfo (я правильно рассуждаю?). НО! После того, как я экспортировал ключ при подписи (CryptExportKey) и при проверке импортировал его (CryptImportKey) - все заработало, проверка проходит успешно (на основании чего я делаю вывод что хеши при подписании и проверке совпадают). То есть CryptImportPublicKeyInfo импортирует открытый ключ, не соответствующий закрытому ключу, который использовался при подписывании? Почему так? Почему на майкрософтовских провайдерах такой ситуации не встречалось? И как мне получить "соответствующий" ключ с помощью CryptImportPublicKeyInfo ? Спасибо. | ||||
| ||||
Что-то тут не так. Настораживает вот что: > CryptAcquireCertificatePrivateKey(pContext, 0, NULL, &csp, &dwKeySpec, &fCallerFreeProv); > Получил ошибку CRYPT_E_NO_KEY_PROPERTY Если сертификат берётся из хранилища, и есть ссылка на ключевой контейнер, то контейнер должен был открыться. Может, сертификат не тот или ключ не тот (AT_SIGNATURE или AT_KEYEXCHANGE)? Попробуйте сохранить этот сертификат в файл и установить его через панель CSP - Сервис - "Установить личный сертификат" - он заодно проверит соответствие закрытого и открытого ключей | ||||
| ||||
Сертификат не сохраняется, если ставить галочку "Экспортировать закрытый" ключ. Сразу хочу отметить, что сертификат 1) тестовый, 2) с истекшим сроком действия. При этом пишет "экспорт выполнен успешно", но сам файл не создается. Если не сохранять без закрытого ключа, то все дальнейшие действия проходят успешно. Во всяком случае ошибок нигде никто не выдает :) | ||||
| ||||
Сертификат ГОСТ нельзя сохранить в файл pfx с закрытым ключом. Это сделано специально, и неоднократно упоминалось на данном форуме. | ||||
| ||||
"То есть CryptImportPublicKeyInfo импортирует открытый ключ, не соответствующий закрытому ключу, который использовался при подписывании?" CryptImportPublicKeyInfo никак с закрытым ключем не связана и импортирует тот ОК, который Вы ей дадите. | ||||