Поделиться через


Практическое руководство. Использование режима безопасности транспорта

Обновлен: Ноябрь 2007

.NET Compact Framework, версия 3.5 поддерживает использование транспорта HTTPS для подключения к службе Windows Communication Foundation (WCF) на компьютере. Сюда входит поддержка проверки подлинности сервера и клиента.

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

Bb629363.alert_note(ru-ru,VS.90).gifПримечание.

В случае проверки подлинности только сервера сертификат клиента не требуется. Платформа .NET Compact Framework 3.5 также поддерживает безопасность обмена сообщениями, но в этом примере она не используется.

Чтобы создать службу WCF для персонального компьютера

  1. Создайте и установите сертификат сервера и сертификат клиента.

    Эти шаги относятся к используемому средству создания сертификата (например, Makecert.exe) и не рассматриваются в данном разделе. Необходимо выполнить следующие задачи:

    • Создайте самостоятельно подписанный сертификат (например, используйте имя своей организации организация).

    • Создайте сертификат сервера, подписанный именем организация. Имя сертификата сервера должно совпадать с именем узла URL-адреса, который используется для доступа к службе.

    • Создайте сертификат клиента, подписанный именем организация.

    Bb629363.alert_note(ru-ru,VS.90).gifПримечание.

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

  2. Создайте новый проект веб-службы.

  3. Замените файл Web.config файлом из примера, показанного на этом шаге. Измените следующие элементы и атрибуты в файле:

    • Измените атрибут service name в соответствии с новой используемой службой.

    • Измените значение атрибута behaviorConfiguration, чтобы он ссылался на новое имя поведения.

    • Измените значение атрибута endpoint contract, чтобы он ссылался на интерфейс службы.

    Bb629363.alert_note(ru-ru,VS.90).gifПримечание.

    Убедитесь, что значение атрибута binding для элемента <endpoint> — "basicHttpBinding". .NET Compact Framework поддерживает шифрование текста, но только не двоичное шифрование.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <system.serviceModel>
        <services>
          <service 
              name="CalculatorService"
              behaviorConfiguration="MyServiceTypeBehaviors">
            <endpoint address=""
                      binding="basicHttpBinding"
                      bindingConfiguration="transport"
                      contract="ICalculatorService" />
            <endpoint address="mex"
                      binding="basicHttpBinding"
                      bindingConfiguration="transport"
                      contract="IMetadataExchange" />
          </service>
        </services>
        <bindings>
          <basicHttpBinding>
            <binding name="transport">
              <security mode="Transport">
                <transport clientCredentialType="Certificate" />
              </security>
            </binding>
          </basicHttpBinding>
        </bindings>
        <!--For debugging purposes set the includeExceptionDetailInFaults attribute to true-->
        <behaviors>
          <serviceBehaviors>
            <behavior name="MyServiceTypeBehaviors">
              <serviceMetadata httpsGetEnabled="True" httpsGetUrl=""/>
              <serviceDebug includeExceptionDetailInFaults="False" />
              <serviceCredentials>
                <clientCertificate>
                   <authentication trustedStoreLocation="LocalMachine"
                               revocationMode="NoCheck"
                               certificateValidationMode="ChainTrust"/>
                </clientCertificate>
              </serviceCredentials>
            </behavior>
          </serviceBehaviors>
        </behaviors>
    
      </system.serviceModel>
    
    </configuration>
    
  4. В исходном коде для службы WCF, удалите все параметры, указанные в ServiceContract и атрибутах OperationContract.

    Bb629363.alert_note(ru-ru,VS.90).gifПримечание.

    Этот пример не реализует поддержку для параметров, указанных в контрактах, таких, как ServiceContract и OperationContract. Если нужна поддержка параметра для этих контактов, чтобы создать код клиента, можно использовать средство ServiceModel Utility для WCF платформы .NET Compact Framework (NetCFSvcUtil.exe). Это средство встраивает поддержку многих из этих параметров в приложения, основанные на .NET Compact Framework. Средство NetCFSvcUtil.exe доступно в Power Toys для .NET Compact Framework. Дополнительные сведения см. в разделе Power Toys for .NET Compact Framework (на английском языке).

    Следующий пример показывает исходный код для службы WCF для упрощенного приложения-калькулятора.

    <ServiceContract()>  _
    Public Interface ICalculatorService
        <OperationContract()>  _
        Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double 
        '<OperationContract()>  _
        Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double
    End Interface
    
    
    Public Class CalculatorService
        Implements ICalculatorService
    
        Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculatorService.Add
            Return n1 + n2
    
        End Function
    
        Public Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculatorService.Subtract
            Return n1 - n2
    
        End Function
    End Class
    
    [ServiceContract()]
    public interface ICalculatorService
    {
        [OperationContract]
        double Add(double n1, double n2);
        [OperationContract]
        double Subtract(double n1, double n2);
    }
    
    public class CalculatorService : ICalculatorService
    {
        public double Add(double n1, double n2) { return n1 + n2; }
        public double Subtract(double n1, double n2) { return n1 - n2; }
    }
    
  5. Создайте веб-узел или виртуальный каталог и ссылку на проект веб-службы. В веб-службе настройте службу, чтобы запросить HTTPS и сертификат клиента.

    Bb629363.alert_note(ru-ru,VS.90).gifПримечание.

    В службе IIS необходимо указать сертификат сервера и сертификат клиента.

  6. Запустите веб-сервер.

    Для просмотра вывода языка описания веб-служб (WSDL) и запуска службы на локальном узле, выберите https://localhost/CalculatorService/Service.svc?wsdl. Используйте то же имя веб-проекта, которое указано для службы WCF.

  7. Проверьте, что имеется доступ к каталогу из обозревателя компьютера и из обозревателя устройства с помощью HTTPS.

    Чтобы можно было получить доступ к службе, убедитесь, что сертификат правильно настроен. Возможно, чтобы обрабатывать запросы к службе WCF, также потребуется настроить веб-сервер.

Чтобы создать клиент .NET Compact Framework

  1. Во время выполнения службы откройте командную строку и перейдите в каталог, в котором расположена служба WCF.

  2. Из командной строки запустите средство ServiceModel Desktop Utility для WCF (SvcUtil.exe), чтобы создать прокси-сервер клиента WCF. В следующем примере показан вызов из командной строки программы SvcUtil, в которой размещена служба на локальном узле:

    svcutil.exe /language:c# https://localhost/CalculatorService/Service.svc
    
  3. Удалите неподдерживаемые атрибуты и элементы из созданного кода прокси-сервера клиента, включая следующие:

    • Все атрибуты System.ServiceModel.

    • Ссылки на класс IClientChannel.

    • Ссылки на имена конфигурации <endpoint>.

    • Методы, в реализации которых используются вызовы методов интерфейс ServiceContract по внутреннему каналу.

    Пример этого шага см. в разделе Пошаговое руководство. Использование транспорта по протоколу HTTP

  4. Создайте клиентский проект.

  5. Добавьте созданный прокси клиента в проект.

  6. В сгенерированном коде прокси измените полное имя ссылки ClientBase<TChannel> на определенный пользователем класс ClientBase.

  7. В сгенерированном коде прокси добавьте реализации метода путем вызова метода Call в определенном пользователем классе ClientBase.

    Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculatorService.Add
        Return System.Convert.ToDouble(MyBase.Call("Add", "https://fabrikam.com/CalcService/ICalculatorService/Add", New String() {"n1", "n2"}, New Object() {n1, n2}, GetType(Double)))
    
    End Function
    
    public double Add(double n1, double n2)
    {
        return (double)base.Call("Add", "https://fabrikam.com/CalcService/ICalculatorService/Add", new string[] { "n1", "n2" }, new object[] { n1, n2 }, typeof(double));
    }
    
  8. Добавьте базовый класс для прокси в проект. Этот класс называется ClientBase.

    Измените ссылку базового класса клиента прокси, чтобы указать реализацию класса ClientBase.

    Bb629363.alert_note(ru-ru,VS.90).gifПримечание.

    В этом примере класс CustomBodyWriter в ClientBase поддерживает только примитивные типы. Для поддержки не примитивных типов необходимо расширить метод OnWriteBodyContents. Например, можно вызвать настраиваемый сериализатор, чтобы сериализовать данные сообщения. В этом случае, следует преобразовать атрибуты кода в созданном прокси клиента в атрибуты, которые может использовать сериализатора XML. В этом случае при запуске SvcUtil следует сначала добавить следующий элемент: /serializer:xmlserializer http://конечная точка.

    В следующем коде показан пример класса ClientBase. Объект ClientCredentials используется, чтобы указать сертификат X.509, используемый клиентом, который в данном примере называется testuser.

    Public Class ClientBase(Of TChannel As Class)
    
        Private requestChannel As IRequestChannel
        Private messageVersion As MessageVersion
    
    
        Public Sub New(ByVal binding As System.ServiceModel.Channels.Binding, ByVal remoteAddress As System.ServiceModel.EndpointAddress)
            'this.remoteAddress = remoteAddress;
            Me.messageVersion = binding.MessageVersion
    
            Dim parameters = New System.ServiceModel.Channels.BindingParameterCollection()
    
            ' Specifies the X.509 certificate used by the client.
            Dim cc As New ClientCredentials()
            cc.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "testuser")
            parameters.Add(cc)
    
            Dim channelFactory As IChannelFactory(Of IRequestChannel)
            channelFactory = binding.BuildChannelFactory(Of IRequestChannel)(parameters)
            channelFactory.Open()
            Me.requestChannel = channelFactory.CreateChannel(remoteAddress)
    
        End Sub
    
    
        Public Function [Call](ByVal op As String, ByVal action As String, ByVal varnames() As String, ByVal varvals() As Object, ByVal returntype As Type) As Object
            requestChannel.Open(TimeSpan.MaxValue)
    
            'Message msg =
            'Message.CreateMessage(MessageVersion.<FromBinding>,
            '      action,
            '      new CustomBodyWriter(op, varnames, varvals,
            '"<ns passed in from Proxy>"));
            Dim msg As Message = Message.CreateMessage(Me.messageVersion, action, New CustomBodyWriter(op, varnames, varvals, "<ns passed in from Proxy>"))
    
            Dim reply As Message = requestChannel.Request(msg, TimeSpan.MaxValue)
            Dim reader As XmlDictionaryReader = reply.GetReaderAtBodyContents()
            reader.ReadToFollowing(op + "Result")
            Return reader.ReadElementContentAs(returntype, Nothing)
    
        End Function
    End Class
    
    
    Friend Class CustomBodyWriter
        Inherits BodyWriter
        Private op As String
        Private varnames() As String
        Private varvals() As Object
        Private ns As String
    
    
        Public Sub New(ByVal op As String, ByVal varnames() As String, ByVal varvals() As Object, ByVal ns As String)
            MyBase.New(True)
            Me.op = op
            Me.varnames = varnames
            Me.varvals = varvals
            Me.ns = ns
    
        End Sub
    
    
        Protected Overrides Sub OnWriteBodyContents(ByVal writer As XmlDictionaryWriter)
            writer.WriteStartElement(op, ns)
            Dim i As Integer
            For i = 0 To varnames.Length
                writer.WriteElementString(varnames(i), varvals(i).ToString())
            Next i
            writer.WriteEndElement()
    
        End Sub
    End Class
    
    public class ClientBase<TChannel>
        where TChannel : class
    {
        private IRequestChannel requestChannel;
        private MessageVersion messageVersion;
    
        public ClientBase(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress)
        {
            //this.remoteAddress = remoteAddress;
            this.messageVersion = binding.MessageVersion;
    
            BindingParameterCollection parameters = new System.ServiceModel.Channels.BindingParameterCollection();
    
            // Specifies the X.509 certificate used by the client.
            ClientCredentials cc = new ClientCredentials();
            cc.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "testuser");
            parameters.Add(cc);
    
            IChannelFactory<IRequestChannel> channelFactory = binding.BuildChannelFactory<IRequestChannel>(
                parameters);
            channelFactory.Open();
            this.requestChannel = channelFactory.CreateChannel(remoteAddress);
        }
    
        public object Call(string op, string action, string[] varnames, object[] varvals, Type returntype)
        {
            requestChannel.Open(TimeSpan.MaxValue);
    
            //Message msg =
            //Message.CreateMessage(MessageVersion.<FromBinding>,
            //      action,
            //      new CustomBodyWriter(op, varnames, varvals,
            //"<ns passed in from Proxy>"));
    
            Message msg =                   
            Message.CreateMessage(this.messageVersion, action,
                  new CustomBodyWriter(op, varnames, varvals,               
            "<ns passed in from Proxy>"));
    
            Message reply = requestChannel.Request(msg, TimeSpan.MaxValue);
            XmlDictionaryReader reader = reply.GetReaderAtBodyContents();
            reader.ReadToFollowing(op + "Result");
            return reader.ReadElementContentAs(returntype, null);
        }
    
    }
    
    internal class CustomBodyWriter : BodyWriter
    {
        private string op;
        private string[] varnames;
        private object[] varvals;
        private string ns;
    
        public CustomBodyWriter(string op, string[] varnames, object[] varvals, string ns)
            : base(true)
        {
            this.op = op;
            this.varnames = varnames;
            this.varvals = varvals;
            this.ns = ns;
        }
    
        protected override void OnWriteBodyContents(XmlDictionaryWriter writer)
        {
            writer.WriteStartElement(op, ns);
            for (int i = 0; i < varnames.Length; i++)
                writer.WriteElementString(varnames[i], varvals[i].ToString());
            writer.WriteEndElement();
        }
    }
    
  9. Добавьте следующие ссылки на ClientBase.cs:

  10. Добавьте класс, чтобы создать экземпляр и использовать прокси клиента.

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

    Class Program
    
        ''' <summary>
        ''' The main entry point for the application.
        ''' </summary>
        <MTAThread()> _
        Shared Sub Main()
    
            Dim serverAddress As String = CalculatorServiceClient.ServiceEndPoint.Uri.AbsoluteUri
    
            Dim binding As New BasicHttpBinding()
    
            ' Specifies transport security over HTTPS and the use of a
            ' client certificate for authentication.
            binding.Security.Mode = BasicHttpSecurityMode.Transport
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate
    
            Dim proxy = New CalculatorServiceClient(binding, New EndpointAddress(serverAddress))
    
            MessageBox.Show("Add 3 + 6...")
            MessageBox.Show(proxy.Add(3, 6).ToString())
            MessageBox.Show("Subtract 8 - 3...")
            MessageBox.Show(proxy.Subtract(8, 3).ToString())
    
        End Sub
    End Class
    
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [MTAThread]
    
        static void Main()
        {
            string serverAddress = CalculatorServiceClient.ServiceEndPoint.Uri.AbsoluteUri;
    
            BasicHttpBinding binding = new BasicHttpBinding();
    
            // Specifies transport security over HTTPS and the use of a
            // client certificate for authentication.
            binding.Security.Mode = BasicHttpSecurityMode.Transport;
            binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
    
            ICalculatorService proxy = new CalculatorServiceClient(binding, new EndpointAddress(serverAddress));
    
            MessageBox.Show("Add 3 + 6...");
            MessageBox.Show((proxy.Add(3, 6)).ToString());
            MessageBox.Show("Subtract 8 - 3...");        
            MessageBox.Show((proxy.Subtract(8, 3)).ToString());
    
        }
    }
    
  11. Убедитесь, что сертификат клиента размещен в хранилище сертификатов текущего пользователя на устройстве.

  12. Постройте клиентское приложение и разверните его на устройстве.

  13. Когда выполняется служба WCF и устройство подключено к сети, запустите клиентское приложение на устройстве.

Компиляция кода

Для исходного кода службы WCF требуются ссылки на следующие пространства имен:

Для исходного кода класса ClientBase требуются ссылки на следующие пространства имен:

Для исходного кода класса, содержащего метод Main, в клиентском приложении требуются ссылки на следующие пространства имен:

Безопасность

В этом примере реализован режим безопасности транспорта на основе взаимной проверки подлинности. В нем не реализован безопасный обмен сообщениями.

См. также

Другие ресурсы

Разработка Windows Communication Foundation и .NET Compact Framework