2011-07-05 25 views
7

Mi problema particular es algo como esto:personalizado de validación de certificados de cliente y nombre de usuario en el servicio de WCF

  • Estamos actualmente en ejecución un conjunto de servicios que requiere los clientes para proporcionar un nombre de usuario y contraseña como la autenticación cuando se llama al servicios.

  • Nos gustaría implementar una infraestructura PKI en estos servicios, pero algunos de nuestros socios utilizarán más tiempo para acomodarse a esta nueva infraestructura que los demás.

  • Como primer paso, queremos solicitar certificados de cliente de algunos de nuestros socios. Se requerirá un certificado de cliente (además del nombre de usuario y la contraseña) para acceder a sus datos en nuestros servidores, mientras que para los demás usuarios solo se requerirán nombre de usuario y contraseña.

Para resolver este problema que estoy tratando de implementar un validador personalizado tanto para la autenticación de nombre de usuario/contraseña (usando UserNamePasswordValidator) y para los certificados de cliente (utilizando X509CertificateValidator) en WCF. El validador de nombre de usuario/contraseña verificará estas credenciales hacia nuestra base de datos, mientras que el validador de certificados del cliente inspeccionará si la solicitud es de un cliente del cual se requiere un certificado y, de ser así, verificará si se proporciona un certificado de cliente válido. No he podido configurar WCF para que use estos dos validadores.

Mi configuración de WCF en el servidor está configurado actualmente como esto:

<behaviors> 
    <serviceBehaviors> 
    <behavior name="MyServiceBehavior"> 
     <serviceMetadata httpsGetEnabled="true" policyVersion="Policy15" /> 
     <serviceDebug includeExceptionDetailInFaults="true" /> 
     <serviceCredentials> 
     <clientCertificate> 
      <authentication customCertificateValidatorType="MyWS.Security.MyServicesCertificateValidator, MyWS" 
      certificateValidationMode="Custom" revocationMode="NoCheck" /> 
     </clientCertificate> 
     <userNameAuthentication userNamePasswordValidationMode="Custom" 
      customUserNamePasswordValidatorType="MyWS.Security.MyServicesUsernameValidator, MyWS" /> 
     </serviceCredentials> 
    </behavior> 
    </serviceBehaviors> 
</behaviors> 
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/> 
<bindings> 
    <basicHttpBinding> 
    <binding name="MySoapBinding"> 
     <security mode="TransportWithMessageCredential"> 
     <transport clientCredentialType="Certificate" /> 
     <message clientCredentialType="UserName" /> 
     </security> 
    </binding> 
    </basicHttpBinding> 
</bindings> 
<services> 
    <service behaviorConfiguration="MyServiceBehavior" name="MyWS.Services.TheService"> 
    <endpoint address="" binding="basicHttpBinding" bindingConfiguration="MySoapBinding" name="TheService" bindingNamespace="https://services.my/TheService" contract="MyWS.Interfaces.Service.ITheService" /> 
    <host> 
     <baseAddresses> 
     <add baseAddress="https://localhost:4434/MyWS/TheService"/> 
     </baseAddresses> 
    </host> 
    </service> 
</services> 

Por lo que yo entiendo que esta configuración no es válida porque no puedo utilizar el customCertificateValidatorType en la capa de transporte (porque IIS inspecciona el certificado antes de que WCF esté involucrado aquí), pero no veo cómo puedo combinar los tipos customCertificateValidatorType y customUserNamePasswordValidatorType como tipos de credenciales en la capa de mensajes.

Implementé un inspector de mensajes y podría resolver el problema usando OperationContext de alguna manera (como se sugiere en el siguiente enlace), pero no he podido ver la forma de hacerlo de esta manera sin embargo ...

http://social.msdn.microsoft.com/Forums/en/wcf/thread/b6ab8b58-516b-41d4-bb0e-75b4baf92716

supongo que podría estar tratando de poner en práctica algo que es incompatible con la forma en que funciona WCF, pero si alguien tiene una idea acerca de cómo esto podría ser fijado estaría encantada de tener su comentarios sobre esto!

Respuesta

6

Creo que he encontrado una solución a mi problema ahora gracias a la valiosa contribución de @ ladislav-mrnka en su respuesta. Me di cuenta de que era necesario proporcionar dos puntos finales para configurar los diferentes requisitos, y también aprendí sobre las posibilidades del token de soporte al configurar los servicios.

Encontré un enlace sobre supporting tokens en MSDN, y siguiendo esta receta implementé el punto final en el servidor con el siguiente enlace personalizado (Cambié a configuración a través del código. No estoy seguro de si esto se puede configurar en la web. config también.)

private static Binding CreateMultiFactorAuthenticationBinding() 
{ 
    var httpsTransport = new HttpsTransportBindingElement(); 

    // The message security binding element will be configured to require 2 tokens: 
    // 1) A username-password encrypted with the service token 
    // 2) A client certificate used to sign the message 

    // Create symmetric security binding element with encrypted username-password token. 
    // Symmetric key is encrypted with server certificate. 
    var messageSecurity = SecurityBindingElement.CreateUserNameForCertificateBindingElement(); 
    messageSecurity.AllowInsecureTransport = false; 

    // Require client certificate as endorsing supporting token for all requests from client to server 
    var clientX509SupportingTokenParameters = new X509SecurityTokenParameters 
                { 
                 InclusionMode = 
                  SecurityTokenInclusionMode.AlwaysToRecipient 
                }; 
    messageSecurity.EndpointSupportingTokenParameters.Endorsing.Add(clientX509SupportingTokenParameters); 

    return new CustomBinding(messageSecurity, httpsTransport); 
} 

Esta unión crea un SymmetricSecurityBindingElement donde una clave simétrica (cifra con el certificado del servidor) se utiliza para cifrar un token de seguridad nombre de usuario/contraseña en el encabezado del mensaje, y el propio cuerpo del mensaje.

Además, se agrega un token de seguridad X509 como apoyo, token de apoyo a la encuadernación. Este token está configurado para estar siempre incluido en las solicitudes del cliente al servidor.

Este enlace personalizado se utilizó posteriormente para configurar un nuevo servicio WCF con un punto final que requiera este enlace. Estoy usando WcfFacility en Castle Windsor para configurar el servicio.

Este código hace lo siguiente:

  • Establece el certificado de servicio
  • Ajusta el modo de validación de los certificados de cliente a la confianza de la cadena, de manera que los certificados de cliente entrantes deben ser emitidos por una autoridad de certificación raíz de confianza en almacén de servidor
  • añade validadores personalizados para las credenciales de nombre de usuario/contraseña y certificado de cliente
//// Registering WCF-services 
var returnFaults = new ServiceDebugBehavior {IncludeExceptionDetailInFaults = true}; 
var metaData = new ServiceMetadataBehavior {HttpsGetEnabled = true}; 

var serviceCredentials = new ServiceCredentials(); 

// Configure service sertificate 
serviceCredentials.ServiceCertificate.SetCertificate(
    StoreLocation.LocalMachine, 
    StoreName.My, 
    X509FindType.FindBySubjectName, 
    "ServerCertificate"); 

// Configure client certificate authentication mode 
serviceCredentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.ChainTrust; 

// Add custom username-password validator 
serviceCredentials.UserNameAuthentication.UserNamePasswordValidationMode = 
    UserNamePasswordValidationMode.Custom; 
serviceCredentials.UserNameAuthentication.CustomUserNamePasswordValidator = 
    _container.Resolve<MyServicesUsernameValidator>(); 

// Add custom certificate validator 
serviceCredentials.ClientCertificate.Authentication.CertificateValidationMode = 
    X509CertificateValidationMode.Custom; 
serviceCredentials.ClientCertificate.Authentication.CustomCertificateValidator = 
    _container.Resolve<MyServicesCertificateValidator>(); 

var serviceModel = new DefaultServiceModel(); 

serviceModel.AddEndpoints(
    WcfEndpoint.ForContract<IMyContract>().BoundTo(CreateMultiFactorAuthenticationBinding())); 
serviceModel.BaseAddresses.Add(new Uri("https://server.com/MyServiceImplementation.svc")); 

serviceModel.AddExtensions(serviceCredentials); 
serviceModel.AddExtensions(metaData); 

_container.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero) 
    .Register(Component.For<IMyContract>() 
        .ImplementedBy<MyServiceImplementation>() 
        .AsWcfService(serviceModel), 
       Component.For<IServiceBehavior>().Instance(returnFaults)); 

MyServicesUsernameValidator hereda UserNamePasswordValidator y MyServicesCertificateValidator hereda X509CertificateValidator. Ambos anulan sus métodos Validate correspondientes.

Esto parece resolver mi problema particular ... ¡Espero que resuelva el tuyo! :)

4

No se puede definir en la configuración con enlaces fuera de la caja. Incluso el enlace personalizado no admite suficiente infraestructura para definir dicho enlace en la configuración.

Primero, definitivamente necesitará dos puntos finales para esto. Uno se usará solo para clientes con nombre de usuario/contraseña. Este punto final se puede configurar con un enlace común esperando la seguridad del mensaje con las credenciales del usuario de nombre de usuario o la seguridad del transporte con las credenciales del mensaje. El segundo punto final será para su validación más compleja. Este punto final necesita un nuevo enlace definido en el código. Esta unión debe utilizar:

  • seguridad asimétrico de unión al elemento (autenticación de certificados de inversión)
  • X.seguridad 509 token como principal testigo de seguridad
  • usuario token de seguridad nombre como apoyo a la seguridad de contadores

Esto es ejemplo de la unión tenía que usar cuando se comunica con un servicio similar:

Custom binding = new CustomBinding(); 
    var userNameToken = new UserNameSecurityTokenParameters(); 
    userNameToken.InclusionMode = SecurityTokenInclusionMode.AlwaysToRecipient; 

    var securityElement = new AsymmetricSecurityBindingElement(); 
    securityElement.IncludeTimestamp = true; 
    securityElement.RecipientTokenParameters = new X509SecurityTokenParameters(X509KeyIdentifierClauseType.SubjectKeyIdentifier, SecurityTokenInclusionMode.Never); 
    securityElement.InitiatorTokenParameters = new X509SecurityTokenParameters(X509KeyIdentifierClauseType.SubjectKeyIdentifier, SecurityTokenInclusionMode.AlwaysToRecipient); 
    securityElement.DefaultAlgorithmSuite = SecurityAlgorithmSuite.Basic256; 
    securityElement.SecurityHeaderLayout = SecurityHeaderLayout.Strict; 
    securityElement.SetKeyDerivation(false); 
    securityElement.EndpointSupportingTokenParameters.SignedEncrypted.Add(userNameToken); 
    securityElement.MessageProtectionOrder = MessageProtectionOrder.EncryptBeforeSign; 
    securityElement.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11; 
    binding.Elements.Add(securityElement); 

    var encodingElement = new TextMessageEncodingBindingElement(); 
    encodingElement.MessageVersion = MessageVersion.Soap12WSAddressingAugust2004; 
    binding.Elements.Add(encodingElement); 

    var httpElement = new HttpTransportBindingElement(); 
    httpElement.UseDefaultWebProxy = true; 
    binding.Elements.Add(httpElement); 

utiliza este ejemplo se CustomBinding definido en el código. Si desea utilizar esto en la configuración, debe crear una extensión de enlace y enlace completamente nueva y registrar esa extensión en el archivo de configuración.

Incluso entonces no estoy seguro de que se utilicen ambos validadores, lo usé como cliente del servicio. El punto principal es que la solicitud puede tener solo token principal único y es posible que la infraestructura predeterminada de WCF elija solo uno para validar, pero dicha lógica también puede ser reemplazada.

+0

Gracias por su comentario. Me guió en la dirección correcta, utilizando múltiples puntos finales y tokens de apoyo en un enlace personalizado. – Hanskun

Cuestiones relacionadas