Partager via


Comment : utiliser la sécurité du transport

Mise à jour : novembre 2007

Le .NET Compact Framework version 3.5 prend en charge l'utilisation du transport HTTPS pour se connecter à un service Windows Communication Foundation (WCF) sur l'ordinateur. Il inclut la prise en charge de l'authentification serveur et client.

Cette rubrique fournit un exemple de la configuration du service et affiche comment modifier le code client pour l'authentification mutuelle.

Remarque :

Le client n'est pas tenu de fournir un certificat pour l'authentification serveur. La sécurité de message est également prise en charge dans le .NET Compact Framework 3.5, mais elle n'est pas utilisée dans cet exemple.

Pour créer le service WCF pour l'ordinateur

  1. Créez et installez un certificat de serveur et un certificat client.

    Ces étapes sont spécifiques à l'outil de génération de certificat que vous utilisez (par exemple, Makecert.exe) et ne sont pas traitées dans cette rubrique. Les tâches suivantes devront être effectuées :

    • Créez un certificat auto-signé et donnez lui un nom (celui de votre société, par exemple société).

    • Créez un certificat de serveur signé par société. Le nom du certificat de serveur doit correspondre au nom d'hôte d'URL utilisé pour accéder au service.

    • Créez un certificat client signé par société.

    Remarque :

    Nous vous recommandons d'installer le certificat de serveur sur l'ordinateur local et pas uniquement pour l'utilisateur actuel. Sinon, si le service est hébergé dans les services IIS et que vous l'installez pour l'utilisateur actuel, cela ne marchera pas.

  2. Créez un projet de service Web.

  3. Remplacez le fichier Web.config par l'exemple utilisé dans cette étape. Modifiez les éléments et attributs suivants dans le fichier :

    • Modifiez l'attribut service name en fonction du nouveau service que vous utilisez.

    • Modifiez l'attribut behaviorConfiguration pour faire référence au nouveau nom de comportement.

    • Modifiez l'attribut endpoint contract pour faire référence à l'interface de service.

    Remarque :

    Assurez-vous que la valeur d'attribut binding pour l'élément <endpoint> est "basicHttpBinding." Le .NET Compact Framework prend en charge le codage de texte, mais pas le codage binaire.

    <?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. Dans le code source du service WCF, supprimez tous les paramètres spécifiés dans les attributs ServiceContract et OperationContract.

    Remarque :

    Cet exemple n'implémente pas la prise en charge des paramètres spécifiés dans les contrats, tels que ServiceContract et OperationContract. Si vous voulez utiliser la prise en charge des paramètres pour ces contrats, vous pouvez faire appel à l'outil ServiceModel Utility (NetCFSvcUtil.exe) WCF du .NET Compact Framework pour générer le code client. Cet outil intègre la prise en charge de ces paramètres dans des applications basées sur le .NET Compact Framework. NetCFSvcUtil.exe est disponible dans les Power Toys pour le .NET Compact Framework. Pour plus d'informations, consultez Power Toys pour le .NET Compact Framework (en anglais).

    L'exemple suivant affiche le code source du service WCF pour une application de calculatrice simplifiée.

    <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. Créez un site Web ou un répertoire virtuel et référencez votre projet de service Web. Sur le serveur Web, configurez le service pour utiliser HTTPS et un certificat client.

    Remarque :

    Dans IIS, vous devez spécifier le certificat de serveur et le certificat client.

  6. Démarrez le serveur Web.

    Si vous souhaitez consulter la sortie Web Services Description Language (WSDL) et exécuter le service sur le localhost, accédez à https://localhost/CalculatorService/Service.svc? wsdl. Utilisez le même nom de projet Web que celui du service WCF.

  7. Vérifiez que vous pouvez accéder au répertoire à partir d'un navigateur d'ordinateur de bureau et d'un navigateur de périphérique à l'aide de HTTPS.

    Vous devez vous assurer que les certificats sont correctement configurés avant d'accéder au service. Vous devrez éventuellement configurer le serveur Web si vous voulez gérer des demandes pour un service WCF.

Pour créer le client .NET Compact Framework

  1. Lorsque le service est en cours d'exécution, ouvrez une ligne de commande et accédez au répertoire contenant le service WCF.

  2. À partir de la ligne de commande, exécutez l'outil WCF ServiceModel Desktop Utility (Svcutil.exe) pour générer un proxy client WCF. L'exemple suivant illustre l'appel de ligne de commande de SvcUtil dans lequel le service est hébergé sur le localhost :

    svcutil.exe /language:c# https://localhost/CalculatorService/Service.svc
    
  3. Supprimez les attributs et éléments non pris en charge du code proxy client généré, y compris les suivants :

    • tous les attributs System.ServiceModel ;

    • les références à la classe IClientChannel ;

    • les références aux noms de configuration du <endpoint> ;

    • les implémentations de méthode qui appellent des méthodes de l'interface ServiceContract sur le canal interne.

    Pour obtenir un exemple de cette étape, consultez Comment : utiliser le transport HTTP.

  4. Créez un projet client.

  5. Ajoutez le proxy client généré au projet.

  6. Dans le code proxy généré, remplacez la référence qualifiée complète à ClientBase<TChannel> par la classe ClientBase définie par l'utilisateur.

  7. Dans le code proxy généré, ajoutez des implémentations de méthode en appelant la méthode Call dans la classe ClientBase définie par l'utilisateur.

    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. Ajoutez au projet la classe de base pour le proxy. Cette classe s'appelle ClientBase.

    Modifiez la référence de classe de base de votre proxy client en la faisant pointer vers votre implémentation de ClientBase.

    Remarque :

    Dans cet exemple, la classe CustomBodyWriter dans ClientBase ne prend en charge que les types primitifs. Pour prendre en charge des types non primitifs, vous devez étendre la méthode OnWriteBodyContents. Par exemple, vous pouvez appeler un sérialiseur personnalisé pour sérialiser des données de message. Dans ce cas, vous convertissez des attributs de code dans le proxy client généré en attributs que le sérialiseur XML pourrait consommer. Dans ce scénario, vous devez commencer par ajouter le commutateur suivant lorsque vous exécutez SvcUtil : /serializer:xmlserializer http://point de terminaison.

    Le code suivant illustre un exemple de la classe ClientBase. Un objet ClientCredentials est utilisé pour spécifier le certificat X.509 utilisé par le client, nommé testuser dans cet exemple.

    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. Ajoutez les références suivantes à ClientBase.cs :

  10. Ajoutez une classe à instancier et utilisez le proxy client.

    L'exemple suivant utilise l'objet de liaison pour spécifier la sécurité du transport sur HTTPS et l'utilisation d'un certificat client pour l'authentification. Il affiche également le code qui appelle le proxy client.

    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. Assurez-vous que le certificat client a été placé dans le magasin de certificats de l'utilisateur actuel sur le périphérique.

  12. Générez l'application cliente et déployez-la sur votre périphérique.

  13. Lorsque le service WCF est en cours d'exécution et que votre périphérique est connecté au réseau, démarrez l'application cliente sur le périphérique.

Compilation du code

Le code source pour le service WCF requiert des références aux espaces de noms suivants :

Le code source pour la classe ClientBase requiert des références aux espaces de noms suivants :

Le code source pour la classe qui contient la méthode Main dans l'application cliente requiert des références aux espaces de noms suivants :

Sécurité

Cet exemple implémente la sécurité du transport basée sur l'authentification mutuelle. Il n'implémente pas la sécurité des messages.

Voir aussi

Autres ressources

Développement Windows Communication Foundation (WCF) et le .NET Compact Framework