-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Rationale
SNI and ALPN have been hanging around for a while as requirements and are becoming more urgent for Kestrel as HTTP/2 approaches (ALPN) and it becomes an edge server (SNI). There was an initial API proposal in issue #15813 however this stalled from my reading because of two issues
- It would break compat with desktop
- It would make the AuthenticateAsXXXXX method overloads become unwieldy
So I think the need has been established just not the API Design and therefore the implementation.
#Proposed API change
I would propose adding extension methods for SslStream.
public static SslStreamExtensions
{
public delegate void ApplicationLayerProtocolCallback(object sender, string matchedProtocol);
public delegate bool ServerNameIndiciationCallback(object sender, string serverName, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors);
public void SetApplicationLayerProtocolNegotiation(this SslStream self, string[] supportedProtocols, ApplicationLayerProtocolNegotiationCallback callback) {}
public void SetServerNameIndiciation(this SslStream self, Dictionary<string, X509Certificate> serverCertificates, ServerNameIndiciationCallback callback) {}
}
One other potential idea is to move this off to a "Builder" method for SslStream. This might help in the server scenario as multiple SslStreams could be built from it and components that each can share could be reused (for instance SSL_CTX in openssl is only required 1x for the same settings rather than per connection).
Example Usage
var supportedProtocols = new string[] {"http1.1", "h2"};
var supportedServerNames = new Dictionary<string,X509Certificate>()
{
"www.myanimatedgifsrock.com" , new X509Certificate("secretpath", "secretpassword"),
"www.immoslesscoolgifs.com", new X509Certificate("expiredcertpath", "secretpassword2"),
};
var SslStream stream = new SslStream(networkStream);
stream.SetApplicationLayerProtocolNegotiation(supportedProtocols, MyAlpnHandler);
stream.SetServerNameIndiciation(supportedServerNames, MySniHandler);
//normal use resumes
Referenced Issues
Potential Issues
The callback for SNI should need to (and does in above) provide the ability to validate the certificate. Consideration would need to be given to the concept that if the SNI callback provides this what happens if a callback is provided in the Authenticate method. Also should there be separate extension methods for client and for server? Maybe but really you probably want both to have the chance to decide if they are happy with the otherside based on the servername that they tried to connect to.
The other issue with this pattern is that there is no "state" held in the SslStream. Once the callback has been called there is no real way to retrieve this information later and it would be up to the user to associate this information with the SslStream and carry it around. To this end perhaps an extension method/methods to later retrieve this information might be considered such as below
public static bool TryGetApplicationLayerProtocol(this SslStream self, out string protocol);
public static bool TryGetServerName(this SslStream self, out string serverName);
Thanks :)