クライアントとサーバーが HTTPS 接続をネゴシエートするときは、最初に TLS 接続を確立する必要があります。 TLS ハンドシェイクの一部として、クライアントは、接続しているサーバーのドメイン名を TLS 拡張機能の 1 つで送信します。 複数の (仮想) サーバーが同じコンピューター上でホストされている場合、TLS プロトコルのこの機能により、クライアントは接続先のサーバーを識別し、それに応じてサーバー証明書などの TLS 設定を構成できます。
HttpClient
を使用した HTTP 要求が行われると、クライアントが接続している URL に基づいて、サーバー名表示 (SNI) 拡張機能の値が自動的に選択されます。 拡張機能をより手動で制御する必要があるシナリオでは、次のいずれかの方法を使用できます。
ホスト ヘッダー
ホスト HTTP ヘッダーは、TLS の SNI 拡張機能と同様の機能を実行します。 これにより、ターゲット サーバーは、1 つの IP アドレスで複数のホスト名の要求を区別できます。
HttpClient
は要求 URI に基づいてホストヘッダーを自動入力します。 ただし、その値を手動で設定することもできます。 HttpClient
では、SNI 拡張機能の新しい値も使用されます。 この効果を実現するには、 HttpRequestMessage.Headers.Host
または HttpClient.DefaultRequestHeaders.Host
を使用できます。
using HttpClient client = new();
client.DefaultRequestHeaders.Host = "www.microsoft.com";
using var response = await client.GetAsync("https://127.0.0.1:5001/");
System.Console.WriteLine(response);
注
この方法では、ホスト名を使用して URL に接続するときに、SNI を完全に送信しないようにすることはできません。 ヘッダーが空の文字列に設定されている場合、 HttpClient
は URL のホスト名を代わりに使用します。
注
Host ヘッダーをカスタマイズすると、サーバー証明書の検証に影響します。 既定では、クライアントはサーバー証明書がホスト ヘッダー内のホスト名と一致することを想定しています。
ConnectCallback を使用した SslStream の手動認証
より複雑ですが、より強力なオプションは、 SocketsHttpHandler.ConnectCallback
を使用することです。 .NET 7 以降では、認証された SslStream
を返し、TLS 接続の確立方法をカスタマイズすることができます。 コールバック内では、任意の SslClientAuthenticationOptions
オプションを使用してクライアント側認証を実行できます。
var handler = new SocketsHttpHandler
{
ConnectCallback = async (context, cancellationToken) =>
{
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp) { NoDelay = true };
try
{
await socket.ConnectAsync(context.DnsEndPoint, cancellationToken);
var sslStream = new SslStream(new NetworkStream(socket, ownsSocket: true));
// When using HTTP/2, you must also keep in mind to set options like ApplicationProtocols
await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions
{
TargetHost = context.DnsEndPoint.Host,
}, cancellationToken);
return sslStream;
}
catch
{
socket.Dispose();
throw;
}
}
};
using HttpClient client = new(handler);
using var response = await client.GetAsync("https://www.microsoft.com");
System.Console.WriteLine(response);
.NET