| ||||
| ||||
Все еще не нашел ответа на вопрос как узнать есть ли для данного сертификата соотв. закрытый ключ. Вариант 1. CertGetCertificateContextProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, NULL, &dwSize) дает странные результаты - для некоторых сертификатов получается что ключа нет, а на самом деле он есть. Вариант 2 CryptAcquireCertificatePrivateKey(pCertContext, NULL, NULL, &hCryptProv,&dwKeySpec, &fCallerFreeProv) дает 100% результаы, но работает достаточно долго и я думаю что он будет пытаться лезть на внешний носитель в случае его применения. Вопрос как лучше сделать ? | ||||
Ответы: | ||||
| ||||
А можно поподробнее (или пример), когда в варианте 1 функция возвращает неправильный результат. | ||||
| ||||
по п.1 делаю так: hCertStore = CertOpenSystemStore(0, "MY"); pCertContext = CertEnumCertificatesInStore(hCertStore, pCertContext) if( CertGetCertificateContextProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, NULL, &dwSize) ) // ключ есть Формирую запрос, получаю сертификат добавляю его (AcceptPCKSFile...) Говорит закр ключ есть. Однако через некоторое время (причину я та и не нашел) сертификат перестает показываться в стандартном окне UI на закладке "личные", хотя все равно виден через EnumCerts и эта функция говорит что закр ключа нету (возвращает 0). Однако если в этом случае вызвать CryptAcquireCertificatePrivateKey то все ок. А сертификат пропадает из окна UI (окошко эксплорера также при вызове двух функций: CertDeleteCertificateFromStore а затем CertAddEncodedCertificateToStore из файла .cer Закрытый ключ создается с флагом EXPORTABLE. Так вот - после удаления и вновь добавления сертифика UI его уже не видит, CertGetCertificateContextProperty возвращает 0 хотя повторю - Acqure возвращает ок и расшифровывать / подписывать с помощью него можно. Т.е. закрытый ключ есть и доступен). Резюме: CertGetCertificateContextProperty работает правильно только если сертификат виден в окне сертификатов эксплорера. Как только он оттуда пропадает - эта функция начинает возвращать неверные данные. Возможно я неправильно добавляю сертификат. Тогда скажите пожалуйста как можно добавить его так, чтобы его увидел и эксплорер. | ||||
| ||||
Резюме не правильное. Функция работает правильно в не зависимости виден ли сертификат или нет в окошке. Просто когда функция не возвращает ссылку на закрытый ключ это тоже правильно и значит, что она не установлена. Обратите внимание, что в браузере показываются только те сертификаты, в которых ссылка на закрытый ключ есть. Видимо так и считается, что в персональных должны быть сертификаты со ссылками на закрытый ключ. Если же запустите консоль mmc то в персональных увидите все сертификаты. Когда вы добавляете сертификат в персональные и у вас есть соответствующий закрытый ключ установите ссылку. Это по технике. По жизни. Вам нужно узнать есть ли ссылка (программно можно сделать связь сертификата с любым ключом) либо необходимо убедиться, что ключ есть и он соттветствует открытому. В последнем случае без подписи/проверки не обойтись. | ||||
| ||||
В описании функции CertAddEncodedCertificateToStore в MSDN есть фраза "The context created does not include any extended properties", в частности, нет ссылки на закр. ключ. Более того, это и идеологически так - сертификат в файле НИКОГДА не содержит ссылку на закр.ключ. Однако, есть особенности, если сертификат был ранее установлен на машине и имел привязку к ключу, а потом был удален из хранилища. Особенность в том, что при удалении из хранилища сертификат не уничтожается физически, а попадает в "архив сертификатов". Поэтому, если установить сертификат из файла, при определённых условиях восстанавливается привязка к ключу из архивного сертификата. Это поведение, по-моему, нигде не описано, не зависит от CSP и подтверждается экспериментально. Резюме такое - при установке сертификата из файла необходимо явным образом установить привязку к закр. ключу: Для нужгого ключа (AT_KEYEXCHANGE или AT_SIGNATURE) заполняем структуру: typedef struct _CRYPT_KEY_PROV_INFO { LPWSTR pwszContainerName; LPWSTR pwszProvName; DWORD dwProvType; DWORD dwFlags; DWORD cProvParam; PCRYPT_KEY_PROV_PARAM rgProvParam; DWORD dwKeySpec; } CRYPT_KEY_PROV_INFO, *PCRYPT_KEY_PROV_INFO; Получаем UNICODE-имя провайдера pwszContainerName = A2W(CryptGetProvParam(hProv, PP_UNIQUE_CONTAINER... ) Получаем UNICODE-уникальное имя контейнера pwszProvName = A2W(CryptGetProvParam(hProv, PP_NAME...) Получаем тип провайдера: dwProvType = CryptGetProvParam(hProv,PP_PROVTYPE... dwFlags = 0 cProvParam = 0 rgProvParam = 0 dwKeySpec = AT_SIGNATURE (для hKeySig) dwKeySpec = AT_KEYEXCHANGE (для hKeyExch) потом вызываем CertSetCertificateContextProperty (ctx, CERT_KEY_PROV_INFO_PROP_ID, 0, provInfo,... | ||||
| ||||
цитата: Обратите внимание, что в браузере показываются только те сертификаты, в которых ссылка на закрытый ключ есть. дело в том после удаления/добавления ссылка на закрытый ключ есть (экспериментально проверено) а вот из окна браузера сертификат пропадает. Конечно случай с удалением/добавлением надуманный (просто я игрался с этим и наткнулся на такой результат), но есть и практический вопрос. После получения сертификата по запросу, если его добавить не через PKCSAccept а через CertAddEncodedCertificateToStore то связь с закрытым ключем НЕ устанавливается. А как можно реализовать добавление с автоматическим связыванием (как это сделано в CAPICOM). Ведь если делать попытку связывания с закрытым ключем каждого добавляемого через CertAdd сертификата, то проблема автоматически снимется, как я понял. Но я же не знаю какой именно контейнер закрытого ключа соответствует данному и сертификату и есть ли вообще он (контейнер) на данной машине. | ||||
| ||||
Я не понял, в чём, собственно, проблема. После получения сертификата по запросу, если его добавить через PKCSAccept связь с закр. ключом будет установлена только в том случае, если на машине есть запрос на этот сертификат (в хранилище REQUEST). Оттуда и берется ссылка на закр. ключ. Если запроса в хранилище почему-то нет (например, при установке сертификата из файла на другой машине), то ссылку на секр. ключ нужно делать самостоятельно. И тут уж Ваша забота знать, какой контейнер соответствует сертификату, т.к. контейнер переносится вместе с сертификатом. Так и сделано при установке сертификата через нашу панель (кнопка "Установить личный сертификат") - пользователь выбирает файл сертификата, а потом указывает контейнер секр. ключа. | ||||
| ||||
При этом производится криптографическая проверка соответствия ключей | ||||