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
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.
Créez un projet de service Web.
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>
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; } }
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.
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.
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
Lorsque le service est en cours d'exécution, ouvrez une ligne de commande et accédez au répertoire contenant le service WCF.
À 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
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.
Créez un projet client.
Ajoutez le proxy client généré au projet.
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.
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)); }
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(); } }
Ajoutez les références suivantes à ClientBase.cs :
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()); } }
Assurez-vous que le certificat client a été placé dans le magasin de certificats de l'utilisateur actuel sur le périphérique.
Générez l'application cliente et déployez-la sur votre périphérique.
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