ruijiexie
ruijiexie

Reputation: 97

Can .NET SslStream AuthenticateAsServer respect client-sent Server Name Indicator?

Client: Visit
1. https://host1.com/
2. https://host2.com/

Server: There are two certificates.
certificates1.pfx CN=host1.com and certificates2.pfx CN=host2.com

use wireshark
Client visit https://host1.com/
1: C --> S SYN
2: C <-- S SYN,ACK
3: C --> S ACK
4: C --> S Client Hello (Contain Server Name: host1.com)
... How do I select certificate1 in C#
5: C <-- S Server Hello, Certificate, Server Hello Done

Client visit https://host2.com/
1: C --> S SYN
2: C <-- S SYN,ACK
3: C --> S ACK
4: C --> S Client Hello (Contain Server Name: host2.com)
... How do I select certificate2 in C#
5: c <-- S Server Hello, Certificate, Server Hello Done

SslStream sslStream = new SslStream(
  clientStream,
  false,
  new RemoteCertificateValidationCallback(ValidateServerCertificate),
  new LocalCertificateSelectionCallback(SelectLocalCertificate)
);

X509Certificate2 certificate = new X509Certificate2("certificates1.pfx");

sslStream.AuthenticateAsServer(certificate , false, SslProtocols.Tls | SslProtocols.Ssl3 | SslProtocols.Ssl2, true);

private X509Certificate SelectLocalCertificate(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
{
  //In Debug, targetHost is empty string and remoteCertificate=null
  //I can't return right Certificates
  return null;
}
private bool ValidateServerCertificate( object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
    return true;
}

Upvotes: 3

Views: 3281

Answers (4)

Pieter-Jan Briers
Pieter-Jan Briers

Reputation: 158

This is possible since .NET Core 2.1/.NET Standard 2.1, through the SslServerAuthenticationOptions that can be passed to AuthenticateAsServer().

https://learn.microsoft.com/en-us/dotnet/api/system.net.security.sslserverauthenticationoptions.servercertificateselectioncallback?view=net-8.0#system-net-security-sslserverauthenticationoptions-servercertificateselectioncallback

Upvotes: 0

Edward Ned Harvey
Edward Ned Harvey

Reputation: 7021

While SslStream itself doesn't support SNI, I've confirmed it's possible to workaround. On the server, if you read some bytes from the NetworkStream before starting SslStream, you can see that the initial packet sent from client to server is in fact the client hello, which includes the requested server name.

There is a problem because NetworkStream doesn't support peeking bytes... So you'd have to use a wrapper stream class. (There is an implementation here: https://stackoverflow.com/a/7281113/1726692 ).

There is another problem - Once you get the bytes, you have to figure out how to process them. I'm sure this is implementation specific, and governed by dozens of standards... I don't currently have an implementation of something to parse those initial bytes, but when I connect a Win7 SSlStream client to a Win8 SslStream server, and I capture the first packet from client to server, I can very clearly see the requested servername sent to the server in those bytes, available to the server via the above PeekableStream, prior to starting the SslStream on the server.

So it's definitely possible. Question is where to find a reliable implementation.

Upvotes: 2

andrei m
andrei m

Reputation: 1157

It is not possible to select a certificate using a LocalCertificateSelectionCallback delegate with SslStream acting as a server. You can specify only one certificate in this case, as the first parameter for the AuthenticateAsServer method.

The documentation for SslStream Class on MSDN also mentions the usage of the LocalCertificateSelectionCallback delegate on the client:

If the server requires client authentication, the client must specify one or more certificates for authentication. If the client has more than one certificate, the client can provide a LocalCertificateSelectionCallback delegate to select the correct certificate for the server.

And finally you can check this question that seems to be related with your issue Does SslStream use LocalCertificateSelectionCallback when acting as a server?

Upvotes: 1

hwiechers
hwiechers

Reputation: 14995

I'm not sure why targetHost is blank.

You can also try checking remoteCertificate.Subject to identity the server. Your ValidateServerCertificate method should make sure that this matches the host.

Upvotes: 0

Related Questions