2011-09-07 4 views
19

Quiero verificar un certificado SSL en Win32 usando C++. Creo que quiero usar la API Cert * para que pueda obtener el beneficio de la tienda de certificados de Windows. Esto es lo que se me ocurrió.¿Cuál es la forma correcta de verificar un certificado SSL en Win32?

  • ¿Es correcto?
  • ¿Hay una manera mejor de hacer esto?
  • ¿Estoy haciendo algo mal?
bool IsValidSSLCertificate(PCCERT_CONTEXT certificate, LPWSTR serverName) 
{ 
    LPTSTR usages[] = { szOID_PKIX_KP_SERVER_AUTH }; 

    CERT_CHAIN_PARA params       = { sizeof(params) }; 
    params.RequestedUsage.dwType      = USAGE_MATCH_TYPE_AND; 
    params.RequestedUsage.Usage.cUsageIdentifier  = _countof(usages); 
    params.RequestedUsage.Usage.rgpszUsageIdentifier = usages; 

    PCCERT_CHAIN_CONTEXT chainContext = 0; 

    if (!CertGetCertificateChain(NULL, 
            certificate, 
            NULL, 
            NULL, 
            &params, 
            CERT_CHAIN_REVOCATION_CHECK_CHAIN, 
            NULL, 
            &chainContext)) 
    { 
     return false; 
    } 

    SSL_EXTRA_CERT_CHAIN_POLICY_PARA sslPolicy = { sizeof(sslPolicy) }; 
    sslPolicy.dwAuthType      = AUTHTYPE_SERVER; 
    sslPolicy.pwszServerName     = serverName; 

    CERT_CHAIN_POLICY_PARA policy = { sizeof(policy) }; 
    policy.pvExtraPolicyPara  = &sslPolicy; 

    CERT_CHAIN_POLICY_STATUS status = { sizeof(status) }; 

    BOOL verified = CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL, 
                 chainContext, 
                 &policy, 
                 &status); 

    CertFreeCertificateChain(chainContext); 
    return verified && status.dwError == 0; 
} 
+0

No mencionaste para qué estás utilizando esto, pero sí, en general, debes aprobar CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT si estás utilizando esto en un escenario HTTPS típico. – EricLaw

+0

Principalmente quiero usar esto para verificar los certificados SSL del servidor LDAP (como, dentro de una función VERIFYSERVERCERT). También estoy pensando en usarlo para verificar certificados de servidor HTTPS en una aplicación cliente/servidor donde los clientes pueden especificar su propio certificado SSL para el servidor. – briangreenery

+0

¿Es más común usar CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT en lugar de CERT_CHAIN_REVOCATION_CHECK_CHAIN? ¿Por qué no verificaría el certificado raíz para las revocaciones? – briangreenery

Respuesta

1

Creo que la mejor respuesta depende de qué es exactamente lo que está intentando hacer.

Le advierto que SSL se basa en el supuesto de que Ambos puntos finales desean una conexión segura. Si alguno de los puntos finales no está interesado en mantener la seguridad, entonces no hay ninguno.

Es un esfuerzo trivial poner códigos de bytes en su código distribuido que simplemente devuelve verdadero para esta función. Es por eso que Windows movió una gran cantidad de validación en el kernel. Pero no anticiparon que las personas ejecuten Windows en el hardware virtual, lo que hace que eludir el sistema operativo sea tan trivial.

Considere que espera recibir un certificado de alguna fuente, pero pretendiendo que esa fuente no pudo ser proporcionada con la misma información de una fuente confiable. Y luego dáselo a ti. Por lo tanto, no puede confiar en certificados para "probar" que alguien es alguien en particular.

La única protección que se obtiene de los certificados es evitar que los intrusos, no los puntos finales, infrinjan la confidencialidad del mensaje que se transporta.

Cualquier otro uso está condenado a fallar, y eventualmente fallará con resultados potencialmente catastróficos.

Perdón por la gran publicación. La sección de comentarios tiene un límite de palabras.

+2

Gracias por esta respuesta, aunque la información contenida es válida, tenía más la esperanza de que alguien pudiera verificar que el código descrito arriba es correcto en el contexto de la verificación de certificados para el uso de SSL utilizando la API crypto de Windows. Soy consciente de las limitaciones de SSL en estos aspectos, pero aquí solo estoy realmente preocupado por hacer que mi endpoint sea absolutamente correcto. Por contexto, he estado trabajando en [este parche] (https://github.com/php/php-src/pull/601) para el núcleo de PHP, por lo que no nos preocupa una aplicación específica, solo que las herramientas que proporcionamos son correctas. – DaveRandom

14

Debe tener en cuenta RFC3280 section 6.1 y RFC5280 section 6.1. Ambos describen algoritmos para validar rutas de certificado. A pesar de que Win32 API se encarga de algunas cosas para usted, aún podría ser valioso saber sobre el proceso en general.

Además, aquí hay una (en mi opinión) referencia bastante confiable: Chromium certificate verification code.

En general, creo que su código no es incorrecto. Pero aquí hay algunas cosas que me gustaría mirar en/cambio, si yo fuera usted:

1. Separar Nombre común de validación

cromo valida nombre común del certificado por separado de la cadena. Aparentemente han notado algunos problemas con eso. Ver los comentarios de su razón de ser:

cert_verify_proc.win.cc:731 // Certificate name validation happens separately, later, using an internal 
cert_verify_proc.win.cc:732 // routine that has better support for RFC 6125 name matching. 

2. Uso CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT

cromo también utiliza el indicador CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT en lugar de CERT_CHAIN_REVOCATION_CHECK_CHAIN. De hecho, comencé a investigar esto antes de encontrar su código, y reforzaba mi creencia de que debería usar CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT.

Aunque ambos RFC antes mencionados especifican que un delimitador de confianza autofirmado no se considera parte de una cadena, la documentación para CertGetCertificateChain (http://msdn.microsoft.com/en-us/library/windows/desktop/aa376078(v=vs.85).aspx) dice que crea una cadena de hasta, si es posible, un certificado raíz de confianza. Un certificado raíz confiable se define (en la misma página) como un certificado autofirmado de confianza.

Esto elimina la posibilidad de que * EXCLUDE_ROOT saltee la comprobación de revocación de un ancla de confianza no raíz (Win32 realmente requiere que los enlaces de confianza sean autofirmados, aunque no lo exija ningún RFC. Aunque esto no es oficialmente documentado).

Ahora, dado que un certificado de CA raíz no puede revocarse a sí mismo (no se pudo firmar/verificar la CRL), me parece que estos dos indicadores son idénticos.

Hice algunos googleando y tropecé con esta publicación en el foro: http://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/9f95882a-1a68-477a-80ee-0a7e3c7ae5cf/x509revocationflag-question?forum=windowssecurity. Un miembro de .NET Product Group (supuestamente) afirma que las banderas en la práctica actúan de la misma manera, si la raíz está autofirmada (en teoría, el indicador ENTIRE_CHAIN ​​verificaría la revocación del certificado raíz si incluía una extensión CDP, pero no puede suceder).

También recomienda utilizar el indicador * EXCLUDE_ROOT, porque el otro indicador podría causar una solicitud de red innecesaria, si la CA raíz con firma automática incluye la extensión CDP.

Desafortunadamente:

  • no puedo encontrar ninguna explicación documentada oficialmente en las diferencias entre las dos banderas.
  • Aunque es probable que la discusión vinculada se aplique a los mismos indicadores de API Win32 bajo el capó de .NET, no está garantizada.

Para estar completamente seguro de que es aceptable utilizar CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT, busqué en Google un poco más y encontré el código de verificación del certificado SSL cromo he vinculado en la parte superior de mi respuesta.

Como un beneficio adicional, el archivo cert_verify_proc_win.cc cromo contiene las siguientes pistas acerca de IE código de verificación:

618: // IE passes a non-NULL pTime argument that specifies the current system 
619: // time. IE passes CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT as the 
620: // chain_flags argument. 

No estoy seguro de cómo habían saben esto, pero en este momento me sentiría cómodo usando CERT_CHAIN_REVOCATION_CHECK_EXCLUDE_ROOT.

3. Certificado Aceptado diferentes usos

me di cromo también especifica 3 usos de certificados en vez de 1:

szOID_PKIX_KP_SERVER_AUTH, 
szOID_SERVER_GATED_CRYPTO, 
szOID_SGC_NETSCAPE 

De lo que he entendido a través de Google, los otros usos pueden ser requeridos por web más antiguo navegadores, de lo contrario, pueden no establecer una conexión segura.

Si Chromium considera adecuado incluir estos usos, lo seguiría.

Tenga en cuenta que si cambia su código, también debe establecer params.RequestedUsage.dwType a USAGE_MATCH_TYPE_OR en lugar de USAGE_MATCH_TYPE_AND.

-

No puedo pensar en cualquier otro comentario por el momento.Pero si yo fuera tú, verificaría la fuente Chromium (y tal vez también Firefox), solo para asegurarme de que no me he perdido nada.

-1

Las funciones CertGetCertificateChain y CertVerifyCertificatePolicy van juntas. Esta parte es correcta

Para CertGetCertificateChain la bandera se puede ajustar a cualquiera de los siguientes tres si desea comprobar la revocación:

  • CERT_CHAIN_REVOCATION_CHECK_END_CERT
  • CERT_CHAIN_REVOCATION_CHECK_CHAIN ​​
  • CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT.

Solo se puede utilizar una de ellas, estas tres opciones no pueden ser ORed. Al lado de uno de estos indicadores, puede considerar cómo se debe crear la cadena; usando local cache o solo CRL o OCSP. Para estas consideraciones read this link.

Error al ejecutar la función o más simplemente si el valor devuelto es 0, no significa que el certificado no es válido, sino que no pudo realizar la operación. Para obtener información de error, use GetLastError(). Por lo tanto, su lógica de devolver falso es incorrecta, se trata más bien de arrojar el error y dejar que el código del cliente decida si volver a intentarlo o seguir haciendo otras cosas.

En this link hay una sección llamada "clasificar el error", por favor lea eso. Básicamente debe verificar certChainContext->TrustStatus.dwErrorStatus. Here a list of error statuses will be ORed. Please check CERT_TRUST_STATUS referencia msdn. Entonces aquí puedes tener tu lógica de negocios. Por ejemplo, si encuentra el estado de error del valor (CERT_TRUST_REVOCATION_STATUS_UNKNOWN | CERT_TRUST_IS_OFFLINE_REVOCATION) de que no se pudo realizar la verificación de revocación de certificados, tiene la opción de decidir qué desea (deje ir el certificado o aún marque como no válido).

Por lo tanto, antes de llamar al CertVerifyCertificatePolicy, tiene la opción de descartar o ya marcar un error de validación.

Si elige venir al CertVerifyCertificatePolicy, el código de cromo es una referencia maravillosa sobre cómo asignar policy_status.dwError a su clase de error/enum.

Cuestiones relacionadas