Freigeben über


Benutzerdefiniertes Token

In diesem Beispiel wird veranschaulicht, wie Sie einer Windows Communication Foundation (WCF)-Anwendung eine benutzerdefinierte Tokenimplementierung hinzufügen. Im Beispiel wird ein CreditCardToken verwendet, um Informationen über Kreditkarten von Kunden sicher an den Dienst zu übermitteln. Das Token wird im WS-Security Nachrichtenkopf übergeben und zusammen mit dem symmetrischen Sicherheitsbindungselement, dem Nachrichtentext und anderen Nachrichtenkopfzeilen signiert und verschlüsselt. Dies ist in Fällen hilfreich, in denen die integrierten Token nicht ausreichen. In diesem Beispiel wird veranschaulicht, wie Ein benutzerdefiniertes Sicherheitstoken für einen Dienst bereitgestellt wird, anstatt eines der integrierten Token zu verwenden. Der Dienst implementiert einen Vertrag, der ein Kommunikationsmuster für die Anforderungsantwort definiert.

Hinweis

Die Einrichtungsverfahren und Build-Anweisungen für dieses Beispiel befinden sich am Ende dieses Themas.

In diesem Beispiel wird Folgendes veranschaulicht:

  • So kann ein Client ein benutzerdefiniertes Sicherheitstoken an einen Dienst übergeben.

  • Wie der Dienst ein benutzerdefiniertes Sicherheitstoken nutzen und überprüfen kann.

  • Wie der WCF-Dienstcode die Informationen zu empfangenen Sicherheitstoken einschließlich des benutzerdefinierten Sicherheitstokens abrufen kann.

  • Wie das X.509-Zertifikat des Servers verwendet wird, um den symmetrischen Schlüssel zu schützen, der für die Nachrichtenverschlüsselung und -signatur verwendet wird.

Clientauthentifizierung mit einem benutzerdefinierten Sicherheitstoken

Der Dienst macht einen einzelnen Endpunkt verfügbar, der programmgesteuert mithilfe BindingHelper und EchoServiceHost Klassen erstellt wird. Der Endpunkt besteht aus einer Adresse, einer Bindung und einem Vertrag. Die Bindung ist mit einer benutzerdefinierten Bindung unter Verwendung von SymmetricSecurityBindingElement und HttpTransportBindingElement konfiguriert. In diesem Beispiel wird festgelegt, dass das SymmetricSecurityBindingElement X.509-Zertifikat eines Diensts verwendet wird, um den symmetrischen Schlüssel während der Übertragung zu schützen und einen benutzerdefinierten CreditCardToken im Nachrichtenkopf des WS-Security als signiertes und verschlüsseltes Sicherheitstoken zu übermitteln. Das Verhalten spezifiziert die Zugangsdaten des Dienstes, die für die Authentifizierung des Clients verwendet werden sollen, sowie Informationen über das X.509-Zertifikat des Dienstes.

public static class BindingHelper
{
    public static Binding CreateCreditCardBinding()
    {
        var httpTransport = new HttpTransportBindingElement();

        // The message security binding element will be configured to require a credit card.
        // The token that is encrypted with the service's certificate.
        var messageSecurity = new SymmetricSecurityBindingElement();
        messageSecurity.EndpointSupportingTokenParameters.SignedEncrypted.Add(new CreditCardTokenParameters());
        X509SecurityTokenParameters x509ProtectionParameters = new X509SecurityTokenParameters();
        x509ProtectionParameters.InclusionMode = SecurityTokenInclusionMode.Never;
        messageSecurity.ProtectionTokenParameters = x509ProtectionParameters;
        return new CustomBinding(messageSecurity, httpTransport);
    }
}

Um ein Kreditkartentoken in der Nachricht zu nutzen, verwendet das Beispiel benutzerdefinierte Dienstanmeldeinformationen, um diese Funktionalität bereitzustellen. Die Dienstanmeldeinformationsklasse befindet sich in der CreditCardServiceCredentials Klasse und wird den Verhaltensauflistungen des Diensthosts in der EchoServiceHost.InitializeRuntime Methode hinzugefügt.

class EchoServiceHost : ServiceHost
{
    string creditCardFile;

    public EchoServiceHost(parameters Uri[] addresses)
        : base(typeof(EchoService), addresses)
    {
        creditCardFile = ConfigurationManager.AppSettings["creditCardFile"];
        if (string.IsNullOrEmpty(creditCardFile))
        {
            throw new ConfigurationErrorsException("creditCardFile not specified in service config");
        }

        creditCardFile = String.Format("{0}\\{1}", System.Web.Hosting.HostingEnvironment.ApplicationPhysicalPath, creditCardFile);
    }

    override protected void InitializeRuntime()
    {
        // Create a credit card service credentials and add it to the behaviors.
        CreditCardServiceCredentials serviceCredentials = new CreditCardServiceCredentials(this.creditCardFile);
        serviceCredentials.ServiceCertificate.SetCertificate("CN=localhost", StoreLocation.LocalMachine, StoreName.My);
        this.Description.Behaviors.Remove((typeof(ServiceCredentials)));
        this.Description.Behaviors.Add(serviceCredentials);

        // Register a credit card binding for the endpoint.
        Binding creditCardBinding = BindingHelper.CreateCreditCardBinding();
        this.AddServiceEndpoint(typeof(IEchoService), creditCardBinding, string.Empty);

        base.InitializeRuntime();
    }
}

Der Clientendpunkt wird auf ähnliche Weise wie der Dienstendpunkt konfiguriert. Der Client verwendet dieselbe BindingHelper Klasse, um eine Bindung zu erstellen. Der Rest des Setups befindet sich in der Client Klasse. Der Client legt außerdem Informationen, die im CreditCardToken enthalten sein sollen, sowie Informationen zum X.509-Zertifikat des Diensts im Setupcode fest, indem eine CreditCardClientCredentials-Instanz mit den entsprechenden Daten zur Auflistung der Clientendpunktverhaltensweisen hinzugefügt wird. Im Beispiel wird ein X.509-Zertifikat verwendet, bei dem der Betreffname auf CN=localhost als Dienstzertifikat festgelegt ist.

Binding creditCardBinding = BindingHelper.CreateCreditCardBinding();
var serviceAddress = new EndpointAddress("http://localhost/servicemodelsamples/service.svc");

// Create a client with given client endpoint configuration.
channelFactory = new ChannelFactory<IEchoService>(creditCardBinding, serviceAddress);

// Configure the credit card credentials on the channel factory.
var credentials =
      new CreditCardClientCredentials(
      new CreditCardInfo(creditCardNumber, issuer, expirationTime));
// Configure the service certificate on the credentials.
credentials.ServiceCertificate.SetDefaultCertificate(
      "CN=localhost", StoreLocation.LocalMachine, StoreName.My);

// Replace ClientCredentials with CreditCardClientCredentials.
channelFactory.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
channelFactory.Endpoint.Behaviors.Add(credentials);

client = channelFactory.CreateChannel();

Console.WriteLine($"Echo service returned: {client.Echo()}");

((IChannel)client).Close();
channelFactory.Close();

Implementierung benutzerdefinierter Sicherheitstoken

Um ein benutzerdefiniertes Sicherheitstoken in WCF zu aktivieren, erstellen Sie eine Objektdarstellung des benutzerdefinierten Sicherheitstokens. Das Beispiel weist diese Darstellung in der CreditCardToken Klasse auf. Die Objektdarstellung ist dafür verantwortlich, alle relevanten Sicherheitstokeninformationen zu halten und eine Liste der Sicherheitsschlüssel bereitzustellen, die im Sicherheitstoken enthalten sind. In diesem Fall enthält das Kreditkartensicherheitstoken keinen Sicherheitsschlüssel.

Im nächsten Abschnitt wird beschrieben, was getan werden muss, um zu ermöglichen, dass ein benutzerdefiniertes Token über die Verbindung übertragen und von einem WCF-Endpunkt genutzt wird.

class CreditCardToken : SecurityToken
{
    CreditCardInfo cardInfo;
    DateTime effectiveTime = DateTime.UtcNow;
    string id;
    ReadOnlyCollection<SecurityKey> securityKeys;

    public CreditCardToken(CreditCardInfo cardInfo) : this(cardInfo, Guid.NewGuid().ToString()) { }

    public CreditCardToken(CreditCardInfo cardInfo, string id)
    {
        if (cardInfo == null)
            throw new ArgumentNullException(nameof(cardInfo));

        if (id == null)
            throw new ArgumentNullException(nameof(id));

        this.cardInfo = cardInfo;
        this.id = id;

        // The credit card token is not capable of any cryptography.
        this.securityKeys = new ReadOnlyCollection<SecurityKey>(new List<SecurityKey>());
    }

    public CreditCardInfo CardInfo { get { return this.cardInfo; } }

    public override ReadOnlyCollection<SecurityKey> SecurityKeys { get { return this.securityKeys; } }

    public override DateTime ValidFrom { get { return this.effectiveTime; } }
    public override DateTime ValidTo { get { return this.cardInfo.ExpirationDate; } }
    public override string Id { get { return this.id; } }
}

Abrufen des benutzerdefinierten Kreditkartentokens in und aus der Nachricht

Serialisierer von Sicherheitstokens in WCF sind dafür verantwortlich, eine Objektdarstellung der Sicherheitstokens aus dem XML-Code der Nachricht zu erstellen und eine XML-Form der Sicherheitstokens zu erzeugen. Sie sind auch für andere Funktionen verantwortlich, z. B. das Lesen und Schreiben von Schlüsselbezeichnern, die auf Sicherheitstoken verweisen, aber in diesem Beispiel werden nur Sicherheitstoken-bezogene Funktionen verwendet. Um ein benutzerdefiniertes Token zu aktivieren, müssen Sie Ihren eigenen Serialisierer für Sicherheitstoken implementieren. In diesem Beispiel wird die CreditCardSecurityTokenSerializer Klasse für diesen Zweck verwendet.

Im Dienst liest der benutzerdefinierte Serializer die XML-Form des benutzerdefinierten Tokens und erstellt die darstellung des benutzerdefinierten Tokenobjekts daraus.

Auf dem Client schreibt die CreditCardSecurityTokenSerializer Klasse die Informationen, die in der Darstellung des Sicherheitstokenobjekts enthalten sind, in den XML-Writer.

public class CreditCardSecurityTokenSerializer : WSSecurityTokenSerializer
{
    public CreditCardSecurityTokenSerializer(SecurityTokenVersion version) : base() { }

    protected override bool CanReadTokenCore(XmlReader reader)
    {
        XmlDictionaryReader localReader = XmlDictionaryReader.CreateDictionaryReader(reader);

        if (reader == null)
            throw new ArgumentNullException(nameof(reader));

        if (reader.IsStartElement(Constants.CreditCardTokenName, Constants.CreditCardTokenNamespace))
            return true;

        return base.CanReadTokenCore(reader);
    }

    protected override SecurityToken ReadTokenCore(XmlReader reader, SecurityTokenResolver tokenResolver)
    {
        if (reader == null)
            throw new ArgumentNullException(nameof(reader));

        if (reader.IsStartElement(Constants.CreditCardTokenName, Constants.CreditCardTokenNamespace))
        {
            string id = reader.GetAttribute(Constants.Id, Constants.WsUtilityNamespace);

            reader.ReadStartElement();

            // Read the credit card number.
            string creditCardNumber = reader.ReadElementString(Constants.CreditCardNumberElementName, Constants.CreditCardTokenNamespace);

            // Read the expiration date.
            string expirationTimeString = reader.ReadElementString(Constants.CreditCardExpirationElementName, Constants.CreditCardTokenNamespace);
            DateTime expirationTime = XmlConvert.ToDateTime(expirationTimeString, XmlDateTimeSerializationMode.Utc);

            // Read the issuer of the credit card.
            string creditCardIssuer = reader.ReadElementString(Constants.CreditCardIssuerElementName, Constants.CreditCardTokenNamespace);
            reader.ReadEndElement();

            var cardInfo = new CreditCardInfo(creditCardNumber, creditCardIssuer, expirationTime);

            return new CreditCardToken(cardInfo, id);
        }
        else
        {
            return WSSecurityTokenSerializer.DefaultInstance.ReadToken(reader, tokenResolver);
        }
    }

    protected override bool CanWriteTokenCore(SecurityToken token)
    {
        if (token is CreditCardToken)
            return true;
        return base.CanWriteTokenCore(token);
    }

    protected override void WriteTokenCore(XmlWriter writer, SecurityToken token)
    {
        if (writer == null)
            throw new ArgumentNullException(nameof(writer));
        if (token == null)
            throw new ArgumentNullException(nameof(token));

        CreditCardToken c = token as CreditCardToken;
        if (c != null)
        {
            writer.WriteStartElement(Constants.CreditCardTokenPrefix, Constants.CreditCardTokenName, Constants.CreditCardTokenNamespace);
            writer.WriteAttributeString(Constants.WsUtilityPrefix, Constants.Id, Constants.WsUtilityNamespace, token.Id);
            writer.WriteElementString(Constants.CreditCardNumberElementName, Constants.CreditCardTokenNamespace, c.CardInfo.CardNumber);
            writer.WriteElementString(Constants.CreditCardExpirationElementName, Constants.CreditCardTokenNamespace, XmlConvert.ToString(c.CardInfo.ExpirationDate, XmlDateTimeSerializationMode.Utc));
            writer.WriteElementString(Constants.CreditCardIssuerElementName, Constants.CreditCardTokenNamespace, c.CardInfo.CardIssuer);
            writer.WriteEndElement();
            writer.Flush();
        }
        else
        {
            base.WriteTokenCore(writer, token);
        }
    }
}

Wie Tokenanbieter- und Tokenauthentifikatorklassen erstellt werden

Client- und Dienstanmeldeinformationen sind für die Bereitstellung der Instanz des Sicherheitstoken-Managers zuständig. Die Instanz des Sicherheitstoken-Managers wird verwendet, um Tokenanbieter, Tokenauthentifikatoren und Token serialisierer abzurufen.

Der Tokenanbieter erstellt eine Objektdarstellung des Tokens basierend auf den Informationen, die in den Client- oder Dienstanmeldeinformationen enthalten sind. Die Tokenobjektdarstellung wird dann mithilfe des Token serializers (im vorherigen Abschnitt erläutert) in die Nachricht geschrieben.

Der Tokenauthentifikator überprüft Token, die in der Nachricht eingehen. Die Darstellung des eingehenden Tokenobjekts wird vom Token serializer erstellt. Diese Objektdarstellung wird dann zur Überprüfung an den Tokenauthentifikator übergeben. Nachdem das Token erfolgreich überprüft wurde, gibt der Tokenauthentifikator eine Auflistung von IAuthorizationPolicy Objekten zurück, die die im Token enthaltenen Informationen darstellen. Diese Informationen werden später während der Nachrichtenverarbeitung verwendet, um Autorisierungsentscheidungen durchzuführen und Ansprüche für die Anwendung bereitzustellen. In diesem Beispiel verwendet CreditCardTokenAuthorizationPolicy der Authentifikator für Kreditkartentoken zu diesem Zweck.

Das Token-Serialisierungsprogramm ist zuständig für das Abrufen der Objektdarstellung des Tokens von der Verbindung und zum Einbinden der Darstellung in die Verbindung. Dies wird im vorherigen Abschnitt erläutert.

In diesem Beispiel verwenden wir einen Tokenanbieter nur für den Client und einen Tokenauthentifikator nur für den Dienst, da wir ein Kreditkartentoken nur in der Client-zu-Dienst-Richtung übertragen möchten.

Die Funktionalität auf dem Client befindet sich in den Klassen CreditCardClientCredentials, CreditCardClientCredentialsSecurityTokenManager und CreditCardTokenProvider.

Der Dienst bietet die Funktionalität in den CreditCardServiceCredentials, CreditCardServiceCredentialsSecurityTokenManager, CreditCardTokenAuthenticator und CreditCardTokenAuthorizationPolicy Klassen an.

    public class CreditCardClientCredentials : ClientCredentials
    {
        CreditCardInfo creditCardInfo;

        public CreditCardClientCredentials(CreditCardInfo creditCardInfo)
            : base()
        {
            if (creditCardInfo == null)
                throw new ArgumentNullException(nameof(creditCardInfo));

            this.creditCardInfo = creditCardInfo;
        }

        public CreditCardInfo CreditCardInfo
        {
            get { return this.creditCardInfo; }
        }

        protected override ClientCredentials CloneCore()
        {
            return new CreditCardClientCredentials(this.creditCardInfo);
        }

        public override SecurityTokenManager CreateSecurityTokenManager()
        {
            return new CreditCardClientCredentialsSecurityTokenManager(this);
        }
    }

    public class CreditCardClientCredentialsSecurityTokenManager : ClientCredentialsSecurityTokenManager
    {
        CreditCardClientCredentials creditCardClientCredentials;

        public CreditCardClientCredentialsSecurityTokenManager(CreditCardClientCredentials creditCardClientCredentials)
            : base (creditCardClientCredentials)
        {
            this.creditCardClientCredentials = creditCardClientCredentials;
        }

        public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)
        {
            // Handle this token for Custom.
            if (tokenRequirement.TokenType == Constants.CreditCardTokenType)
                return new CreditCardTokenProvider(this.creditCardClientCredentials.CreditCardInfo);
            // Return server cert.
            else if (tokenRequirement is InitiatorServiceModelSecurityTokenRequirement)
            {
                if (tokenRequirement.TokenType == SecurityTokenTypes.X509Certificate)
                {
                    return new X509SecurityTokenProvider(creditCardClientCredentials.ServiceCertificate.DefaultCertificate);
                }
            }

            return base.CreateSecurityTokenProvider(tokenRequirement);
        }

        public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
        {

            return new CreditCardSecurityTokenSerializer(version);
        }

    }

    class CreditCardTokenProvider : SecurityTokenProvider
    {
        CreditCardInfo creditCardInfo;

        public CreditCardTokenProvider(CreditCardInfo creditCardInfo) : base()
        {
            if (creditCardInfo == null)
                throw new ArgumentNullException(nameof(creditCardInfo));

            this.creditCardInfo = creditCardInfo;
        }

        protected override SecurityToken GetTokenCore(TimeSpan timeout)
        {
            SecurityToken result = new CreditCardToken(this.creditCardInfo);
            return result;
        }
    }

    public class CreditCardServiceCredentials : ServiceCredentials
    {
        string creditCardFile;

        public CreditCardServiceCredentials(string creditCardFile)
            : base()
        {
            if (creditCardFile == null)
                throw new ArgumentNullException(nameof(creditCardFile));

            this.creditCardFile = creditCardFile;
        }

        public string CreditCardDataFile
        {
            get { return this.creditCardFile; }
        }

        protected override ServiceCredentials CloneCore()
        {
            return new CreditCardServiceCredentials(this.creditCardFile);
        }

        public override SecurityTokenManager CreateSecurityTokenManager()
        {
            return new CreditCardServiceCredentialsSecurityTokenManager(this);
        }
    }

    public class CreditCardServiceCredentialsSecurityTokenManager : ServiceCredentialsSecurityTokenManager
    {
        CreditCardServiceCredentials creditCardServiceCredentials;

        public CreditCardServiceCredentialsSecurityTokenManager(CreditCardServiceCredentials creditCardServiceCredentials)
            : base(creditCardServiceCredentials)
        {
            this.creditCardServiceCredentials = creditCardServiceCredentials;
        }

        public override SecurityTokenAuthenticator CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, out SecurityTokenResolver outOfBandTokenResolver)
        {
            if (tokenRequirement.TokenType == Constants.CreditCardTokenType)
            {
                outOfBandTokenResolver = null;
                return new CreditCardTokenAuthenticator(creditCardServiceCredentials.CreditCardDataFile);
            }

            return base.CreateSecurityTokenAuthenticator(tokenRequirement, out outOfBandTokenResolver);
        }

        public override SecurityTokenSerializer CreateSecurityTokenSerializer(SecurityTokenVersion version)
        {
            return new CreditCardSecurityTokenSerializer(version);
        }
    }

    class CreditCardTokenAuthenticator : SecurityTokenAuthenticator
    {
        string creditCardsFile;
        public CreditCardTokenAuthenticator(string creditCardsFile)
        {
            this.creditCardsFile = creditCardsFile;
        }

        protected override bool CanValidateTokenCore(SecurityToken token)
        {
            return (token is CreditCardToken);
        }

        protected override ReadOnlyCollection<IAuthorizationPolicy> ValidateTokenCore(SecurityToken token)
        {
            CreditCardToken creditCardToken = token as CreditCardToken;

            if (creditCardToken.CardInfo.ExpirationDate < DateTime.UtcNow)
                throw new SecurityTokenValidationException("The credit card has expired.");
            if (!IsCardNumberAndExpirationValid(creditCardToken.CardInfo))
                throw new SecurityTokenValidationException("Unknown or invalid credit card.");

            // the credit card token has only 1 claim - the card number. The issuer for the claim is the
            // credit card issuer

            var cardIssuerClaimSet = new DefaultClaimSet(new Claim(ClaimTypes.Name, creditCardToken.CardInfo.CardIssuer, Rights.PossessProperty));
            var cardClaimSet = new DefaultClaimSet(cardIssuerClaimSet, new Claim(Constants.CreditCardNumberClaim, creditCardToken.CardInfo.CardNumber, Rights.PossessProperty));
            var policies = new List<IAuthorizationPolicy>(1);
            policies.Add(new CreditCardTokenAuthorizationPolicy(cardClaimSet));
            return policies.AsReadOnly();
        }

        /// <summary>
        /// Helper method to check if a given credit card entry is present in the User DB
        /// </summary>
        private bool IsCardNumberAndExpirationValid(CreditCardInfo cardInfo)
        {
            try
            {
                using (var myStreamReader = new StreamReader(this.creditCardsFile))
                {
                    string line = "";
                    while ((line = myStreamReader.ReadLine()) != null)
                    {
                        string[] splitEntry = line.Split('#');
                        if (splitEntry[0] == cardInfo.CardNumber)
                        {
                            string expirationDateString = splitEntry[1].Trim();
                            DateTime expirationDateOnFile = DateTime.Parse(expirationDateString, System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.AdjustToUniversal);
                            if (cardInfo.ExpirationDate == expirationDateOnFile)
                            {
                                string issuer = splitEntry[2];
                                return issuer.Equals(cardInfo.CardIssuer, StringComparison.InvariantCultureIgnoreCase);
                            }
                            else
                            {
                                return false;
                            }
                        }
                    }
                    return false;
                }
            }
            catch (Exception e)
            {
                throw new Exception("BookStoreService: Error while retrieving credit card information from User DB " + e.ToString());
            }
        }
    }

    public class CreditCardTokenAuthorizationPolicy : IAuthorizationPolicy
    {
        string id;
        ClaimSet issuer;
        IEnumerable<ClaimSet> issuedClaimSets;

        public CreditCardTokenAuthorizationPolicy(ClaimSet issuedClaims)
        {
            if (issuedClaims == null)
                throw new ArgumentNullException(nameof(issuedClaims));
            this.issuer = issuedClaims.Issuer;
            this.issuedClaimSets = new ClaimSet[] { issuedClaims };
            this.id = Guid.NewGuid().ToString();
        }

        public ClaimSet Issuer { get { return this.issuer; } }

        public string Id { get { return this.id; } }

        public bool Evaluate(EvaluationContext context, ref object state)
        {
            foreach (ClaimSet issuance in this.issuedClaimSets)
            {
                context.AddClaimSet(this, issuance);
            }

            return true;
        }
    }

Anzeigen der Anruferinformationen

Um die Informationen des Anrufers anzuzeigen, verwenden Sie das ServiceSecurityContext.Current.AuthorizationContext.ClaimSets, wie im folgenden Beispiel gezeigt. Die ServiceSecurityContext.Current.AuthorizationContext.ClaimSets enthält Autorisierungsansprüche, die dem aktuellen Aufrufer zugeordnet sind. Die Ansprüche werden von der CreditCardToken-Klasse in der AuthorizationPolicies-Auflistung angegeben.

bool TryGetStringClaimValue(ClaimSet claimSet, string claimType, out string claimValue)
{
    claimValue = null;
    IEnumerable<Claim> matchingClaims = claimSet.FindClaims(claimType, Rights.PossessProperty);
    if (matchingClaims == null)
        return false;
    IEnumerator<Claim> enumerator = matchingClaims.GetEnumerator();
    enumerator.MoveNext();
    claimValue = (enumerator.Current.Resource == null) ? null :
        enumerator.Current.Resource.ToString();
    return true;
}

string GetCallerCreditCardNumber()
{
     foreach (ClaimSet claimSet in
         ServiceSecurityContext.Current.AuthorizationContext.ClaimSets)
     {
         string creditCardNumber = null;
         if (TryGetStringClaimValue(claimSet,
             Constants.CreditCardNumberClaim, out creditCardNumber))
             {
                 string issuer;
                 if (!TryGetStringClaimValue(claimSet.Issuer,
                        ClaimTypes.Name, out issuer))
                 {
                     issuer = "Unknown";
                 }
                 return $"Credit card '{creditCardNumber}' issued by '{issuer}'";
        }
    }
    return "Credit card is not known";
}

Wenn Sie das Beispiel ausführen, werden die Vorgangsanforderungen und -antworten im Clientkonsolenfenster angezeigt. Drücken Sie im Clientfenster die EINGABETASTE, um den Client zu schließen.

Batchdatei einrichten

Mit der in diesem Beispiel enthaltenen Setup.bat Batchdatei können Sie den Server mit relevanten Zertifikaten konfigurieren, um die von IIS gehostete Anwendung auszuführen, die serverzertifikatbasierte Sicherheit erfordert. Diese Batchdatei muss so geändert werden, dass sie auf Computern oder in einem nicht gehosteten Fall funktioniert.

Im Folgenden finden Sie eine kurze Übersicht über die verschiedenen Abschnitte der Batchdateien, sodass sie so geändert werden können, dass sie in der entsprechenden Konfiguration ausgeführt werden können.

  • Erstellen des Serverzertifikats:

    In den folgenden Zeilen aus der Setup.bat Batchdatei wird das zu verwendende Serverzertifikat erstellt. Die %SERVER_NAME% Variable gibt den Servernamen an. Ändern Sie diese Variable, um Ihren eigenen Servernamen anzugeben. Die Standardeinstellung in dieser Batchdatei ist "localhost". Wenn Sie die %SERVER_NAME% Variable ändern, müssen Sie die Dateien Client.cs und Service.cs durchgehen und alle Vorkommen von localhost durch den Servernamen ersetzen, den Sie im Skript Setup.bat verwenden.

    Das Zertifikat wird im Speicher "Mein (Persönlich)" unter dem LocalMachine Speicherort gespeichert. Das Zertifikat wird im LocalMachine-Speicher für die von IIS gehosteten Dienste gespeichert. Für selbst gehostete Dienste sollten Sie die Batchdatei ändern, um das Clientzertifikat am Speicherort "CurrentUser" zu speichern, indem Sie die Zeichenfolge "LocalMachine" durch CurrentUser ersetzen.

    echo ************
    echo Server cert setup starting
    echo %SERVER_NAME%
    echo ************
    echo making server cert
    echo ************
    makecert.exe -sr LocalMachine -ss MY -a sha1 -n CN=%SERVER_NAME% -sky exchange -pe
    
  • Installieren des Serverzertifikats im vertrauenswürdigen Zertifikatspeicher des Clients:

    Die folgenden Zeilen in der Setup.bat Batchdatei kopieren das Serverzertifikat in den Clientspeicher für vertrauenswürdige Personen. Dieser Schritt ist erforderlich, da von Makecert.exe generierte Zertifikate nicht implizit vom Clientsystem vertrauenswürdig sind. Wenn Sie bereits über ein Zertifikat verfügen, das in einem vertrauenswürdigen Stammzertifikat des Clients verwurzelt ist, z. B. ein von Microsoft ausgestelltes Zertifikat, ist dieser Schritt zum Auffüllen des Clientzertifikatspeichers mit dem Serverzertifikat nicht erforderlich.

    echo ************
    echo copying server cert to client's TrustedPeople store
    echo ************
    certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r CurrentUser -s TrustedPeople
    
  • Um den Zugriff auf den privaten Zertifikatschlüssel über den von IIS gehosteten Dienst zu ermöglichen, muss dem Benutzerkonto, unter dem der von IIS gehostete Prozess ausgeführt wird, entsprechende Berechtigungen für den privaten Schlüssel erteilt werden. Dies erfolgt durch die letzten Schritte im skript Setup.bat.

    echo ************
    echo setting privileges on server certificates
    echo ************
    for /F "delims=" %%i in ('"%ProgramFiles%\ServiceModelSampleTools\FindPrivateKey.exe" My LocalMachine -n CN^=%SERVER_NAME% -a') do set PRIVATE_KEY_FILE=%%i
    set WP_ACCOUNT=NT AUTHORITY\NETWORK SERVICE
    (ver | findstr /C:"5.1") && set WP_ACCOUNT=%COMPUTERNAME%\ASPNET
    echo Y|cacls.exe "%PRIVATE_KEY_FILE%" /E /G "%WP_ACCOUNT%":R
    iisreset
    

Hinweis

Die Setup.bat Batchdatei ist so konzipiert, dass sie über eine Visual Studio-Eingabeaufforderung ausgeführt werden kann. Die PATH-Umgebungsvariable, die in der Visual Studio-Eingabeaufforderung festgelegt ist, verweist auf das Verzeichnis, das ausführbare Dateien enthält, die vom skript Setup.bat benötigt werden.

So richten Sie das Beispiel ein und erstellen es

  1. Stellen Sie sicher, dass Sie das One-Time Setup-Verfahren für die Windows Communication Foundation-Beispieleausgeführt haben.

  2. Befolgen Sie zum Erstellen der Lösung die Anweisungen im Erstellen der Windows Communication Foundation-Beispiele.

So führen Sie das Beispiel auf demselben Computer aus

  1. Öffnen Sie ein Visual Studio-Eingabeaufforderungsfenster mit Administratorrechten, und führen Sie Setup.bat aus dem Beispielinstallationsordner aus. Dadurch werden alle Zertifikate installiert, die für die Ausführung des Beispiels erforderlich sind. Stellen Sie sicher, dass der Pfad den Ordner enthält, in dem sich Makecert.exe befindet.

Hinweis

Achten Sie darauf, die Zertifikate zu entfernen, indem Sie Cleanup.bat ausführen, wenn Sie mit dem Beispiel fertig sind. Andere Sicherheitsbeispiele verwenden dieselben Zertifikate.

  1. Starten Sie Client.exe aus dem Verzeichnis "client\bin". Clientaktivität wird in der Clientkonsolenanwendung angezeigt.

  2. Wenn der Client und der Dienst nicht kommunizieren können, schauen Sie sich Tipps zur Problembehandlung für WCF-Samplesan.

So führen Sie das Beispiel computerübergreifend aus

  1. Erstellen Sie ein Verzeichnis auf dem Dienstcomputer für die Dienstbinärdateien.

  2. Kopieren Sie die Dienstprogrammdateien in das Dienstverzeichnis auf dem Dienstcomputer. Vergessen Sie nicht, CreditCardFile.txtzu kopieren; andernfalls kann der Kreditkartenauthentifikator keine Kreditkarteninformationen überprüfen, die vom Kunden gesendet wurden. Kopieren Sie auch die dateien Setup.bat und Cleanup.bat auf den Dienstcomputer.

  3. Sie müssen über ein Serverzertifikat mit dem Betreffnamen verfügen, der den vollqualifizierten Domänennamen des Computers enthält. Sie können eine mithilfe der Setup.bat erstellen, wenn Sie die %SERVER_NAME% Variable in den vollqualifizierten Namen des Computers ändern, auf dem der Dienst gehostet wird. Beachten Sie, dass die Setup.bat Datei in einer Entwickler-Eingabeaufforderung für Visual Studio ausgeführt werden muss, die mit Administratorrechten geöffnet wird.

  4. Kopieren Sie das Serverzertifikat in den CurrentUser-TrustedPeople Speicher auf dem Client. Sie müssen dies nur tun, wenn das Serverzertifikat nicht von einem vertrauenswürdigen Aussteller ausgestellt wird.

  5. Ändern Sie in der Datei EchoServiceHost.cs den Wert des Zertifikatsantragstellers, und geben Sie anstelle von localhost einen vollqualifizierten Computernamen an.

  6. Kopieren Sie die Clientprogrammdateien aus dem Ordner "\client\bin\" unter dem sprachspezifischen Ordner auf den Clientcomputer.

  7. Ändern Sie in der datei Client.cs den Adresswert des Endpunkts so, dass er mit der neuen Adresse Ihres Diensts übereinstimmt.

  8. Ändern Sie in der Datei Client.cs den Antragstellernamen des X.509-Dienstzertifikats, und geben Sie anstelle von localhost den vollqualifizierten Computernamen des Remotehosts an.

  9. Starten Sie auf dem Clientcomputer Client.exe über ein Eingabeaufforderungsfenster.

  10. Wenn der Client und der Dienst nicht kommunizieren können, schauen Sie sich Tipps zur Problembehandlung für WCF-Samplesan.

So stellen Sie den Zustand vor Ausführung des Beispiels wieder her

  1. Führen Sie Cleanup.bat im Beispielordner aus, nachdem Sie die Ausführung des Beispiels abgeschlossen haben.