Cómo: Usar la seguridad del transporte

Actualización: noviembre 2007

.NET Compact Framework versión 3.5 admite el uso del transporte HTTPS para conectarse a un servicio de Windows Communication Foundation (WCF) en el escritorio. Incluye compatibilidad con la autenticación de servidor y la autenticación de cliente.

En este tema se proporciona un ejemplo de la configuración de servicios y se muestra cómo modificar el código de cliente para la autenticación mutua.

Nota:

Si se va a trabajar únicamente con la autenticación de servidor, no será necesario utilizar certificados de cliente. .NET Compact Framework 3.5 también admite la seguridad de mensajes, aunque no se utiliza en este ejemplo.

Para crear el servicio WCF para el escritorio

  1. Cree e instale un certificado de servidor y un certificado de cliente.

    Estos pasos cambiarán en función de la herramienta de generación de certificados que se utilice (por ejemplo, Makecert.exe) y se escapan del ámbito de este tema. Será necesario que realice las siguientes tareas:

    • Cree un certificado autofirmado y asígnele un nombre (por ejemplo, el nombre de la compañía: compañía).

    • Cree un certificado de servidor firmado por compañía. El nombre del certificado de servidor debe coincidir con el nombre del host de la dirección URL que se utiliza para obtener acceso al servicio

    • Cree un certificado de cliente firmado por compañía.

    Nota:

    Le recomendamos que instale el certificado de servidor en el equipo local en lugar de instalarlo en el usuario actual. De lo contrario, si el servicio se hospeda en Internet Information Services (IIS) y lo instala en el usuario actual, no funcionará.

  2. Cree un nuevo proyecto de servicio web.

  3. Reemplace el archivo Web.config por el ejemplo que se muestra en este paso. Modifique los siguientes elementos y atributos del archivo:

    • Cambie el atributo service name por el nuevo servicio que esté utilizando.

    • Cambie el atributo behaviorConfiguration para que haga referencia al nuevo nombre de comportamiento.

    • Cambie el atributo endpoint contract para que haga referencia a la interfaz de servicio.

    Nota:

    Asegúrese de que el valor del atributo binding para el elemento <endpoint> sea "basicHttpBinding". .NET Compact Framework admite la codificación de texto, pero no la codificación binaria.

    <?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. En el código fuente del servicio WCF, quite todos los parámetros especificados en los atributos ServiceContract y OperationContract del código.

    Nota:

    En este ejemplo no se implementa compatibilidad alguna con los parámetros especificados en contratos como ServiceContract y OperationContract. Si necesita agregar compatibilidad con los parámetros para estos contratos, puede utilizar la utilidad ServiceModel (NetCFSvcUtil.exe) de .NET Compact Framework de WCF para generar el código de cliente. Esta herramienta agrega compatibilidad para muchos de estos parámetros en las aplicaciones basadas en .NET Compact Framework. NetCFSvcUtil.exe se incluye en las herramientas avanzadas de .NET Compact Framework. Para obtener más información, vea Power Toys for .NET Compact Framework.

    En el ejemplo siguiente se muestra el código fuente del servicio WCF para una aplicación de calculadora simplificada.

    <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. Cree un sitio web o un directorio virtual y establezca referencias a su proyecto de servicio web. En el servidor web, configure el servicio para que solicite HTTPS y un certificado de cliente.

    Nota:

    En IIS, debe especificar el certificado de servidor y el certificado de cliente.

  6. Inicie el servidor web.

    Si desea ver los resultados de Lenguaje de descripción de servicios web (WSDL) y ejecutar el servicio en el host local, vaya a https://localhost/CalculatorService/Service.svc?wsdl. Utilice el mismo nombre de proyecto web que especificó para el servicio WCF.

  7. Compruebe que puede obtener acceso al directorio desde un explorador de escritorio y un explorador de dispositivo utilizando HTTPS.

    Debe asegurarse de que los certificados estén configurados correctamente para poder obtener acceso al servicio. El servidor web también puede estar configurado para controlar las solicitudes de un servicio WCF.

Para crear el cliente de .NET Compact Framework

  1. Mientras el servicio esté en ejecución, abra una línea de comandos y desplácese al directorio en el que se encuentre el servicio WCF.

  2. Desde la línea de comandos, ejecute la utilidad de escritorio WCF ServiceModel (SvcUtil.exe) para generar un proxy cliente de WCF. En el ejemplo siguiente se muestra la invocación de línea de comandos para SvcUtil en la que el servicio se hospeda en el host local:

    svcutil.exe /language:c# https://localhost/CalculatorService/Service.svc
    
  3. Quite los atributos y elementos no compatibles del código proxy cliente generado, incluidos los elementos siguientes:

    • Todos los atributos de System.ServiceModel.

    • Las referencias a la clase IClientChannel.

    • Las referencias a los nombres de configuración <endpoint>.

    • Las implementaciones de métodos que llamen a los métodos de la interfaz ServiceContract en el canal interno.

    Para obtener un ejemplo de este paso, consulte Cómo: Usar el transporte HTTP.

  4. Cree un proyecto de cliente.

  5. Agregue el proxy cliente generado al proyecto.

  6. En el código proxy generado, cambie la referencia completa a ClientBase<TChannel> por la clase ClientBase definida por el usuario.

  7. En el código proxy generado, agregue implementaciones de método invocando al método Call de la clase ClientBase definida por el usuario.

    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. Agregue la clase base del proxy al proyecto. Esta clase se denomina ClientBase.

    Cambie la referencia de clase base del proxy cliente para que apunte a su implementación de ClientBase.

    Nota:

    En este ejemplo, la clase CustomBodyWriter de ClientBase sólo admite tipos primitivos. Para que se admitan tipos no primitivos, tiene que extender el método OnWriteBodyContents. Por ejemplo, puede llamar a un serializador personalizado para serializar los datos del mensaje. En este caso, los atributos del código proxy cliente generado se traducirían en atributos que el serializador XML podría utilizar. En este escenario, primero debe agregar el siguiente modificador al ejecutar SvcUtil: /serializer:xmlserializer http://endpoint.

    En el código siguiente se muestra un ejemplo de la clase ClientBase. Se utiliza un objeto ClientCredentials para especificar el certificado X.509 utilizado por el cliente, que se denomina testuser en este ejemplo.

    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. Agregue las referencias siguientes a ClientBase.cs:

  10. Agregue una clase para crear instancias y utilizar el proxy cliente.

    En el ejemplo siguiente se utiliza el objeto de enlace para especificar la seguridad de transporte sobre HTTPS y el uso de un certificado de cliente para la autenticación. También se muestra el código que invoca al proxy cliente.

    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. Asegúrese de que el certificado de cliente se haya colocado en el almacén de certificados del usuario actual del dispositivo.

  12. Genere la aplicación cliente e impleméntela en su dispositivo.

  13. Cuando el servicio WCF esté en ejecución y su dispositivo esté conectado a la red, inicie la aplicación cliente en el dispositivo.

Compilar el código

El código fuente del servicio WCF necesita establecer referencias a los espacios de nombres siguientes:

El código fuente de la clase ClientBase necesita establecer referencias a los espacios de nombres siguientes:

El código fuente de la clase que contiene el método Main de la aplicación cliente necesita establecer referencias a los espacios de nombres siguientes:

Seguridad

En este ejemplo se implementa una seguridad de transporte basada en la autenticación mutua. No se implementa la seguridad de mensajes.

Vea también

Otros recursos

Desarrollo de Windows Communication Foundation (WCF) y .NET Compact Framework