08.06.2007 15:45:08ЭЦП Web-службой, размещение контейнера закрытых ключей Ответов: 14
Максим
Добрый день!

На IIS 5 работает Web-service, осуществляющий подпись данных. Служба по файлу сертификата (cer) получает закрытый ключ с помощью функции CryptAcquireCertificatePrivateKey и этим ключем производит подпись.

Контейнер закрытых ключей скопирован в реестр, данный сертификат находится в Personal пользователя под которым работает IIS.

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

Как скопировать контейнер закрытого ключа с дискеты в реестр и куда установить сертификат, чтобы ключ брался из реестра и не было обращений в дисководу?
 
Ответы:
08.06.2007 17:40:11Kirill Sobolev
Рекомендую такой алгоритм, без обращения к CryptAcquireCertificatePrivateKey для сертификата из файла, т.к. эта функция работает с кэшем а средств для его очистки нет.
0)Если у сертификата IIS, который установлен в хранилише пользователя нет ссылки на секретный ключ, то делаете ее с помощью контрольной панели (однократная операция)
1)Читаете из файла сертификат и по нему находите нужный в Personal.
2)У сертификата их хранилища читаете CertGetCertificateContextProperty(CERT_KEY_PROV_INFO_PROP_ID), по нему уже открываете контейнер с помощью CryptAcquireContext.
08.06.2007 17:42:30Василий
> Служба по файлу сертификата (cer) получает закрытый ключ...
Это абсолютно неправильно. В файле сертификата нет ссылки на закрытый ключ. То, что это работает (при условии, что сертификат установлен в хранилище Personal с привязкой к ключу) - это недокументированная особенность CryptoAPI. Правильно открывать хранилище (CertOpenStore) и искать сертификат в хранилище (CertFindCertificateInStore) и потом уже CryptAcquireCertificatePrivateKey.

Тогда гарантированно будет найден тот ключевой контейнер, который был указан при установке сертификата в хранилище Личные.

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

Установка сертификата:
Панель управления - КриптоПро CSP - Сервис - Установить личный сертификат
09.06.2007 9:54:13Максим
Kirill, Василий, спасибо за исчерпывающие ответы!
Буду пробывать, напишу о результате.
09.06.2007 12:49:37Максим
хех, попробывал. Не работает (((

Делаю так.
Беру файл сертификата. Получаю его контекст с помощью CertCreateCertificateContext.
Получаю контекст криптопровайдера и хранилища сертификатов с закрытыми ключами:
CryptAcquireContext(ref hCryptProv, null, null, 75, CRYPT_VERIFYCONTEXT);
...
CertOpenSystemStore(hCryptProv, "MY");

Провожу поиск сертфиката в хранилище
CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_EXISTING, pSenderCertContextFromFile, IntPtr.Zero);
Т.е. пытаюсь получить контекст сертификата из хранилища, соответствующий сертификату из файла.

Вот на этом шаге и возникают проблемы.
Приложение, запущенное пользователем у котого в Personal установлен сертификат с закрытым ключем отрабатывает прекресно - сертификат в хранилище находится, его контектст получает, далее удается сделать все что нужно.

Web-служба работающая на IIS через ананимный доступ от _этого_ же пользователя сертификат в хранилище найти не может.

В чем может быть дело, что я делаю не так?

Вопрос наверно не из простых, но все же помогите пожалуйста разобраться. Спасибо!
09.06.2007 13:14:17Василий
IIS работает не из-под простого пользователя. Для 2003 это специальная учётная запись - NETWORK SERVICE.
Как правило, для служб сертификат ставится в хранилище "Личные" локального компьютера, а не пользователя.
09.06.2007 13:35:22Максим
Пробывал и в Computer/Personal ставить. Результат тот же.

IIS 5.1 WinXP SP2. Работает под моей учетной администраторской учетной записью. Приложения, использующие крипто прогоняю под ней же, в них все работает.

Почему же из под IIS не находит сертификат в хранилище?.. (
09.06.2007 15:55:03Василий
> Пробывал и в Computer/Personal ставить. Результат тот же.

Вопрос - при этом при открытии хранилища указывали, что теперь это хранилище компьютера?
13.06.2007 12:16:55Максим
Перепробывал уже кажется все, во всех вариациях.
Не работает.
Получаю контекст криптопровайдера так:
CryptAcquireContext(ref hCryptProv, null, null, 75, CRYPT_VERIFYCONTEXT)

Хранилище пробывал открывать так:
CertOpenSystemStore(hCryptProv, "MY");
Открывается. Но при вызове CertFindCertificateInStore по контексту сертификата из файла результат - ошибка 80092004 - CRYPT_E_NOT_FOUND "CRYPT_E_NOT_FOUND".

Сертификат установлен в Personal User'а (от которого работает IIS) и в Computer/Personal. Привязки к закрытым ключам есть везде. Контейнеры закрытых ключей опять же есть в реесте и User и в Computer.

Если же открываю хранилище через
CertOpenStore(9, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, "MY") возращается null и LastError = 5, что как я понимаю значит ERROR_ACCESS_DENIED.
Пробывал открывать через CertOpenStore гм... с разными аргументами. Открывает только для CERT_SYSTEM_STORE_CURRENT_USER, и опять же потом в хранилище сертификат не находится.

Вся странность, как я уже писал, заключается в том, что этот код не работает из Web-сервиса под IIS при анонимном доступе, используется аккаунт администратора компьютера. Приложения, использующие тот же код запущенные тем же пользователем нормально работают. Т.е.
CertOpenStore(9, 0, 0, CERT_SYSTEM_STORE_LOCAL_MACHINE, "MY") возвращает нормальный хендл хранилища, и контекст нужного мне сертификата там находится.

Куда копать?
13.06.2007 13:52:58Василий
1. Если Вы вызываете CryptAcquireContext(...), а ключ в хранилище компьютера, то нужно добавить флажок CRYPT_MACHINEKEYSET.

2. Можно уточнить - в "Диспетчере служб IIS" в Свойствах веб-узла (или каталога) - Безопасность каталога - верхняя кнопка Изменить - какие сейчас галки? Если есть галка "Анонимный доступ" - то по нажатию Изменить на ней - Вы вручную заменили аккаунт по умолчанию на "Администратор"(или имя пользователя Win с правами админа) и ввели правильный пароль и подтверждение (и перезапустили IIS)?
13.06.2007 15:21:41Максим
1. Добавил. Результат тот же.

2. Стоит только галка "Анонимный доступ". Да, вручную задавал login/password административной учетной записи. Причем, даже создавал нового админа, ставил ему сертификат в Personal, из под него же ставил в Computer/Personal. Не урезает ли IIS какие либо параметры учетной записи при анонимном доступе? Тот же Context.User.Identity.Name WebService выдает как "" а не "Administrator", может где-то тут подвох. Под Web сервером от Visual Web Developer 2005 EE все нормально работает. А вот IIS...(
13.06.2007 15:24:34Максим
Возможно стоит сбросить вам мой код на mail (какой?), так проще будет?
13.06.2007 16:26:30Максим
Заработало! Проблема была в ограничениях ASPNET. Посоветовали подправить machine.config - помогло :)
13.06.2007 22:27:45Василий
Если не сложно, напишите здесь подробности, чтобы решение имелось в этой теме.
14.06.2007 9:44:21Максим
Сам пока толком не разбирался, просто сделал как посоветовали - помогло.

http://www.aspnetmania.com/Forums/ForumMessage/327925.html
http://forum.codeby.net/topic15488.html