Partekatu bidez


Proveedor de tokens SAML

En este ejemplo se muestra cómo implementar un proveedor de tokens SAML de cliente personalizado. Se usa un proveedor de tokens en Windows Communication Foundation (WCF) para proporcionar credenciales a la infraestructura de seguridad. El proveedor de tokens en general examina el destino y emite las credenciales adecuadas para que la infraestructura de seguridad pueda proteger el mensaje. WCF incluye el proveedor de tokens del Administrador de credenciales predeterminado. WCF también viene con un proveedor de tokens de CardSpace. Los proveedores de tokens personalizados son útiles en los casos siguientes:

  • Si tiene un almacén de credenciales con el que estos proveedores de tokens no pueden funcionar.

  • Si desea proporcionar su propio mecanismo personalizado para transformar las credenciales desde el punto en que el usuario proporciona los detalles a cuando el marco de cliente WCF usa las credenciales.

  • Si está creando un token personalizado.

En este ejemplo se muestra cómo crear un proveedor de tokens personalizado que permite usar un token SAML obtenido desde fuera del marco de cliente de WCF.

En resumen, este ejemplo muestra lo siguiente:

  • Cómo se puede configurar un cliente con un proveedor de tokens personalizado.

  • Cómo se puede pasar un token SAML a las credenciales de cliente personalizadas.

  • Cómo se proporciona el token SAML al marco de cliente WCF.

  • Cómo el cliente autentica al servidor utilizando el certificado X.509 del servidor.

El servicio expone dos puntos de conexión para comunicarse con el servicio, definidos mediante el archivo de configuración App.config. Cada punto de conexión consta de una dirección, un enlace y un contrato. El enlace se configura con un estándar wsFederationHttpBinding, que usa la seguridad de los mensajes. Un punto de conexión espera que el cliente se autentique con un token SAML que use una clave de prueba simétrica, mientras que el otro espera que el cliente se autentique con un token SAML que use una clave de prueba asimétrica. El servicio también configura el certificado de servicio mediante el comportamiento serviceCredentials. El serviceCredentials comportamiento permite configurar un certificado de servicio. Un cliente usa un certificado de servicio para autenticar el servicio y proporcionar protección de mensajes. La siguiente configuración hace referencia al certificado "localhost" instalado durante la configuración de ejemplo, tal como se describe en las instrucciones de instalación al final de este tema. El serviceCredentials comportamiento también permite configurar certificados de confianza para firmar tokens SAML. La siguiente configuración hace referencia al certificado "Alice" instalado durante el ejemplo.

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

Los pasos siguientes muestran cómo desarrollar un proveedor de tokens SAML personalizado e integrarlo con WCF: marco de seguridad:

  1. Escriba un proveedor de tokens SAML personalizado.

    El ejemplo implementa un proveedor de tokens SAML personalizado que devuelve un token de seguridad basado en una aserción de SAML que se proporciona en tiempo de construcción.

    Para realizar esta tarea, el proveedor de tokens personalizado se deriva de la SecurityTokenProvider clase e invalida el GetTokenCore método . Este método crea y devuelve un nuevo 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. Redacte un administrador de tokens de seguridad personalizado.

    La clase SecurityTokenManager se utiliza para crear SecurityTokenProvider para el SecurityTokenRequirement concreto que se pasa en el método CreateSecurityTokenProvider. También se usa un administrador de tokens de seguridad para crear autenticadores de tokens y serializador de tokens, pero estos no están cubiertos por este ejemplo. En este ejemplo, el administrador de tokens de seguridad personalizado hereda de la clase ClientCredentialsSecurityTokenManager y sobrescribe el método CreateSecurityTokenProvider para devolver el proveedor de tokens SAML personalizado cuando los requisitos de token proporcionados indican que se solicita el token SAML. Si la clase de credenciales de cliente (consulte el paso 3) no ha especificado una aserción, el administrador de tokens de seguridad crea una instancia adecuada.

    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. Escriba una credencial de cliente personalizada.

    La clase de credenciales de cliente se usa para representar las credenciales configuradas para el proxy de cliente y crea un administrador de tokens de seguridad que se usa para obtener autenticadores de tokens, proveedores de tokens y serializador de tokens.

    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 el cliente para que use la credencial de cliente personalizada.

    El ejemplo elimina la clase de credenciales de cliente predeterminada y proporciona la nueva clase de credencial de cliente para que el cliente pueda usar la credencial de cliente 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);
    

En el servicio, se muestran las reclamaciones asociadas al llamante. Al ejecutar el ejemplo, las solicitudes de operación y las respuestas se muestran en la ventana de la consola del cliente. Presione ENTRAR en la ventana del cliente para apagar el cliente.

Instalar el archivo por lotes

El Setup.bat archivo por lotes incluido con este ejemplo permite configurar el servidor con el certificado pertinente para ejecutar una aplicación autohospedada que requiera seguridad basada en certificados de servidor. Este archivo por lotes debe modificarse para que funcione en varios equipos o en un escenario sin hospedaje.

A continuación, se proporciona una breve descripción de las diferentes secciones de los archivos por lotes de manera que se puedan modificar para ejecutarse con la configuración adecuada.

  • Creación del certificado de servidor:

    Las líneas siguientes del archivo por lotes de Setup.bat crean el certificado de servidor que se va a usar. La variable %SERVER_NAME%especifica el nombre del servidor. Cambie esta variable para especificar su propio nombre de servidor. El valor predeterminado de este archivo por lotes es localhost.

    El certificado se almacena en el almacén My (Personal), en la ubicación de almacenamiento 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
    
  • Instalación del certificado de servidor en el almacén de certificados de confianza del cliente:

    Las líneas siguientes del archivo por lotes Setup.bat copian el certificado de servidor en el almacén de los usuarios de confianza del cliente. Este paso es necesario porque el sistema cliente no confía implícitamente en los certificados generados por Makecert.exe. Si ya tiene un certificado que se basa en un certificado raíz de confianza de cliente (por ejemplo, un certificado emitido por Microsoft), este paso para rellenar el almacén de certificados de cliente con el certificado de servidor no es necesario.

    certmgr.exe -add -r LocalMachine -s My -c -n %SERVER_NAME% -r LocalMachine -s TrustedPeople
    
  • Creación del certificado del emisor.

    Las siguientes líneas del archivo por lotes Setup.bat crean el certificado del emisor que se utilizará. La %USER_NAME% variable especifica el nombre del emisor. Cambie esta variable para especificar su propio nombre del emisor. El valor predeterminado de este archivo por lotes es Alice.

    El certificado está almacenado en My store en la ubicación del almacén 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
    
  • Instalar el certificado del emisor en el almacén de certificados de confianza del servidor.

    Las líneas siguientes del archivo por lotes Setup.bat copian el certificado de servidor en el almacén de los usuarios de confianza del cliente. Este paso es necesario porque el sistema cliente no confía implícitamente en los certificados generados por Makecert.exe. Si ya tiene un certificado que se basa en un certificado raíz de confianza de cliente (por ejemplo, un certificado emitido por Microsoft), este paso para rellenar el almacén de certificados del servidor con el certificado del emisor no es necesario.

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

Para instalar y compilar el ejemplo

  1. Asegúrese de que ha realizado el procedimiento de instalación única para los ejemplos de Windows Communication Foundation.

  2. Para compilar la solución, siga las instrucciones que se indican en Compilación de los ejemplos de Windows Communication Foundation.

Nota:

Si usa Svcutil.exe para volver a generar la configuración de este ejemplo, asegúrese de modificar el nombre del punto de conexión en la configuración de cliente para que coincida con el código de cliente.

Para ejecutar el ejemplo en el mismo equipo

  1. Ejecute Setup.bat desde la carpeta de instalación de ejemplo dentro de una ventana de comandos de Visual Studio que se ejecute con privilegios de administrador. Esto instala todos los certificados necesarios para ejecutar el ejemplo.

    Nota:

    El archivo por lotes Setup.bat está diseñado para ejecutarse desde el símbolo del sistema de Visual Studio. La variable de entorno PATH que se establece en el símbolo del sistema de Visual Studio señala al directorio que contiene los archivos ejecutables que requiere el script Setup.bat.

  2. Inicie Service.exe desde service\bin.

  3. Inicie Client.exe desde \client\bin. La actividad de cliente se muestra en la aplicación de consola cliente.

  4. Si el cliente y el servicio no pueden comunicarse, consulte Sugerencias de solución de problemas para ejemplos de WCF.

Para ejecutar el ejemplo entre equipos

  1. Cree un directorio en el equipo de servicio para los archivos binarios del servicio.

  2. Copie los archivos del programa de servicio en el directorio del servicio en el equipo destinado al servicio. Copie también los archivos Setup.bat y Cleanup.bat en el equipo de servicio.

  3. Debe tener un certificado de servidor con el nombre del sujeto que contiene el nombre de dominio completo del equipo. El archivo Service.exe.config debe actualizarse para reflejar este nuevo nombre de certificado. Puede crear un certificado de servidor modificando el archivo por lotes Setup.bat. Tenga en cuenta que el archivo Setup.bat debe ejecutarse desde el Símbolo del sistema para desarrolladores de Visual Studio con privilegios de administrador. Debe establecer la variable %SERVER_NAME% en el nombre de host completo del equipo que se utiliza para hospedar el servicio.

  4. Copie el certificado de servidor en el almacén CurrentUser-TrustedPeople del cliente. Este paso no es necesario cuando un emisor de confianza de cliente emite el certificado de servidor.

  5. En el archivo Service.exe.config del ordenador de servicio, cambie el valor de la dirección base para especificar un nombre de equipo totalmente calificado en lugar de localhost.

  6. En el equipo del servicio, ejecute Service.exe desde un símbolo del sistema.

  7. Copie los archivos de programa cliente de la carpeta \client\bin\, en la carpeta específica del idioma, en el equipo cliente.

  8. En el archivo Client.exe.config del equipo cliente, cambie el valor de dirección del punto de conexión para que coincida con la nueva dirección del servicio.

  9. En el equipo cliente, inicie Client.exe desde una ventana de línea de comandos.

  10. Si el cliente y el servicio no pueden comunicarse, consulte Sugerencias de solución de problemas para ejemplos de WCF.

Para limpiar después de la muestra.

  1. Ejecute Cleanup.bat en la carpeta samples una vez que haya terminado de ejecutar el ejemplo.