2012-03-25 16 views
16

Estoy desarrollando una aplicación web ASP.NET que envía una solicitud a otro servidor usando HttpWebRequest. Envía la solicitud a través de HTTPS y el servidor remoto requiere un certificado de cliente. La solicitud falla en la aplicación .NET, aparentemente no puede enviar el certificado de cliente correcto. I am capaz de conectar con éxito y enviar el certificado del cliente si simplemente visito la URL con un navegador web (específicamente Chrome).. La aplicación .NET no puede enviar el certificado del cliente: ¿gana 7 frente a Win XP?

El siguiente código es una reproducción simple, con solo una solicitud GET básica.

var r = WebRequest.Create(url) as HttpWebRequest; 
r.ClientCertificates = new X509CertificateCollection { myX509Cert }; 
using (var resp = r.GetResponse() as HttpWebResponse) { 
    ... 
} 

que tener en nuestras excepción favorito, "No se pudo crear un canal seguro SSL/TLS". Normalmente, este tipo de problemas apuntan a problemas con los permisos en la clave privada de su certificado. Intenté todo lo que pude pensar para asegurarme de que todo esté configurado correctamente, pero tal vez me perdí algo. Para abreviar, el servidor remoto está enviando un TLS CertificateRequest con una lista que parece identificar correctamente mi certificado de cliente, pero mi aplicación no responde con ningún certificado de cliente.

Aquí está mi configuración:

  • Windows 7 Professional de 64 bits
  • capaz de reproducir el problema en un 3/4 aplicación .NET ASP.NET MVC se ejecuta en el servidor dev Estudio Visual, ASP. Aplicación NET WebForms/.NET 3.5 ejecutándose en IIS local, y en una aplicación de consola .NET/.NET 4
  • Se ha instalado recientemente Microsoft .NET Framework 4.5. no han examinado aún si esto podría ser un problema

Aquí es todo lo que he intentado, y lo que sé:

  • Este código parecía funcionar bien cuando se ejecuta en una máquina Windows XP
  • me aseguró que mi certificado de cliente se importa en el equipo local, almacén de certificados personales, con los permisos de clave privada adecuadamente configurados para mí y para todos los usuarios de IIS pertinentes
  • que intentó volver a instalar el certificado en mi máquina un par de veces
  • He confirmado que la aplicación .NET puede acceder al certificado X509 y HasPrivateKey = verdadero
  • Aseguré que mi certificado de cliente sea válido. En realidad, es un certificado SSL para el servidor web en el que se ejecutará esta aplicación
  • Configuré PreAuthenticate = true en el objeto de solicitud. No hacer una diferencia
  • Intenté fijar ServicePointManager.Expect100Continue = false, no hacer una diferencia
  • Intenté fijar ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3, pero al parecer el servidor remoto requiere TLS de modo que no está ayudando
  • puse un delegado ServicePointManager.ServerCertificateValidationCallback para volver siempre es cierto. Pero la solicitud falla antes en el saludo de TLS, incluso antes de llamar al delegado
  • Cuando voy a la URL en Chrome, me pide que proporcione un certificado de cliente, presenta el certificado de cliente correcto como una opción , Elijo ese certificado y obtengo una respuesta válida. También funciona correctamente cuando construyo una solicitud en Fiddler. Así que definitivamente parece ser específicamente un problema con mi código .NET, no el certificado en sí, ni el servidor remoto, etc.
  • El proveedor con el que estoy trabajando tiene el mismo servicio configurado en un servidor remoto diferente, y ese servicio requiere un certificado de cliente diferente. Así que he intentado lo mismo con los diferentes URL y cert cliente y obtuve el mismo fracaso

añadí System.Net trace y vi esto:

SecureChannel # 26717201 - Tenemos certificados proporcionados por el usuario. El servidor ha especificado 6 emisor (es). Buscando certificados que coincidan con cualquiera de los emisores.

SecureChannel # 26717201 - Se fue con 0 certificados de cliente para elegir.

...

InitializeSecurityContext (A-buffers count = 2, fuera Tampón de longitud = 0, código devuelto = CertUnknown).

me permitió completa SCHANNEL logging, y vi esta advertencia:

El servidor remoto ha solicitado la autenticación de cliente SSL, pero sin certificado de cliente adecuado se puede conocer. Se intentará una conexión anónima. Esta solicitud de conexión SSL puede tener éxito o fallar, dependiendo de la configuración de la política del servidor.

me corrieron Wireshark, y vio el servidor remoto envía una CertificateRequest, y parece tener una entrada Distinguished Name con los valores O CN \ \ OU de mi certificado de cliente especificados con exactitud. Después de eso, mi aplicación envía una respuesta de certificado sin certificados.

Parece que me falta algo con la configuración de este certificado para funcionar correctamente con las aplicaciones .NET en Windows 7. Mi mejor opción es que hay algo diferente en mi nueva máquina con Windows 7, en comparación con la máquina XP, que está causando que esto falle ahora. No tengo acceso a un entorno de Windows XP en este momento para confirmar esto; Lo haré en un par de días, pero me gustaría resolverlo lo antes posible.

Cualquier idea sería muy apreciada. ¡Gracias!

EDIT Como se describió anteriormente, al conectarse a la URL en Chrome, el navegador me pide un certificado de cliente, y puedo proporcionar el certificado correcto y conectarme correctamente. Sin embargo, no puedo hacerlo con éxito en Internet Explorer (9). Simplemente me aparece "Internet Explorer no puede mostrar la página web", sin otras indicaciones ni explicaciones. Me dijeron que esto puede ser relevante ya que WebRequest.Create tiene un comportamiento similar al de IE. Estoy investigando lo que esto podría significar, pero valoraría cualquier idea al respecto.

EDIT Además, debo tener en cuenta que el servidor remoto utiliza un certificado SSL autofirmado. Originalmente pensé que ese podría ser el problema, así que agregué el certificado como una raíz de confianza en MMC para que el certificado parezca válido en mi máquina. Esto no solucionó el problema.

+1

¿Incluye el certificado la "Autenticación de cliente (1.3.6.1.5.5.7.3.2)" Extensión de uso de clave mejorada? De lo contrario, puede ser que Chrome esté siendo malo al permitir que se seleccione. –

+0

Resulta que el certificado del cliente no tiene esta extensión de Uso de clave mejorada. Estoy bastante seguro de que este código funcionaba correctamente en el pasado cuando apuntaba a una URL remota diferente y un certificado de cliente diferente, a pesar de que el certificado de cliente no tenía el uso de clave mejorada de autenticación de cliente. Esto me da un camino para investigar, gracias. –

+0

En realidad, Damien, me equivoqué; todos estos certificados incluyen la extensión de uso de clave mejorada de Autenticación de cliente. –

Respuesta

8

Esto resultó ser un problema bastante simple, pero fue difícil de detectar. El servidor remoto que mi aplicación tenía mi certificado de cliente en su almacén de claves, pero no ninguno de los certificados raíz en la cadena de confianza de mi certificado de cliente.

Pude usar mi código para enviar con éxito una solicitud a un servidor diferente que requería certificados de cliente.Tomé una captura en Wireshark mientras enviaba esta solicitud exitosa, y también tomé una captura mientras enviaba la solicitud fallida al otro servidor. En la captura de Wireshark, encontré el "Servidor Hola" y comparé los mensajes enviados desde los servidores remotos. El servidor remoto "bueno" enviaba mi certificado de cliente y también también su certificado raíz en la parte "Solicitud de certificado" de ese mensaje. El servidor remoto "malo" solo enviaba mi certificado de cliente.

Esto me recordó que el rastreo de diagnóstico de System.Net decía "El servidor ha especificado 6 emisor (es) ... Se ha dejado con 0 certificados de cliente para elegir". Entonces resulta que "emisor" es el término clave aquí. Simplemente fue fácil pasar por alto inicialmente porque en mi análisis inicial del protocolo de enlace TLS, el servidor enviaba mi certificado de cliente en la Solicitud de certificado. En retrospectiva, tiene sentido que el servidor envíe su certificado raíz, no el certificado del cliente.

+5

¿Puede ayudarnos a aclarar qué hizo para resolver este problema? estoy usando un certificado firmado y sólo tiene que utilizar el siguiente para validar el certificado: ServicePointManager.ServerCertificateValidationCallback = delegado (s objeto, certificado X509Certificate, cadena X509Chain, SslPolicyErrors sslPolicyErrors) {return true; }; Mi host se ha actualizado a .NET 4.5 y ahora mis servicios devuelven el siguiente error "No se pudo crear el canal SSL/TLS seguro" – Watson

+0

Esto resolvió mi problema también. Me llevó 2 semanas enteras resolver esto. El servidor remoto solo enviaba el certificado del cliente, sin incluir el certificado raíz CA de RSA. Una vez que leí esta respuesta y sugerí la solución para incluir el certificado raíz RSA CA en el "reconocimiento de solicitud de certificado", resolvió el problema. ¡¡¡Gracias!!! – vampire203

7

Me gustaría agregar otra "solución" al problema.

El servidor puede no enviar la lista correcta de emisores si esa lista es demasiado larga. Esto parece ser una limitación de Microsoft. http://support.microsoft.com/kb/933430

Fuente: http://netsekure.org/2011/04/tls-client-authentication-and-trusted-issuers-list/

La solución es que pedir el servidor para no enviar la lista en absoluto. (Al editar un valor de registro)

HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ SecurityProviders \ SCHANNEL

Nombre del valor: SendTrustedIssuerList tipo valor: REG_DWORD Información del valor: 0 (falso)

0

Para mí, estos síntomas exactos fueron causados ​​por el uso de TLS 1.0, que el servidor no permitía. Esto se puede encontrar en los registros de seguimiento como tal:

Información System.Net: 0: ProcessAuthentication (Protocolo = Tls, Cipher = TripleDES fuerza 168 bits, Hash = SHA1 fuerza de 160 bits, Key Cambio = RsaKeyX 2048 bit de resistencia).

TLS 1.0 es el valor predeterminado para .NET 4.5, pero puede ser anulado por el establecimiento:

ServicePointManager.SecurityProtocol = 
    SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; 

Hay también some registry flags que permiten establecer esta sin cambiar el código existente.

Cuestiones relacionadas