ValidationResultCode Enumeration

Namespace: System.Net.Security

Indicates the result of a certificate validation operation.

Syntax

public enum ValidationResultCode

Members

The ValidationResultCode enumeration contains the following members:

Member Description
Success The certificate validation succeeded.
InvalidChain The certificate chain is invalid. This could be due to a missing intermediate certificate or a self-signed root certificate that is not trusted.
Expired The certificate has expired.
InvalidPurpose The certificate is not valid for the intended purpose (e.g., it's an email certificate being used for SSL/TLS).
Revoked The certificate has been revoked by the issuing Certificate Authority.
UntrustedRoot The certificate chain does not end with a trusted root certificate.
NotTimeValid The certificate is not currently valid within its validity period (either not yet valid or expired).
UnknownError An unspecified error occurred during certificate validation.
InvalidCertificateUsage The certificate's usage is invalid for the requested operation.
PartialChain The certificate chain is incomplete.
BadSignature The certificate's signature is invalid.
InvalidName The certificate's subject name does not match the host name.

Remarks

The ValidationResultCode enumeration is used by the SslCertValidationCallback delegate to communicate the outcome of a certificate validation process. When a certificate needs to be validated, the .NET runtime invokes this callback, providing a X509Certificate2 object and the results of the validation, including a ValidationResultCode.

Developers can implement custom certificate validation logic by providing a delegate for SslCertValidationCallback. This allows for more granular control over which certificates are trusted in different scenarios.

Examples

The following example shows how to use the ValidationResultCode within a custom certificate validation callback.

using System;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

public class CertificateValidator
{
    public static void SetupValidation()
    {
        // For HttpClient
        var httpClientHandler = new HttpClientHandler();
        httpClientHandler.ServerCertificateCustomValidationCallback = ValidateServerCertificate;
        using (var client = new HttpClient(httpClientHandler))
        {
            // Use the client to make requests
        }

        // For ServicePointManager (older APIs)
        ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
    }

    private static bool ValidateServerCertificate(
        object sender,
        X509Certificate? certificate,
        X509Chain? chain,
        SslPolicyErrors sslPolicyErrors)
    {
        Console.WriteLine($"SSL Policy Errors: {sslPolicyErrors}");

        if (certificate == null || chain == null)
        {
            Console.WriteLine("Certificate or chain is null.");
            return false;
        }

        // Get the validation result code from the chain
        // Note: In newer .NET versions, you might get more specific results directly
        // from sslPolicyErrors or by inspecting the chain.elements.
        // For demonstration, let's simulate checking common issues.

        // A simple check: if there are no policy errors, it's usually good.
        if (sslPolicyErrors == SslPolicyErrors.None)
        {
            Console.WriteLine("Certificate validation succeeded (no policy errors).");
            return true;
        }

        // Manually inspect chain status for more details if needed
        foreach (var status in chain.ChainStatus)
        {
            Console.WriteLine($"Chain Status Error: {status.Status} - {status.StatusInformation}");

            // Here you would map chain status information to ValidationResultCode
            // For a true mapping, you'd need to parse status.Status flags.
            // This is a simplified example.
            switch (status.Status)
            {
                case X509ChainStatusFlags.NotTimeValid:
                    Console.WriteLine("Validation Result: NotTimeValid");
                    break;
                case X509ChainStatusFlags.Expired:
                    Console.WriteLine("Validation Result: Expired");
                    break;
                case X509ChainStatusFlags.Revoked:
                    Console.WriteLine("Validation Result: Revoked");
                    break;
                case X509ChainStatusFlags.UntrustedRoot:
                    Console.WriteLine("Validation Result: UntrustedRoot");
                    break;
                case X509ChainStatusFlags.InvalidCertificateAuthority:
                    Console.WriteLine("Validation Result: InvalidChain (or UntrustedRoot)");
                    break;
                case X509ChainStatusFlags.CyclicName:
                case X509ChainStatusFlags.InvalidNameConstraints:
                case X509ChainStatusFlags.InvalidPolicy:
                case X509ChainStatusFlags.InvalidUsage:
                    Console.WriteLine("Validation Result: InvalidCertificateUsage / InvalidPurpose");
                    break;
                case X509ChainStatusFlags.PartialChain:
                     Console.WriteLine("Validation Result: PartialChain");
                     break;
                 case X509ChainStatusFlags.BadSignature:
                     Console.WriteLine("Validation Result: BadSignature");
                     break;
                 case X509ChainStatusFlags.NameMismatch:
                     Console.WriteLine("Validation Result: InvalidName");
                     break;
                // Add more mappings as needed
                default:
                    Console.WriteLine("Validation Result: UnknownError (or other)");
                    break;
            }
        }

        // In a real application, you would decide based on the errors whether to allow the connection.
        // For this example, we'll allow it if the specific errors are ones we've decided to ignore,
        // otherwise deny. For simplicity, let's deny if there are any errors not explicitly handled.
        // A common scenario is ignoring untrusted root for internal testing.

        bool allowConnection = true; // Default to allow, or deny based on your policy

        if (sslPolicyErrors != SslPolicyErrors.None)
        {
            Console.WriteLine("Certificate validation encountered errors. Decide whether to proceed.");
            // Example: Allow if only untrusted root, otherwise deny
            // if (!sslPolicyErrors.HasFlag(SslPolicyErrors.UntrustedRoot))
            // {
            //     allowConnection = false;
            // }
        }

        return allowConnection;
    }
}
Important: The ValidationResultCode itself is not directly returned by the standard ServerCertificateCustomValidationCallback. Instead, you typically inspect the SslPolicyErrors enum and the X509ChainStatus array within the callback to determine the validation status and map it to conceptual outcomes similar to those in ValidationResultCode. The ValidationResultCode is more of an internal representation or used in specific APIs that might abstract this process.

Requirements

Namespace: System.Net.Security

Assembly: System.Net.Primitives (in .NET Core and .NET 5+)

Assembly: System (in .NET Framework)

See Also