Исходники.Ру - Программирование
Исходники
Статьи
Книги и учебники
Скрипты
Новости RSS
Магазин программиста

Главная » Статьи по программированию » .NET - Web Services »

Обсудить на форуме Обсудить на форуме

WS-Security Аутентификация и цифровые подписи с помощью Web Services Enhancements
 
Перевод: Шатохина Надежда(sna@uneta.org), Ukraine .Net Alliance (http://www.uneta.org/)
 
Декабрь 2002
Применяется к:
  • Microsoft® ASP.NET
  • Microsoft® .NET Framework
  • проекты Microsoft® Visual Studio®
  • SOAP
  • Web Services Enhancements 1.0 для Microsoft® .NET
  • спецификация WS-Security
  • Web Services Enhancements 2.0 для Microsoft® .NET
  • Обзор:
    В этой статье речь идет о том, как работать с Web Services Enhancements 1.0/2.0 для Microsoft .NET, чтобы воспользоваться преимуществом спецификации WS-Security для аутентифицирования и подписания данных для ваших Web сервисов.
    Содерджание:
  • Введение
  • Среда WSE
  • Базовая аутентификация имя-пользователь – маркер (UsernameToken)
  • Провайдер пароля (Password Provider)
  • Создание WebMethod, использующего WS-Security
  • Создание клиента, использующего WS-Security
  • Проверка достоверности UsernameToken на сервере
  • Использование сертификатов X.509 в качестве маркеров WS-Security
  • Хранилища сертификатов
  • Добавление маркера сертификата X.509 в SOAP сообщение
  • Цифровые подписи
  • Проверка достоверности цифровой подписи
  • Подпись части сообщения
  • Заключение
  • SOAP сообщение с хэшированным UsernameToken
  • SOAP сообщение с маркером сертификата X.509, используемом для создания цифровой подписи сообщения
  • SOAP сообщение с маркером сертификата X.509, используемым для создания цифровой подписи части сообщения
  • Введение

    С появлением Web Services Enhancements 1.0 для Microsoft .NET (WSE), Microsoft предоставил первый инструментарий для реализации системы безопасности в рамках SOAP сообщения. Больше Web сервисы не привязаны строго к использованию возможностей обеспечения безопасности, предоставляемых средствами транспортировки. Теперь SOAP сообщение самостоятельно может провести аутентификацию, проверить целостность и даже может кодироваться полностью в рамках SOAP конверта, используя механизмы, определенные Спецификацией WS-Security (http://msdn.microsoft.com/webservices/building/security/default.aspx?pull=/library/en-us/dnglobspec/html/ws-security.asp). В этой статье мы рассмотрим, как можно использовать WSE для получения преимуществ WS-Security при аутентификации и подписании данных для ваших Web сервисов.

    Среда WSE

    WSE располагается на вершине поддержки, предоставляемой .NET Framework для написания и потребления Web сервисов. В центре WSE поддержки находится класс Microsoft.Web.Services.SoapContext, обеспечивающий интерфейс для проверки заголовка WS-Security и других заголовков в поступающих SOAP сообщениях и добавления заголовка WS-Security и других заголовков в исходящие SOAP сообщения. Для разработчиков, пишущих код для потребления Web сервисов, был создан интерфейсный класс, чтобы путем добавления SoapContext для SOAP запроса и ответа усовершенствовать возможности оболочки. На сервере было создано Microsoft ASP.NET SOAP расширение, которое проверяет достоверность поступающих SOAP сообщений и создает запрос и ответ SoapContext, к которому можно доступиться из WebMethods.

    Настройка основной среды WSE влечет за собой конфигурирование вашего ASP.NET приложения для использования расширения WSE SOAP. Это возможно на базе многих машин путем добавления некоторых данных в Machine.config, но возможны случаи, когда вам не понадобится или вы не захотите поддержки WSE. Мы будем добавлять поддержку WSE для конкретной виртуальной директории. Чтобы сделать это, добавьте элемент /configuration/system.web/webServices/soapExtensionTypes/Add в Web.config для вашей виртуальной директории. Добавленный элемент должен выглядеть примерно так, как представлено ниже, лишь с тем исключением, что атрибут type должен весь помещаться в одну строку (здесь он разбит для читабельности).

    <webServices>
      <soapExtensionTypes>
        <add type="Microsoft.Web.Services.WebServicesExtension, 
            Microsoft.Web.Services, Version=1.0.0.0, Culture=neutral, 
            PublicKeyToken=31bf3856ad364e35" priority="1" group="0" />
      </soapExtensionTypes>
    </webServices>
    

    С точки зрения WSE, теперь мы готовы следовать дальше. Будут еще дополнительные настройки конфигурации, которые нам понадобится сделать для определенных аспектов WS-Security, мы обсудим их позже. Сейчас, все что нам надо сделать – это добавить ссылку на Microsoft.Web.Services.dll в наш Visual Studio .NET проект, и мы готовы.

    Базовая аутентификация имя-пользователь – маркер (UsernameToken)

    Перед тем как мы можем попытаться создать цифровые подписи для наших SOAP сообщений, нам сначала надо уметь определять, кто подписался. Нет никакого смысла проверять целостность сообщения, если оно поступило от Evil Chuck, когда мы ожидаем, что оно придет от Bob. Поэтому мы начнем наше исследование WS-Security с рассмотрения концепции UsernameTokens и того, как WSE обеспечивает проверку их достоверности.

    Элемент UsernameToken определен в WS-Security, чтобы обеспечить средства для проведения базовой проверки достоверности имени пользователя/маркера. Если у вас есть опыт с HTTP, вы можете сравнить сходство этой системы с базовой аутентификацией (Basic Authentication). WS-Security предоставляет несколько вариантов через три формы элемента UsernameToken. Различные формы показаны ниже:

    <!-- No Password -->
    <UsernameToken>
      <Username>Bob</Username>
    </UsernameToken>
    
    <!-- Clear Text Password -->
    <UsernameToken>
      <Username>Bob</Username>
      <Password Type="wsse:PasswordText">Opensezme</Password>
    </UsernameToken>
    
    <!-- Digest: SHA1 hash of base64-encoded Password -->
    <UsernameToken>
      <Username>Bob</Username>
      <Password Type="wsse:PasswordDigest">
        QSMAKo67+vzYnU9TcMSqOFXy14U=
      </Password>
    </UsernameToken>			
    			

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

    Чтобы обойти эту проблему, Web Services Security Addendum (http://msdn.microsoft.com/webservices/building/security/default.aspx?pull=/library/en-us/dnglobspec/html/ws-security-addendum.asp) добавил дополнительные защитные меры. Вместо того, чтобы отправлять просто хеша пароля, приложение определяет, что должна быть отправлена цифровая версия пароля. Эта цифровая версия содержит хеш, который является комбинацией пароля, Nonce (функционально уникальной строки, которая идентифицирует этот запрос) и времени создания. Следовательно, невозможно существование двух одинаковых хешей пароля. Версия Revised UsernameToken приведена ниже:

    <!-- Revised UsernameToken -->
    <wsse:UsernameToken 
        xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility" 
        wsu:Id="SecurityToken-58564463-5bdc-4a6b-a7fb-94a0d7357a20">
      <wsse:Username>Joe</wsse:Username>
      <wsse:Password Type="wsse:PasswordDigest">
        gpBDXjx79eutcXdtlULIlcrSiRs=
      </wsse:Password>
      <wsse:Nonce>
        h52sI9pKV0BVRPUolQC7Cg==
      </wsse:Nonce>
      <wsu:Created>2002-11-04T19:16:50Z</wsu:Created>
    </wsse:UsernameToken>
    			

    Хотя у каждого легитимного запроса будет отличный от других хэш, вам надо быть осторожными со злонамеренными пользователями, вытягивающими целый UsernameToken из чьего-то легитимного запроса и помещающими его в собственные нелегитимные ответы. Вы можете минимизировать риск, задавая достаточно малый срок действия Временной метки (Timestamp) (еще одна возможность WSE) и логически уменьшать срок действия на сервере. Например, возможно, вы захотите показать, что срок действия сообщения закончится через 30 секунд, таким образом оно не будет переходит в режим отсрочки, а на сервере через 30 секунд от времени, указанного в элементе Created, UsernameTokens не будет приниматься. Помните, что проблемы синхронизации часов между машинами могут обусловить проблемы непринятия действительных запросов и, в определенных ситуациях, принятия простроченных запросов. Таким образом, использование времени создания не решает всей проблемы. Чтобы еще более исключить риск, Web сервис может сохранять таблицу значений Nonce от недавно полученных UsernameTokens и не давать разрешение на ответ, если Nonce уже использовался ранее. В таблицу будут входить только текущие сообщения и находится там до истечения срока их действия. Если вы получили несколько запросов с одинаковым Nonce, вы должны отвергнуть их все, поскольку возможно, что нелегитимный запрос был получен первым. Также помните, что проверка Nonce не спасет в случае, когда злонамеренный посредник перехватывает поступающие сообщения и заменяет их своими, используя UsernameToken исходного сообщения. Чтобы защититься от такого рода атак, вам понадобится добавить цифровую подпись. Чуть позже в этой статье мы рассмотрим цифровые подписи.

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

    Провайдер пароля (Password Provider)

    WSE определяет интерфейс Microsoft.Web.Services.Security.IPasswordProvider в качестве класса, который надо реализовать, чтобы зарегистрироваться как Провайдер пароля. В интерфейсе есть одна функция, GetPassword, и в качестве входных данных они принимает Microsoft.Web.Services.Security.UsernameToken. Функция GetPassword возвращает пароль для данного пользователя. Идея в том, что для сохранения действительных пар имя пользователя/пароль вы можете использовать любой механизм, какой пожелаете, а затем обеспечивать класс, реализовывающий интерфейс IPasswordProvider, чтобы обеспечить WSE возможность доступаться к вашему конкретному механизму сохранения пароля. У вас есть также вариант осуществления собственной комбинации цифр и хэшей в UsernameToken, возможно с совместно используемым секретом, в целях дальнейшего контроля вашей инфраструктуры аутентификации.

    Чтобы передавать данные в WSE вашего конкретного Провайдера пароля, вы должны задать соответствующие установочные параметры конфигурации WSE. Это требует добавления элемента microsoft.web.services в файл конфигурации вашего приложения. Также это влечет за собой необходимость определения WSE класса, который может понимать именно эту информацию конфигурации. Приведенный далее configSections может быть добавлен в Machine.config или конкретный Web.configs на вашей машине. Опять же, атрибут type должен весь помещаться в одной строке, но в данном случае он разбит в целях читабельности. Этот узел размещен в /configuration/configSections в вашем .config файле.

    <configSections>
      <section name="microsoft.web.services"
        type="Microsoft.Web.Services.Configuration.WebServicesConfiguration,
        Microsoft.Web.Services, Version=1.0.0.0, Culture=neutral, 
        PublicKeyToken=31bf3856ad364e35" />
    </configSections>	
    			

    Для ASP.NET приложения в качестве потомка элемента родительской конфигурации, чтобы проинформировать WSE вашего конкретного Провайдера пароля, в Web.config могут быть добавлены следующие входные данные:

    <microsoft.web.services>        
      <security>
        <!-- This is the class which will provide the password hashes
                   for the UsernameToken signatures. -->
        <passwordProvider 
            type="WSE_Security.PasswordProvider, WSE-Security" />
      </security>
    </microsoft.web.services>
    			

    В атрибуте type элемента passwordProvider указан написанный вами класс, который реализует интерфейс IPasswordProvider. В данном случае, мой класс называется PasswordProvider, определен он в пространстве имен WSE_Security и размещен в сборке WSE-Security.dll. Ради простоты, мой класс возвращает пароль "opensezme" для всех имен пользователей. В большинстве сценариев именно здесь вы должны реализовать логику, которая считывает соответствующий пароль из SQL базы данных или какого-нибудь другого хранилища. Код для моего обычного класса приведен ниже:

    namespace WSE_Security
    {
        public class PasswordProvider : IPasswordProvider
        {
            public string GetPassword(UsernameToken token)
            {
                return "opensezme";
            }
        }
    }
    			
    Создание WebMethod, использующего WS-Security

    Теперь мы действительно готовы к созданию Web сервиса и его вызову. Web сервис, который я хочу создать, будет простым Web сервисом типа "Hello World", за исключением того, что я хочу доступаться к SoapContext для класса, чтобы иметь возможность персонализировать ответ. Код для моего WebMethod показан ниже:

    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Web;
    using System.Web.Services;
    using System.Web.Services.Protocols;
    using Microsoft.Web.Services.Security;
    using Microsoft.Web.Services;
    
    
    namespace WSE_Security
    {
        [WebService]
        public class Hello : System.Web.Services.WebService
        {
            [WebMethod]
            public string PersonalHello()
            {
                string response = "";
    
                // Only accept SOAP requests
                SoapContext requestContext = HttpSoapContext.RequestContext;
                if (requestContext == null)
                {
                    throw new ApplicationException("Non-SOAP request.");
                }
    
                // Look through all the tokens in the Tokens collection
                // for a UsernameToken.
                foreach (SecurityToken tok in requestContext.Security.Tokens)
                {
                    if (tok is UsernameToken)
                    {
                        response += "Hello " + ((UsernameToken)tok).Username;
                    }
                }
                return response;
            }
    
        }
    }			
    			

    До того, как я смогу что-либо сделать с WSE в моем WebMethod, в своем проекте я должен добавить ссылку на Microsoft.Web.Services.dll. Затем я добавляю в код для Microsoft.Web.Services и Microsoft.Web.Services.Security два оператора using. Теперь я могу создать свой PersonalHello WebMethod.

    Первое, что делает моя функция PersonalHello - с помощью статического члена HttpSoapContext.RequestContext выбирает объект SoapContext для запроса. Теперь я могу доступаться ко всей информации заголовков WS-Security. Если в них нет контекста, мой Web метод возвращает значение Fault, в противном случае, я пронумеровываю все маркеры доступа, которые посылаю со своим запросом, и проверяю, не является ли один из них UsernameTokens. Если я нахожу UsernameToken, я создаю строку ответа, которая включает приветствие, в которое входит имя пользователя.

    Создание клиента, использующего WS-Security

    Чтобы увидеть, как воспользоваться преимуществами WS-Security с WSE со стороны клиента, я создал простое приложение Windows Form, которое будет вызывать мой Web сервис по нажатию кнопки. Так же, как я делал это для класса моего Web сервиса, я добавил ссылку на Microsoft.Web.Services.dll и включил такие же выражения using, как мы добавляли в коде нашего сервера.

    Для клиентской стороны WSE предоставляет класс Microsoft.Web.Services.WebServicesClientProtocol, который наследуется от класса System.Web.Services.Protocols.SoapHttpClientProtocol. Класс SoapHttpClientProtocol используется, когда для создания кода клиента на основе WSDL вы выбираете опцию Add Web Reference в Visual Studio .NET или используете утилиту WSDL.exe. Все, что я могу сделать – использовать или опцию Add Web Reference из Visual Studio .NET, или утилиту WSDL.exe, чтобы сгенерировать прокси классы для вашего клиента, затем заменить родителя сгенерированного прокси класс с SoapHttpClientProtocol на WebServicesClientProtocol. Теперь прокси класс будет иметь свойства RequestSoapContext и ResponseSoapContext, которые вы можете использовать для доступа к отправляемым и получаемым вами заголовкам WS-Security. Если вы используете опцию Add Web Reference, вы найдете код для сгенерированного прокси класса в директории Web References. Для проектов C# вы найдете файл Reference.cs в директории, имя которой совпадает с именем хоста, в котором находится WSDL. Для проектов Microsoft® Visual Basic® .NET файл называется Reference.vb. Я заменил описание типа

    public class Service1 :
        System.Web.Services.Protocols.SoapHttpClientProtocol {
    			

    на

    public class Service1 : 
        Microsoft.Web.Services.WebServicesClientProtocol {
    			

    Если вы используете опцию Visual Studio .NET Add Web Reference, вам надо быть аккуратными при внесении изменений в файл, в котором размещается генерируемый код. Если вы выбираете опцию Update Web Reference, Visual Studio .NET регенерирует код и перезаписывает ваши изменения.

    Для создания UsernameToken для запроса код моего клиента выглядит следующим образом.

    localhost.Hello proxy = new localhost.Hello();
    proxy.Url = endpointInput.Text;
    UsernameToken TextToken 
        = new UsernameToken(usernameInput.Text, 
                            passwordInput.Text,
                            PasswordOption.SendHashed);
    proxy.RequestSoapContext.Security.Tokens.Add(TextToken);
    string result;
    try
    {
        result = proxy.PersonalHello();
    }
    catch (Exception ex)
    {
        result = ex.Message;
    }
    MessageBox.Show(result);
    			

    Единственное различие между этим кодом и кодом, который будет вызывать обычный Web сервис, в том, что я создал объект UsernameToken и добавил его в коллекцию Tokens для запроса. Конструктор для объекта UsernameToken принимает три параметра: имя пользователя, пароль и PasswordOption. В данном случае я посылаю хэшированный пароль. Краткая версия SOAP запроса, сгенерированного из этого кода, показана ниже. Вы заметите, что заголовок Security включен в запрос, в который входит дочерний элемент UsernameToken, содержащий имя пользователя, хэшированный пароль, случайный Nonce, который мы можем использовать для идентификации этого конкретного запроса, и время создания. Все сообщение приведено в конце этой статьи с разделе Ссылки.

    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <soap:Header>
           ...
        <wsse:Security 
            soap:mustUnderstand="1"
            xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/07/secext">
          <wsse:UsernameToken 
              xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility" 
              wsu:Id="SecurityToken-58564463-5bdc-4a6b-a7fb-94a0d7357a20">
            <wsse:Username>Joe</wsse:Username>
            <wsse:Password Type="wsse:PasswordDigest">
              gpBDXjx79eutcXdtlULIlcrSiRs=
            </wsse:Password>
            <wsse:Nonce>
              h52sI9pKV0BVRPUolQC7Cg==
            </wsse:Nonce>
            <wsu:Created>2002-11-04T19:16:50Z</wsu:Created>
          </wsse:UsernameToken>
        </wsse:Security>
      </soap:Header>
      <soap:Body>
        <PersonalHello xmlns="http://tempuri.org/" />
      </soap:Body>
    </soap:Envelope>			
    			

    Теперь сообщение может быть отправлено Web сервису, где WSE SOAP расширение будет проверять достоверность запроса, сравнивать хэш пароля с паролем, полученным от Провайдера пароля, и затем, если все пройдет хорошо, запрос передастся в наш Web метод. Наш Web метод получает поступающий запрос с заполненным SoapContext, в заголовке Security ищет UsernameToken и создает строку ответа на базе полученного имени. Ответ возвращается как обычный WebMethod ответ, и наше клиентское приложение отображает возвращенную строку. Мы успешно выполнили наше первое WS-Security приложение!

    Проверка достоверности UsernameToken на сервере

    Хотя WSE проверяет достоверность синтаксиса заголовка Security и сравнивает хэш пароля с паролем из Провайдера пароля, для запроса должны быть проведены еще некоторые дополнительные проверки. Например, в запрос может быть включен не один элемент UsernameToken. WS-Security предоставляет поддержку для включения в запрос любого количества маркеров, которые могут использоваться в разных целях. Тот факт, что в коде нашего клиента добавляется UsernameToken в коллекцию Tokens, должен быть четким предупреждением, что в одно сообщение может входить больше одного. Кстати, если вы изменили код клиента, чтобы создать второй UsernameToken и добавить его в коллекцию, наш текущий код вернет строку, подобную следующей:

    Hello Bob.Hello Alice.
    

    Я изменил код нашего Web метода, чтобы проверять, что UsernameToken включает хэшированный пароль, и принимать поступающие запросы, содержащие только один UsernameToken. Мой модифицированный код приведен ниже.

    [WebMethod]
    public string PersonalHello()
    {
        SoapContext requestContext = HttpSoapContext.RequestContext;
        if (requestContext == null)
        {
            throw new ApplicationException("Non-SOAP request.");
        }
        // We only allow requests with one security token
        if (requestContext.Security.Tokens.Count == 1)
        {
            foreach (SecurityToken tok 
                         in requestContext.Security.Tokens)
            {
                // Only accept UsernameTokens
                if (tok is UsernameToken)
                {
                    UsernameToken UserToken = (UsernameToken)tok;
                    // Only accept UsernameTokens with a 
                    // hashed password.
                    if (UserToken.PasswordOption 
                        == PasswordOption.SendHashed)
                    {
                        return "Hello " + UserToken.Username;
                    }
                    else
                    {
                        throw new SoapException(
                            "Invalid UsernameToken password type.", 
                            SoapException.ClientFaultCode);
                    }
                }
                else
                {
                    throw new SoapException(
                        "UsernameToken security token required.", 
                        SoapException.ClientFaultCode);
                }
            }
        }
        else
        {
            throw new SoapException(
                "Request must have exactly one security token.", 
                SoapException.ClientFaultCode);
        }
        return null;
    }
    			

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

    Использование сертификатов X.509 в качестве маркеров WS-Security

    Кроме UsernameTokens, в WS-Security также определен элемент BinarySecurityToken, предназначенный для сохранения пары хорошо известных типов маркеров, имеющих собственные форматы. Спецификация определяет типы BinarySecurityToken для X.509 v3 сертификатов и Kerberos v5 tickets. WSE поддерживает X.509 сертификаты, и вы обнаружите, что в большинстве случаев они интерпретируются так же как и UsernameToken.

    Хранилища сертификатов

    До того как мы сможем включить сертификат в наш запрос, мы должны определить, где найти его. В традиционной разработке программного обеспечения Microsoft® Windows сертификаты сохранялись централизованно в так называемом хранилище сертификатов. У каждого пользователя было персональное хранилище сертификатов, чтобы осуществлять такие вещи, как формирование цифровых подписей для сообщений электронной почты или аутентификация Web серверами через SSL. Сами сертификаты не являются персональными, поскольку они - просто способ выдачи открытых ключей. Важно то, что секретный ключ, соответствующий открытому ключу в сертификате, находится в защищенном хранилище ключей лица, чей сертификат используется. Это позволит пользователям создавать цифровые подписи для категорий с помощью своих секретных ключей, а затем кто угодно сможет проверить достоверность их подписи с помощью открытого ключа сертификата.

    В первоначальной версии .NET Framework не было классов для доступа к хранилищам Windows сертификатов, хотя были классы для обработки X.509 сертификатов. Впрочем, WSE предоставил классы, которые позволяют вам открывать хранилища Windows сертификатов и использовать сертификаты из этого свободно управляемого пространства. В следующем коде для заполнения ListBox общими именами всех сертификатов в персональном хранилище сертификатов пользователя используется класс Microsoft.Web.Services.Security.X509.

     
      ...
    using Microsoft.Web.Services;
    using Microsoft.Web.Services.Security;
    using Microsoft.Web.Services.Security.X509;
      ...
    private X509CertificateStore store;
      ...
    private void Form1_Load(object sender, System.EventArgs e)
    {
        store = X509CertificateStore.CurrentUserStore(
            X509CertificateStore.MyStore);
        store.OpenRead();
        foreach(X509Certificate cert in store.Certificates)
        {
            listBox1.Items.Add(cert.GetName());
        }
    }			
    			

    Пример полнофункционального диалога для выбора X.509 сертификата из хранилища сертификатов приведен в X509CertificateStoreDialog.cs директории примеров кода WSE.

    Добавление маркера сертификата X.509 в SOAP сообщение

    Теперь, когда мы можем найти наш персональный сертификат, процесс их добавления в SOAP запрос очень похож на добавление UsernameToken, используемого нами ранее. Следующий код получает сертификат из уже открытого хранилища сертификатов из ListBox, заполненного нами ранее, и затем добавляет двоичный маркер, созданный из сертификата, в коллекцию Tokens.

    X509Certificate cert =
        (X509Certificate)store.Certificates[listBox1.SelectedIndex];
    proxy.RequestSoapContext.Security.Tokens.Add(
        new X509SecurityToken(cert));			
    			

    Организация доступа к сертификату со стороны сервера также очень похожа на получение UsernameToken. Модифицированная версия логики нашего предыдущего Web метода, в котором используется общее имя сертификата для сборки персонифицированного ответа, показана ниже.

     [WebMethod]
    public string PersonalHello()
    {
        // Only accept SOAP requests
        SoapContext requestContext = HttpSoapContext.RequestContext;
        if (requestContext == null)
        {
            throw new ApplicationException("Non-SOAP request.");
        }
        // We only allow requests with one security token
        if (requestContext.Security.Tokens.Count == 1)
        {
            foreach (SecurityToken tok in requestContext.Security.Tokens)
            {
                // Only accept X.509 Certificates
                if (tok is X509SecurityToken)
                {
                    X509SecurityToken certToken = (X509SecurityToken)tok;
                    return "Hello " + certToken.Certificate.GetName();
                }
                else
                {
                    throw new SoapException(
                        "X.509 security token required.", 
                        SoapException.ClientFaultCode);
                }
            }
        }
        else
        {
            throw new SoapException(
                "Request must have exactly one security token.", 
                SoapException.ClientFaultCode);
        }
        return null;
    }
    			
    Цифровые подписи

    Простая пересылка сертификатов X.509 с запросом, на самом деле, не очень хороший способ аутентификации чего-либо. Сертификаты общеизвестны, поэтому кто угодно может включить чужой сертификат в свой запрос. Механизм использования сертификатов для аутентификации основан на идее, которую мы упоминали ранее в связи с подписанием некоторой категории с помощью секретного ключа, для которого в сертификате есть соответствующий открытый ключ. В случае отправления SOAP сообщения, если Bob создает цифровую подпись элемента SOAP тела с помощью своего секретного ключа, вместе с сигнатурой он может включить в заголовки запроса соответствующий сертификат, чтобы все получатели сообщения могли проверить достоверность того, что запрос поступил от Bob и что он оставался неизменным с момента подписания. Включение сертификата в запрос необязательно, но удобно для тех, кто пытается проверить достоверность подписи.

    WSE открыто поддерживает создание цифровых подписей. Наряду с коллекцией Tokens, здесь есть коллекция SoapContext.Security.Elements, которая позволит вам добавлять различные элементы WS-Security, включая элемент Signature. Основываясь на нашем предыдущем коде клиента, который включает цифровой сертификат, теперь мы используем этот же сертификат для подписания запроса:

    X509Certificate cert = 
        (X509Certificate)store.Certificates[listBox1.SelectedIndex];
    X509SecurityToken certToken = new X509SecurityToken(cert);
    proxy.RequestSoapContext.Security.Tokens.Add(certToken);
    proxy.RequestSoapContext.Security.Elements.Add(
        new Signature(certToken));			
    			

    Так же как мы делали ранее, мы извлекаем сертификат из предварительно открытого хранилища сертификатов и добавляем его в коллекцию Tokens. Затем, используя X509SecurityToken в качестве параметра для конструктора Signature, мы создаем новый объект Signature и добавляем его в коллекцию Elements. Сокращенная версия SOAP сообщения, генерируемого из этого кода, показана ниже. Полностью SOAP сообщение приведено в конце этой статьи.

    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope 
        xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <soap:Header>
        <wsrp:path 
            soap:actor="http://schemas.xmlsoap.org/soap/actor/next" 
            soap:mustUnderstand="1" 
            xmlns:wsrp="http://schemas.xmlsoap.org/rp">
          <wsrp:action 
              wsu:Id="Id-b856ae70-7a1b-4895-a05c-5f6596ca4429" 
                 ...
        </wsrp:path>
                 ...
        <wsse:Security 
            soap:mustUnderstand="1" 
            xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/07/secext">
          <wsse:BinarySecurityToken 
              ValueType="wsse:X509v3" 
              EncodingType="wsse:Base64Binary" 
              xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility" 
              wsu:Id="SecurityToken-f6f96b4b-23c5-421e-92ff-f1050d531e82">
            MIIGkzCCBXugAwIBAgIK  . . . 39Vmjd20Lw==
          </wsse:BinarySecurityToken>
          <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
            <SignedInfo>
              <CanonicalizationMethod 
                  Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
              <SignatureMethod 
                  Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
              <Reference URI="#Id-24cc3660-6f1a-41fe-a949-71d7ed9fc636">
                <Transforms>
                  <Transform 
                      Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                </Transforms>
                <DigestMethod 
                    Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                <DigestValue>
                  /8iL3OP9mfzuixI/ilkhHMbatV0=
                </DigestValue>
              </Reference>
              <Reference URI="#Id-b856ae70-7a1b-4895-a05c-5f6596ca4429">
                <Transforms>
                   ...
            </SignedInfo>
            <SignatureValue>
    ZY4MhHzBYz+CBdAz1LhAFjy6QxQoKJoA7l2eG45QV0hDIJrmXwLEGrPnpX+uPan5+MS6hm+oL
    /sGTbKJ/DJMp/t5ZyqY1qvngGQLcYXRy538zemwFfeGN5R2wmOoUSeCBUqprQVUbnkz+qlVp/
    5f7t7VGW2Ee55Q3ol+ApVoFQE=
            </SignatureValue>
            <KeyInfo>
              <wsse:SecurityTokenReference>
                <wsse:Reference 
        URI="#SecurityToken-f6f96b4b-23c5-421e-92ff-f1050d531e82" />
              </wsse:SecurityTokenReference>
            </KeyInfo>
          </Signature>
        </wsse:Security>
      </soap:Header>
      <soap:Body 
          wsu:Id="Id-24cc3660-6f1a-41fe-a949-71d7ed9fc636"
          xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
        <PersonalHello xmlns="http://tempuri.org/" />
      </soap:Body>
    </soap:Envelope>			
    			

    Для полного понимания различных элементов заголовка, включенных в этот запрос, ознакомьтесь со спецификациями WS-Routing (http://msdn.microsoft.com/webservices/building/security/default.aspx?pull=/library/en-us/dnglobspec/html/ws-routing.asp) и Web Services Security Addendum (http://msdn.microsoft.com/webservices/building/security/default.aspx?pull=/library/en-us/dnglobspec/html/ws-security-addendum.asp). Для нашего обсуждения мы рассмотрим особенности того, как WSE использует WS-Security для подписания нашего запроса.

    Первое, что надо отметить по поводу этого сообщения – это то, что здесь в заголовке Security есть элемент BinarySecurityToken. Атрибуты этого элемента показывают, что это Base 64 кодированный сертификат X.509 v3. Это X509SecurityToken, который мы добавили в коллекцию Tokens в наших двух последних примерах. И опять же, вовсе не обязательно включать его в сообщение, но это удобно для проверки достоверности подписи.

    Следующее, на что надо обратить внимание – это то, что теперь элемент Signature находится в заголовке Security. У Signature есть три дочерних элемента: SignedInfo, SignatureValue и KeyInfo. Элемент SignedInfo определяет конкретные данные, подписанные в этом запросе, как они были канонизированны и какой алгоритм использовался для вычисления подписи. Важно понять список используемых элементов, который определяет, что конкретно в этом элементе было подписано. Для краткости я удалил некоторое количество используемых элементов, но вы заметите, что у каждого из двух оставшихся есть ассоциированный атрибут URI. Этот атрибут URI соответствует атрибуту ID элемента, включенному в подпись. Например, в первой ссылке показан URI, начинающийся с "#Id-24cc3660-". Если вы просмотрите сообщение, вы увидите, что этот URI соответствует ID для элемента Body нашего сообщения. Поэтому Body является частью пописанной информации для этого запроса. Вторая ссылка касается элемента action заголовка Path, который является частью спецификации WS-Routing. WSE автоматически включает заголовки Path и Timestamp, и большинство дополнительной информации этих заголовков включается в цифровую подпись. Далее приведен список элементов, на которые WSE будет ссылаться в заголовке SignedInfo, и которые, таким образом, будут включены в цифровую подпись.

    /soap:Envelope/soap:Header/wsrp:path/wsrp:action
    /soap:Envelope/soap:Header/wsrp:path/wsrp:to
    /soap:Envelope/soap:Header/wsrp:path/wsrp:from
    /soap:Envelope/soap:Header/wsrp:path/wsrp:id
    /soap:Envelope/soap:Header/wsu:Timestamp/wsu:Created
    /soap:Envelope/soap:Header/wsu:Timestamp/wsu:Expires
    /soap:Envelope/soap:Body			
    			

    Причина, по которой подписаны подэлементы заголовков path и Timestamp, а не полностью элементы path и Timestamp, в том, что в сценариях WS-Routing ожидается, что посредники будут добавлять элементы в заголовки path и Timestamp по мере передачи сообщения через маршрутизаторы уровня SOAP. Поэтому, если бы какой-нибудь из этих заголовков был бы изменен, цифровая подпись разрушилась бы.И последнее по поводу элемента Signature в этом запросе – в KeyInfo есть ссылка на BinarySecurityToken, содержащий наши сертификаты. Он показывает, откуда поступил ключ, который использовался для подписания нашего запроса. Конечно же, для сертификата – это секретный ключ, соответствующий открытому ключу в сертификате, который использовался для создания цифровой подписи. Затем открытый ключ может использоваться для проверки достоверности того, что данные могут поступать только от того, кто знает соответствующий секретный ключ.

    Проверка достоверности цифровой подписи

    Проверять достоверность синтаксиса цифровой подписи будет WSE SOAP расширение, но простого знания того, что подпись в сообщении достоверна, не достаточно для определения, что сообщения поступило от конкретного лица. WS-Security и спецификация XML Digital Signature, (Extensible Markup Language) XML-Signature Syntax and Processing (http://www.ietf.org/rfc/rfc3275.txt), очень гибки в том смысле, что они обеспечивают возможность включать подписи в XML. Например, вы ожидаете сообщение от Alice, вы, возможно, видите, что сообщение имеет подпись от Alice, но эта подпись может подписывать только маршрутную информацию заголовка path. Пока вы не провели некоторые собственные проверки, нет гарантии того, что интересующие вас вещи (обычно тело сообщения) поступили от Alice.

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

     [WebMethod]
    public string PersonalHello()
    {
        // Only accept SOAP requests
        SoapContext requestContext = HttpSoapContext.RequestContext;
        if (requestContext == null)
        {
            throw new ApplicationException("Non-SOAP request.");
        }
        // Look for Signatures in the Elements collection
        foreach (Object elem in requestContext.Security.Elements)
        {
            if (elem is Signature)
            {
                Signature sign = (Signature)elem;
                // Verify that signature signs the body of the request
                if (sign != null 
                    && (sign.SignatureOptions &
                        SignatureOptions.IncludeSoapBody) != 0)
                {
                    // Determine what kind of token is used 
                    // with the signature.
                    if (sign.SecurityToken is UsernameToken)
                    {
                        return "Hello " +
                            ((UsernameToken)sign.SecurityToken).Username;
                    }
                    else if (sign.SecurityToken is X509SecurityToken)
                    {
                        return "Hello " +
                           ((X509SecurityToken)sign.SecurityToken)
                           .Certificate.GetName();
                    }
               }
            }
        }
        // No approriate signature found
        throw new SoapException("No valid signature found", 
            SoapException.ClientFaultCode);
    }			
    			
    Подпись части сообщения

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

    У вас есть два варианта для определения того, какие части сообщения охраняются цифровой подписью. В предыдущем примере мы пользовались свойством SignatureOptions класса Signature, чтобы определить, включает ли подпись SOAP тело. Вы также можете применять свойство SignatureOptions, чтобы установить, какие части SOAP сообщения должны быть защищены подписью. Вы можете использовать любую комбинацию следующих флагов, определенных перечнем SignatureOptions:

    IncludePath
    IncludePathAction
    IncludePathFrom
    IncludePathId
    IncludePathTo
    IncludeSoapBody
    IncludeTimestamp
    IncludeTimestampCreated
    IncludeTimestampExpires			
    			

    Есть еще одно значение, которое вы можете задать для SignatureOptionsIncludeNone. Оно показывает, что ни один из приведенных выше вариантов не должен быть включен в подпись. Тогда зачем все эти беспокойства по поводу подписания сообщения, если вы собираетесь задать SignatureOption значение IncludeNone? Ответ можно найти, используя другой механизм определения того, какие части сообщения мы хотим отправить.

    Для установления того, что подписывается, а что нет, WS-Security предлагает два варианта. Первый – определить преобразование сообщения и затем на базе результатов создать подпись. Другая возможность – задать атрибут Id в элементе, который вы хотите подписать, и затем включить ссылку на этот элемент в блоке SignedInfo элемента Signature. Так WSE создает свои подписи для различных вариантов, приведенных в перечне SignatureOptions. Но WSE также позволяет вам создавать атрибут Id самостоятельно и затем вручную добавлять ссылку в блок SignedInfo подписи. Это обеспечивает вам возможность подписывать специальные SOAP заголовки так же, как и любые отдельные части тела, которые вы хотите подписать. Вам только надо гарантировать, что атрибут Id задан и что href для него включен в ссылки. Мы рассмотрим пример, в котором подпишем только часть SOAP тела.

    Для создания и синтаксического анализа отправляемых и получаемых SOAP сообщений ASP.NET Web сервисы используют XmlSerializer из .NET Framework. Вы можете контролировать многие детали того, как конкретно происходит сериализация, путем задания атрибутов, которые контролируют работу XmlSerializer. Поскольку не сложно добавить атрибут Id для части XML, задайте его значение и затем добавьте это значение в ссылки подписи.

    Чтобы проиллюстрировать, как это сделать, я создал ASP.NET WebMethod, возвращающий комплексный тип, который включает заказ. Я хочу создать цифровую подпись только для элемента заказа, чтобы клиент знал наверняка, что он поступил от меня. Сначала мы создаем комплексный класс, который будем возвращать из нашего WebMethod. В этом класса есть номер действительного заказа, но также там есть свойство ID, которое будет сериализовано как атрибут. На этот ID будет ссылаться наша подпись. Важно, чтобы мы включили верное пространство имен для этого атрибута, которое совпадает с пространством имен в спецификации WS-Security, поэтому мы используем параметр Namespace атрибута XmlAttribute, чтобы задать http://schemas.xmlsoap.org.ws/2002/07/utility.

    public class PONum
    {
        [XmlAttribute("Id", 
          Namespace="http://schemas.xmlsoap.org/ws/2002/07/utility")]
        public string ID;
        [XmlText]
        public string PONumber;
    } 			
    			

    Наш WebMethod должен будет сделать несколько вещей. Во-первых, ему надо будет сгенерировать GUID, который будет использоваться как уникальный ID. Он должен будет создать экземпляр приведенного выше класса и задать свойство ID в формате, соответствующем включенному значению GUID. Он также задает значение номера действительного заказа, который в нашем конкретном случае всегда одинаковый. Далее он создаст маркер доступа на базе сертификата X.509, который будет использоваться для подписания номера заказа.

    Чтобы использовать сертификат из ASP.NET, он должен быть в хранилище сертификатов, доступном из рабочего процесса ASP.NET. Аналогично, секретный ключ, соответствующий открытому ключу в сертификате, должен находится в хранилище ключей, доступном из ASP.NET. Для моего примера я использовал сертификат из хранилища сертификатов машины. Чтобы получить возможность работать с ним, я предоставил полномочия "ASPNET пользователя" (учетная запись, под которой которой запускается рабочий процесс ASP.NET) физическому хранилищу ключей машины. Это потенциально открывает доступ к хранилищу ключей машины для других приложений, поэтому вы должны делать такого рода изменения с предельной осторожностью. Более подробная информация по проблемам хранения и доступа к сертификатам и ключам на вашей машине представлена в документации WSE "Managing X.509 Certificates".

    Имея маркер доступа, передавая его в конструктор мы можем создать экземпляр Signature. Мы сразу задаем свойство SignatureOptions для SignatureOptions.IncludeNone таким образом, чтобы он не включал все применяемые по умолчанию части сообщения. Теперь мы вызываем метод AddReference объекта Signature, задавая такую же строку, какую мы задавали ранее для свойства ID, за исключением стоящего впереди символа '#', чтобы показать, что это локальная ссылка. Код для Web метода показан ниже:

     [WebMethod]
    public PONum GetPONumber()
    {
        SoapContext responseContext 
            = HttpSoapContext.ResponseContext;
        PONum PO = new PONum();
        Guid referenceID = Guid.NewGuid();
        PO.ID = "Id:" + referenceID.ToString();
        PO.PONumber = "PO10025";
        X509CertificateStore store = 
            X509CertificateStore.LocalMachineStore(
                X509CertificateStore.MyStore);
        store.OpenRead();
        X509Certificate cert 
            = store.FindCertificateBySubjectName(
                "CN = mattpo.redmond.corp.microsoft.com")[0];
        X509SecurityToken token = new X509SecurityToken(cert);
        responseContext.Security.Tokens.Add(token);
        Signature sig = new Signature(token);
        sig.SignatureOptions = SignatureOptions.IncludeNone;
        sig.AddReference(new Reference("#" + PO.ID));
        responseContext.Security.Elements.Add(sig);
        return PO;
    }			
    			

    Последний шаг –клиент проверяет соответствие подписи в возвращенном заказе. Мы делаем это, сверяя возвращаемый ID с ссылками для подписи, и проверяя, что используемый для этой подписи сертификат тот, который мы ожидали. Код для вызова Web сервиса и проверки используемого сертификата показан ниже.

    localhost.Service1Wse proxy = new localhost.Service1Wse();
    localhost.PONum po = proxy.GetPONumber();
    foreach (Object element in proxy.ResponseSoapContext.Security.Elements)
    {
        if (element is Signature)
        {
            Signature sig = (Signature)element;
            foreach (Reference reference in sig.SignedInfo.References)
            {
                if (reference.Uri == "#" + po.Id)
                {
                    X509Certificate signatureCert 
                        = ((X509SecurityToken)
                                sig.SecurityToken).Certificate;
                    if (signatureCert.Equals(poCert))
                        MessageBox.Show("It's signed!");
                }
            }
        }
    }			
    			
    Заключение

    Эта статья предлагает вам ознакомится с возможностями спецификации WS-Security через Web Services Enhancements для Microsoft .NET, путем рассмотрения того, как вы можете использовать WSE для проведения аутентификации UsernameToken, и используя аутентификацию X.509 для создания цифровой подписи частей SOAP сообщения. WSE позволяет вам изучить и проверить вид WS-Security поддержки, которая включена в SOAP сообщение. Существует большое количество возможностей WS-Security, предоставляемых WSE и не осужденных здесь, например, использование различных форм кодирования. Я рекомендую вам инсталлировать WSE, написать кое-какой код и проверить созданные сообщения и сообщения, включенные в раздел References, чтобы получить представление о том, как WS-Security может увеличить функциональность ваших Web сервисов.

    SOAP сообщение с хэшированным UsernameToken
    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <soap:Header>
        <wsrp:path 
            soap:actor="http://schemas.xmlsoap.org/soap/actor/next" 
            soap:mustUnderstand="1" 
            xmlns:wsrp="http://schemas.xmlsoap.org/rp">
          <wsrp:action>
            http://tempuri.org/PersonalHello
          </wsrp:action>
          <wsrp:to>
            http://localhost:8080/wsdk-security/verifiedhello.asmx
          </wsrp:to>
          <wsrp:id>
            uuid:14f61c50-586c-42ec-8286-c5c9fa8bfce1
          </wsrp:id>
        </wsrp:path>
        <wsu:Timestamp 
            xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
          <wsu:Created>2002-11-04T19:16:50Z</wsu:Created>
          <wsu:Expires>2002-11-04T19:21:50Z</wsu:Expires>
        </wsu:Timestamp>
        <wsse:Security 
            soap:mustUnderstand="1"
            xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/07/secext">
          <wsse:UsernameToken 
              xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility" 
              wsu:Id="SecurityToken-58564463-5bdc-4a6b-a7fb-94a0d7357a20">
            <wsse:Username>Joe</wsse:Username>
            <wsse:Password Type="wsse:PasswordDigest">
              gpBDXjx79eutcXdtlULIlcrSiRs=
            </wsse:Password>
            <wsse:Nonce>
              h52sI9pKV0BVRPUolQC7Cg==
            </wsse:Nonce>
            <wsu:Created>2002-11-04T19:16:50Z</wsu:Created>
          </wsse:UsernameToken>
        </wsse:Security>
      </soap:Header>
      <soap:Body>
        <PersonalHello xmlns="http://tempuri.org/" />
      </soap:Body>
    </soap:Envelope>			
    			
    SOAP сообщение с маркером сертификата X.509, используемом для создания цифровой подписи сообщения
    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope 
        xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <soap:Header>
        <wsrp:path 
            soap:actor="http://schemas.xmlsoap.org/soap/actor/next" 
            soap:mustUnderstand="1" 
            xmlns:wsrp="http://schemas.xmlsoap.org/rp">
          <wsrp:action 
              wsu:Id="Id-b856ae70-7a1b-4895-a05c-5f6596ca4429" 
              xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
            http://tempuri.org/PersonalHello
          </wsrp:action>
          <wsrp:to 
              wsu:Id="Id-e3fa8752-df7d-4a16-a883-f98800bf24f7"
              xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
            http://localhost:8080/wsdk-security/signedhello.asmx
          </wsrp:to>
          <wsrp:id 
              wsu:Id="Id-64bdd986-1d54-4176-bbc3-c38255fcfedf" 
              xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
            uuid:15810d11-91d9-44cb-b3c8-016ff9d93b78
          </wsrp:id>
        </wsrp:path>
        <wsu:Timestamp 
            xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
          <wsu:Created 
              wsu:Id="Id-134651fa-1791-4679-abf7-cbbc100fbeb9">
            2002-11-05T23:35:59Z
          </wsu:Created>
          <wsu:Expires 
              wsu:Id="Id-9c106440-3955-4f62-903d-c30c6c9e8d27">
            2002-11-05T23:40:59Z
          </wsu:Expires>
        </wsu:Timestamp>
        <wsse:Security 
            soap:mustUnderstand="1" 
            xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/07/secext">
          <wsse:BinarySecurityToken 
              ValueType="wsse:X509v3" 
              EncodingType="wsse:Base64Binary" 
              xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility" 
              wsu:Id="SecurityToken-f6f96b4b-23c5-421e-92ff-f1050d531e82">
            MIIGkzCCBXugAwIBAgIK  . . . 39Vmjd20Lw==
          </wsse:BinarySecurityToken>
          <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
            <SignedInfo>
              <CanonicalizationMethod 
                  Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
              <SignatureMethod 
                  Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
              <Reference URI="#Id-24cc3660-6f1a-41fe-a949-71d7ed9fc636">
                <Transforms>
                  <Transform 
                      Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                </Transforms>
                <DigestMethod 
                    Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                <DigestValue>
                  /8iL3OP9mfzuixI/ilkhHMbatV0=
                </DigestValue>
              </Reference>
              <Reference URI="#Id-b856ae70-7a1b-4895-a05c-5f6596ca4429">
                <Transforms>
                  <Transform 
                      Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                </Transforms>
                <DigestMethod 
                    Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                <DigestValue>
                  rH3m6W9zglaAMMzP7sD9yvwzEdA=
                </DigestValue>
              </Reference>
              <Reference URI="#Id-e3fa8752-df7d-4a16-a883-f98800bf24f7">
                <Transforms>
                  <Transform 
                      Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                </Transforms>
                <DigestMethod 
                    Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                <DigestValue>
                  tzgO0kc7HSomAxAokgHw3/hkL+E=
                </DigestValue>
              </Reference>
              <Reference URI="#Id-64bdd986-1d54-4176-bbc3-c38255fcfedf">
                <Transforms>
                  <Transform 
                      Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                </Transforms>
                <DigestMethod 
                    Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                <DigestValue>
                  Ls05OCaMfqqaIFj2vV8a/aHQQBo=
                </DigestValue>
              </Reference>
              <Reference URI="#Id-134651fa-1791-4679-abf7-cbbc100fbeb9">
                <Transforms>
                  <Transform 
                      Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                </Transforms>
                <DigestMethod 
                    Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                <DigestValue>
                  VqOIop+9EeJXGIwB3TqAqXgiKUI=
                </DigestValue>
              </Reference>
              <Reference URI="#Id-9c106440-3955-4f62-903d-c30c6c9e8d27">
                <Transforms>
                  <Transform 
                      Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                </Transforms>
                <DigestMethod 
                    Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                <DigestValue>
                  3d9WMNwuLIrRfSyaWCsl63d+wDA=
                </DigestValue>
              </Reference>
            </SignedInfo>
            <SignatureValue>
    ZY4MhHzBYz+CBdAz1LhAFjy6QxQoKJoA7l2eG45QV0hDIJrmXwLEGrPnpX+uPan5+MS6hm+oL/sGTbKJ/DJMp/t5ZyqY1qvngGQLcYXRy538zemwFfeGN5R2wmOoUSeCBUqprQVUbnkz+qlVp/5f7t7VGW2Ee55Q3ol+ApVoFQE=
            </SignatureValue>
            <KeyInfo>
              <wsse:SecurityTokenReference>
                <wsse:Reference 
        URI="#SecurityToken-f6f96b4b-23c5-421e-92ff-f1050d531e82" />
              </wsse:SecurityTokenReference>
            </KeyInfo>
          </Signature>
        </wsse:Security>
      </soap:Header>
      <soap:Body 
          wsu:Id="Id-24cc3660-6f1a-41fe-a949-71d7ed9fc636"
          xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
        <PersonalHello xmlns="http://tempuri.org/" />
      </soap:Body>
    </soap:Envelope>			
    			
    SOAP сообщение с маркером сертификата X.509, используемым для создания цифровой подписи части сообщения
    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope 
        xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
        xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <soap:Header>
        <wsu:Timestamp
            xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility">
          <wsu:Created>2002-11-07T21:51:51Z</wsu:Created>
          <wsu:Expires>2002-11-07T21:56:51Z</wsu:Expires>
        </wsu:Timestamp>
        <wsse:Security 
            soap:mustUnderstand="1"
            xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/07/secext">
          <wsse:BinarySecurityToken 
              ValueType="wsse:X509v3" 
              EncodingType="wsse:Base64Binary"
              xmlns:wsu="http://schemas.xmlsoap.org/ws/2002/07/utility"
              wsu:Id="SecurityToken-547309ee-532f-40ce-a370-a64be85e977e">
            MIIHRjC ... HVUjaoy
          </wsse:BinarySecurityToken>
          <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
            <SignedInfo>
              <CanonicalizationMethod 
                  Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
              <SignatureMethod 
                  Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
              <Reference URI="#Id:ce249a29-aa9a-427a-b0c4-830cdc7f481a">
                <DigestMethod 
                    Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
                <DigestValue>7yhtDGpxNtkFGT9+1vWHI7sQL1c=</DigestValue>
              </Reference>
            </SignedInfo>
            <SignatureValue>
    tNj18ILnxAyc/3AoNCRb+ZBcYcIp5KCKTFLCTNhAzuokk5m1S8FOBvFYTUdy1qCU2i655/KCcIzZ7lzLSqY57iaoWgdpQBAWvEEhxkSNuGGl/qoknNhc4B2SN24t1AniB4UwNFvo2u6rHiBr3nSfAv0rSPuGa32c3Ri8LRcqZ5M=
            </SignatureValue>
            <KeyInfo>
              <wsse:SecurityTokenReference>
                <wsse:Reference 
                    URI=
                    "#SecurityToken-547309ee-532f-40ce-a370-a64be85e977e" />
              </wsse:SecurityTokenReference>
            </KeyInfo>
          </Signature>
        </wsse:Security>
      </soap:Header>
      <soap:Body>
        <GetPONumberResponse xmlns="http://tempuri.org/">
          <GetPONumberResult 
              d4p1:Id="Id:ce249a29-aa9a-427a-b0c4-830cdc7f481a" 
              xmlns:d4p1="http://schemas.xmlsoap.org/ws/2002/07/utility">
            PO10025
          </GetPONumberResult>
        </GetPONumberResponse>
      </soap:Body>
    </soap:Envelope>			
    			


    Может пригодится:


    Автор: Matt Powell
    Прочитано: 11188
    Рейтинг:
    Оценить: 1 2 3 4 5

    Комментарии: (0)

    Добавить комментарий
    Ваше имя*:
    Ваш email:
    URL Вашего сайта:
    Ваш комментарий*:
    Код безопастности*:

    Рассылка новостей
    Рейтинги
    © 2007, Программирование Исходники.Ру