Skip to content

Proposed SslStream additions for ALPN and SNI #23107

@Drawaes

Description

@Drawaes

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

  1. It would break compat with desktop
  2. 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


#17677 SNI
#15813 ALPN

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 :)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions