Procedimientos recomendados de TLS/SSL

TLS (Seguridad de la capa de transporte) es un protocolo criptográfico diseñado para proteger la comunicación entre dos equipos a través de Internet. El protocolo TLS se expone en .NET a través de la SslStream clase .

En este artículo se presentan los procedimientos recomendados para configurar la comunicación segura entre el cliente y el servidor y se supone el uso de .NET. Para conocer los procedimientos recomendados con .NET Framework, consulte Procedimientos recomendados de seguridad de la capa de transporte (TLS) con .NET Framework.

Selección de la versión de TLS

Aunque es posible especificar la versión del protocolo TLS que se usará a través de la EnabledSslProtocols propiedad , se recomienda aplazar la configuración del sistema operativo mediante None el valor (este es el valor predeterminado).

Aplazar la decisión del sistema operativo automáticamente usa la versión más reciente de TLS disponible y permite a la aplicación recoger los cambios después de las actualizaciones del sistema operativo. El sistema operativo también puede impedir el uso de versiones TLS que ya no se consideran seguras.

Selección de conjuntos de cifrado

SslStream permite a los usuarios especificar qué conjuntos de cifrado se pueden negociar mediante el protocolo de enlace TLS a través de la CipherSuitesPolicy clase . Al igual que con las versiones de TLS, se recomienda permitir que el sistema operativo decida cuáles son los mejores conjuntos de cifrado con los que negociar y, por lo tanto, se recomienda evitar el uso de CipherSuitesPolicy.

Nota:

CipherSuitesPolicy no se admite en Windows y los intentos de crear instancias de él producirán una excepción NotSupportedException.

Especificar un certificado local

Al autenticarse como servidor, SslStream siempre requiere un certificado. Al autenticarse como cliente, también se proporciona un certificado si el servidor solicita uno para TLS mutua (mTLS). En ambos roles, el certificado debe ser una X509Certificate2 instancia que contenga la clave privada.

Las versiones recientes de .NET controlan los lados del servidor y del cliente simétricamente, por lo que las instrucciones siguientes se aplican si la aplicación se autentica como servidor, cliente o ambos.

Puede proporcionar el certificado a SslStream de varias maneras.

Al autenticarse como servidor:

Al autenticarse como cliente:

Nota:

La ClientCertificateContext propiedad está disponible a partir de .NET 8.

Para mejorar el rendimiento, use la propiedad de contexto del certificado (ServerCertificateContext o ClientCertificateContext). Cuando proporciona el certificado de alguna de las otras maneras, SslStream crea un SslStreamCertificateContext internamente. La creación del contexto crea un X509Chain, que es una operación que consume mucha CPU, por lo que es más eficaz crear el contexto una vez y reutilizarlo en varias SslStream instancias.

La reutilización de una SslStreamCertificateContext instancia también permite características adicionales, como la reanudación de la sesión TLS en servidores Linux.

Enviar certificados intermedios al par

Cuando una entidad de certificación intermedia emite el certificado local, es posible que el mismo nivel no pueda compilar la cadena de certificados completa a menos que el protocolo de enlace incluya los certificados intermedios. Para enviar estos certificados intermedios, cree un SslStreamCertificateContext con el método Create y pase los certificados intermedios en el parámetro additionalCertificates:

X509Certificate2 leafCertificate = GetLeafCertificate();
X509Certificate2Collection intermediates = GetIntermediateCertificates();

SslStreamCertificateContext certificateContext =
    SslStreamCertificateContext.Create(leafCertificate, intermediates);

// When you authenticate as a server.
serverOptions.ServerCertificateContext = certificateContext;

// When you authenticate as a client for mutual TLS.
clientOptions.ClientCertificateContext = certificateContext;

El contexto funciona de la misma manera para ambos roles. En el lado cliente, el contexto del certificado es la manera recomendada de enviar intermedios, ya que la alternativa( agregar los intermedios al equipo o al almacén de certificados de usuario) afecta a todas las aplicaciones del sistema.

Validación personalizada X509Certificate

Hay ciertos escenarios en los que el procedimiento de validación de certificados predeterminado no es adecuado y se requiere cierta lógica de validación personalizada. Las partes de la lógica de validación se pueden personalizar especificando SslClientAuthenticationOptions.CertificateChainPolicy o SslServerAuthenticationOptions.CertificateChainPolicy. Como alternativa, se puede proporcionar lógica completamente personalizada a través de la <propiedad System.Net.Security.SslClientAuthenticationOptions.RemoteCertificateValidationCallback> . Para obtener más información, consulte Confianza de certificado personalizada.

Confianza de certificado personalizada

Al encontrar un certificado que no ha sido emitido por ninguna de las entidades de certificación de confianza del equipo (incluidos los certificados autofirmados), se producirá un error en el procedimiento de validación de certificados predeterminado. Una manera posible de resolver esto es agregar los certificados de emisor necesarios al almacén de confianza de la máquina. Sin embargo, esto podría afectar a otras aplicaciones del sistema y no siempre es posible.

La solución alternativa consiste en especificar certificados raíz de confianza personalizados a través de .X509ChainPolicy Para especificar una lista de confianza personalizada que se usará en lugar de la lista de confianza del sistema durante la validación, considere el ejemplo siguiente:

SslClientAuthenticationOptions clientOptions = new();

clientOptions.CertificateChainPolicy = new X509ChainPolicy()
{
    TrustMode = X509ChainTrustMode.CustomRootTrust,
    CustomTrustStore =
    {
        customIssuerCert
    }
};

Los clientes configurados con la directiva anterior solo aceptarían certificados en los que confíe customIssuerCert.

Omitir errores de validación específicos

Considere un dispositivo IoT sin un reloj persistente. Después de encenderse, el reloj del dispositivo comenzaría muchos años atrás y, por tanto, todos los certificados se considerarían "no válidos aún". Vea el siguiente código, que muestra una implementación de devolución de llamada de validación que omite las infracciones del período de validez.

static bool CustomCertificateValidationCallback(
    object sender,
    X509Certificate? certificate,
    X509Chain? chain,
    SslPolicyErrors sslPolicyErrors)
{
    // Anything that would have been accepted by default is OK
    if (sslPolicyErrors == SslPolicyErrors.None)
    {
        return true;
    }

    // If there is something wrong other than a chain processing error, don't trust it.
    if (sslPolicyErrors != SslPolicyErrors.RemoteCertificateChainErrors)
    {
        return false;
    }

    Debug.Assert(chain is not null);

    // If the reason for RemoteCertificateChainError is that the chain built empty, don't trust it.
    if (chain.ChainStatus.Length == 0)
    {
        return false;
    }

    foreach (X509ChainStatus status in chain.ChainStatus)
    {
        // If an error other than `NotTimeValid` (or `NoError`) is present, don't trust it.
        if ((status.Status & ~X509ChainStatusFlags.NotTimeValid) != X509ChainStatusFlags.NoError)
        {
            return false;
        }
    }

    return true;
}

Fijación de certificados

Otra situación en la que es necesaria la validación de certificados personalizada es cuando los clientes esperan que los servidores usen un certificado específico o un certificado de un pequeño conjunto de certificados conocidos. Esta práctica se conoce como anclaje de certificados. El fragmento de código siguiente muestra una devolución de llamada de validación que comprueba que el servidor presenta un certificado con una clave pública conocida específica.

static bool CustomCertificateValidationCallback(
    object sender,
    X509Certificate? certificate,
    X509Chain? chain,
    SslPolicyErrors sslPolicyErrors)
{
    // If there is something wrong other than a chain processing error, don't trust it.
    if ((sslPolicyErrors & ~SslPolicyErrors.RemoteCertificateChainErrors) != 0)
    {
        return false;
    }

    Debug.Assert(certificate is not null);

    const string ExpectedPublicKey =
        "3082010A0282010100C204ECF88CEE04C2B3D850D57058CC9318EB5C" +
        "A86849B022B5F9959EB12B2C763E6CC04B604C4CEAB2B4C00F80B6B0" +
        "F972C98602F95C415D132B7F71C44BBCE9942E5037A6671C618CF641" +
        "42C546D31687279F74EB0A9D11522621736C844C7955E4D16BE8063D" +
        "481552ADB328DBAAFF6EFF60954A776B39F124D131B6DD4DC0C4FC53" +
        "B96D42ADB57CFEAEF515D23348E72271C7C2147A6C28EA374ADFEA6C" +
        "B572B47E5AA216DC69B15744DB0A12ABDEC30F47745C4122E19AF91B" +
        "93E6AD2206292EB1BA491C0C279EA3FB8BF7407200AC9208D98C5784" +
        "538105CBE6FE6B5498402785C710BB7370EF6918410745557CF9643F" +
        "3D2CC3A97CEB931A4C86D1CA850203010001";

    return certificate.GetPublicKeyString().Equals(ExpectedPublicKey);
}

Consideraciones para la validación de certificados de cliente

Las aplicaciones de servidor deben tener cuidado al requerir y validar certificados de cliente. Los certificados pueden contener la extensión AIA (acceso a la información de la autoridad) que especifica dónde se puede descargar el certificado del emisor. Por lo tanto, el servidor puede intentar descargar el certificado del emisor desde un servidor externo cuando compila el X509Chain para el certificado de cliente. Del mismo modo, es posible que los servidores necesiten ponerse en contacto con servidores externos para asegurarse de que no se ha revocado el certificado de cliente.

La necesidad de ponerse en contacto con servidores externos al compilar y validar el X509Chain podría exponer la aplicación a ataques por denegación de servicio si los servidores externos son lentos para responder. Por tanto, las aplicaciones de servidor deben configurar el comportamiento de compilación de X509Chain con CertificateChainPolicy.

Nota:

A partir de .NET 11, SslStream deshabilita las descargas de certificados AIA de forma predeterminada al validar certificados de cliente como servidor. Si no se proporciona un CertificateChainPolicy personalizado, el servidor no intentará obtener los certificados intermedios faltantes mediante AIA. Para obtener más información, consulte las descargas de certificados AIA del lado del servidor SslStream que están deshabilitadas de forma predeterminada.