consumir - ¿La aplicación.NET no puede enviar el certificado del cliente-Win 7 vs Win XP?
soap ssl (3)
Esto resultó ser un problema bastante simple, pero era difícil de detectar. El servidor remoto en el 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 Hello" y comparé los mensajes enviados desde los servidores remotos. El "buen" servidor remoto estaba enviando mi certificado de cliente y también su certificado raíz en la parte de "Solicitud de certificado" de ese mensaje. El servidor remoto "malo" solo estaba enviando mi certificado de cliente.
Esto me recordó que el rastreo de diagnóstico de System.Net decía "El servidor ha especificado 6 emisores (s) ... Ha dejado con 0 certificados de cliente para elegir". Así que resulta que "emisor" es el término clave aquí. Al principio, fue fácil pasarlo por alto porque en mi análisis inicial del protocolo de enlace TLS, el servidor estaba enviando mi certificado de cliente en la Solicitud de certificado. En retrospectiva, tiene sentido que el servidor deba enviar su certificado raíz, no el certificado de su propio cliente.
Estoy desarrollando una aplicación web ASP.NET que envía una solicitud a otro servidor utilizando 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. Puedo conectarme y enviar el certificado del cliente con éxito si simplemente visito la url con un navegador web (Chrome específicamente).
El código a continuación 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) {
...
}
Obtengo nuestra excepción favorita, "No se pudo crear el canal seguro SSL / TLS". Normalmente, estos tipos de problemas apuntan a problemas con los permisos en la clave privada de su certificado. Intenté todo lo que podía pensar para garantizar que todo estuviera configurado correctamente, pero tal vez me perdí algo. En pocas palabras, 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.
He aquí mi arreglo:
- Windows 7 profesional de 64 bits
- Capaz de reproducir el problema en una aplicación ASP.NET MVC 3 / .NET 4 que se ejecuta en el servidor de desarrollo de Visual Studio, una aplicación ASP.NET WebForms / .NET 3.5 que se ejecuta en IIS local y en una aplicación de consola .NET / .NET 4
- Microsoft .NET Framework 4.5 se instaló recientemente. Aún no he examinado si esto podría ser un problema.
Aquí está todo lo que he probado, y lo que sé:
- Este código parece funcionar bien cuando se ejecuta en una máquina con Windows XP
- Me aseguré de que mi certificado de cliente se importara a la computadora local, al almacén de certificados personales, con los permisos de la clave privada configurados correctamente para mí y para todos los usuarios relevantes de IIS
- Intenté reinstalar el certificado en mi máquina un par de veces
- He confirmado que la aplicación .NET puede acceder al certificado
HasPrivateKey
yHasPrivateKey
= true - Asegurado que mi certificado de cliente es válido. En realidad, es un certificado SSL para el servidor web en el que se ejecutará esta aplicación
- Establecí
PreAuthenticate = true
en el objeto de solicitud. No hizo una diferencia - Intenté configurar
ServicePointManager.Expect100Continue = false
, no hice una diferencia - Intenté configurar
ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3
, pero aparentemente el servidor remoto requiere TLS, por lo que no ayuda. - Configuré un delegado de
ServicePointManager.ServerCertificateValidationCallback
para que siempre devuelva true. Pero la solicitud falla antes en el protocolo de enlace TLS, antes de que llegue al punto de llamar a este 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 recibo una respuesta válida. También funciona correctamente cuando construyo una solicitud en Fiddler. Así que definitivamente parece ser un problema específico con mi código .NET, no con el certificado en sí, ni con 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 intenté lo mismo con las diferentes URL y certificados de clientes y obtuve el mismo error.
Agregué el rastro de System.Net y vi esto:
SecureChannel # 26717201 - Tenemos certificados proporcionados por el usuario. El servidor ha especificado 6 emisor (es). Buscando certificados que coincidan con alguno de los emisores.
SecureChannel # 26717201 - Se dejó con 0 certificados de cliente para elegir.
...
InitializeSecurityContext (Recuento de buffers de entrada = 2, Longitud de Buffer de salida = 0, código devuelto = CertUnknown).
Permití el registro completo de SCHANNEL y vi esta advertencia:
El servidor remoto ha solicitado la autenticación de cliente SSL, pero no se pudo encontrar un certificado de cliente adecuado. Se intentará una conexión anónima. Esta solicitud de conexión SSL puede tener éxito o fallar, según la configuración de la política del servidor.
Ejecuté Wireshark y vi que el servidor remoto envió una CertificateRequest
, y parece que tiene una entrada de Distinguished Name
con los valores CN / OU / O de mi certificado de cliente especificados exactamente. 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 que funcione correctamente con las aplicaciones .NET en Windows 7. Mi mejor estimación es que hay algo diferente en mi nueva máquina con Windows 7, en comparación con la máquina con XP, que está causando que esto suceda. falla 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 realmente me gustaría resolver esto lo antes posible.
Cualquier idea será altamente apreciada. ¡Gracias!
EDITAR Como se describió anteriormente, cuando me conecto 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 hacer esto con éxito en Internet Explorer (9). Simplemente obtengo "Internet Explorer no puede mostrar la página web", sin otras indicaciones o 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.
EDITAR También, 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 confiable en MMC para que el certificado aparezca como válido en mi máquina. Esto no solucionó el problema.
Me gustaría añadir 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 pedirle al servidor que no envíe la lista. (Al editar un valor de registro)
HKEY_LOCAL_MACHINE / SYSTEM / CurrentControlSet / Control / SecurityProviders / SCHANNEL
Nombre del valor: SendTrustedIssuerList Tipo de valor: REG_DWORD Datos del valor: 0 (Falso)
Para mí, estos síntomas exactos fueron causados por el uso de TLS 1.0, que el servidor no estaba permitiendo. Esto se puede encontrar en los registros de seguimiento como tal:
System.Net Information: 0: ProcessAuthentication (Protocolo = Tls , Cipher = TripleDes 168 bits de fuerza, Hash = Sha1 160 bits de fuerza, Key Exchange = RsaKeyX 2048 bits de fuerza).
TLS 1.0 es el valor predeterminado para .NET 4.5, pero se puede anular configurando:
ServicePointManager.SecurityProtocol =
SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
También hay algunos indicadores de registro que permiten configurar esto sin cambiar el código existente.