| ||||
| ||||
При вызове функции CryptSignHash() Получаю ошибку (0x8009000D) - Key does not exist. Сертификат ГОСТ 34.10-2001. Аналогичный сертификат по ГОСТ 34.10-94 подписывает на ура. | ||||
Ответы: | ||||
| ||||
Забыл написать: csp 2.0 | ||||
| ||||
Странный у Вас код - в вызове CryptSignHash сертификат не участвует. Можете рассказать поподробнее про порядок вызова функций? | ||||
| ||||
STDMETHODIMP CSignMaker::OpenKeyContainer(BSTR certSubj, CertStoreLocation keyLoc, BSTR certNum) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); DWORD dwFlags = 0; HCRYPTPROV hCertStore = NULL; DWORD dwNameLen = 0; LPCWSTR nm = NULL; BYTE* name = NULL; DWORD dwKeySpec; BOOL fCallerFreeProv; try{ CloseKeyContainer(); dwFlags = (keyLoc==CSL_LOCAL_MACHINE) ? CERT_SYSTEM_STORE_LOCAL_MACHINE : CERT_SYSTEM_STORE_CURRENT_USER; F_hCertStore = CertOpenStore(CERT_STORE_PROV_SYSTEM, MY_CERT_ENCODING, F_hCrProv, dwFlags, L"MY"); if(!F_hCertStore) ThrowLastErrorString("CertOpenStore()"); nm = ((CComBSTR)certSubj).Detach(); CComBSTR _certNum = certNum; _certNum.ToUpper(); string sn0 = (certNum != NULL) ? (string)(_bstr_t)_certNum : ""; F_phCert = NULL; while(true){ F_phCert = CertFindCertificateInStore(F_hCertStore, MY_CERT_ENCODING, 0, CERT_FIND_SUBJECT_STR_A, nm, F_phCert); if(!F_phCert) ThrowLastErrorString("CertFindCertificateInStore()"); string sn1 = CertNoToHex(F_phCert->pCertInfo->SerialNumber.pbData, F_phCert->pCertInfo->SerialNumber.cbData); if((sn0 == "") || (sn0 == sn1)) break; } if(!CryptAcquireCertificatePrivateKey(F_phCert, MY_ACQUIRE_FLAG, NULL, &F_hCrProv, &dwKeySpec, &fCallerFreeProv)) ThrowLastErrorString("CryptAcquireCertificatePrivateKey()"); if(!fCallerFreeProv) ThrowStringException("Certificate Public Key Is Not Usable."); if(!CryptImportPublicKeyInfo(F_hCrProv, MY_CERT_ENCODING, &(F_phCert->pCertInfo->SubjectPublicKeyInfo), &F_hUserKey)) ThrowLastErrorString("CryptImportPublicKeyInfo()"); } catch(string &e){ return Error(e.c_str(), IID_ISignMaker); } return S_OK; } STDMETHODIMP CSignMaker::Sign(BSTR b64str, BSTR* res) { AFX_MANAGE_STATE(AfxGetStaticModuleState()); try{ CloseHash(); if(!CryptCreateHash(F_hCrProv, CRYPTO_PRO_ALG_ID, NULL, 0, &F_hHash)) ThrowLastErrorString("CryptCreateHash()"); string b64s = (string)(_bstr_t)b64str; BYTE* pData = (BYTE*)alloca(DATA_BUF_LEN + 1); DWORD dwDataLen; while((dwDataLen = Base64_ToBin(b64s, (char*)pData, DATA_BUF_LEN)) > 0){ if(!CryptHashData(F_hHash, pData, dwDataLen, 0)) ThrowLastErrorString("CryptHashData()"); } DWORD dwSignLen = 1024; // если без вычисления размера хэша (должно хватить по любому) if(!CryptSignHash(F_hHash, AT_SIGNATURE, NULL, 0, NULL, &dwSignLen)) ThrowLastErrorString("CryptSignHash(NULL)"); pData = (BYTE*)alloca(dwSignLen + 128); // На всякий случай ! if(!CryptSignHash(F_hHash, AT_SIGNATURE, NULL, 0, pData, &dwSignLen)) ThrowLastErrorString("CryptSignHash(data)"); b64s = Bin_ToBase64((char*)pData, dwSignLen); *res = CComBSTR(b64s.c_str()).Detach(); } catch(string &e){ CloseHash(); return Error(e.c_str(), IID_ISignMaker); } CloseHash(); return S_OK; } | ||||
| ||||
#define CRYPTO_PRO_TYPE 71 #define CRYPTO_PRO_NEW_TYPE 75 #define CRYPTO_PRO_ALG_ID 32798 #define MY_CERT_ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) #define MY_NAME_ENCODING (CERT_X500_NAME_STR | CERT_NAME_STR_ENABLE_UTF8_UNICODE_FLAG | CERT_NAME_STR_REVERSE_FLAG) #define MY_ACQUIRE_FLAG (CRYPT_ACQUIRE_COMPARE_KEY_FLAG | CRYPT_ACQUIRE_SILENT_FLAG) | ||||
| ||||
Здесь скорее всего в ключевом контейнере отсутствует сгенерированый ключ для AT_SIGNATURE / AT_KEYEXCHANGE. Сгенерируйте предварительно ключ и все будет впорядке. | ||||
| ||||
Оба сертификата лежат в хранилище сертификатов, в одном каталоге. Оба при просмотре показывают наличие секретного ключа. Инсталлировались абсолютно одинаково. Один подписывает, другой - нет. | ||||
| ||||
Не важно какие сертификаты где лежат, главное что в ключевых контейнерах, с которыми ассоциированы эти сертификаты, должны быть соответствующие ключи. Смотреть в MSDN, CryptGenKey. | ||||
| ||||
Все ключи и сертификаты устанавливались через Copy container + Install personal certificate. У обоих сертификатов есть право Client authentication (1.3.6.1.5.5.7.3.2). У того, который не работает, есть еще Secure Email (1.3.6.1.5.5.7.3.4) и Пользователь Центра Регистрации (1.2.643.2.2.34.6). Более того, оба сертификата подписывают через CAPICOM. | ||||
| ||||
Мне все-равно как сертификаты были установлены или сделаны. Я говорю, что Вам нужно сделать для того чтобы Ваш же код у Вас же работал. | ||||
| ||||
Я как-то предполагал, что функция CryptAcquireCertificatePrivateKey (см.выше) и предназначена для загрузки секретного ключа из контейнера. Не совсем понятно зачем мне генерить новую пару ключей на лету. Такой вариант нужен, скорее, для выработки сеансового ключа. Вопрос №2: по содержимому файлов name.key, header.key, primary.key, masks.key, primary2.key, masks2.key можно как-то понять разрешено ли ставить подпись на ключе из данного контейнера? Может какой битовый флаг посмотреть? | ||||
| ||||
То есть Вы предполагаете, что следующая последовательность успешно сгенерирует пары ключей? 1) CryptAcquireContext(..., CRYPT_NEWKEYSET) 2) CryptReleaseContext | ||||
| ||||
А почему у Вас в CryptSignHash жестко прописано AT_SIGNATURE? Это неправильно, там должна быть переменная dwKeySpec, которую возвращает CryptAcquireCertificatePrivateKey. | ||||
| ||||
То ли я тупой, то ли вы о чем-то не о том... CertOpenStore() - открываем хранилище CertFindCertificateInStore() - находим сертификат CryptAcquireCertificatePrivateKey() - грузим секретный ключ сертификата и получаем криптопровайдера CryptImportPublicKeyInfo() - импортируем открытый ключ в криптопровайдер. Теоретически, если нет ошибок, криптопровайдер готов к подписи. Что и происходит в случае с первым сертификатом. Почему не работает второй? ГОСТ 2001 года предусматривает принципиально другой алгоритм подписи? С выработкой дополнительных пар ключей? Действительно, в переменной dwKeySpec возвращается AT_KEYEXCHANGE. | ||||
| ||||
Заработало! После подстановки dwKeySpec в CryptSignHash. Спасибо! | ||||
| ||||
Для подписи не надо импортировать ОК, это надо для проверки. Алгоритм подписи там действительно не такой, как в 94 из-за другого алгоритма ОК, но тут дело было не в этом. В контейнере могут быть 2 ключа - подписи и обмена, в Вашем случае на 94 ГОСТе ключ AT_SIGNATURE был, а на 2001- нет, поэтому и не работало. А подписывать можно и ключами подписи, и ключами обмена. | ||||