publicar - wcf service c#
Autenticación de una solicitud de WCF a través de un certificado de cliente a través de HTTPS (4)
¿El apretón de manos de SSL tiene éxito? Habilite el registro de SChannel para solucionar problemas de la capa SSL. Vea este viejo artículo de KB: Cómo habilitar el registro de eventos de Schannel en IIS . Aunque es una KB para W2K y XP, los pasos para habilitar el registro de SChannel son los mismos y siguen siendo válidos en los sistemas más nuevos. Con el registro habilitado, podrá determinar por qué SSL rechaza el certificado.
He estado luchando con la configuración de este maldito servicio WCF durante la semana pasada, y me estoy desacelerando al comenzar a sospechar que lo que estoy tratando de hacer simplemente no es posible, a pesar de la documentación.
Simplemente, quiero que un servicio WCF requiera un certificado de cliente (que el servidor tendrá en su almacén de certificados) y luego acceda a esa identidad con System.ServiceModel.ServiceSecurityContext. Además, esto necesita usar seguridad de transporte.
Aquí está mi configuración de servidor:
<system.serviceModel>
<services>
<service behaviorConfiguration="requireCertificate" name="Server.CXPClient">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" name="wsHttpEndpoint" contract="PartnerComm.ContentXpert.Server.ICXPClient" />
<endpoint address="mex" binding="wsHttpBinding" bindingConfiguration="wsHttpEndpointBinding" name="mexEndpoint" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="https://localhost:8371/Design_Time_Addresses/Server/CXPClient/" />
</baseAddresses>
</host>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="requireCertificate">
<serviceMetadata httpsGetEnabled="true" />
<serviceCredentials>
<serviceCertificate findValue="CyberdyneIndustries" storeLocation="LocalMachine" storeName="TrustedPeople" x509FindType="FindBySubjectName"/>
<clientCertificate>
<authentication certificateValidationMode="ChainTrust" trustedStoreLocation="LocalMachine" />
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<wsHttpBinding>
<binding name="wsHttpEndpointBinding" maxBufferPoolSize="5242880" maxReceivedMessageSize="5242880">
<readerQuotas maxDepth="32" maxStringContentLength="5242880" maxArrayLength="1073741824" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
</system.serviceModel>
Aquí está la configuración de mi cliente:
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="wsHttpEndpoint" closeTimeout="00:01:00" openTimeout="00:01:00"
receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false"
transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Transport">
<transport clientCredentialType="Certificate" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="https://localhost:8371/Design_Time_Addresses/Server/CXPClient/"
binding="wsHttpBinding" bindingConfiguration="wsHttpEndpoint" behaviorConfiguration="ClientCertificateBehavior"
contract="ContentXPertServer.ICXPClient" name="wsHttpEndpoint" />
</client>
<behaviors>
<endpointBehaviors>
<behavior name="ClientCertificateBehavior">
<clientCredentials>
<clientCertificate x509FindType="FindBySubjectName" findValue="CyberdyneIndustries" storeLocation="LocalMachine" storeName="TrustedPeople" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
El código funciona perfectamente cuando el modo de seguridad = ''Ninguno'' sobre http, pero por supuesto, no hay autenticación, y nada en System.ServiceModel.ServiceSecurityContext. He intentado docenas de variaciones en todos estos elementos, y todo termina inevitablemente con la solicitud lanzando una excepción "Una conexión existente fue cerrada a la fuerza por el host remoto".
Estoy usando un certificado autofirmado "CyberdyneIndustries", cuyo certificado CA he agregado a la tienda CA de confianza. El certificado se retira cuando lo veo. He pasado por el infierno de la administración del espacio de nombres http y también he resuelto esos problemas. Simplemente parece que WCF realmente no es compatible con esto ... por favor dime que estoy equivocado.
TIA.
En última instancia, decidí probar la seguridad de los mensajes, para ver si eso arrojaría algo de luz sobre la situación; así fue, y voy a reducir mis pérdidas y seguir con eso. Entonces, no hay una respuesta definitiva a esto.
La implementación de la seguridad de los mensajes, sin embargo, expone un GRAN problema, y esta puede haber sido la raíz del problema de seguridad del transporte. Hay una pieza de documentación envenenada de MSDN:
http://msdn.microsoft.com/en-us/library/ff650751.aspx
En esta página, el comando para crear el certificado autofirmado es el siguiente:
makecert -sk MyKeyName -iv RootCaClientTest.pvk -n "CN = tempClientcert" -ic RootCaClientTest.cer -sr actualuser -ss my -sky signature -pe
El argumento "firma" debería ser "intercambio". Una vez que regeneré todos mis certs, la seguridad de los mensajes comenzó a funcionar. Una gran conclusión de todo esto es que si desea implementar la seguridad del transporte, la seguridad de los mensajes funciona primero, porque los mensajes de error que obtiene del sistema son mucho más descriptivos.
WsHttpBinding DOES admite la autenticación de certificados para la seguridad del transporte.
Puede haber algunas cosas mal:
¿Agregaste ambos certificados a tu tienda? ¿Ciberdyne Industries también una CA que usaste para firmarlo? CA debe estar en "Autoridades de certificación de raíz de confianza"
Además, he hecho esto alojado automáticamente, nunca en el servidor Visual Studio Dev. Intente alojar su servicio en IIS al menos. No estoy seguro de si el servidor VS Dev admite certificados.
Intente desactivar la autenticación del servicio. Entonces el cliente no tiene que autenticar el servicio. No sé si quieres esto en tu aplicación o no, solo para probarlo, así podemos descartarlo
<behavior name="ClientCertificateBehavior"> <clientCredentials> <clientCertificate x509FindType="FindBySubjectName" findValue="CyberdyneIndustries" storeLocation="LocalMachine" storeName="TrustedPeople" /> <serviceCertificate> <authentication certificateValidationMode="None"/> </serviceCertificate> </clientCredentials>
Sé que esto tiene 3 años, pero para aquellos que aún podrían estar interesados ...
Estoy en el proceso de aprender WCF (seguridad, entre otras cosas) y pude hacer que las cosas funcionen correctamente con netTcpBinding (presumiblemente, esto también funcionará para WsHttpBindings) usando el modo de seguridad de transporte con un clientCredentialType = "Certificate" (y, protectionLevel = "EncryptAndSign", aunque eso no estaba relacionado con el problema).
Encontré el error de cierre de conexión forzada desde el lado del servidor también, pero descubrí que me faltaba una configuración. Todo está funcionando ahora.
Aquí está mi configuración del lado del servidor:
<configuration>
<system.serviceModel>
<services>
<service name="MyNamespace.MyService" behaviorConfiguration="MyServiceBehavior">
<endpoint address="net.tcp://localhost:9002/MyServer" binding="netTcpBinding" bindingConfiguration="TcpCertSecurity" contract="MyNamespace.IMyService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceCredentials>
<serviceCertificate findValue="MyServiceCert" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectName" />
<clientCertificate>
<authentication certificateValidationMode="PeerTrust"/>
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<netTcpBinding>
<binding name="TcpCertSecurity">
<security mode="Transport">
<transport clientCredentialType="Certificate" protectionLevel="EncryptAndSign" />
</security>
</binding>
</netTcpBinding>
</bindings>
</system.serviceModel>
</configuration>
Y mi configuración del lado del cliente:
<configuration>
<system.serviceModel>
<client>
<endpoint address="net.tcp://localhost:9002/MyServer" binding="netTcpBinding"
bindingConfiguration="TcpCertSecurity" contract="MyNamespace.IMyService"
behaviorConfiguration="MyServiceBehavior">
<identity>
<dns value="MyServiceCert" />
</identity>
</endpoint>
</client>
<bindings>
<netTcpBinding>
<binding name="TcpCertSecurity">
<security mode="Transport">
<transport clientCredentialType="Certificate" protectionLevel="EncryptAndSign" />
</security>
</binding>
</netTcpBinding>
</bindings>
<behaviors>
<endpointBehaviors>
<behavior name="MyServiceBehavior">
<clientCredentials>
<serviceCertificate>
<authentication certificateValidationMode="PeerTrust" />
</serviceCertificate>
<clientCertificate findValue="MyServiceCert" storeLocation="LocalMachine" storeName="TrustedPeople" x509FindType="FindBySubjectName" />
</clientCredentials>
</behavior>
</endpointBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
Creé una cadena de certificados para el servidor (certificado de raíz de confianza autofirmado + un certificado creado usando esa raíz) usando la técnica descrita aquí y almacené tanto el certificado raíz como el certificado hijo en el almacén de certificados de la máquina host de mi servidor. Y, finalmente, importé ese certificado de servidor + clave pública en la tienda de certificaciones en mi equipo host cliente (en LocalMachine / TrustedPeople).