26.12.2001 13:41:50Инсталляция сертификата с дискеты Ответов: 14
Weasel

Здравствуйте.
Итак, очередной вопрос:

Есть клиет, ОС - Win98. У него нет связи с сервером сертификации по http. Вопрос - как ему получить сертификат ?

Напрашивается вариант с дискетой. Я выполняю генерацию сертификата на сервере, как хранилище используя дискету. Потом переношу дискету на клиента. Просто так система не заработает - сертификат не инстллирован. Но файла *.cer или еще чего-нибудь в этом духе на дискете нет !

Хорошо, несу дискету снова на сервер, складываю на нее в файлы *.cer сетификаты клиента и CA. СА.cer ставится на ура, клиентский тоже инсталлируется, но не попадает в хранилище Personal. А оказывается он в Intermediate Certification Authorities и не находит своего секретного ключа.

Можно правда открыть апплет панели управления Crypto-Pro, посмотреть сертификат, лежащий на носителе-дискете и там нажать кнопку Install. И тогда все пройдет успешно.

Но наших клиентов не научить такой сложной последовательности действий, а как выполнить то же самое программно - непонятно.

Как правильно инсталлировать сертификат с дискеты на новом компьютере с Win98 ? С минимальным участием юзера ?

 
Ответы:
26.12.2001 17:27:23kure
По описанной вами технологии просто не получится.

Как это работает у Микрософт.
Обычно при генерации ключа и запроса используется xenrool.dll (COM). Он же пользуется на VBS из ASP страниц центра сертификации.
Но при генерации запроса, он (запрос) сохраняется в реестре компьютера, где был сделан.

Установка сертификата обычно производится через функции того же xenroll.dll и довольно просто:
Option Explicit
Dim xenroll
Set xenroll = CreateObject("CEnroll.CEnroll.1")
xenroll.DeleteRequestCert = FALSE
xenroll.WriteCertToCSP = TRUE

xenroll.acceptFilePKCS7 (Wscript.Arguments(0))

If 0=Err.Number Then
Wscript.Echo "Сертификат OK"
Else
Wscript.Echo "Сертификат не мог быть инсталлирован"
End If

Есть функция acceptFilePKCS7 и acceptPKCS7, которая делает довольно много, а именно:
- находит сохраненный запрос в реестре, соответствующий сертификату.
- если он найден, устанавливает сертификат в My и формирует ссылку на секретный ключ. Эта ссылка в виде структуры CRYPT_KEY_PROV_INFO добавляется как атрибут в справочник.

Если сертификат не найден, acceptPKCS7 выдаст ошибку, так как он предназначен для установки именно своего. Verisign решает эту проблему в скриптах вызовом по ошибке InstallPKCS7, которая предназначена для установки не своих сертификатов.
Дополнительно.
Если флаг xenroll.DeleteRequestCert = FALSE
не задан (а он по умолчанию TRUE) запрос удалится и второй раз удтановить свой сертификат будет нельзя.

Таким образом, если есть запрос не сохранен (ключ делался на другой машине) воспользоваться xenroll.acceptPKCS7 не удасться.
Если не пользоваться xenroll, то через CryptoAPI нужно сделать реализацию, аналогичную acceptPKCS7.
27.12.2001 7:59:07Weasel
Значит, получается, что культурным образом установить сертификат на машине, не имеющей связи с центром сертификации, вообще невозможно ?

А как это соотносится с тем, что компьютер с CA вообще не рекомендуется подключать к сети ?
27.12.2001 10:04:33kure
Ну как это не возможно.
Вот варианты:
1. Ключ делает пользователь, потом acceptPKCS7
2. Ключ делается на центре, после этого только через панель управления.
27.12.2001 13:55:53Weasel
Хорошо, при первичной генерации сертификата можно создать запрос прямо на компьютере клиента.

Но клиенты у нас непоседливые, хотят работать сразу с нескольких станций. Дома и на работе, например. И тогда вопрос инсталляции на новый компьютер встает снова.

Всвязи с этим у меня два вопроса:
1) Можно ли перенести на новый компьютер тот ключик реестра, который xenroll создает при генерации сертификата. Тогда ведь Accept должен пройти и на новом компьютере ?

2) Через панель управления инстллировать сертификат возможно. Но ведь этот апплет вызывает какие-то функции апи, значит его действия можно повторить программно ? Как показать сертификат, лежащий на дискете, если он еще не инсталлирован ?
Если удастся его хотя-бы показать, то уж мы научим юзера нажимать кнопку Install :)
27.12.2001 14:21:29kure
Запросы лежат в Current_user/Software/Microsoft/SystemCertificates/REQUEST/Certificates

А как устанавливать свой сертификат через панель с ключевого носителя написано в доке (она есть на сервере). Дополнительно послал почтой.
27.12.2001 14:43:45Weasel
>А как устанавливать свой сертификат через панель с ключевого носителя
>написано в доке (она есть на сервере)
Спасибо. Но суть моего вопроса в другом.

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

Но наши клиенты могут не знать, что такое панель управления вообще. Они должны иметь одну кнопку, на которой написано - зарегистрироваться на этом компьютере.

Мой вопрос - как сделать такую кнопку ?
27.12.2001 16:33:48ROmaN
Для тех, кто не внимательно читал MSDN рассказываю как создать такую кнопку:

1. Вызываем CryptAcquireContext(NULL,... - чтобы получить диалог выбора контейнера секретного ключа и открыть контекст, но это для версии 1.1. В версии 1.2 это не работает, там надо честно, как и описывается в MSDN-е перенумеровывать все контейнеры вручную и выбирать нужный. Получаем hProv.

2. Вызываем
CryptGetUserKey (hProv, AT_SIGNATURE, hKeySig)
CryptGetUserKey (hProv, AT_KEYEXCHANGE, hKeyExch)
чтобы получить HKEY для обоих ключей пользователя.
(Конечно, может быть только один, обрабатываем эту ситуацию).

3. Для каждого из ключей hKeySig и hKeyExch заполняем структуру:

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)

4. Для каждого из ключей hKeySig и hKeyExch вызываем:

binary_cert =
CryptGetKeyParam (hKeySig, KP_CERTIFICATE...
CryptGetKeyParam (hKeyExch, KP_CERTIFICATE...

и получаем сертификаты в бинарном виде в буфере.

Опять же, не в каждом контейнере может быть сертификат, обрабатываем и эту ситуацию.

5. Для каждого из ключей hKeySig и hKeyExch вызываем:

ctx = CertCreateCertificateContext (...,binary_cert,length..)

для получения PCCERT_CONTEXT - контекста сертификата.

6. Теперь самое главное - проставляем ссылку на секретный ключ в контекст сертификата:

CertSetCertificateContextProperty (ctx,
CERT_KEY_PROV_INFO_PROP_ID, 0, provInfo,

где provInfo - заполняли на шаге 3.

7. Теперь контекст сертификата с проставленной ссылкой устанавливаем в нужное хранилище сертификатов:
hCertStore = CertOpenStore (...)
::CertAddCertificateContextToStore (hCertStore, ctx, CERT_STORE_ADD_ALWAYS, ...

8. Все дескрипторы закрываем...
28.12.2001 8:26:17Weasel

Большое спасибо, теперь все понятно :)
16.08.2004 16:51:08antonz
а можно ссылку на MSDN выложить или вышлите, пожалуйста, мне этот материал
02.03.2006 16:22:44Николай
Вопрос к ROmaN'у. Стоит такая задача - при наличии "на руках" контекста открытого сертификата, полученного из УЦ и закрытого аппартного ключа нужно корректно произвести установку сертификата в локальное хранилище (с простановкой связи с закрытым ключом).

Так вот - я могу получить список всех имен контейнеров с криптопровайдера. Но - как мне узнать какой именно контейнер (по имени) соотносится с нужным мне сертификатом?
02.03.2006 17:29:47ROmaN
Николай,
Вопрос достаточно интерес и однозначного ответа на него просто нет. Многие решают его по своему.

Первое основное решение состоит в том, чтобы "догадаться" про имя ключа. В одном банке, например, случайно созданное имя ключа вставляют внутрь сертификата, таким образом описанная вами задача серьезно упрощалась на клиентской стороне (сильно усложняясь при изготолении ключа и сертификата).

Вторым основным решением является перечисление всех ключевых контейнеров, и выбора из них нужного на клиентской стороне. Если пользоваться дополнительными знаниями о своей системе (например, зная, что все ключи должны быть строго на опреленном типе носителя, скажем, дискете), можно существенно сузить перебор (для этого обычно получают т.н. "полное имя ключевого контейнера", которое в качестве префикса имеет тип носителя). И потом для кадого контейнера надо проверить соотвествие открытого ключа из имеющегося сертификата секретному ключу открытого носителя (для этого используем функции CryptExportPublicKeyInfo и CertComparePublicKeyInfo). К недостаткам можно отнести введение всех PIN-кодов перечисляемых носителей, если они используются.
03.03.2006 10:53:43Василий
Позволю себе дополнение к последнему ответу.
Если в системе есть несколько контейнеров с одним и тем же значением ключа (например, после копирования контейнера), то будет выбран первый попавшийся из них (что не всегда хорошо).
03.03.2006 12:38:11Николай
Еще один вопрос. Только что осенило.

Итак - у меня есть контейнеры с закрытыми ключами на аппаратном ключе (например). Используя способ, указанный ROmaN'ом я могу выполнить установку всех(!) контекстов сертификатов для всех(!) контейнеров, найденных в криптопровайдере (программном или аппаратном). При этом будет четко прописана связь с закрытым ключом.

Я всё правильно описал? Такой вариант возможен?
03.03.2006 13:17:29Василий
Почему нет. Можно.
Недостатки (уже подчёркивались ранее):
1) будут нужны пароли на все доступные контейнеры
2) при условии наличия одинаковых ключей в контейнере связка с сертификатом будет установлена на случайный из них.