Gewusst wie: Verwenden der Übertragungssicherheit

Aktualisiert: November 2007

.NET Compact Framework Version 3.5 unterstützt die Verwendung der HTTPS-Übertragung zur Verbindung mit einem Windows Communication Foundation (WCF)-Dienst auf dem Desktop. Es bietet Unterstützung für die Serverauthentifizierung und die Clientauthentifizierung.

Dieses Thema enthält ein Beispiel für die Dienstkonfiguration und veranschaulicht, wie Sie den Clientcode für die gegenseitige Authentifizierung ändern.

Hinweis:

Lediglich für die Serverauthentifizierung ist kein Clientzertifikat erforderlich. Obwohl die Meldungssicherheit in .NET Compact Framework 3.5 unterstützt wird, wird sie in diesem Beispiel nicht verwendet.

So erstellen Sie den WCF-Dienst für den Desktop

  1. Erstellen und installieren Sie ein Serverzertifikat und ein Clientzertifikat.

    Diese Schritte sind für das verwendete Certificate Generation-Tool (z. B. Makecert.exe) spezifisch und gehen über das hier behandelte Thema hinaus. Folgende Aufgaben müssen ausgeführt werden:

    • Erstellen und benennen Sie ein selbstsigniertes Zertifikat (verwenden Sie beispielsweise Ihren Firmennamen, z. B. Firma).

    • Erstellen Sie ein von Firma signiertes Serverzertifikat. Der Serverzertifikatsname muss dem URL-Hostnamen entsprechen, mit dem auf den Dienst zugegriffen wird.

    • Erstellen Sie ein von Firma signiertes Clientzertifikat.

    Hinweis:

    Es wird empfohlen, das Serverzertifikat auf dem lokalen Computer und nicht beim aktuellen Benutzer zu installieren. Wenn der Dienst in den Internetinformationsdiensten (IIS) gehostet und beim aktuellen Benutzer installiert wird, ist er nicht funktionsfähig.

  2. Erstellen Sie ein neues Webdienstprojekt.

  3. Ersetzen Sie die Datei Web.config durch das in diesem Schritt veranschaulichte Beispiel. Ändern Sie folgende Elemente und Attribute in der Datei:

    • Ändern Sie das service name-Attribut, sodass es dem neuen verwendeten Dienst entspricht.

    • Ändern Sie das behaviorConfiguration-Attribut, sodass es auf den Namen des neuen Verhaltens verweist.

    • Ändern Sie das endpoint contract-Attribut, sodass es auf die Dienstschnittstelle verweist.

    Hinweis:

    Stellen Sie sicher, dass der binding-Attributwert für das <endpoint>-Element "basicHttpBinding" ist. .NET Compact Framework unterstützt die Textcodierung, aber keine binäre Codierung.

    <?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. Entfernen Sie im Quellcode des WCF-Dienstes alle im ServiceContract-Attribut und im OperationContract-Attribut angegebenen Parameter aus dem Code.

    Hinweis:

    In diesem Beispiel wurde keine Unterstützung für Parameter implementiert , die in Verträgen wie ServiceContract und OperationContract angegeben wurden. Wenn Sie Parameterunterstützung für diese Verträge benötigen, können Sie mit dem WCF .NET Compact Framework ServiceModel-Dienstprogramm (NetCFSvcUtil.exe) Clientcode generieren. Durch dieses Tool wird Unterstützung für viele dieser Parameter in Anwendungen integriert, die auf .NET Compact Framework basieren. NetCFSvcUtil.exe ist in den Power Toys für .NET Compact Framework verfügbar. Weitere Informationen finden Sie unter Power Toys for .NET Compact Framework.

    Im folgenden Beispiel wird der Quellcode des WCF-Dienstes für eine vereinfachte Rechneranwendung veranschaulicht.

    <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. Erstellen Sie eine Website oder ein virtuelles Verzeichnis, und verweisen Sie auf das Webdienstprojekt. Konfigurieren Sie den Dienst auf dem Webserver in der Weise, dass HTTPS und ein Clientzertifikat benötigt werden.

    Hinweis:

    In IIS müssen Sie das Serverzertifikat und das Clientzertifikat angeben.

  6. Starten Sie den Webserver.

    Wenn Sie die Web Services Description Language (WSDL)-Ausgabe anzeigen und den Dienst unter localhost ausführen möchten, suchen Sie https://localhost/CalculatorService/Service.svc?wsdl. Verwenden Sie den gleichen Webprojektnamen, den Sie für den WCF-Dienst angegeben haben.

  7. Überprüfen Sie, ob Sie von einem Desktopbrowser und einem Gerätebrowser aus über HTTPS auf das Verzeichnis zugreifen können.

    Sie müssen sicherstellen, dass Zertifikate ordnungsgemäß konfiguriert werden, bevor Sie auf den Dienst zugreifen können. Der Webserver muss möglicherweise auch für die Verarbeitung von Anforderungen für einen WCF-Dienst konfiguriert werden.

So erstellen Sie den .NET Compact Framework-Client

  1. Öffnen Sie, während der Dienst ausgeführt wird, eine Befehlszeile, und navigieren Sie zu dem Verzeichnis, in dem sich der WCF-Dienst befindet.

  2. Führen Sie von der Befehlszeile das WCF ServiceModel Desktop-Dienstprogramm (SvcUtil.exe) aus, um einen WCF-Clientproxy zu generieren. Im folgenden Beispiel wird der Befehlszeilenaufruf für SvcUtil veranschaulicht, in dem sich der Dienst auf localhost befindet:

    svcutil.exe /language:c# https://localhost/CalculatorService/Service.svc
    
  3. Entfernen Sie nicht unterstützte Attribute und Elemente aus dem generierten Clientproxycode, darunter folgende:

    • Alle System.ServiceModel-Attribute

    • Verweise auf die IClientChannel-Klasse

    • Verweise auf <endpoint>-Konfigurationsnamen

    • Methodenimplementierungen, durch die Methoden der ServiceContract-Schnittstelle im internen Kanal aufgerufen werden

    Ein Beispiel für diesen Schritt finden Sie unter Gewusst wie: Verwenden der HTTP-Übertragung.

  4. Erstellen Sie ein Clientprojekt.

  5. Fügen Sie dem Projekt den generierten Clientproxy hinzu.

  6. Ändern Sie im generierten Proxycode den vollqualifizierten Verweis auf ClientBase<TChannel> in die benutzerdefinierte ClientBase-Klasse.

  7. Fügen Sie im generierten Proxycode Methodenimplementierungen hinzu, indem Sie die Call-Methode in der benutzerdefinierten ClientBase-Klasse aufrufen.

    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. Fügen Sie dem Projekt die Basisklasse für den Proxy hinzu. Diese Klasse wird ClientBase genannt.

    Ändern Sie den Basisklassenverweis des Clientproxys, sodass er auf Ihre Implementierung von ClientBase verweist.

    Hinweis:

    In diesem Beispiel unterstützt die CustomBodyWriter-Klasse in ClientBase nur primitive Typen. Für die Unterstützung nicht primitiver Typen müssen Sie die OnWriteBodyContents-Methode erweitern. Beispielsweise können Sie einen benutzerdefinierten Serialisierer aufrufen, um Meldungsdaten zu serialisieren. In diesem Fall würden Sie Codeattribute im generierten Clientproxy in Attribute übersetzen, die vom XML-Serialisierer verwendet werden können. In diesem Szenario müssen Sie zuerst den folgenden Schalter hinzufügen, wenn Sie SvcUtil ausführen: /serializer:xmlserializer http://endpoint.

    Im folgenden Code wird ein Beispiel der ClientBase-Klasse veranschaulicht. Ein ClientCredentials-Objekt wird verwendet, um das vom Client verwendete X.509-Zertifikat anzugeben, das in diesem Beispiel den Namen testuser hat.

    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. Fügen Sie ClientBase.cs die folgenden Verweise hinzu:

  10. Fügen Sie eine Klasse zum Instanziieren und Verwenden des Clientproxys hinzu.

    Im folgenden Beispiel wird das Bindungsobjekt verwendet, um Übertragungssicherheit über HTTPS und die Verwendung eines Clientzertifikats für die Authentifizierung anzugeben. Außerdem wird der Code zum Aufrufen des Clientproxys veranschaulicht.

    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. Stellen Sie sicher, dass das Clientzertifikat auf dem Gerät im Zertifikatsspeicher des aktuellen Benutzers abgelegt wurde.

  12. Erstellen Sie die Clientanwendung, und stellen Sie sie auf dem Gerät bereit.

  13. Wenn der WCF-Dienst ausgeführt wird und Ihr Gerät mit dem Netzwerk verbunden ist, starten Sie die Clientanwendung auf dem Gerät.

Kompilieren des Codes

Der Quellcode für den WCF-Dienst erfordert Verweise auf die folgenden Namespaces:

Der Quellcode für die ClientBase-Klasse erfordert Verweise auf die folgenden Namespaces:

Der Quellcode für die Klasse, in der die Main-Methode in der Clientanwendung enthalten ist, erfordert Verweise auf die folgenden Namespaces:

Sicherheit

In diesem Beispiel wird Übertragungssicherheit auf Grundlage gegenseitiger Authentifizierung implementiert. Es wird keine Meldungssicherheit implementiert.

Siehe auch

Weitere Ressourcen

Windows Communication Foundation (WCF)-Entwicklung und .NET Compact Framework