Represents information about a client's initial TLS/SSL handshake message (ClientHello).
Table of Contents
Properties
ProtocolType
Gets the protocol type of the client's connection.
TargetHost
Gets the target host name for the client's connection.
SslProtocol
Gets the SSL/TLS protocol version requested by the client.
CipherSuites
Gets the list of cipher suites supported by the client.
Extensions
Gets the list of TLS extensions sent by the client.
Remarks
The ClientHelloInfo class is used to inspect the details of an incoming TLS/SSL handshake from a client. This information can be valuable for security analysis, custom TLS server implementations, or for enforcing specific client requirements.
This class is typically accessed within event handlers for security-related operations, such as when customizing the behavior of an TcpClient or HttpClient.
Extensions may vary depending on the TLS version and client implementation.
Examples
C# Example: Inspecting ClientHelloInfo
This example demonstrates how to capture and inspect ClientHelloInfo in a custom TLS server scenario.
using System;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
public class CustomTlsServer
{
public static async Task StartServer(int port)
{
TcpListener listener = new TcpListener(IPAddress.Any, port);
listener.Start();
Console.WriteLine($"Server started on port {port}. Waiting for connections...");
while (true)
{
TcpClient client = await listener.AcceptTcpClientAsync();
_ = Task.Run(() => HandleClient(client));
}
}
private static void HandleClient(TcpClient tcpClient)
{
try
{
// Obtain the certificate for the server
X509Certificate serverCertificate = GetServerCertificate();
// Use SslStream to wrap the network stream and enable SSL/TLS
SslStream sslStream = new SslStream(
tcpClient.GetStream(),
false,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
new LocalCertificateSelectionCallback(SelectServerCertificate)
);
// Begin the SSL/TLS handshake.
// This is where ClientHelloInfo becomes available if we hook into it.
// For simplicity, we'll just initiate the handshake here.
// A more advanced scenario might involve intercepting the handshake process.
sslStream.AuthenticateAsServer(serverCertificate,
clientCertificateRequired: false,
enabledSslProtocols: System.Security.Authentication.SslProtocols.Tls12 | System.Security.Authentication.SslProtocols.Tls13,
checkCertificateRevocation: true);
// At this point, the handshake is complete.
// To directly access ClientHelloInfo during authentication,
// you'd typically need a lower-level interception mechanism or a
// custom SslStream implementation, which is more complex.
// However, some properties might be inferable or accessible post-authentication
// through different means depending on the framework version.
Console.WriteLine($"Client authenticated. SSL/TLS Protocol: {sslStream.SslProtocol}");
Console.WriteLine($"Cipher Suite: {sslStream.CipherSuite}");
// Example: Reading data from the client
byte[] buffer = new byte[1024];
int bytesRead = sslStream.Read(buffer, 0, buffer.Length);
string request = System.Text.Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.WriteLine($"Received: {request}");
// Send a response back to the client
byte[] responseBytes = System.Text.Encoding.ASCII.GetBytes("Hello from server!");
sslStream.Write(responseBytes, 0, responseBytes.Length);
sslStream.Close();
}
catch (Exception ex)
{
Console.WriteLine($"Error handling client: {ex.Message}");
}
finally
{
tcpClient.Close();
}
}
// Placeholder for getting the server certificate
private static X509Certificate GetServerCertificate()
{
// In a real application, you would load your server certificate here.
// For demonstration, we'll assume a self-signed certificate exists.
// Replace with your certificate loading logic.
try
{
// Example: Load from a PFX file
// return new X509Certificate2("server.pfx", "password");
// Example: Find a certificate in the local machine store
var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, "localhost", false);
if (certificates.Count > 0)
{
return certificates[0];
}
else
{
throw new Exception("Server certificate not found. Please create a self-signed certificate for 'localhost'.");
}
}
catch (Exception ex)
{
Console.WriteLine($"Error loading certificate: {ex.Message}");
throw;
}
}
// Callback to validate the client certificate (optional)
private static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: " + sslPolicyErrors);
return false; // Reject the connection if there are errors
}
// Callback to select the server certificate (if multiple are available)
private static X509Certificate SelectServerCertificate(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
{
// If no certificates were passed in, return null.
if (localCertificates == null || localCertificates.Count == 0)
return null;
// Use the first certificate in the collection.
// In a real-world scenario, you might have logic to select the best certificate.
return localCertificates[0];
}
// Example of how to start the server
// public static void Main(string[] args)
// {
// // Make sure you have a certificate for 'localhost' in your LocalMachine\My store
// // Or replace GetServerCertificate with your own logic to load a certificate.
// StartServer(8080).GetAwaiter().GetResult();
// }
}
VB.NET Example: Inspecting ClientHelloInfo
This example demonstrates how to capture and inspect ClientHelloInfo in a custom TLS server scenario.
Imports System
Imports System.Net
Imports System.Net.Security
Imports System.Net.Sockets
Imports System.Security.Cryptography.X509Certificates
Imports System.Threading.Tasks
Public Class CustomTlsServerVB
Public Shared Async Function StartServer(port As Integer) As Task
Dim listener As New TcpListener(IPAddress.Any, port)
listener.Start()
Console.WriteLine($"Server started on port {port}. Waiting for connections...")
While True
Dim client As TcpClient =Await listener.AcceptTcpClientAsync()
_ = Task.Run(Sub() HandleClient(client))
End While
End Function
Private Shared Sub HandleClient(tcpClient As TcpClient)
Try
' Obtain the certificate for the server
Dim serverCertificate As X509Certificate = GetServerCertificate()
' Use SslStream to wrap the network stream and enable SSL/TLS
Dim sslStream As New SslStream(
tcpClient.GetStream(),
False,
New RemoteCertificateValidationCallback(AddressOf ValidateServerCertificate),
New LocalCertificateSelectionCallback(AddressOf SelectServerCertificate)
)
' Begin the SSL/TLS handshake.
' This is where ClientHelloInfo becomes available if we hook into it.
' For simplicity, we'll just initiate the handshake here.
' A more advanced scenario might involve intercepting the handshake process.
sslStream.AuthenticateAsServer(serverCertificate,
clientCertificateRequired:=False,
enabledSslProtocols:=System.Security.Authentication.SslProtocols.Tls12 Or System.Security.Authentication.SslProtocols.Tls13,
checkCertificateRevocation:=True)
' At this point, the handshake is complete.
' To directly access ClientHelloInfo during authentication,
' you'd typically need a lower-level interception mechanism or a
' custom SslStream implementation, which is more complex.
' However, some properties might be inferable or accessible post-authentication
' through different means depending on the framework version.
Console.WriteLine($"Client authenticated. SSL/TLS Protocol: {sslStream.SslProtocol}")
Console.WriteLine($"Cipher Suite: {sslStream.CipherSuite}")
' Example: Reading data from the client
Dim buffer(1023) As Byte
Dim bytesRead As Integer = sslStream.Read(buffer, 0, buffer.Length)
Dim request As String = System.Text.Encoding.ASCII.GetString(buffer, 0, bytesRead)
Console.WriteLine($"Received: {request}")
' Send a response back to the client
Dim responseBytes As Byte() = System.Text.Encoding.ASCII.GetBytes("Hello from server!")
sslStream.Write(responseBytes, 0, responseBytes.Length)
sslStream.Close()
Catch ex As Exception
Console.WriteLine($"Error handling client: {ex.Message}")
Finally
tcpClient.Close()
End Try
End Sub
' Placeholder for getting the server certificate
Private Shared Function GetServerCertificate() As X509Certificate
' In a real application, you would load your server certificate here.
' For demonstration, we'll assume a self-signed certificate exists.
' Replace with your certificate loading logic.
Try
' Example: Load from a PFX file
' Return New X509Certificate2("server.pfx", "password")
' Example: Find a certificate in the local machine store
Dim store As New X509Store(StoreName.My, StoreLocation.LocalMachine)
store.Open(OpenFlags.ReadOnly)
Dim certificates = store.Certificates.Find(X509FindType.FindBySubjectName, "localhost", False)
If certificates.Count > 0 Then
Return certificates(0)
Else
Throw New Exception("Server certificate not found. Please create a self-signed certificate for 'localhost'.")
End If
Catch ex As Exception
Console.WriteLine($"Error loading certificate: {ex.Message}")
Throw
End Try
End Function
' Callback to validate the client certificate (optional)
Private Shared Function ValidateServerCertificate(sender As Object, certificate As X509Certificate, chain As X509Chain, sslPolicyErrors As SslPolicyErrors) As Boolean
If sslPolicyErrors = SslPolicyErrors.None Then
Return True
End If
Console.WriteLine("Certificate error: " + sslPolicyErrors.ToString())
Return False ' Reject the connection if there are errors
End Function
' Callback to select the server certificate (if multiple are available)
Private Shared Function SelectServerCertificate(sender As Object, targetHost As String, localCertificates As X509CertificateCollection, remoteCertificate As X509Certificate, acceptableIssuers As String()) As X509Certificate
' If no certificates were passed in, return null.
If localCertificates Is Nothing OrElse localCertificates.Count = 0 Then
Return Nothing
End If
' Use the first certificate in the collection.
' In a real-world scenario, you might have logic to select the best certificate.
Return localCertificates(0)
End Function
' Example of how to start the server
' Public Shared Sub Main(args As String())
' ' Make sure you have a certificate for 'localhost' in your LocalMachine\My store
' ' Or replace GetServerCertificate with your own logic to load a certificate.
' StartServer(8080).GetAwaiter().GetResult()
' End Sub
End Class
Requirements
.NET Framework
- Supported in: 4.5, 4.5.1, 4.5.2, 4.6, 4.6.1, 4.6.2, 4.7, 4.7.1, 4.7.2, 4.8
- Platforms: Windows
.NET Core / .NET 5+
While there might not be a direct 1:1 mapping of the ClientHelloInfo class as seen in .NET Framework, the underlying TLS/SSL functionality and access to handshake information are available through System.Net.Security.SslStream and related classes in .NET Core and later. Developers can achieve similar inspection capabilities using lower-level APIs or by leveraging libraries designed for advanced network programming.
- Supported in: .NET Core 1.0, 1.1, 2.0, 2.1, 2.2, 3.0, 3.1, .NET 5, .NET 6, .NET 7, .NET 8
- Platforms: Windows, macOS, Linux