Tag Archives: httpcfg

Безопасная передача данных через WCF с использованием SSL

В современном мире одним из основных требований при разработке сетевых приложений является безопасность передачи данных по открытым каналам между узлами системы, например, между клиентом и сервером. Одним из стандартизированных и самых надежных способов является использование SSL-протокола. SSL (Secure Sockets Layer — уровень защищённых сокетов) — это криптографический протокол, обеспечивающий безопасность обмена данными по сети. Подробную информацию о SSL можно найти здесь. В данной статье будет показано, как создать простейшее приложение, поддерживающее передачу данных посредством WCF, работающее в автономном (standalone application) режиме без использования IIS и поддерживающее безопасный обмен данными посредством SSL на уровне транспорта.

Создание SSL-сертификатов

Для начала нам потребуется создать два SSL-сертификата. Создавать их будем при помощи утилиты makecert. Подробную документацию к ней можно найти здесь

  • Создадим самозаверенный (самоподписанный) сертификат. Он будет заменять общеизвестный (общедоступный) сертификат авторизационного центра:
1
2
3
4
5
makecert -r -n "CN=имя_самоподписанного_сертификата" -sv имя_файла_ключа.pvk имя_файла_сертификата.cer
 
-r   - Создает самозаверенный сертификат.
-n   - Задает имя сертификата субъекта. Имя должно соответствовать стандарту X.500. Проще всего заключить имя в двойные кавычки и поставить перед ним префикс CN=, например: -n "CN=myName".
-sv  - Задает PVK-файл закрытого ключа субъекта. Если файл не существует, он будет создан.

Результатом исполнения команды будет самоподписанный сертификат, размещенный в текущей папке. В той же папке будет создан файл с ключом. Созданный сертификат необходимо импортировать в корневое хранилище доверенных сертификатов при помощи mmc-консоли (Start->Run->mmc, File->Add/Remove Snap-in) и оснастки “Сертификаты”.

  • Создадим SSL-сертификат для серверной части нашего WCF-приложения. Он будет заменять сертификат, выданный нашей компании сертификационным центром. Подпишем его созданным на предыдущем шаге самоподписанным сертификатом, имитируя таким образом выдачу данного сертификата центром авторизации:
1
2
3
4
5
6
7
8
9
10
11
12
makecert -pe -n "CN=имя_SSL_сертификата" -iv имя_файла_ключа_самоподписанного_сертификата.pvk -ic имя_файла_самоподписанного_сертификата.cer -sr localMachine -ss My -sky exchange имя_файла_SSL_сертификата.cer
 
-pe  - Помечает созданный закрытый ключ как экспортируемый. Это позволит включить закрытый ключ в сертификат.
-n   - Задает имя сертификата субъекта. Имя должно соответствовать стандарту X.500. Проще всего заключить имя в двойные кавычки и поставить перед ним префикс CN=, например: -n "CN=myName".
-iv  - Задает PVK-файл закрытого ключа поставщика. Закрытый ключ созданного ранее самоподписанного сертификата.
-ic  - Задает файл сертификата поставщика. Созданный ранее сертификат, которым будет подписан создаваемый сертификат.
-sr  - Задает местонахождение хранилища сертификатов субъекта. Расположение может быть либо currentuser (значение по умолчанию), либо localmachine.
-ss  - Задает имя хранилища сертификатов субъекта, в котором будет храниться созданный сертификат. Например (CA, MY и т.д.)
-sky - Определяет тип ключа субъекта, который должен быть одним из следующих:
         signature (что означает, что этот ключ используется для цифровой подписи),
         exchange (что означает, что этот ключ используется для шифрования и обмена ключами,)
         или целое число, представляющее тип поставщика. По умолчанию можно указать 1 для ключа обмена или 2 для ключа сигнатуры.

Созданный SSL-сертификат будет автоматически помещен в хранилище сертификатов “LocalMachine”->”Personal”. Это можно проверить при помощи mmc-консоли и оснастки “Сертификаты”. Если в хранилище нет созданного SSL-сертификата, его необходимо туда импортировать. Я назвал созданный сертификат localhost (это имя понадобится нам в будущем)

1
...-n "CN=localhost"...

Серверная часть приложения

Для создания серверной части приложения воспользуемся средой разработки Miscosoft Visual Studio 2010 в которой создадим новое консольное приложение (File->New->Project-> Console Application) и сохраним его как Hosting. Теперь надо открыть свойства проекта и изменить имя сборки и пространство имен по умолчанию на Hosting (в принципе можно этого не делать, но тогда Ваше пространство имен будет отличаться от используемого в приведенных примерах кода).
SimpleWCFApplication
Теперь добавим к нему WCF-сервис. Кликнем правой кнопкой на нашем проекте в дереве проектов, выберем пункт меню Add->New Item, выберем в списке в открывшемся окне элемент WCF Service и нажмем кнопку Add. Среда автоматически добавит в наш проект два новых файла: Service1.cs и IService1.cs. Первый будет содержать код класса, реализующего наш WCF-сервис:

1
2
3
4
5
6
7
8
9
10
11
using System;
 
namespace Hosting
{
  public class Service1 : IService1
  {
    public void DoWork()
    {
    }
  }
}

Второй будет содержать контракт нашего WCF-сервиса:

1
2
3
4
5
6
7
8
9
10
11
using System.ServiceModel;
 
namespace Hosting
{
  [ServiceContract]
  public interface IService1
  {
    [OperationContract]
    void DoWork();
  }
}

Также некоторые изменения появятся в файле конфигурации приложения app.config, но о них чуть позже.
Изменим контракт нашего сервиса:

1
2
3
4
5
6
7
8
9
10
11
12
//IService1.cs
using System.ServiceModel;
 
namespace Hosting
{
  [ServiceContract]
  public interface IService1
  {
    [OperationContract]
    string SayHello(string name);
  }
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
//Service1.cs
using System;
 
namespace Hosting
{
  public class Service1 : IService1
  {
    public string SayHello(string name)
    {
      return String.Format("Hello, {0}!", name);
    }
  }
}

Теперь наш сервис умеет принимать от клиента его имя и возвращать клиенту строку приветствия. Самое время заняться конфигурацией. Для экономии места я не буду приводить здесь конфигурацию по умолчанию, автоматически созданную средой при добавлении в проект wcf-сервиса, а приведу уже готовую конфигурацию app.config, которую мы подробно рассмотрим.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?xml version="1.0"?>
<configuration>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="SecurityBehavior">
          <serviceMetadata httpsGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <serviceCredentials>
            <serviceCertificate findValue="localhost"
                                storeLocation="LocalMachine"
                                storeName="My"
                                x509FindType="FindBySubjectName"/>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service name="Hosting.Service1"
               behaviorConfiguration="SecurityBehavior">
        <endpoint address=""
                  binding="basicHttpBinding"
                  bindingConfiguration="TransportSecurityBinding"
                  contract="Hosting.IService1">
        </endpoint>
        <endpoint address="mex"
                  binding="mexHttpsBinding"
                  contract="IMetadataExchange"/>
        <host>
          <baseAddresses>
            <add baseAddress="https://localhost:7586/Service1/"/>
          </baseAddresses>
        </host>
      </service>
    </services>
    <bindings>
      <basicHttpBinding>
        <binding name="TransportSecurityBinding">
          <security mode="Transport">
            <transport clientCredentialType="None"/>
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
  </system.serviceModel>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  </startup>
</configuration>

Посмотрим, что же изменилось:
SimpleWCFApplication_hosting_app_config_diff

  • строчка 6: У секции, описывающей поведение сервиса, появилось имя “SecurityBehavior”. Это сделано для того, чтобы можно было ссылаться на эту секцию из секции, описывающей сам сервис
  • строчка 7: httpsGetEnabled=”true” вместо httpGetEnabled=”true”. Теперь для получения информации о сервисе будет использоваться https протокол
  • строки 9-14: Добавилось описание serviceCredentials. Именно здесь размещена информация об SSL-сертификате, который будет использовать наше серверное приложение для аутентификации себя как авторизованного приложения. Здесь указано имя используемого сертификата, его месторасположение и способ его поиска в хранилище сертификатов
  • строчка 20: Добавилась ссылка на описание поведения сервиса
  • строчка 22: Изменен биндинг (привязка). По умолчанию система предлагала использовать wsHttpBinding, но мы его заменим на basicHttpBinding как более простой и быстрый
  • строчка 23: Добавилась ссылка на конфигурацию биндинга
  • строчка 27: Изменен биндинг (привязка), отвечающий за получение метаинформации о нашем wcf-сервисе. Теперь будет использован mexHttpsBinding и соответственно https протокол
  • строчка 31: Изменен url сервиса и указано, что для доступа к нему надо обязательно использовать защищенный https протокол. Обратите внимание на номер порта: 7586. Для корректной работы нашего приложения необходимо будет произвести дополнительную настройку операционной системы, но об этом чуть ниже. 7586 – это произвольно выбранный порт из диапазона свободных портов. Вы можете выбрать себе любой другой свободный порт.
  • строчки 36-44: Описание конфигурации нашего биндинга (привязки). В первую очередь здесь надо обратить внимание на строчки 39-41. Они содержат информацию о том, что для данного биндинга необходимо включить поддержку безопасности на уровне транспорта, но не использовать при этом механизм аутентификации пользователей. Другими словами, будет использован SSL-протокол, обеспечивающий безопасную передачу данных, но проверка пользователей производиться не будет, т.е. наш сервис может быть использован любым клиентом, а не только авторизованными пользователями.

Теперь остается только активировать сервис и обработать возможные исключения при запуске приложения. Сделаем это в функции Main() файла Program.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//Program.cs
using System;
using System.ServiceModel;
 
namespace Hosting
{
  class Program
  {
    static void Main(string[] args)
    {
      try
      {
        var service = new ServiceHost(typeof(Service1));
        service.Open();
        Console.WriteLine("Hosting started. Press any key to exit...");
      }
      catch (Exception ex)
      {
        Console.WriteLine(ex.Message);
      }
      finally
      {
        Console.ReadKey();
      }
    }
  }
}

На этом серверная часть нашего приложения закончена.

Конфигурирование портов операционной системы при использовании SSL

Подробную информацию о конфигурировании портов при использовании SSL-сертификатов можно найти здесь. Здесь я кратко резюмирую что необходимо сделать:

  • Для просмотра текущей конфигурации портов в Windows Server 2003 или Windows XP необходимо использовать утилиту HttpCfg.exe:
1
httpcfg query ssl
  • Для просмотра текущей конфигурации портов в Windows Vista или Windows 7 необходимо использовать утилиту Netsh.exe:
1
netsh http show sslcert
  • Для выполнения привязки сертификата к номеру порта в Windows Server 2003 или Windows XP необходимо использовать утилиту HttpCfg.exe в режиме “set”:
1
2
3
4
5
6
7
8
9
10
11
httpcfg set ssl -i IP:port -c MY -m 65536 -h Thumbprint
 
-i IP:port    - 0.0.0.0 или реальный IP сервера : номер используемого порта
-h Thumbprint - отпечаток сертификата. Можно просмотреть при при помощи mmc с установленной оснасткой 'cсертификаты'
                Найти требуемый сертификат в общем списке -> Свойства -> Детали -> Thumbprint, скопировать в блокнот и удалить все пробелы
-c StoreName. String that specifies the name of the store where the certificate being added resides. If no string is specified, the name "MY" is used by default.
-m CheckMode. string containing one or more numbers representing flags that determine the default mode for checking the certificate. The numbers may consist of one or more of the following flag values:
    1     - Client certificate will not be verified for revocation.
    2     - Use cached client certificate revocation.
    4     - Enable revocation freshness time.
    65536 - No usage check.

Пример использования:

1
httpcfg set ssl -i 0.0.0.0:7586 -c MY -m 65536 -h a44ddc00cc645e6227b5606c6c351b62d4f7c8c1

Внимательно проверьте отпечаток сертификата и номер порта, к которому нужно осуществлять привязку. Номер порта следует взять из файла конфигурации вашего сервиса, отпечаток сертификата – см. выше.

  • Для выполнения привязки сертификата к номеру порта в Windows Vista или Windows 7 необходимо использовать утилиту Netsh.exe:
1
2
3
4
5
6
netsh http add sslcert ipport=0.0.0.0:7586 certhash=a44ddc00cc645e6227b5606c6c351b62d4f7c8c1 appid={00112233-4455-6677-8899-AABBCCDDEEFF}
 
-ipport IP:port    - 0.0.0.0 или реальный IP сервера : номер используемого порта
-certhash          - отпечаток сертификата. Можно просмотреть при при помощи mmc с установленной оснасткой 'cсертификаты'
                     Найти требуемый сертификат в общем списке -> Свойства -> Детали -> Thumbprint, скопировать в блокнот и удалить все пробелы
-appid:            - представляет собой идентификатор GUID, который можно использовать для определения приложения-владельца.

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

Клиентская часть приложения

Возвращаемся в Visual Studio и добавляем в наше решение еще один консольный проект. Назовем его Client. Изменим в его свойствах наименование сборки и пространство имен на Client.
Теперь добавим в него ссылку на созданный ранее wcf-сервис. ВНИМАНИЕ: чтобы добавление ссылки прошло успешно, серверная часть должна быть запущена! Проще всего запустить серверную часть через командную строку: Start->Run->cmd.exe, перейти в папку где лежит собранный hosting.exe и запустить его. Тоже самое можно сделать используя стандартный проводник файлов (windows file explorer).
Кликаем правой кнопкой мыши по нашему проекту в дереве проектов и выбираем Add Service Reference. В открывшемся окне в поле адрес вбиваем “https://localhost:7586/Service1/” и нажимаем кнопку “Go”
SimpleWCFApplication_add_service_reference
Нажимаем “Ok”. Все, ссылка добавлена. Теперь остается только вызвать сервис и обработать возможные исключения при запуске приложения. Сделаем это в функции Main() файла Program.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//Program.cs
using System;
 
namespace Client
{
  class Program
  {
    static void Main(string[] args)
    {
      try
      {
        Console.Write("Please, enter you name: ");
        var name = Console.ReadLine();
        var service = new ServiceReference1.Service1Client();
        var result = service.SayHello(name);
        Console.WriteLine(result);
      }
      catch(Exception ex)
      {
        Console.WriteLine(ex.Message);
      }
      finally
      {
        Console.Write("Press any key to exit...");
        Console.ReadKey();
      }
    }
  }
}

Обратите внимание на строки 14-16. Это и есть вызов сервиса и печать результатов его работы. Все готово, можно компилировать и запускать приложение.
SimpleWCFApplication_client_start
Вводим имя, например Vasia, отправляем запрос на сервер и получаем в ответ строку приветствия, которую и выводим на экран:
SimpleWCFApplication_client_get_response
На этом все: мы создали готовое клиент-серверное приложение, работающее через https-протокол с использованием SSL-сертификата и отвечающее требованию к безопасности передачи данных через открытые каналы данных. Удачи 🙂