воскресенье, октября 22, 2006

Web сервис и протокол HTTPS (SSL)

Подключение к ASP.NET web сервису по протоколу HTTPS (SSL)

(.Net Framework 1.1)



Введение


Наиболее простым и надежным способом обеспечения безопасного взаимодействия с XML web сервисом, разработанном на ASP.NET является подключение по протоколу HTTPS (SSL). Данный способ обеспечивает полное шифрование трафика на уровне транспортного протокола и не требует от разработчика web сервиса принятия, каких либо дополнительных мер для обеспечения его поддержки.


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


Использование ASP.NET web сервисов совместно с протоколом HTTPS открывает нам большие возможности построения распределенных систем с хорошим уровнем защищенности без особых затрат. Однако на этом пути нас поджидает масса неожиданностей и подводных камней, заботливо расставленных парнями из Редмонта, которые запросто могут поставить нас в тупик. Данная статья обобщает опыт автора в данной области. Предполагается, что читатель уже знаком с web сервисами ASP.NET и имеет некоторый опыт практического использования их.


Включаем HTTPS на сервере.


Прежде всего, для того, чтобы иметь возможность работать с HTTPS протоколом, необходимо включить его поддержку на сервере IIS. Делается это очень просто, однако есть одна сложность, которая может показаться на первый взгляд непреодолимой – необходимо получить серверный сертификат


Руководство рекомендует открыть оснастку MMC “Internet Information Services”, выбрать сайт и открыть окно его свойств. На вкладке “Directory security” есть кнопка “Server Certificate…”, которая запускает мастера запроса нового сертификата. Этот мастер, задав вам массу вопросов, сформирует файл запроса, который предлагается отправить в “certification authority” для получения серверного сертификата. Беда в том, что фирмы, занимающиеся выдачей сертификатов, требуют за это деньги, и вам придется долго объяснять в бухгалтерии для чего необходимо перечислить 20 долларов в какой-то там VeriSign


Возможно, в локальной сети вашей компании развернута служба “Enterprise Certificate Services”. В таком случае вам сильно повезло, и вам остается обратиться к администратору и получить заветный сертификат.


Ну и, наконец, есть третий способ. Специально для того, чтобы разработчики могли тестировать свои продукты, Microsoft выпустила “ IIS6 Resource Kit Tools”, в состав которого входит утилита SelfSSL (скачать можно здесь http://www.microsoft.com/downloads/details.aspx?FamilyID=56fc92ee-a71a-4c73-b628-ade629c89499&DisplayLang=en). Несмотря на то, что Resource Kit предназначен для сервера IIS6 большинство входящих в него утилит (и SelfSSL в том числе) прекрасно работают и с IIS5.1, который входит в состав Windows XP Prof. Сертификаты, которые генерирует эта утилита, вполне пригодны для целей тестирования.


Итак, для того, чтобы установить серверный сертификат, вам необходимо проинсталлировать “IIS Resource Kit”. Далее запускаем утилиту: меню Start -> All Programs -> IIS Resources ->SelfSSL. Например, следующая команда создаст сертификат с именем компьютера, для дефолтного сайта со сроком действия в один год:



selfssl.exe /s:1 /v:365

Умная утилита не только создаст сертификат, но и установит его на сайт. Чтобы убедиться в этом, открываем оснастку MMC “Internet Information Services”, выбираем “Default web site” и открываем окно его свойств. На вкладке “Directory security” теперь доступна не только кнопка “Server Ccertificate…” но и “View certificate…”. Нажав на нее, мы можем увидеть наш свежий сертификат.





Значок сертификата будет помечен красным крестиком и сопровожден надписью “This CA Root certificate is not trusted because it is not in the Trusted Root Certification Authorities store”. Пока мы не станем обращать на это внимание, потому что это не мешает нам работать с нашим сертификатом.


После того, как серверный сертификат установлен на сайте, нам остается включить поддержку SSL. Делается это на той же вкладке “Directory security” на странице свойств сайта. В разделе “Security Communications” нажимаем кнопку “Edit” (она тоже стала доступна после установки сертификата). В появившемся диалоге выставляем флажок “Require secure communications” и жмем “Ok”.





Все, поддержка SSL включена, можно приступать к тестированию web сервиса over HTTPS.


Конфигурирование клиентского прокси класса.


В данной статье я не буду рассматривать вопросы создания web сервиса и клиентского прокси для него. Предположим, web сервис уже существует, и для его использования вы уже добавили web reference web сервис в свой проект. Рассмотрим, как сконфигурировать клиентский прокси класс web сервиса для работы с ним по HTTPS.


Прежде всего, URL web сервиса должен начинаться с “https://”



WebServiceProxy service = new
WebServiceProxy(); service.Url =”https://server/webservice.asmx”;



Очевидно также, что если вам необходимо защищенное соединение с web сервисом, доступ к нему должен предоставляться только аутентифицированным пользователям. Для этого мы должны установить удостоверения пользователя (Credentials) от имени которого будет отправлен запрос к web сервису. Существует два способа установки удостоверений. Первый, вызов от имени текущего пользователя.



service.credentials = CredentialCache.DefaultCredentials;


Текущим пользователем будет являться пользователь, который запустил ваше приложение. Этот режим аутентификации называется “Windows Integrated”, его например, активно использует Internet Explorer при работе с ASP и ASP.NET web приложениями под IIS. При этом пользователю не надо вводить свое имя и пароль при посещении сайта. Для аутентификации используются имя и пароль, введенные при входе в домен Active Directory или NT.


Данный способ аутентификации хорош для консольного или Windows Forms приложения. Однако если вы разрабатываете web приложение, или windows службу CredentialCache.DefaultCredentials вернет вам удостоверения учетной записи, под которой исполняется серверный процесс. Ею может оказаться учетная запись ASPNET, SYSTEM, NETWORK SERVICE или какая ни будь другая, и это будет совсем не то, что нужно вам. В этих случаях необходимо явно указать удостоверения пользователя:



service.credentials = new
NetworkCredential(“user”, “password”, “domain”);


При этом в качестве “domain” может быть указано имя компьютера, если используется учетная запись локального пользователя, либо ничего не казано. В последнем случае используем перегруженный конструктор NetworkCredential с двумя параметрами.


И, наконец, третье условие - это клиентский сертификат. Строго говоря, для установления SSL соединения клиентский сертификат не нужен, достаточно серверного. Но некторые web серверы могут быть сконфигурированы так, чтобы требовать обязательное наличие клиентского сертификата. В этом случае у вас должен быть файл клиентского сертификата вашего сервера, и вы должны указать его прокси классу. К сожалению, .Net Framework версии 1.1, о котором мы ведем речь, не поддерживает работу с хранилищем сертификатов Windows. Поэтому нам придется загружать сертификат из файла:



service.ClientCertificates.Add( System.Security.Cryptography.X509Certificates.X509Certificate.CreateFromCertFile(“c:\\test.cer”));

Теперь можно считать, что прокси класс готов к вызову web методов сервиса. В некоторых случаях вам может потребоваться установить значение свойства Proxy, однако рассмотрение этого вопроса выходит за рамки данной статьи.



При установлении SSL соединения возможно возникновение ошибок сертификата, которые сопровождаются разрывом соединения и исключением с текстом «Underlined connection was closed». Вы можете обрабатывать эти ошибки. Для этого надо создать свой класс, реализующий интерфейс ICertificatePolicy. Вот пример такого класса, который игнорирует все ошибки сертификатов



public class AcceptAllCertificatePolicy: ICertificatePolicy
{
// Перечень возможных ошибок сертификата
public enum CertificateProblem : long
{
CertEXPIRED = 0x800B0101,
CertVALIDITYPERIODNESTING = 0x800B0102,
CertROLE = 0x800B0103,
CertPATHLENCONST = 0x800B0104,
CertCRITICAL = 0x800B0105,
CertPURPOSE = 0x800B0106,
CertISSUERCHAINING = 0x800B0107,
CertMALFORMED = 0x800B0108,
CertUNTRUSTEDROOT = 0x800B0109,
CertCHAINING = 0x800B010A,
CertREVOKED = 0x800B010C,
CertUNTRUSTEDTESTROOT = 0x800B010D,
CertREVOCATION_FAILURE = 0x800B010E,
CertCN_NO_MATCH = 0x800B010F,
CertWRONG_USAGE = 0x800B0110,
CertUNTRUSTEDCA = 0x800B0112
}

// Этот метод вызывается при возникновении ошибки сертификата.
// Если он возвращает true, то обработка запроса продолжается
// Если он возвращает false, то обработка запроса прекращается
// и выдается исключение.
public bool CheckValidationResult(ServicePoint srvPoint,
X509Certificate certificate,
WebRequest request,
int certificateProblem)
{
// этот метод выдает диагностическое сообщение на консоль
// и игнорирует все ошибки сертификатов
// Это только пример. Не делайте так в своих приложениях.
CertificateProblem problem = (CertificateProblem)certificateProblem;
Console.WriteLine("Certificate Problem with accessing {0} " +
request.RequestUri);
Console.WriteLine("Problem is 0x{0:X8} ‘{1}’ \n",
certificateProblem, Enum.GetName(problem.GetType(),problem));
return true;
}
}


Данный класс нужно назначить в качестве текущего обработчика в классе ServicePointManager:


ServicePointManager.CertificatePolicy = new AcceptAllCertificatePolicy();


Сделать это необходимо до первого вызова web метода.


Получение серверного сертификата X.509


HTTPS для установления защищенного соединения использует протокол SSL, которому для шифрования данных требуется открытый ключ, который в свою очередь и содержится в серверном сертификате X.509. Как получить доступ к серверному сертификату? Для этого воспользуемся Internet Explorer-ом. Введем в адресной строке URL вашего web сервиса начинающегося с https. С большой вероятностью, при этом мы увидим окошко “Security alert”.






При нажатии кнопку “View Certificate” откроется уже знакомое нам окно сертификата. В случае если предупреждение не отображается на экране, мы все равно можем увидеть сертификат, щелкнув по иконке замочка в правом нижнем углу окна IE.





В появившемся окне “Certificate” выбираем вторую вкладку “Details” и нажимаем кнопку “Copy to file…”. Появится “Certificate Export Wizard”





Выбираем опцию “DER encoded X.509 (.CER)”, вводим имя файла на следующем экране визарда и сохраняем сертификат в файл. Теперь файл сертификата у нас есть.


Остается обратить внимание на два существенных момента.


Первое. Сертификат содержит имя сервера, которому он выдан.


ПРЕДУПРЕЖДЕНИЕ

Будьте внимательны! URL сервиса должен содержать именно это имя в своем составе, иначе соединение не будет установлено.


Будьте внимательны. Например, у вас есть сервер Myserver.Company.com и вам выдан сертификат на имя Myserver. При этом соединение по адресу
https://Myserver/service.asmx
будет успешно установлено, а по адресу
https://Myserver.Company.com/service.asmx
вызовет ошибку. Помните об этом.


Второе. Серверный сертификат, о котором говорилось выше, должен быть проинсталлирован в хранилище сертификатов локальной машины. В противном случае при попытке установить соединение мы получим невнятное сообщение об ошибке “Underlined connection was closed”. Чтобы проинсталлировать сертификат откроем его файл, и на первой вкладке окна “Certificate” нажмем кнопку “Install Certificate…”





Откроется окно “Certificate Import Wizard”. Нам надо указать, что инсталлировать сертификат надо именно в хранилище локального компьютера (а не локального пользователя). Нажмем кнопку “Next” выберем опцию “Place all certificates in the following store” и жмем кнопку “Browse…”. В появившемся окне “Select Certificate Story” надо отметить флаг “Show physical stores”, а в дереве выбрать “Trusted Root Certification Authorities” -> “Local Computer”. Нажимаем “Ok”.


В окне “Certificate Import Wizard” нажимаем "Next” и “Finish”.


Заключение


Итак, мы рассмотрели, как обеспечить безопасное и защищенное соединение с web сервисом, используя для этого SSL. Мы узнали, как получить и установить серверный сертификат и включить на сайте использование HTTPS (SSL). Рассмотрели, как сконфигурировать клиентский прокси класс web сервиса для соединения по HTTPS. Рассмотрели самый простой способ получения файла серверного сертификата X.509 на клиенте. А также узнали, как проинсталлировать клиентский сертификат в хранилище локальной машины.



12 комментариев:

vad комментирует...

IIS6 Resource Kit Tools:

http://www.microsoft.com/downloads/details.aspx?FamilyID=56fc92ee-a71a-4c73-b628-ade629c89499&DisplayLang=en

Sergey Rozovik комментирует...

Спасибо. Поправил ссылочку.

Анонимный комментирует...

Сергей!
Я столкнулся с ситуацией которая отличается от описанной вами схемы работы с сервисами по htpps. А именно - есть сторонний сервис https://
dip.7cont.ru/DataExch/DataExchangeService.asmx, для меня черный ящик с интерфейсом, есть мой прокси класс созданный визардом VS. На компьютере где запускается клиент устновлен сертификат сервера(или может быть точнее сказать - сертификат клиента полученный от сервера?), т.е. iexplorer открывает страницу сервиса без каких либо предупреждений. Странность в том, что два метода вебсервиса (из порядка 40) работают без "зарядки" экземпляра прокси
- ни сертификатом
//srv.ClientCertificates.Add(cr)
- ни NetworkCredential
//srv.Credentials = new NetworkCredential
Методы это самые простые - один типа HelloWorld, другой возвращает DataSet с информацией о сервисе. Т.е. на этом этапе можно рассуждать так – сервис не требует аутентификации, а ssl лишь обеспечивает шифрование канала (сертификат на компьютере пользователя установлен и используется прозрачно для программы-клиента).

Дальше, пробуем вызвать другие методы, но уже требующие lgoin (без пароля!!!) в качестве параметра. Получаем сообщение – Server was unable to process request. ---> Access denied! Что за дела? Обращаемся за помощью к поисковикам и находим среди прочего и вашу статью. 

Дальше, сформулировать проблему становится сложновато. Все похоже на набор разрозненных фактов из которых ни как ни сложиться хоть какая-то картина.
У меня самого есть небольшой опыт разработки вебсервисов и клиентов к ним, в частности я использую на IIS аутентификацию windows (без SSL), внешнее проявление которой – запрос пароля пользователя стандартными средствами ОС при попытке доступа к странице вебсервиса с помощью iexplorer-a. Вебсервис о котором идет речь – пускает к своей странице всех у кого установлен его сертификат без запроса какого либо пароля. Но мы знаем точно, что логин и пароль используются системой, частью которой является этот сервис, т.к. в нашем распоряжении есть программа-клиент написанная разработчиком самого сервиса и в его настройках необходимо прописать логин и пароль (к слову - сам разработчик сейчас не доступен). Можно предположить что используется собственный механизм аутентификации, но был случай когда пользователь сбил настройки программы-клиента и сделал несколько попыток обращения к сервису, после чего, даже восстановление настроек не привели к работоспособности клиента - Server was unable to process request. ---> Access denied! и все. Звонок в техподдержку держателя сервиса дал понять что «учетная запись заблокирована после нескольких неудачных попыток входа» - больше похоже на поведение стандартной политики windows. Еще – ни один из методов не требует пароля в качестве параметра! Для схемы используемой и упомянутой мной (с windows аутентификацией) такое возможно, пароль передается при установлении самого соединения, через CredentialCache или NetworkCredential. Тут же, если это случай с собственной схемой аутентификации или, возможно, аутентификации на уровне SQL сервера, на какой-то стадии необходимо передать пароль.
Сергей! буду благодарен если вы прокомментируете этот "случай".

Sergey Rozovik комментирует...

Мне бы не хотелось коментировать в публичном блоге реально работающий сервис обмена данными известного ретейлера :)
Можно рассматирвать его в качестве примера того, как не надо делать такие сервисы. Одно только отсутствие Xml namespace у сервиса говорит о том, что тот кто его делал имеет весьма поверхностное представление о предмете.
Вам лучше обратиться в поставщику сервиса за разьяснениями протокола работы с ним.
Удачи.

Анонимный комментирует...

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

Если без персоналий, получается что проксикласс для доступа к методам вебсервиса может работать без выставленного сертификата, если этот сертификат уже установлен на клиентском компьютере? То есть описанный у Вас способ просто позволяет снять с пользователя обязанность установки сертификата в хранилище.
И второе, правильно ли я понимаю - использование SSL не влияет на способ/процесс аутентификации. SSL – это только шифрованный трафик, плюс проявление доверия к корневому центру сертификации? :)
Спасибо.

Sergey Rozovik комментирует...

> получается что проксикласс для доступа к методам вебсервиса может работать без выставленного сертификата,

Да, в принципе может.

> И второе, правильно ли я понимаю - использование SSL не влияет на способ/процесс аутентификации.

Тоже верно. Аутентификация не входит задачи протокола SSL. Он просто обеспечивает установление защищенного канала к конкретным клиентом.

Sergey Rozovik комментирует...

06.02.2008
Устранил некоторые неточности и добавил описание обработки ошибок сертификатов.

Анонимный комментирует...

Здравствуйте Сергей.

У меня такая ситуаця:
Стоит приложение веб, на нем настроен ССЛ.
С этого приложения осущевстляется редирект на другое внешнее веб-приложение, которое требует от нашего клиентский сертификат.Эту часть реализовали, но проблема случается когда внешнее приложение требует от нас тоже ССЛ.

Что в таком случае делать?

IIS ведь поддерживает только один сертификат.

Sergey Rozovik комментирует...

Что значит "С этого приложения осущевстляется редирект на другое внешнее веб-приложение"?
Вы делаете новый запрос или редиректите поступивший от клиента запрос?
Судя по тому, что "требует от нашего клиентский сертификат.Эту часть реализовали", вы делаете новый запрос. Т.е. теперь ваше приложение выступает в роли клиента к другому серверу. Если для этого соединения требуется SSL - чудесно. Указывайте в адресе "https" и вперед.
Не путайте клиентский и серверный сертификаты. Серверный сертификат используется для организации сесии SSL. А клиентский используется только для аутентификации клиента на сервере.

Анонимный комментирует...

Все верно.
Наши приложение становится клиентом для приложения Б.

А потом мы становимся сервером на который посылается Пост запрос от приложения Б и от нас требуют серверный сертификат.
Что в этом случае?

dima комментирует...

Super

sad comrade комментирует...

Спасибо..так подробно описано.
У вас ошибочка в абзаце:
При этом в кач.. не казано. В пос...