07.02.2007 11:40:48Проблема с установкаой личного сертификата Ответов: 11
Денис
При программной установке сертификата в раздел "ЛИЧНЫЕ" они не отображаются если зайти IE->Свойства обозревателя->Сертификаты, хотя при запуске консоли mmc видно что они есть! При установке в "Другие пользователи", "Промежуточный центры" и "Доверенные корневые" отображаются исправно.
Код установки:

PCCERT_CONTEXT pUserCert = NULL;
HANDLE hCertStore = 0;
HANDLE hMyStore = 0;
WideString L = "My";

hCertStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
0,
CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_CURRENT_USER,
L
);

rv = CertAddEncodedCertificateToStore(hCertStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
pBuffer, cBuffer,
CERT_STORE_ADD_REPLACE_EXISTING|CERT_STORE_ADD_ALWAYS,
NULL);

Заранее благодарен за ответы!
 
Ответы:
07.02.2007 14:04:02Василий
IE (а равно, Outlook, Outlook Express), как правило, не отображают те сертификаты из хранилища Личные, у которых нет ссылки на закрытый ключ.
08.02.2007 4:47:14Денис
Но ключ то есть в реестре. Или надо как то явно ссылку указывать?
08.02.2007 12:58:48Василий
Конечно, надо. Сама она не проставится :)
CertSetCertificateContextProperty (...CERT_KEY_PROV_INFO_PROP_ID...).
12.02.2007 7:04:00Денис
Спасибо, Василий!
Сетификат в IE виден. Однако, при попытке зайти на сервер по защищенному соединению, IE закрывает соединение с сообщением "Не возможно отобразить страницу". Если установить этот же сертификат в личное хранилище, используя панель управления CryptoPro, все работает нормально.

Т.е. ссылки на закрытый ключ нет или она как то "криво" проставилась???
Вот код:

HCRYPTPROV hProv = NULL;
HCRYPTKEY hKeySig = NULL;
DWORD dwNameLength = 0;
DWORD dwProvLength = 0;
DWORD dwUserCertLength = 0;
CRYPT_KEY_PROV_INFO provInfoSig = {0};

HANDLE hCertStore = 0;
HANDLE hMyStore = 0;
WideString L;
DWORD cBuffer = 0;
BYTE *pBuffer = NULL;
BYTE *pbUserCert = NULL;

CryptAcquireContext(&hProv, UserName, NULL, 75, 0);
CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hKeySig);
CryptGetProvParam(hProv, PP_UNIQUE_CONTAINER, NULL, &dwNameLength, 0);
provInfoSig.pwszContainerName = (wchar_t *)malloc(dwNameLength);
CryptGetProvParam(hProv, PP_UNIQUE_CONTAINER,
(BYTE*)provInfoSig.pwszContainerName,
&dwNameLength, 0);
CryptGetProvParam(hProv, PP_NAME, NULL, &dwProvLength, 0);
provInfoSig.pwszProvName = (wchar_t *)malloc(dwProvLength);
CryptGetProvParam(hProv, PP_NAME,
(BYTE*)provInfoSig.pwszProvName,
&dwProvLength, 0);
provInfoSig.dwFlags = 0;
provInfoSig.dwKeySpec = AT_KEYEXCHANGE;
DWORD pdw = sizeof(provInfoSig.dwProvType);
CryptGetProvParam(hProv, PP_PROVTYPE, (BYTE*)&provInfoSig.dwProvType, &pdw, 0);
CryptGetKeyParam(hKeySig, KP_CERTIFICATE, NULL, &dwUserCertLength, 0);
pbUserCert = (LPBYTE)malloc(dwUserCertLength);
CryptGetKeyParam(hKeySig, KP_CERTIFICATE, pbUserCert, &dwUserCertLength, 0);
hCertStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM,
0,
0,
CERT_STORE_OPEN_EXISTING_FLAG | CERT_SYSTEM_STORE_CURRENT_USER,
L"My"
);
....
// Считываем файл сертификата в буфер pBuffer
.....
PCCERT_CONTEXT CertCont = NULL;
CertCont = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
pBuffer, cBuffer);
CertSetCertificateContextProperty(CertCont, CERT_KEY_PROV_INFO_PROP_ID, 0, &provInfoSig);
CertAddCertificateContextToStore(hCertStore, CertCont,
/*CERT_STORE_ADD_USE_EXISTING*/
CERT_STORE_ADD_REPLACE_EXISTING, NULL);
CryptSetKeyParam(hKeySig, KP_CERTIFICATE, CertCont->pbCertEncoded, 0);
...
//Освобождение
.....

Насколько я понял, собственно "привязка" сертификата к закрытому ключу производиться функцией CertSetCertificateContextProperty(CertCont, CERT_KEY_PROV_INFO_PROP_ID, 0, &provInfoSig);
где например provInfoSig.pwszContainerName = "REGISTRY\\123456"
или еще что то необходимо сделать?
12.02.2007 11:07:57Василий
По поводу заполнения структуры CRYPT_KEY_PROV_INFO
посмотрите в http://www.cryptopro.ru/CryptoPro/forum/view.asp?q=81
По-видимому, всё различие в том, что нужен UNICODE для имени контейнера и имени криптопровайдера.
12.02.2007 11:40:23Денис
Извините, небольшая ошибочка вышла. Я немного неточно выразился.
Имелось в виду (BYTE *)provInfoSig.pwszContainerName = "REGISTRY\\123456", а provInfoSig.pwszContainerName при трассировке естественно в UNICODE "\x4552\x4947 ..... и т.д."
14.02.2007 11:39:20Kirill Sobolev
Поищите отличия с
http://www.cryptopro.ru/cryptopro/forum/view.asp?q=2648
15.02.2007 12:22:40Денис
Отличия поискал.... но результат тот же.
код:

HCRYPTPROV hProv = NULL;
HCRYPTKEY hKeySig = NULL;
DWORD dwNameLength = 0;
DWORD dwProvLength = 0;
DWORD dwUserCertLength = 0;
CRYPT_KEY_PROV_INFO provInfoSig = {0};
HCERTSTORE hCertStore = 0;
WideString L = "MY";
DWORD cBuffer = 0;
BYTE *pBuffer = NULL;
BYTE *pbUserCert = NULL;

// Инициализация контекста криптопровайдера
if(!CryptAcquireContext(&hProv, UserName, NULL, 75, 0)) return ERR_NO_CONTEXT;

// Получаем хендел на закрытый ключ
rv = CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hKeySig);

// Получаем уникальное имя контейнера
CryptGetProvParam(hProv, PP_UNIQUE_CONTAINER, NULL, &dwNameLength, 0);
provInfoSig.pwszContainerName = (wchar_t *)malloc(dwNameLength);
rv = CryptGetProvParam(hProv, PP_UNIQUE_CONTAINER, (BYTE *)provInfoSig.pwszContainerName, &dwNameLength, 0);

// Получаем имя криптопровайдера
CryptGetProvParam(hProv, PP_NAME, NULL, &dwProvLength, 0);
provInfoSig.pwszProvName = (wchar_t *)malloc(dwProvLength);
rv = CryptGetProvParam(hProv, PP_NAME,(BYTE *)provInfoSig.pwszProvName, &dwProvLength, 0);

// устанавливаем флаги
provInfoSig.dwFlags = 0;
provInfoSig.dwKeySpec = AT_KEYEXCHANGE;

// Получаем тип провайдера
DWORD pdw = sizeof(provInfoSig.dwProvType);
rv = CryptGetProvParam(hProv, PP_PROVTYPE, (BYTE*)&provInfoSig.dwProvType, &pdw, 0);

....
// Считываем файл сертификата в буфер pBuffer
......

PCCERT_CONTEXT CertCont = NULL;
PCCERT_CONTEXT pDesiredCert = NULL

// Создаем контекст сертификата
CertCont = CertCreateCertificateContext(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, pBuffer, cBuffer);

// Открываем хранилище
hCertStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM, 0, NULL, CERT_SYSTEM_STORE_CURRENT_USER, L);

// Добавляем сертификат в хранилище
rv = CertAddCertificateContextToStore(hCertStore, CertCont, CERT_STORE_ADD_NEW, &pDesiredCert);

// "Связываем" сертификат и закр. ключ
rv = CertSetCertificateContextProperty(pDesiredCert, CERT_KEY_PROV_INFO_PROP_ID, 0, &provInfoSig);

// Устанавливаем сертификат в контейнер закр. ключа
rv = CryptSetKeyParam(hKeySig, KP_CERTIFICATE, CertCont->pbCertEncoded, 0);

...
// Освобождение
....

Мы тестируем TLS. Может при привязке к TLS следует осуществить еще какие то действия?????
15.02.2007 14:42:43Василий
Нет, TLS использует обычным образом установленные сертификаты.
Предлагаю сравнить:
1) установить сертификат через панель CSP (Сервис - Установить личный сертификат).
Далее взять файл \Documents and Settings\<имя пользователя Win>\Application Data\Microsoft\SystemCertificates\My\Certificates\<Отпечаток> и переместить его в другую папку. (Отпечаток - это нижнее поле на вкладке Состав при просмотре сертификата в окошке)
2) установить сертификат Вашей программой, найти (там же) этот файл и сравнить.
16.02.2007 6:43:15Денис
Файлы сравнил. Отличия как по размеру, так и по содержанию.

При просмотре в текстовом режиме в файле, установленном с помощью панели управления CryptoPro присутствует такие строки:

   &#57500;&#25197;&#30278;&#21503;&#45602;&#8060;&#15947;&#2987;&#49825;&#27565;  &#188;  D K  REGISTRY\\87654321 Crypto-Pro GOST R 34.10-2001 Cryptographic Service Provider    &#19423;?&#26139;&#58881;&#34998;&#12824;&#2881;&#158;&#17787;&#30471;  &#1181; &#33328;&#39172;&#33328;&#17924;&#928;&#258;&#514;&#24842;&#29698;&#153;

в файле установленном моей программой, вместо этого:

   &#57500;&#25197;&#30278;&#21503;&#45602;&#8060;&#15947;&#2987;&#49825;&#27565;   &#19423;?&#26139;&#58881;&#34998;&#12824;&#2881;&#158;&#17787;&#30471;  \  4 K  &#17746;&#18759;&#21587;&#22866;&#23644;&#14136;&#13622;&#13108;&#12594; &#29251;&#28793;&#28532;&#20525;&#28530;&#18208;&#21327;&#8276;&#8274;&#13363;&#12590;&#11568;&#12338;&#12592;&#19232;&#12611;&#17184;&#20563;  &#1181; &#33328;&#39172;&#33328;&#17924;&#928;&#258;&#514;&#24842;&#29698;&#153;

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

Как же бороться то с этой бедой?
16.02.2007 11:58:47Василий
Собственно, видно, что отличие в кодировке имени контейнера и имени криптопровайдера.