Skip to content

Add ALPN support to System.Net.Security.SslStream API #15813

@jubarat

Description

@jubarat

Rationale

In order to initiate HTTP/2 connection over TLS, application protocol must be negotiated (References: rfc7540 section-3.3,rfc7301). ALPN support is available in SChannel for Windows Server 2012 R2, Windows 8.1 or later. Support in SslStream is a popular feature request: See here and #15077.

Client and server applications need a way to specify their supported application protocol list and be able to query the negotiated protocol.

Proposed API change

    public partial class SslStream : AuthenticatedStream
    {
        ...
        public virtual void AuthenticateAsClient(string targetHost, System.Security.Cryptography.X509Certificates.X509CertificateCollection clientCertificates, System.Security.Authentication.SslProtocols enabledSslProtocols, bool checkCertificateRevocation, string[] applicationProtocols);
        public virtual System.Threading.Tasks.Task AuthenticateAsClientAsync(string targetHost, System.Security.Cryptography.X509Certificates.X509CertificateCollection clientCertificates, System.Security.Authentication.SslProtocols enabledSslProtocols, bool checkCertificateRevocation, string[] applicationProtocols);
        public virtual void AuthenticateAsServer(System.Security.Cryptography.X509Certificates.X509Certificate serverCertificate, bool clientCertificateRequired, System.Security.Authentication.SslProtocols enabledSslProtocols, bool checkCertificateRevocation, string[] applicationProtocols);
        public virtual System.Threading.Tasks.Task AuthenticateAsServerAsync(System.Security.Cryptography.X509Certificates.X509Certificate serverCertificate, bool clientCertificateRequired, System.Security.Authentication.SslProtocols enabledSslProtocols, bool checkCertificateRevocation, string[] applicationProtocols);
        public virtual string NegotiatedApplicationProtocol;
    }

(New method overloads of AuthenticateAsClient/AuthenticateAsServer (+their async versions) are being added having new parameter string[] applicationProtocols. New property string NegotiatedApplicationProtocol is added as well).

Full view of SslStream API containing the proposed changes is in PR.

Example usage

Client:

SslStream sslStream = new SslStream(serverStream);
string[] applicationProtocols = new[] { "h2", "http/1.1" };
sslStream.AuthenticateAsClient(certificate.Subject, new X509CertificateCollection(), SslProtocols.Tls12, false, applicationProtocols);
string protocol = sslStream.NegotiatedApplicationProtocol;

Server:

SslStream sslStream = new SslStream(serverStream)
string[] applicationProtocols = new[] { "h2", "http/1.1" };
sslStream.AuthenticateAsServer(certificate, false, SslProtocols.Tls12, false, applicationProtocols);
string protocol = sslStream.NegotiatedApplicationProtocol;

Details

Client advertises list of protocols in descending order of preference. After the TLS handshake is complete the client queries the TLS stack for negotiated application protocol.

Similar way server side application provides its own list of supported protocols (again in descending order of preference). The TLS stack makes the application protocol match internally. After the handshake completes the server application queries the negotiated application protocol. Note: This behavior is supported by SChannel and is compatible with OpenSSL ALPN API. Unlike OpenSSL the SChannel does not expose possibility of querying client's advertised list of protocols therefore this API proposal does not contain server side protocol selecting based on callback.

AuthenticateAsClient/AuthenticateAsServer methods

  • null value passed to applicationProtocols parameter means the caller is not requesting application protocol negotiation. This is also the behavior of existing method overloads.
  • If both client and server specified their application protocol list and there was no match then AuthenticateAsServer throws AuthenticationException with inner exception having OS native error code. At this point the TLS handshake is aborted so the client gets IOException due to server application closing the transport connection.
  • An alternative approach how to pass application protocol list instead of AuthenticateAs… methods could be as parameter of SslStream constructor.
  • In following cases ArgumentException is thrown:
    • Empty protocol list.
    • Any element of protocol list is null.
    • Any element has more than 255 characters.
    • The full protocol list serialized size is more than 32767 bytes.

NegotiatedApplicationProtocol property

  • If successfully negotiated then it returns the negotiated application protocol string.
  • If peer did not advertise ALPN TLS extension or applicationProtocols was null then this property returns null.
  • Throws InvalidOperationException if calling before AuthenticateAsClient or AuthenticateAsServer completes.

Examples of API behavior are in functional tests of PR.

Pull request

dotnet/corefx#4685

Metadata

Metadata

Assignees

Labels

api-needs-workAPI needs work before it is approved, it is NOT ready for implementationarea-System.NetwishlistIssue we would like to prioritize, but we can't commit we will get to it yet

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions