Partager via


Jeton personnalisé

Cet exemple montre comment ajouter une implémentation de jeton personnalisée dans une application Windows Communication Foundation (WCF). L’exemple utilise un CreditCardToken moyen de transmettre en toute sécurité des informations sur les cartes de crédit client au service. Le jeton est transmis dans l’en-tête de message WS-Security et est signé et chiffré à l’aide de l’élément de liaison de sécurité symétrique, ainsi que du corps du message et d’autres en-têtes de message. Cela est utile dans les cas où les jetons intégrés ne sont pas suffisants. Cet exemple montre comment fournir un jeton de sécurité personnalisé à un service au lieu d’utiliser l’un des jetons intégrés. Le service implémente un contrat qui définit un modèle de communication de demande-réponse.

Remarque

La procédure d’installation et les instructions de génération de cet exemple se trouvent à la fin de cette rubrique.

Pour résumer, cet exemple illustre les éléments suivants :

  • Comment un client peut transmettre un jeton de sécurité personnalisé à un service.

  • Comment le service peut consommer et valider un jeton de sécurité personnalisé.

  • Comment le code du service WCF peut obtenir les informations sur les jetons de sécurité reçus, y compris le jeton de sécurité personnalisé.

  • Comment le certificat X.509 du serveur est utilisé pour protéger la clé symétrique utilisée pour le chiffrement et la signature des messages.

Authentification du client à l’aide d’un jeton de sécurité personnalisé

Le service expose un point de terminaison unique créé par programmation à l’aide BindingHelper et EchoServiceHost aux classes. Le point de terminaison se compose d'une adresse, d'une liaison et d'un contrat. La liaison est configurée avec une liaison personnalisé à l'aide de SymmetricSecurityBindingElement et HttpTransportBindingElement. Cet exemple définit l’utilisation SymmetricSecurityBindingElement du certificat X.509 d’un service pour protéger la clé symétrique pendant la transmission et passer un code personnalisé CreditCardToken dans un en-tête de message WS-Security en tant que jeton de sécurité signé et chiffré. Le comportement spécifie les informations d’identification du service à utiliser pour l’authentification du client et également des informations sur le certificat X.509 du service.

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);
    }
}

Pour utiliser un jeton de carte de crédit dans le message, l’exemple utilise des informations d’identification de service personnalisées pour fournir cette fonctionnalité. La classe d’informations d’identification du service se trouve dans la CreditCardServiceCredentials classe et est ajoutée aux collections de comportements de l’hôte de service dans la EchoServiceHost.InitializeRuntime méthode.

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();
    }
}

Le point de terminaison client est configuré de la même manière que le point de terminaison de service. Le client utilise la même BindingHelper classe pour créer une liaison. Le reste de la configuration se trouve dans la Client classe. Le client définit également les informations à contenir dans le CreditCardToken certificat X.509 du service dans le code d’installation en ajoutant une CreditCardClientCredentials instance avec les données appropriées à la collection de comportements de point de terminaison client. L’exemple utilise le certificat X.509 avec le nom de l’objet défini sur CN=localhost le certificat de service.

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();

Implémentation de jeton de sécurité personnalisée

Pour activer un jeton de sécurité personnalisé dans WCF, créez une représentation d’objet du jeton de sécurité personnalisé. L’exemple a cette représentation dans la CreditCardToken classe. La représentation d’objet est chargée de contenir toutes les informations de jeton de sécurité pertinentes et de fournir la liste des clés de sécurité contenues dans le jeton de sécurité. Dans ce cas, le jeton de sécurité de carte de crédit ne contient aucune clé de sécurité.

La section suivante décrit ce qui doit être fait pour permettre à un jeton personnalisé d’être transmis sur le câble et consommé par un point de terminaison WCF.

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; } }
}

Obtention du jeton de carte de crédit personnalisé vers et à partir du message

Les sérialiseurs de jeton de sécurité dans WCF sont responsables de la création d’une représentation d’objet des jetons de sécurité à partir du code XML dans le message et de la création d’une forme XML des jetons de sécurité. Ils sont également responsables d’autres fonctionnalités telles que la lecture et l’écriture d’identificateurs de clé pointant vers des jetons de sécurité, mais cet exemple utilise uniquement des fonctionnalités liées aux jetons de sécurité. Pour activer un jeton personnalisé, vous devez implémenter votre propre sérialiseur de jeton de sécurité. Cet exemple utilise la CreditCardSecurityTokenSerializer classe à cet effet.

Sur le service, le sérialiseur personnalisé lit la forme XML du jeton personnalisé et crée la représentation d’objet de jeton personnalisé à partir de celle-ci.

Sur le client, la CreditCardSecurityTokenSerializer classe écrit les informations contenues dans la représentation d’objet du jeton de sécurité dans l’enregistreur XML.

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);
        }
    }
}

Création de classes de fournisseur de jetons et d’authentificateur de jeton

Les informations d’identification du client et du service sont responsables de la fourniture de l’instance du gestionnaire de jetons de sécurité. L’instance du gestionnaire de jetons de sécurité est utilisée pour obtenir des fournisseurs de jetons, des authentificateurs de jetons et des sérialiseurs de jetons.

Le fournisseur de jetons crée une représentation d’objet du jeton en fonction des informations contenues dans les informations d’identification du client ou du service. La représentation de l’objet de jeton est ensuite écrite dans le message à l’aide du sérialiseur de jetons (décrit dans la section précédente).

L’authentificateur de jeton valide les jetons qui arrivent dans le message. La représentation d’objet de jeton entrante est créée par le sérialiseur de jetons. Cette représentation d’objet est ensuite passée à l’authentificateur de jeton pour validation. Une fois le jeton validé, l’authentificateur de jeton retourne une collection d’objets IAuthorizationPolicy qui représentent les informations contenues dans le jeton. Ces informations sont utilisées ultérieurement pendant le traitement des messages afin d’effectuer des décisions d’autorisation et de fournir des revendications pour l’application. Dans cet exemple, l’authentificateur de jeton de carte de crédit utilise CreditCardTokenAuthorizationPolicy à cet effet.

Le sérialiseur de jetons est chargé d’obtenir la représentation d’objet du jeton vers et à partir du câble. Ceci est abordé dans la section précédente.

Dans cet exemple, nous utilisons un fournisseur de jetons uniquement sur le client et un authentificateur de jeton uniquement sur le service, car nous voulons transmettre un jeton de carte de crédit uniquement dans le sens du client à service.

La fonctionnalité sur le client se trouve dans les classes et CreditCardTokenProvider les CreditCardClientCredentialsCreditCardClientCredentialsSecurityTokenManager classes.

Sur le service, la fonctionnalité réside dans les classes et CreditCardServiceCredentialsSecurityTokenManagerCreditCardTokenAuthenticatorCreditCardTokenAuthorizationPolicy les CreditCardServiceCredentialsclasses.

    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;
        }
    }

Affichage des informations des appelants

Pour afficher les informations de l’appelant, utilisez l’exemple ServiceSecurityContext.Current.AuthorizationContext.ClaimSets de code suivant. ServiceSecurityContext.Current.AuthorizationContext.ClaimSets contient les revendications d’autorisation associées à l’appelant actuel. Les revendications sont fournies par la CreditCardToken classe dans sa AuthorizationPolicies collection.

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";
}

Lorsque vous exécutez l’exemple, les demandes et réponses de l’opération s’affichent dans la fenêtre de la console cliente. Appuyez sur Entrée dans la fenêtre du client pour arrêter le client.

Configurer le fichier Batch

Le fichier batch Setup.bat inclus dans cet exemple vous permet de configurer le serveur avec des certificats pertinents pour exécuter l’application hébergée par IIS nécessitant une sécurité basée sur des certificats de serveur. Ce fichier de commandes doit être modifié pour fonctionner sur plusieurs ordinateurs ou pour fonctionner dans un cas non hébergé.

Voici une brève vue d’ensemble des différentes sections des fichiers batch afin qu’ils puissent être modifiés pour s’exécuter dans la configuration appropriée.

  • Création du certificat de serveur :

    Les lignes suivantes du Setup.bat fichier batch créent le certificat de serveur à utiliser. La %SERVER_NAME% variable spécifie le nom du serveur. Modifiez cette variable pour spécifier votre propre nom de serveur. La valeur par défaut de ce fichier batch est localhost. Si vous modifiez la %SERVER_NAME% variable, vous devez parcourir les fichiers Client.cs et Service.cs et remplacer toutes les instances de localhost par le nom du serveur que vous utilisez dans le script Setup.bat.

    Le certificat est stocké dans le magasin My (personnel) sous l'emplacement de magasin LocalMachine. Le certificat est stocké dans le magasin LocalMachine pour les services hébergés par IIS. Pour les services auto-hébergés, vous devez modifier le fichier batch pour stocker le certificat client à l’emplacement du magasin CurrentUser en remplaçant la chaîne LocalMachine par CurrentUser.

    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
    
  • Installation du certificat de serveur dans le magasin de certificats approuvé du client :

    Les lignes suivantes du fichier batch Setup.bat copient le certificat de serveur dans le répertoire des contacts de confiance du client. Cette étape est requise, car les certificats générés par Makecert.exe ne sont pas implicitement approuvés par le système client. Si vous disposez déjà d’un certificat rooté dans un certificat racine approuvé client( par exemple, un certificat émis par Microsoft), cette étape de remplissage du magasin de certificats client avec le certificat de serveur n’est pas nécessaire.

    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
    
  • Pour activer l’accès à la clé privée de certificat à partir du service hébergé par IIS, le compte d’utilisateur sous lequel le processus hébergé par IIS est en cours d’exécution doit recevoir les autorisations appropriées pour la clé privée. Cette opération est effectuée par les dernières étapes du script 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
    

Remarque

Le fichier de commandes Setup.bat est conçu pour être exécuté à partir d'une fenêtre de commande Visual Studio. La variable d’environnement PATH définie dans l’invite de commandes Visual Studio pointe vers le répertoire qui contient des exécutables requis par le script Setup.bat.

Pour configurer et compiler l’exemple

  1. Assurez-vous d’avoir effectué la Procédure d’installation unique pour les exemples Windows Communication Foundation.

  2. Pour générer la solution, suivez les instructions de Création des exemples Windows Communication Foundation.

Pour exécuter l’exemple sur le même ordinateur

  1. Ouvrez une fenêtre d’invite de commandes Visual Studio avec des privilèges d’administrateur et exécutez Setup.bat à partir de l’exemple de dossier d’installation. Cela installe tous les certificats requis pour l’exécution de l’exemple. Assurez-vous que le chemin d’accès inclut le dossier où se trouve Makecert.exe.

Remarque

Veillez à supprimer les certificats en exécutant Cleanup.bat une fois l’exemple terminé. D’autres exemples de sécurité utilisent les mêmes certificats.

  1. Lancez Client.exe à partir du répertoire client\bin. L’activité du client s’affiche sur l’application console cliente.

  2. Si le client et le service ne sont pas en mesure de communiquer, consultez Conseils de résolution des problèmes pour les exemples WCF.

Pour exécuter l’exemple sur un ordinateur

  1. Créez un répertoire sur l’ordinateur de service pour les fichiers binaires de service.

  2. Copiez les fichiers de programme de service dans le répertoire de service sur l’ordinateur de service. N’oubliez pas de copier CreditCardFile.txt; sinon, l’authentificateur de carte de crédit ne peut pas valider les informations de carte de crédit envoyées par le client. Copiez également les fichiers Setup.bat et Cleanup.bat sur l’ordinateur de service.

  3. Le nom de sujet de votre certificat de serveur doit contenir le nom de domaine complet de l'ordinateur. Vous pouvez en créer un à l’aide de l'Setup.bat si vous modifiez la %SERVER_NAME% variable en nom complet de l’ordinateur sur lequel le service est hébergé. Notez que le fichier Setup.bat doit être exécuté dans une invite de commandes développeur pour Visual Studio ouverte avec des privilèges d’administrateur.

  4. Copiez le certificat de serveur dans le magasin CurrentUser-TrustedPeople sur le client. Vous devez effectuer cette opération uniquement si le certificat de serveur n’est pas émis par un émetteur approuvé.

  5. Dans le fichier EchoServiceHost.cs, modifiez la valeur du nom de l’objet du certificat pour spécifier un nom d’ordinateur complet au lieu de localhost.

  6. Copiez les fichiers de programme client à partir du dossier \client\bin\, sous le dossier spécifique à la langue, sur l’ordinateur client.

  7. Dans le fichier Client.cs, modifiez la valeur d’adresse du point de terminaison pour qu’elle corresponde à la nouvelle adresse de votre service.

  8. Dans le fichier Client.cs modifiez le nom de l’objet du certificat X.509 de service pour qu’il corresponde au nom d’ordinateur complet de l’hôte distant au lieu de localhost.

  9. Sur l’ordinateur client, lancez Client.exe à partir d’une fenêtre d’invite de commandes.

  10. Si le client et le service ne sont pas en mesure de communiquer, consultez Conseils de résolution des problèmes pour les exemples WCF.

Pour nettoyer après le test

  1. Exécutez Cleanup.bat dans le dossier d’exemples une fois que vous avez terminé d’exécuter l’exemple.