Schannel TLS Decrypt Error (Pure C)

Pablo Diez 0 Puntos de reputación
2024-12-15T16:13:50.1666667+00:00

Buenas, estoy desarrollando un programa con sockets en puro "C" y estoy mirando de cifrar las conexiones con TLS, estoy usando schannel, el handshake TLS esta completado, puedo ver desde WireShark los siguientes mensajes:

7983 1534.290037 192.168.131.34 192.168.131.34 TLSv1.2 191 Client Hello

7985 1534.290850 192.168.131.34 192.168.131.34 TLSv1.2 1292 Server Hello, Certificate, Server Key Exchange, Server Hello Done

7987 1534.293445 192.168.131.34 192.168.131.34 TLSv1.2 202 Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message

7989 1534.294099 192.168.131.34 192.168.131.34 TLSv1.2 95 Change Cipher Spec, Encrypted Handshake Message


Conozco que existen las funciones "EncryptMessage" y "DecryptMessage" que el servidor cifra con su hContext y el cliente descifra con su hContext configurado después del Handshake TLS.

He creado dos funciones que facilitan el envió y recibo de mensajes cifrados, en concreto tengo dos funciones "SendEncrypted" y "ReciveEncrypted". La funcion de SendEncrypted funciona perfecto y permite cifrar y enviar mensajes. Desde wireshark veo como lo detecta como application data.

El cliente lo recibe, puedo ver como llega la data, mostrarla en hexadecimal, pero a la hora de descifrar arroja el siguiente error

Encrypted data (hex): 17 03 03 00 1f 00 00 00 00 00 00 00 01 2d 15 8b ef 38 95 49 7a 3f 63 3f 09 7c f7 b7 44 2b 00 88 00 00 00 00
DecryptMessage failed with error code: 0x80090330 (SEC_E_DECRYPT_FAILURE)

Se ve como tiene los encabezados correctos (o eso creo)

17: Application data
03 03: Version del protocolo TLS (TLS 1.2)

Otra cosa que veo, con el siguiente codigo:

SecPkgContext_ConnectionInfo connInfo;

if (QueryContextAttributes(&hContext, SECPKG_ATTR_CONNECTION_INFO, &connInfo) == SEC_E_OK) {
printf("TLS protocol: %u\n", connInfo.dwProtocol); // Debe ser TLS 1.2 o 1.3
else {
    printf("Failed to query context attributes\n");
}   

Es que el servidor arroja 1024 y el cliente 2048, no se si esto puede ser un error, creo saber que se debe a las longitudes de las claves.

Cabe recalcar que se esta usando un certificado autofirmado para TLS con RSA 2048, he configurado el cliente para confiar en el certificado con lo siguiente y no creo que sea un problema ya que la funcion "InitializeSecurityContext" ya no me da error de SEC_E_UNTRUSTED_ROOT

schannelCred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION | SCH_CRED_IGNORE_NO_REVOCATION_CHECK | SCH_CRED_IGNORE_REVOCATION_OFFLINE | SCH_CRED_NO_SERVERNAME_CHECK;
  • Server: TLS protocol: 1024
  • Client: TLS protocol: 2048

El problema grave es el no poder descifrar los mensajes enviados, os dejo un código parcial de la funcion de como estoy descifrando el mensaje una vez recibido, no he conseguido pasar de "DecryptMEssage failed with error code: ..."

...
	char encryptedData[4096]; // Buffer para los datos cifrados
    SecBuffer InBuffers[4];
    SecBufferDesc InBuffer;

    // Recibir datos cifrados desde el servidor
    int bytesReceived = recv(conn, encryptedData, sizeof(encryptedData), 0);
    if (bytesReceived <= 0) {
        printf("recv failed or connection closed\n");
        return SOCKET_ERROR;
    }

    printf("Encrypted data (hex): ");
    for (int i = 0; i < bytesReceived; i++) {
        printf("%02x ", (unsigned char)encryptedData[i]);
    }
    printf("\n");
 
    InBuffers[0].pvBuffer = encryptedData;
    InBuffers[0].cbBuffer = bytesReceived;
    InBuffers[0].BufferType = SECBUFFER_DATA;

    InBuffers[1].BufferType = SECBUFFER_EMPTY;
    InBuffers[2].BufferType = SECBUFFER_EMPTY;
    InBuffers[3].BufferType = SECBUFFER_EMPTY;

    InBuffer.cBuffers = ARRAYSIZE(InBuffers);
    InBuffer.pBuffers = InBuffers;
    InBuffer.ulVersion = SECBUFFER_VERSION;

 
    SECURITY_STATUS secStatus = DecryptMessage(hContext, &InBuffer, 0, NULL);
    if (secStatus != SEC_E_OK) {
        printf("DecryptMessage failed with error code: 0x%x\n", secStatus); // No pasa de aquí
        if (secStatus == SEC_E_CONTEXT_EXPIRED) {
            printf("The context has expired. Re-handshake may be needed.\n");
        }
        return SOCKET_ERROR;
    }

Muchas gracias de antemano.

ASP.NET
ASP.NET
Conjunto de tecnologías de .NET Framework para la creación de aplicaciones y servicios web XML.
46 preguntas
0 comentarios No hay comentarios
{count} votos

3 respuestas

Ordenar por: Lo más útil
  1. Jonathan Pereira Castillo 10,430 Puntos de reputación Proveedor de Microsoft
    2024-12-17T18:04:54.4933333+00:00

    ¡Hola Pablo Diez!

    Bienvenido a Microsoft Q&A.

    Parece que estás enfrentando un problema común al trabajar con Schannel y TLS en C. Y a la vez bastante específico, déjame intentar ayudarte con la información general que podamos compartir en un foro público.

    Aquí hay algunas cosas que podrías verificar para solucionar el error SEC_E_DECRYPT_FAILURE:

    Verifica los encabezados y la longitud de los datos cifrados:

    • Asegúrate de que los datos cifrados que estás recibiendo tienen el formato correcto y la longitud esperada. Los encabezados que mencionas parecen correctos, pero verifica que los datos no estén truncados o corruptos.

    Alineación de los buffers:

    • Asegúrate de que los buffers que estás utilizando para DecryptMessage estén correctamente alineados y configurados. Los SecBuffer deben estar correctamente inicializados y no deben contener datos adicionales que puedan interferir con el proceso de descifrado.

    Revisa las versiones del protocolo TLS:

    • Aunque mencionas que el servidor y el cliente están usando TLS 1.2, verifica que ambos lados estén configurados para usar la misma versión del protocolo y que no haya discrepancias en las configuraciones de seguridad.

    Certificados y claves:

    • Dado que estás usando un certificado autofirmado, asegúrate de que el cliente confíe en el certificado del servidor. Aunque mencionas que InitializeSecurityContext no da error de SEC_E_UNTRUSTED_ROOT, verifica que no haya otros problemas con el certificado.

    Depuración adicional:

    • Agrega más mensajes de depuración para verificar el estado de los contextos de seguridad (hContext) y los atributos de conexión (SecPkgContext_ConnectionInfo). Esto puede ayudarte a identificar si hay algún problema con la configuración del contexto. Aquí tienes un ejemplo de cómo podrías ajustar tu código para agregar más mensajes de depuración y verificar los buffers:
    SecPkgContext_ConnectionInfo connInfo;
    if (QueryContextAttributes(&hContext, SECPKG_ATTR_CONNECTION_INFO, &connInfo) == SEC_E_OK) {
        printf("TLS protocol: %u\n", connInfo.dwProtocol); // Debe ser TLS 1.2 o 1.3
    } else {
        printf("Failed to query context attributes\n");
    }
    
    char encryptedData[4096]; // Buffer para los datos cifrados
    SecBuffer InBuffers[4];
    SecBufferDesc InBuffer;
    
    // Recibir datos cifrados desde el servidor
    int bytesReceived = recv(conn, encryptedData, sizeof(encryptedData), 0);
    if (bytesReceived <= 0) {
        printf("recv failed or connection closed\n");
        return SOCKET_ERROR;
    }
    
    printf("Encrypted data (hex): ");
    for (int i = 0; i < bytesReceived; i++) {
        printf("%02x ", (unsigned char)encryptedData[i]);
    }
    printf("\n");
    
    InBuffers[0].pvBuffer = encryptedData;
    InBuffers[0].cbBuffer = bytesReceived;
    InBuffers[0].BufferType = SECBUFFER_DATA;
    
    InBuffers[1].BufferType = SECBUFFER_EMPTY;
    InBuffers[2].BufferType = SECBUFFER_EMPTY;
    InBuffers[3].BufferType = SECBUFFER_EMPTY;
    
    InBuffer.cBuffers = ARRAYSIZE(InBuffers);
    InBuffer.pBuffers = InBuffers;
    InBuffer.ulVersion = SECBUFFER_VERSION;
    
    SECURITY_STATUS secStatus = DecryptMessage(hContext, &InBuffer, 0, NULL);
    if (secStatus != SEC_E_OK) {
        printf("DecryptMessage failed with error code: 0x%x\n", secStatus); // No pasa de aquí
        if (secStatus == SEC_E_CONTEXT_EXPIRED) {
            printf("The context has expired. Re-handshake may be needed.\n");
        }
    return SOCKET_ERROR;
    }
    
    // Si el descifrado es exitoso, procesa los datos descifrados aquí
    

    Espero que estos consejos ayuden a resolver el problema. Si necesitas más asistencia, estoy a tu disposición.

    Saludos,

    Jonathan.

    ----------*

    Tu opinión es muy importante para nosotros! Si esta respuesta resolvió tu consulta, por favor haz clic en ''. Esto nos ayuda a mejorar continuamente la calidad y relevancia de nuestras soluciones.

    0 comentarios No hay comentarios

  2. Eliminado

    Esta respuesta se ha eliminado debido a una infracción del Código de conducta. La respuesta se informó o identificó manualmente a través de la detección automatizada antes de tomar medidas. Consulte nuestro Código de conducta para obtener más información.


    Los comentarios se han desactivado. Más información

  3. Pablo Diez 0 Puntos de reputación
    2024-12-17T22:13:56.6233333+00:00

    Buenas de nuevo y gracias por su respuesta, siguiendo sus consejos:

    • Verifica los encabezados y la longitud de los datos cifrados:
      • En su momento añadí una breve depuración de los buffers (un simple printf de los buffers) y he añadido una pequeña lógica para ver la longitud del contenido cifrado, el header, el payload y el trailer. Para corroborar si los datos están corruptos, no tengo mucha idea de como hacerlo.
    Encrypted data length: 47
    Header size: 13, Payload size: 18, Trailer size: 16
    Buffer[0]: Type=1, Size=47
    Buffer[1]: Type=0, Size=0
    Buffer[2]: Type=0, Size=0
    Buffer[3]: Type=0, Size=0
    
    • Alineación de los buffers:
      • En el caso de los buffers, viendo la documentación oficial se que tienen que ser 4 (1 obligatorio y 3 opcionales) con uno que sea de tipo "SECBUFFER_DATA" y los demás que sean "SECBUFFER_EMPTY". Estan inicializados de la siguiente manera:
    InBuffers[0].pvBuffer = encryptedData;
    InBuffers[0].cbBuffer = bytesReceived;
    InBuffers[0].BufferType = SECBUFFER_DATA;
    
    for (int i = 1; i < 4; i++) {
        InBuffers[i].pvBuffer = NULL;
        InBuffers[i].cbBuffer = 0;
        InBuffers[i].BufferType = SECBUFFER_EMPTY;
    }
    
    • Revisa las versiones del protocolo TLS:
      • En este caso, sigo sin saber que esta pasando y por que el servidor muestra 1024 y el cliente 2048, yo diria que estan bien configurados, te facilito la configuración del cliente y del servidor:
    // Servidor 
    SCHANNEL_CRED schannelCred = {0};
    schannelCred.dwVersion = SCHANNEL_CRED_VERSION;
    schannelCred.cCreds = 1;
    schannelCred.paCred = &pCertContext;
    schannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_SERVER;
    schannelCred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION | SCH_CRED_IGNORE_NO_REVOCATION_CHECK |SCH_CRED_NO_SERVERNAME_CHECK;
    
    // Cliente
    
    SCHANNEL_CRED schannelCred = {0};
    schannelCred.dwVersion = SCHANNEL_CRED_VERSION;
    schannelCred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
    schannelCred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS | SCH_CRED_MANUAL_CRED_VALIDATION | SCH_CRED_IGNORE_NO_REVOCATION_CHECK | SCH_CRED_IGNORE_REVOCATION_OFFLINE | SCH_CRED_NO_SERVERNAME_CHECK;
    
    • Certificados y claves:
      • Podria confiarme en decir que el certificado no tiene que ser un problema, he probado de añadirlo a las "Entidades de certificación raiz de confianza" y sigue el mismo problema.
    • Depuración adicional:
      • He añadido alguna depuración aducional y lo unico que veo distinto entre servidor y cliente es el 1024 y el 2048...
    // Servidor
    Connection Info - Protocol: 1024, Cipher: 26128, Cipher Strength: 256
    Hash Algorithm: 32781, Hash Strength: 0
    Exchange Algorithm: 44550, Exchange Strength: 384
    Cipher Info - Protocol: 771, Cipher Suite: 49200
    Cipher: AES, Cipher Length: 256, Block Length: 16
    Hash: , Hash Length: 0
    Exchange: ECDH, Min Exchange Length: 0, Max Exchange Length: 65536
    Certificate: RSA, Key Type: 24
    DecryptMessage failed with error code: 0x80090330
    Error: No se puede descifrar los datos especificados.
    
    // Cliente
    Connection Info - Protocol: 2048, Cipher: 26128, Cipher Strength: 256
    Hash Algorithm: 32781, Hash Strength: 0
    Exchange Algorithm: 44550, Exchange Strength: 384
    Cipher Info - Protocol: 771, Cipher Suite: 49200
    Cipher: AES, Cipher Length: 256, Block Length: 16
    Hash: , Hash Length: 0
    Exchange: ECDH, Min Exchange Length: 0, Max Exchange Length: 65536
    Certificate: RSA, Key Type: 24
    

    A pesar de todo esto, sigue sin funcionar... Seguiré probando y documentándome...

    0 comentarios No hay comentarios

Su respuesta

Las respuestas pueden ser marcadas como Respuestas aceptadas por el autor de la pregunta, lo que indica a los usuarios que la respuesta resolvió su problema.