18.05.2004 17:10:40наличие закрытого ключа. Ответов: 7
Михаил
Все еще не нашел ответа на вопрос как узнать есть ли для данного сертификата соотв. закрытый ключ.
Вариант 1.
CertGetCertificateContextProperty(pCertContext, CERT_KEY_PROV_INFO_PROP_ID, NULL, &dwSize)

дает странные результаты - для некоторых сертификатов получается что ключа нет, а на самом деле он есть.

Вариант 2
CryptAcquireCertificatePrivateKey(pCertContext, NULL, NULL, &hCryptProv,&dwKeySpec,
&fCallerFreeProv)

дает 100% результаы, но работает достаточно долго и я думаю что он будет пытаться лезть на внешний носитель в случае его применения.

Вопрос как лучше сделать ?
 
Ответы:
19.05.2004 13:39:21Василий
А можно поподробнее (или пример), когда в варианте 1 функция возвращает неправильный результат.
19.05.2004 17:51:48Михаил
по п.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 работает правильно только если сертификат виден в окне сертификатов эксплорера. Как только он оттуда пропадает - эта функция начинает возвращать неверные данные. Возможно я неправильно добавляю сертификат. Тогда скажите пожалуйста как можно добавить его так, чтобы его увидел и эксплорер.
20.05.2004 9:48:36kure
Резюме не правильное. Функция работает правильно в не зависимости виден ли сертификат или нет в окошке.
Просто когда функция не возвращает ссылку на закрытый ключ это тоже правильно и значит, что она не установлена.

Обратите внимание, что в браузере показываются только те сертификаты, в которых ссылка на закрытый ключ есть. Видимо так и считается, что в персональных должны быть сертификаты со ссылками на закрытый ключ. Если же запустите консоль mmc то в персональных увидите все сертификаты.
Когда вы добавляете сертификат в персональные и у вас есть соответствующий закрытый ключ установите ссылку.

Это по технике. По жизни. Вам нужно узнать есть ли ссылка (программно можно сделать связь сертификата с любым ключом) либо необходимо убедиться, что ключ есть и он соттветствует открытому. В последнем случае без подписи/проверки не обойтись.
20.05.2004 9:52:59Василий
В описании функции 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,...
20.05.2004 15:53:56Михаил
цитата: Обратите внимание, что в браузере показываются только те сертификаты, в которых ссылка на закрытый ключ есть.

дело в том после удаления/добавления ссылка на закрытый ключ есть (экспериментально проверено) а вот из окна браузера сертификат пропадает.
Конечно случай с удалением/добавлением надуманный (просто я игрался с этим и наткнулся на такой результат), но есть и практический вопрос. После получения сертификата по запросу, если его добавить не через PKCSAccept а через CertAddEncodedCertificateToStore то связь с закрытым ключем НЕ устанавливается. А как можно реализовать добавление с автоматическим связыванием (как это сделано в CAPICOM). Ведь если делать попытку связывания с закрытым ключем каждого добавляемого через CertAdd сертификата, то проблема автоматически снимется, как я понял. Но я же не знаю какой именно контейнер закрытого ключа соответствует данному и сертификату и есть ли вообще он (контейнер) на данной машине.
21.05.2004 11:33:43Василий
Я не понял, в чём, собственно, проблема.
После получения сертификата по запросу, если его добавить через PKCSAccept связь с закр. ключом будет установлена только в том случае, если на машине есть запрос на этот сертификат (в хранилище REQUEST). Оттуда и берется ссылка на закр. ключ.
Если запроса в хранилище почему-то нет (например, при установке сертификата из файла на другой машине), то ссылку на секр. ключ нужно делать самостоятельно. И тут уж Ваша забота знать, какой контейнер соответствует сертификату, т.к. контейнер переносится вместе с сертификатом.
Так и сделано при установке сертификата через нашу панель (кнопка "Установить личный сертификат") - пользователь выбирает файл сертификата, а потом указывает контейнер секр. ключа.
21.05.2004 11:37:11kure
При этом производится криптографическая проверка соответствия ключей