Compartilhar via


Fornecedor de token SAML

Este exemplo demonstra como implementar um provedor de token SAML de cliente personalizado. Um provedor de token no WCF (Windows Communication Foundation) é usado para fornecer credenciais à infraestrutura de segurança. O provedor de token em geral examina o destino e emite as credenciais apropriadas para que a infraestrutura de segurança possa proteger a mensagem. O WCF é fornecido com o provedor de token padrão do Gerenciador de Credenciais. O WCF também é fornecido com um provedor de token CardSpace. Os provedores de token personalizados são úteis nos seguintes casos:

  • Se você tiver um repositório de credenciais com o qual esses provedores de token não podem operar.

  • Se você quiser fornecer o próprio mecanismo personalizado para transformar as credenciais a partir do ponto em que o usuário fornece os detalhes para quando a estrutura de cliente do WCF usa as credenciais.

  • Se você estiver criando um token personalizado.

Este exemplo mostra como criar um provedor de token personalizado que permite que um token SAML obtido de fora da estrutura de cliente do WCF seja usado.

Este exemplo demonstra o seguinte:

  • Como um cliente pode ser configurado com um provedor de token personalizado.

  • Como um token SAML pode ser passado para as credenciais personalizadas do cliente.

  • Como o token SAML é fornecido para a estrutura de cliente do WCF.

  • Como o servidor é autenticado pelo cliente usando o certificado X.509 do servidor.

O serviço expõe dois pontos de extremidade para comunicação com o serviço, definidos usando o arquivo de configuração App.config. Cada ponto de extremidade consiste em um endereço, uma associação e um contrato. A associação é configurada com um wsFederationHttpBinding padrão, que usa a segurança da mensagem. Um ponto de extremidade espera que o cliente se autentique com um token SAML que usa uma chave de prova simétrica e o outro com um que usa uma chave de prova assimétrica. O serviço também configura o certificado de serviço usando o comportamento serviceCredentials. O comportamento serviceCredentials permite configurar um certificado de serviço. Um certificado de serviço é usado por um cliente para autenticar o serviço e fornecer proteção de mensagem. A configuração a seguir faz referência ao certificado "localhost" instalado durante a configuração de exemplo, conforme descrito nas instruções de instalação no final deste tópico. O comportamento serviceCredentials também permite configurar certificados confiáveis para assinar tokens SAML. A configuração a seguir faz referência ao certificado “Alice” instalado durante o exemplo.

<system.serviceModel>
 <services>
  <service
          name="Microsoft.ServiceModel.Samples.CalculatorService"
          behaviorConfiguration="CalculatorServiceBehavior">
   <host>
    <baseAddresses>
     <!-- configure base address provided by host -->
     <add
  baseAddress="http://localhost:8000/servicemodelsamples/service/" />
    </baseAddresses>
   </host>
   <!-- use base address provided by host -->
   <!-- Endpoint that expect SAML tokens with Symmetric proof keys -->
   <endpoint address="calc/symm"
             binding="wsFederationHttpBinding"
             bindingConfiguration="Binding1"
             contract="Microsoft.ServiceModel.Samples.ICalculator" />
   <!-- Endpoint that expect SAML tokens with Asymmetric proof keys -->
   <endpoint address="calc/asymm"
             binding="wsFederationHttpBinding"
             bindingConfiguration="Binding2"
             contract="Microsoft.ServiceModel.Samples.ICalculator" />
  </service>
 </services>

 <bindings>
  <wsFederationHttpBinding>
   <!-- Binding that expect SAML tokens with Symmetric proof keys -->
   <binding name="Binding1">
    <security mode="Message">
     <message negotiateServiceCredential ="false"
              issuedKeyType="SymmetricKey"
              issuedTokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"  />
    </security>
   </binding>
   <!-- Binding that expect SAML tokens with Asymmetric proof keys -->
   <binding name="Binding2">
    <security mode="Message">
     <message negotiateServiceCredential ="false"
              issuedKeyType="AsymmetricKey"
              issuedTokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"  />
    </security>
   </binding>
  </wsFederationHttpBinding>
 </bindings>

 <behaviors>
  <serviceBehaviors>
   <behavior name="CalculatorServiceBehavior">
    <!--
    The serviceCredentials behavior allows one to define a service certificate.
    A service certificate is used by a client to authenticate the service and provide message protection.
    This configuration references the "localhost" certificate installed during the setup instructions.
    -->
    <serviceCredentials>
     <!-- Set allowUntrustedRsaIssuers to true to allow self-signed, asymmetric key based SAML tokens -->
     <issuedTokenAuthentication allowUntrustedRsaIssuers ="true" >
      <!-- Add Alice to the list of certs trusted to issue SAML tokens -->
      <knownCertificates>
       <add storeLocation="LocalMachine"
            storeName="TrustedPeople"
            x509FindType="FindBySubjectName"
            findValue="Alice"/>
      </knownCertificates>
     </issuedTokenAuthentication>
     <serviceCertificate storeLocation="LocalMachine"
                         storeName="My"
                         x509FindType="FindBySubjectName"
                         findValue="localhost"  />
    </serviceCredentials>
   </behavior>
  </serviceBehaviors>
 </behaviors>

</system.serviceModel>

As etapas a seguir mostram como desenvolver um provedor de token SAML personalizado e integrá-lo à estrutura de segurança do WCF:

  1. Grave um provedor de token SAML personalizado.

    O exemplo implementa um provedor de token SAML personalizado que retorna um token de segurança com base em uma declaração SAML fornecida em tempo de construção.

    Para executar essa tarefa, o provedor de token personalizado é derivado da classe SecurityTokenProvider e substitui o método GetTokenCore. Esse método cria e retorna um novo SecurityToken.

    protected override SecurityToken GetTokenCore(TimeSpan timeout)
    {
     // Create a SamlSecurityToken from the provided assertion
     SamlSecurityToken samlToken = new SamlSecurityToken(assertion);
    
     // Create a SecurityTokenSerializer that will be used to
     // serialize the SamlSecurityToken
     WSSecurityTokenSerializer ser = new WSSecurityTokenSerializer();
     // Create a memory stream to write the serialized token into
     // Use an initial size of 64Kb
     MemoryStream s = new MemoryStream(UInt16.MaxValue);
    
     // Create an XmlWriter over the stream
     XmlWriter xw = XmlWriter.Create(s);
    
     // Write the SamlSecurityToken into the stream
     ser.WriteToken(xw, samlToken);
    
     // Seek back to the beginning of the stream
     s.Seek(0, SeekOrigin.Begin);
    
     // Load the serialized token into a DOM
     XmlDocument dom = new XmlDocument();
     dom.Load(s);
    
     // Create a KeyIdentifierClause for the SamlSecurityToken
     SamlAssertionKeyIdentifierClause samlKeyIdentifierClause = samlToken.CreateKeyIdentifierClause<SamlAssertionKeyIdentifierClause>();
    
    // Return a GenericXmlToken from the XML for the
    // SamlSecurityToken, the proof token, the valid from and valid
    // until times from the assertion and the key identifier clause
    // created above
    return new GenericXmlSecurityToken(dom.DocumentElement, proofToken, assertion.Conditions.NotBefore, assertion.Conditions.NotOnOrAfter, samlKeyIdentifierClause, samlKeyIdentifierClause, null);
    }
    
  2. Grave o gerenciador de tokens de segurança personalizado.

    A classe SecurityTokenManager é usada para criar SecurityTokenProvider para o SecurityTokenRequirement específico que é passado para ela no método CreateSecurityTokenProvider. Um gerenciador de tokens de segurança também é usado para criar autenticadores de token e serializador de token, mas não são tratados neste exemplo. Neste exemplo, o gerenciador de tokens de segurança personalizado herda da classe ClientCredentialsSecurityTokenManager e substitui o método CreateSecurityTokenProvider para retornar o provedor de token SAML personalizado quando os requisitos de token passados indicam que o token SAML é solicitado. Se a classe de credenciais do cliente (consulte a etapa 3) não tiver especificado uma declaração, o gerenciador de tokens de segurança criará uma instância apropriada.

    public class SamlSecurityTokenManager : ClientCredentialsSecurityTokenManager
    {
     SamlClientCredentials samlClientCredentials;
    
     public SamlSecurityTokenManager ( SamlClientCredentials samlClientCredentials)
      : base(samlClientCredentials)
     {
      // Store the creating client credentials
      this.samlClientCredentials = samlClientCredentials;
     }
    
     public override SecurityTokenProvider CreateSecurityTokenProvider ( SecurityTokenRequirement tokenRequirement )
     {
      // If token requirement matches SAML token return the
      // custom SAML token provider
      if (tokenRequirement.TokenType == SecurityTokenTypes.Saml ||
          tokenRequirement.TokenType == "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1")
      {
       // Retrieve the SAML assertion and proof token from the
       // client credentials
       SamlAssertion assertion = this.samlClientCredentials.Assertion;
       SecurityToken prooftoken = this.samlClientCredentials.ProofToken;
    
       // If either the assertion of proof token is null...
       if (assertion == null || prooftoken == null)
       {
        // ...get the SecurityBindingElement and then the
        // specified algorithm suite
        SecurityBindingElement sbe = null;
        SecurityAlgorithmSuite sas = null;
    
        if ( tokenRequirement.TryGetProperty<SecurityBindingElement> ( "http://schemas.microsoft.com/ws/2006/05/servicemodel/securitytokenrequirement/SecurityBindingElement", out sbe))
        {
         sas = sbe.DefaultAlgorithmSuite;
        }
    
        // If the token requirement is for a SymmetricKey based token..
        if (tokenRequirement.KeyType == SecurityKeyType.SymmetricKey)
        {
         // Create a symmetric proof token
         prooftoken = SamlUtilities.CreateSymmetricProofToken ( tokenRequirement.KeySize );
         // and a corresponding assertion based on the claims specified in the client credentials
         assertion = SamlUtilities.CreateSymmetricKeyBasedAssertion ( this.samlClientCredentials.Claims, new X509SecurityToken ( samlClientCredentials.ClientCertificate.Certificate ), new X509SecurityToken ( samlClientCredentials.ServiceCertificate.DefaultCertificate ), (BinarySecretSecurityToken)prooftoken, sas);
        }
        // otherwise...
        else
        {
         // Create an asymmetric proof token
         prooftoken = SamlUtilities.CreateAsymmetricProofToken();
         // and a corresponding assertion based on the claims
         // specified in the client credentials
         assertion = SamlUtilities.CreateAsymmetricKeyBasedAssertion ( this.samlClientCredentials.Claims, prooftoken, sas );
        }
       }
    
       // Create a SamlSecurityTokenProvider based on the assertion and proof token
       return new SamlSecurityTokenProvider(assertion, prooftoken);
      }
      // otherwise use base implementation
      else
      {
       return base.CreateSecurityTokenProvider(tokenRequirement);
      }
    }
    
  3. Grave uma credencial de cliente personalizada.

    A classe de credenciais do cliente é usada para representar as credenciais configuradas para o proxy do cliente e cria um gerenciador de tokens de segurança usado para obter autenticadores, provedores e serializadores de token.

    public class SamlClientCredentials : ClientCredentials
    {
     ClaimSet claims;
     SamlAssertion assertion;
     SecurityToken proofToken;
    
     public SamlClientCredentials() : base()
     {
      // Set SupportInteractive to false to suppress Cardspace UI
      base.SupportInteractive = false;
     }
    
     protected SamlClientCredentials(SamlClientCredentials other) : base ( other )
     {
      // Just do reference copy given sample nature
      this.assertion = other.assertion;
      this.claims = other.claims;
      this.proofToken = other.proofToken;
     }
    
     public SamlAssertion Assertion { get { return assertion; } set { assertion = value; } }
    
     public SecurityToken ProofToken { get { return proofToken; } set { proofToken = value; } }
     public ClaimSet Claims { get { return claims; } set { claims = value; } }
    
     protected override ClientCredentials CloneCore()
     {
      return new SamlClientCredentials(this);
     }
    
     public override SecurityTokenManager CreateSecurityTokenManager()
     {
      // return custom security token manager
      return new SamlSecurityTokenManager(this);
     }
    }
    
  4. Configure o cliente para usar a credencial de cliente personalizada.

    O exemplo exclui a classe de credencial de cliente padrão e fornece a nova classe de credencial de cliente para que ele possa usar a credencial personalizada.

    // Create new credentials class
    SamlClientCredentials samlCC = new SamlClientCredentials();
    
    // Set the client certificate. This is the cert that will be used to sign the SAML token in the symmetric proof key case
    samlCC.ClientCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "Alice");
    
    // Set the service certificate. This is the cert that will be used to encrypt the proof key in the symmetric proof key case
    samlCC.ServiceCertificate.SetDefaultCertificate(StoreLocation.CurrentUser, StoreName.TrustedPeople, X509FindType.FindBySubjectName, "localhost");
    
    // Create some claims to put in the SAML assertion
    IList<Claim> claims = new List<Claim>();
    claims.Add(Claim.CreateNameClaim(samlCC.ClientCertificate.Certificate.Subject));
    ClaimSet claimset = new DefaultClaimSet(claims);
    samlCC.Claims = claimset;
    
    // set new credentials
    client.ChannelFactory.Endpoint.Behaviors.Remove(typeof(ClientCredentials));
    client.ChannelFactory.Endpoint.Behaviors.Add(samlCC);
    

No serviço, as declarações associadas ao chamador são exibidas. Ao executar 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 o certificado pertinente para executar um aplicativo auto-hospedado 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 valor padrão neste arquivo de lote é localhost.

    O certificado é armazenado no repositório Meu (Pessoal) no local de armazenamento LocalMachine.

    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.

    certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r LocalMachine -s TrustedPeople
    
  • Criação do certificado do emissor.

    As linhas a seguir do arquivo em lote Setup.bat criam o certificado do servidor a ser usado. A variável %USER_NAME% especifica o nome do emissor. Altere essa variável para especificar o nome do seu próprio emissor. O valor padrão neste arquivo de lote é Alice.

    O certificado é armazenado no repositório Meu Armazenamento no local do repositório CurrentUser.

    echo ************
    echo Server cert setup starting
    echo %SERVER_NAME%
    echo ************
    echo making server cert
    echo ************
    makecert.exe -sr CurrentUser -ss My -a sha1 -n CN=%USER_NAME% -sky exchange -pe
    
  • Instalação do certificado do emissor no repositório de certificados confiável do servidor.

    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 para o 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 servidor com o certificado do emissor não será necessária.

    certmgr.exe -add -r CurrentUser -s My -c -n %USER_NAME% -r LocalMachine -s TrustedPeople
    

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.

Observação

Se você usar Svcutil.exe para regenerar a configuração desta amostra, modifique o nome do ponto de extremidade na configuração do cliente para corresponder ao código do cliente.

Para executar o exemplo no mesmo computador

  1. Execute Setup.bat na pasta de instalação de exemplo dentro de um prompt de comando do Visual Studio executado com privilégios de administrador. Isso instalará todos os certificados necessários para executar a amostra.

    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.

  2. Inicie Service.exe a partir do service\bin.

  3. Inicialize o Client.exe a partir do \client\bin. A atividade do cliente é exibida no aplicativo do console do cliente.

  4. 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 em vários computadores

  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. 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. O arquivo Service.exe.config precisa ser atualizado para refletir esse novo nome de certificado. Você pode criar um certificado de servidor modificando o arquivo em lote Setup.bat. Observe que o arquivo setup.bat precisa ser executado em uma janela do Prompt de Comando do Desenvolvedor para Visual Studio aberta com privilégios de administrador. Você precisa definir a variável %SERVER_NAME% como o nome de host totalmente qualificado do computador usado para hospedar o serviço.

  4. Copie o certificado do servidor no repositório CurrentUser-TrustedPeople do cliente. Essa etapa não é necessária quando o certificado do servidor é emitido por um emissor confiável do cliente.

  5. No arquivo Service.exe.config no computador de serviço, altere o valor do endereço base para especificar um nome de computador totalmente qualificado em vez de localhost.

  6. No computador de serviço, inicie Service.exe a partir de um prompt de comando.

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

  8. No arquivo Client.exe.config do computador cliente, altere o valor do endereço do ponto de extremidade para que ele corresponda ao novo endereço do serviço.

  9. No computador cliente, inicie 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.