Compartilhar via


Token personalizado

Este exemplo demonstra como adicionar uma implementação de token personalizado a um aplicativo WCF (Windows Communication Foundation). O exemplo usa CreditCardToken para transmitir informações com segurança sobre cartões de crédito do cliente para o serviço. O token é passado no cabeçalho da mensagem WS-Security e é assinado e criptografado usando o elemento de associação de segurança simétrico junto com o corpo da mensagem e outros cabeçalhos da mensagem. Isso é útil quando os tokens internos não são suficientes. Este exemplo demonstra como fornecer um token de segurança personalizado a um serviço em vez de usar um dos tokens internos. O serviço implementa um contrato que define um padrão de comunicação solicitação-resposta.

Observação

O procedimento de instalação e as instruções de compilação dessa amostra estão no final deste tópico.

Este exemplo demonstra o seguinte:

  • Como um cliente pode passar um token de segurança personalizado para um serviço.

  • Como o serviço pode consumir e validar um token de segurança personalizado.

  • Como o código de serviço WCF pode obter as informações sobre tokens de segurança recebidos, incluindo o token de segurança personalizado.

  • Como o certificado X.509 do servidor é usado para proteger a chave simétrica usada para criptografia de mensagem e assinatura.

Autenticação do cliente usando um token de segurança personalizado

O serviço expõe um único ponto de extremidade criado programaticamente usando as classes BindingHelper e EchoServiceHost. O ponto de extremidade é composto por um endereço, uma associação e um contrato. A associação é configurada com uma associação personalizada usando SymmetricSecurityBindingElement e HttpTransportBindingElement. Este exemplo define SymmetricSecurityBindingElement para usar um certificado X.509 de serviço para proteger a chave simétrica durante a transmissão e passar um CreditCardToken personalizado em um cabeçalho de mensagem WS-Security como um token de segurança assinado e criptografado. O comportamento especifica as credenciais de serviço que devem ser usadas para autenticação do cliente, bem como informações sobre o certificado X.509 do serviço.

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

Para consumir um token de cartão de crédito na mensagem, o exemplo usa credenciais de serviço personalizadas para fornecer essa funcionalidade. A classe de credenciais de serviço está localizada na classe CreditCardServiceCredentials e é adicionada às coleções de comportamentos do host de serviço no método EchoServiceHost.InitializeRuntime.

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

O ponto de extremidade do cliente é configurado de maneira semelhante ao ponto de extremidade de serviço. O cliente usa a mesma classe BindingHelper para criar uma associação. A configuração restante está localizada na classe Client. O cliente também define informações a serem incluídas no CreditCardToken e informações sobre o certificado X.509 de serviço no código de instalação adicionando uma instância CreditCardClientCredentials com os dados apropriados à coleção de comportamentos do ponto de extremidade do cliente. A amostra usa o certificado X.509 como o certificado de serviço com o nome da entidade definido como CN=localhost.

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

Implementação de token de segurança personalizada

Para habilitar um token de segurança personalizado no WCF, crie uma representação de objeto do token de segurança personalizado. A amostra tem essa representação na classe CreditCardToken. A representação de objeto é responsável por manter todas as informações relevantes do token de segurança e fornecer uma lista de chaves de segurança contidas no token de segurança. Nesse caso, o token de segurança do cartão de crédito não contém nenhuma chave de segurança.

A próxima seção descreve o que deve ser feito para permitir que um token personalizado seja transmitido na nuvem e consumido por um ponto de extremidade 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; } }
}

Obtendo o token de cartão de crédito personalizado de e para a mensagem

Serializadores de token de segurança no WCF são responsáveis por criar uma representação de objeto de tokens de segurança do XML na mensagem e criar uma forma XML dos tokens de segurança. Eles também são responsáveis por outras funcionalidades, como ler e gravar identificadores de chave apontando para tokens de segurança, mas este exemplo usa apenas a funcionalidade relacionada ao token de segurança. Para habilitar um token personalizado, você deve implementar seu próprio serializador de token de segurança. Esta amostra usa a classe CreditCardSecurityTokenSerializer para essa finalidade.

No serviço, o serializador personalizado lê a forma XML do token personalizado e cria a representação de objeto do token personalizado.

No cliente, a classe CreditCardSecurityTokenSerializer grava as informações contidas na representação do objeto de token de segurança no gravador 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);
        }
    }
}

Como as classes de provedor de token e autenticador de token são criadas

As credenciais de cliente e serviço são responsáveis por fornecer a instância do gerenciador de tokens de segurança. A instância do gerenciador de tokens de segurança é usada para obter provedores de token, autenticadores de token e serializadores de token.

O provedor de token cria uma representação de objeto do token com base nas informações contidas nas credenciais de cliente ou serviço. A representação do objeto de token é então gravada na mensagem usando o serializador de token (discutido na seção anterior).

O autenticador de token valida os tokens recebidos na mensagem. A representação de objeto de token de entrada é criada pelo serializador de token. Essa representação de objeto é então passada para o autenticador de token para validação. Depois que o token é validado com êxito, o autenticador de token retorna uma coleção de objetos IAuthorizationPolicy que representam as informações contidas no token. Essas informações são usadas posteriormente durante o processamento de mensagens para executar decisões de autorização e para fornecer declarações para o aplicativo. Neste exemplo, o autenticador de token de cartão de crédito usa CreditCardTokenAuthorizationPolicy para essa finalidade.

O serializador de token é responsável por obter a representação de objeto do token de e para a nuvem. Isso foi abordado na seção anterior.

Neste exemplo, usamos um provedor de token somente no cliente e um autenticador de token somente no serviço, pois queremos transmitir um token de cartão de crédito somente do cliente para o serviço.

A funcionalidade no cliente está localizada nas classes e CreditCardClientCredentials, CreditCardClientCredentialsSecurityTokenManager e CreditCardTokenProvider.

No serviço, a funcionalidade reside nas classes CreditCardServiceCredentials, CreditCardServiceCredentialsSecurityTokenManager, CreditCardTokenAuthenticator e CreditCardTokenAuthorizationPolicy.

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

Exibindo as informações dos chamadores

Para exibir as informações do chamador, use ServiceSecurityContext.Current.AuthorizationContext.ClaimSets, conforme mostrado no código de exemplo a seguir. ServiceSecurityContext.Current.AuthorizationContext.ClaimSets contém declarações de autorização associadas ao chamador atual. As declarações são fornecidas pela classe CreditCardToken em sua coleção AuthorizationPolicies.

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

Quando você executa a amostra, as solicitações de operação e as respostas são exibidas na janela do console do cliente. Pressione ENTER na janela do cliente para desligar o cliente.

Arquivo de configuração em lote

O arquivo Setup.bat em lote, incluído com este exemplo, permite que você configure o servidor com os certificados relevantes para executar um aplicativo hospedado no IIS que exige segurança baseada em certificado do servidor. Esse arquivo em lote precisa ser modificado para funcionar em computadores ou em um caso não hospedado.

A seguir, oferecemos uma visão geral das diferentes seções dos arquivos em lote que podem ser modificadas para executar a configuração apropriada.

  • Criação do certificado do servidor:

    As linhas a seguir do arquivo em lote Setup.bat criam o certificado do servidor a ser usado. A variável %SERVER_NAME% especifica o nome do servidor. Altere essa variável para especificar o nome do seu próprio servidor. O padrão neste arquivo de lote é localhost. Se você alterar a variável %SERVER_NAME%, deverá percorrer os arquivos Client.cs e Service.cs e substituir todas as instâncias de localhost pelo nome do servidor usado no script Setup.bat.

    O certificado é armazenado no repositório Meu (Pessoal) no local de armazenamento LocalMachine. O certificado é armazenado no repositório LocalMachine para os serviços hospedados no IIS. Para serviços auto-hospedados, você deve modificar o arquivo em lote para armazenar o certificado do cliente no local do repositório CurrentUser substituindo a cadeia de caracteres LocalMachine por 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
    
  • Instalação do certificado do servidor no repositório de certificados confiáveis do cliente:

    As linhas a seguir no arquivo em lote Setup.bat copiam o certificado do servidor no repositório de pessoas confiáveis do cliente. Essa etapa é necessária porque os certificados gerados por Makecert.exe não são implicitamente confiáveis pelo sistema do cliente. Se você já tiver um certificado com raiz em um certificado raiz confiável do cliente, por exemplo, um certificado emitido pela Microsoft, essa etapa de preenchimento do repositório de certificados do cliente com o certificado do servidor não será necessária.

    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
    
  • Para habilitar o acesso à chave privada do certificado do serviço hospedado no IIS, a conta de usuário na qual o processo hospedado no IIS está em execução deve receber as permissões apropriadas para a chave privada. Isso é feito nas últimas etapas no 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
    

Observação

O arquivo de lote Setup.bat foi projetado para ser executado a partir de um prompt de comando do Visual Studio. A variável de ambiente PATH definida no prompt de comando do Visual Studio aponta para o diretório que contém executáveis exigidos pelo script Setup.bat.

Para configurar e compilar o exemplo

  1. Verifique se você executou o Procedimento de instalação única para os exemplos do Windows Communication Foundation.

  2. Para compilar a solução, siga as instruções contidas em Como compilar as amostras do Windows Communication Foundation.

Para executar a amostra no mesmo computador

  1. Abra a janela do Prompt de Comando do Visual Studio com privilégios de administrador e execute Setup.bat na pasta de instalação do exemplo. Isso instala todos os certificados necessários para executar a amostra. Verifique se o caminho inclui a pasta onde Makecert.exe está localizado.

Observação

Lembre-se de remover os certificados executando Cleanup.bat quando terminar de usar o exemplo. Outros exemplos de segurança usam os mesmos certificados.

  1. Inicie Client.exe no diretório \client\bin. A atividade do cliente é exibida no aplicativo de console do cliente.

  2. Se o cliente e o serviço não puderem se comunicar, confira Dicas de solução de problemas para exemplos de WCF.

Para executar a amostra no computador

  1. Crie um diretório no computador de serviço para os binários de serviço.

  2. Copie os arquivos do programa de serviço para o diretório de serviço no computador de serviço. Não se esqueça de copiar CreditCardFile.txt; caso contrário, o autenticador de cartão de crédito não poderá validar as informações do cartão de crédito enviadas pelo cliente. Copie também os arquivos Setup.bat e Cleanup.bat para o computador de serviço.

  3. Você precisa ter um certificado de servidor com o nome da entidade que contém o nome de domínio totalmente qualificado do computador. Você pode criar um usando o Setup.bat se alterar a variável %SERVER_NAME% para o nome totalmente qualificado do computador em que o serviço está hospedado. Observe que o arquivo setup.bat precisa ser executado em um Prompt de Comando do Desenvolvedor para Visual Studio aberto com privilégios de administrador.

  4. Copie o certificado do servidor no repositório CurrentUser-TrustedPeople no cliente. Você só deve fazer isso se o certificado do servidor não for emitido por um emissor confiável.

  5. No arquivo EchoServiceHost.cs, altere o valor do nome da entidade do certificado para especificar um nome de computador totalmente qualificado em vez de localhost.

  6. Copie os arquivos do programa cliente da pasta \client\bin\ na pasta específica do idioma, para o computador cliente.

  7. No arquivo Client.cs, altere o valor do endereço do ponto de extremidade para que ele corresponda ao novo endereço do serviço.

  8. No arquivo Client.cs, altere o nome da entidade do certificado X.509 de serviço para corresponder ao nome do computador totalmente qualificado do host remoto em vez de localhost.

  9. No computador cliente, inicialize Client.exe a partir de uma janela do prompt de comando.

  10. Se o cliente e o serviço não puderem se comunicar, confira Dicas de solução de problemas para exemplos de WCF.

Para fazer uma limpeza após o exemplo

  1. Execute Cleanup.bat na pasta de amostras depois de concluir a execução da amostra.