consume - wcf transport security
Autenticación SSL mutua con WCF: no CertificateRequest y CertificateVerify en la fase de handshake (1)
Estoy trabajando en un servicio WCF que debe ser consumido por un cliente que no ha sido desarrollado por mí y tampoco es .NET (posiblemente Java).
En cualquier caso, el servicio debería admitir la autenticación mutua SSL, en la que tanto el servicio como el cliente se autentican con los certificados X.509 certs en la capa de transporte. Los certificados han sido intercambiados entre las partes en un momento anterior.
Mi problema es que parece que no puedo obtener la configuración de WCF correcta para que la autenticación del certificado del cliente funcione correctamente. Lo que espero es que, como parte del handshake de TLS, el servidor también incluya una Certificate Request
, como se ve a continuación:
Después de esto, el cliente debe responder con un ''Certificado de verificación'' entre otras cosas:
La (última) configuración de servicio es esta. Estoy usando un enlace personalizado, con el modo de autenticación establecido en MutualSslNegotiated
.
<bindings>
<customBinding>
<binding name="CarShareSecureHttpBindingCustom">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="MutualSslNegotiated"/>
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>
</bindings>
...
<serviceBehaviors>
<behavior name="ServiceBehavior">
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" httpHelpPageEnabled="false" />
<serviceCredentials>
<serviceCertificate findValue="..." storeLocation="LocalMachine" x509FindType="FindByIssuerName" storeName="My" />
<clientCertificate>
<certificate findValue="..." storeName="My" storeLocation="LocalMachine" x509FindType="FindByIssuerName"/>
</clientCertificate>
</serviceCredentials>
</behavior>
</serviceBehaviors>
La parte Servidor Hola del apretón de manos se ve así para todas las configuraciones de servicio que he probado, sin CertificateRequest.
Otras cosas que debería mencionar:
- El servicio se aloja automáticamente y escucha en un puerto no predeterminado (no 443). El certificado SSL del servidor se ha vinculado a este puerto.
- También probé un
basicHttpBinding
y unwsHttpBidning
con el modo de seguridad establecido enTransport
y autenticación de cliente establecida enCertificate
, sin resultados (los mismos resultados en realidad).
Cualquier idea sería apreciada.
OK, después de algunos intentos más lo descubrí. Publicar esto en caso de que otros se encuentren con el mismo problema.
Debo continuar mencionando que este comportamiento realmente debe mencionarse en algún lugar en MSDN, en una ubicación que sea realmente visible para cualquiera que busque información de seguridad WCF y no esté profundamente enterrado en la documentación de alguna herramienta.
Las plataformas donde he podido reproducir y corregir esto: Windows 8.1 x64 y Windows Server 2008 R2 Standard.
Como mencioné, mi problema era que no podía configurar la seguridad de WCF de modo que el servicio requiriera certificados de cliente. Una confusión común que noté al buscar una solución es que muchas personas creen que el cliente puede enviar el certificado si lo tiene, sin ser cuestionado. Por supuesto, este no es el caso: el servidor debe solicitarlo primero y, además, especificar qué CA se permiten a través de una respuesta CertificateRequest
.
Para resumir, mi situación fue:
- El servicio es autohospedado.
- El servicio se ejecuta en HTTPS, en un puerto no estándar (no 443 sino 9000).
Esto significaba que tenía que crear un certificado SSL vinculante para el puerto 9000 mediante el uso de netsh.exe http add sslcert
. Bueno, el enlace se había creado, pero había una trampa. Solo encontré el problema después de ejecutar netsh http show sslcert
solo para verificar mi enlace:
IP:port : 0.0.0.0:9000
Certificate Hash : ...
Application ID : ...
Certificate Store Name : MY
Verify Client Certificate Revocation : Enabled
Verify Revocation Using Cached Client Certificate Only : Disabled
Usage Check : Enabled
Revocation Freshness Time : 0
URL Retrieval Timeout : 0
Ctl Identifier : (null)
Ctl Store Name : (null)
DS Mapper Usage : Disabled
-->Negotiate Client Certificate : Disabled
El culpable fue la última propiedad del enlace, "Negotiate Client Certificate" , documentado here . Aparentemente, de manera predeterminada, esta propiedad está deshabilitada. Debe habilitarlo explícitamente mientras crea el enlace.
Volver a crear el enlace con la siguiente declaración resolvió el problema:
netsh.exe http add sslcert ipport=0.0.0.0:9000 certhash=... appid=... certstorename=MY verifyclientcertrevocation=Enable VerifyRevocationWithCachedClientCertOnly=Disable UsageCheck=Enable clientcertnegotiation=Enable
Antes de verificar los enlaces intenté alojar un servicio WCF simple en IIS y habilitar la autenticación del certificado del cliente desde allí. Fue muy curioso ver que a pesar de que no hubo CertificateRequest
emitido por IIS, todavía falló con un 403.7
. Incluso IIS no creó el enlace con los parámetros apropiados.
De todos modos, ahora funciona y así es como puedes arreglarlo.
Sin olvidar, la configuración del servicio también cambió (la seguridad de enlace) para permitir la negociación de certificados:
<customBinding>
<binding name="CustomHttpBindingCustom" receiveTimeout="01:00:00">
<textMessageEncoding messageVersion="Soap11" />
<security authenticationMode="SecureConversation" requireSecurityContextCancellation="true">
<secureConversationBootstrap allowInsecureTransport="false" authenticationMode="MutualSslNegotiated" requireSecurityContextCancellation="true"></secureConversationBootstrap>
</security>
<httpsTransport requireClientCertificate="true" />
</binding>
</customBinding>