Partilhar via


Token personalizado

Este exemplo demonstra como adicionar uma implementação de token personalizado em um aplicativo Windows Communication Foundation (WCF). O exemplo usa a CreditCardToken para passar com segurança informações 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 vinculação de segurança simétrica junto com o corpo da mensagem e outros cabeçalhos de mensagem. Isso é útil nos casos em que os tokens embutidos não são suficientes. Este exemplo demonstra como fornecer um token de segurança personalizado para 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.

Nota

O procedimento de configuração e as instruções de compilação para este exemplo estão localizados no final deste tópico.

Para resumir, 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 e assinatura de mensagens.

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

O serviço expõe um único ponto de extremidade que é criado programaticamente usando BindingHelper classes e EchoServiceHost . O ponto de extremidade consiste em um endereço, uma vinculação e um contrato. A associação é configurada com uma associação personalizada usando SymmetricSecurityBindingElement e HttpTransportBindingElement. Este exemplo define o para usar o SymmetricSecurityBindingElement certificado X.509 de um serviço para proteger a chave simétrica durante a transmissão e para passar um personalizado CreditCardToken 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 de cliente e também informações sobre o certificado X.509 de 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 CreditCardServiceCredentials classe e é adicionada às coleções de comportamentos do host de serviço no EchoServiceHost.InitializeRuntime método.

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 do serviço. O cliente usa a mesma BindingHelper classe para criar uma ligação. O resto da configuração está localizado na Client classe. O cliente também define as CreditCardToken informações a serem contidas no e informações sobre o certificado X.509 do serviço no código de instalação, adicionando uma CreditCardClientCredentials instância com os dados adequados à coleção de comportamentos de ponto de extremidade do cliente. O exemplo usa o certificado X.509 com o nome do assunto definido como CN=localhost o certificado de serviço.

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 personalizado

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 CreditCardToken classe. A representação do objeto é responsável por armazenar 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 por fio 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

Os serializadores de token de segurança no WCF são responsáveis por criar uma representação de objeto de tokens de segurança a partir do XML na mensagem e criar um formulário 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 a tokens de segurança. Para habilitar um token personalizado, você deve implementar seu próprio serializador de token de segurança. Este exemplo usa a CreditCardSecurityTokenSerializer classe para essa finalidade.

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

No cliente, a classe grava CreditCardSecurityTokenSerializer 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 o provedor de token e as classes de 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 token de segurança. A instância do gerenciador de token 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 do 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 que chegam na mensagem. A representação do 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 IAuthorizationPolicy objetos que representam as informações contidas no token. Essas informações são usadas posteriormente durante o processamento da mensagem para executar decisões de autorização e fornecer solicitaçõ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 o fio. Isso é discutido na seção anterior.

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

A funcionalidade no cliente está localizada nas CreditCardClientCredentialsclasses e CreditCardClientCredentialsSecurityTokenManagerCreditCardTokenProvider .

No serviço, a funcionalidade reside nas CreditCardServiceCredentialsclasses , CreditCardServiceCredentialsSecurityTokenManagerCreditCardTokenAuthenticator 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 o ServiceSecurityContext.Current.AuthorizationContext.ClaimSets conforme mostrado no código de exemplo a seguir. O ServiceSecurityContext.Current.AuthorizationContext.ClaimSets contém declarações de autorização associadas ao chamador atual. As reivindicações são fornecidas pela CreditCardToken classe em sua AuthorizationPolicies cobrança.

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 o exemplo, 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 em lote de instalação

O arquivo em lote de Setup.bat incluído neste exemplo permite configurar o servidor com certificados relevantes para executar o aplicativo hospedado no IIS que requer segurança baseada em certificado de servidor. Esse arquivo em lotes deve ser modificado para funcionar entre computadores ou para funcionar em um caso não hospedado.

A seguir é fornecida uma breve visão geral das diferentes seções dos arquivos em lote para que eles possam ser modificados para serem executados na configuração apropriada.

  • Criando o certificado do servidor:

    As linhas a seguir do arquivo em lotes criam o Setup.bat certificado do servidor a ser usado. A %SERVER_NAME% variável especifica o nome do servidor. Altere essa variável para especificar seu próprio nome de servidor. O padrão neste arquivo em lotes é localhost. Se você alterar a %SERVER_NAME% variável, 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 em Minha loja (Pessoal) sob o local da LocalMachine loja. O certificado é armazenado no armazenamento LocalMachine para os serviços hospedados no IIS. Para serviços auto-hospedados, você deve modificar o arquivo em lotes para armazenar o certificado do cliente no local de armazenamento 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
    
  • Instalando o certificado do servidor no armazenamento de certificados confiáveis do cliente:

    As linhas a seguir no arquivo em lote Setup.bat copiam o certificado do servidor para o armazenamento de pessoas confiáveis do cliente. Esta etapa é necessária porque os certificados gerados por Makecert.exe não são implicitamente confiáveis pelo sistema cliente. Se você já tiver um certificado enraizado em um certificado raiz confiável do cliente, por exemplo, um certificado emitido pela Microsoft, esta etapa de preencher o armazenamento 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 sob a qual o processo hospedado no IIS está sendo executado deve receber permissões apropriadas para a chave privada. Isso é feito pelas ú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
    

Nota

O arquivo em lotes 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. Certifique-se de ter executado o procedimento de instalação única para os exemplos do Windows Communication Foundation.

  2. Para criar a solução, siga as instruções em Criando os exemplos do Windows Communication Foundation.

Para executar o exemplo no mesmo computador

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

Nota

Certifique-se de remover os certificados executando Cleanup.bat quando terminar a amostra. Outros exemplos de segurança usam os mesmos certificados.

  1. Inicie Client.exe a partir do 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, consulte Dicas de solução de problemas para exemplos de WCF.

Para executar o exemplo no computador

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

  2. Copie os arquivos de programa de serviço para o diretório de serviço no computador de serviço. Não se esqueça de copiá CreditCardFile.txt; caso contrário, o autenticador do cartão de crédito não pode 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ê deve ter um certificado de servidor com o nome do assunto que contém o nome de domínio totalmente qualificado do computador. Você pode criar um usando o Setup.bat se alterar a %SERVER_NAME% variável para nome totalmente qualificado do computador onde o serviço está hospedado. Observe que o arquivo Setup.bat deve ser executado em um prompt de comando do desenvolvedor para Visual Studio aberto com privilégios de administrador.

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

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

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

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

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

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

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

Para limpar após a amostra

  1. Execute Cleanup.bat na pasta de exemplos assim que terminar de executar o exemplo.