-
Notifications
You must be signed in to change notification settings - Fork 5.4k
Description
Background and motivation
-
SslStreamCertificateContext
In System.Net.Quic, we're handling certificates fromSslServerAuthenticationOptionsand need to pass them to msquic. SinceSslServerAuthenticationOptionsallows to specify certificate with its intermediates viaSslStreamCertificateContext, we need to be able to extract both properties from the class in order to convert them and pass them to msquic. -
SslClientHelloInfo
We'll also be adding our version ofServerOptionsSelectionCallback:
public delegate ValueTask<SslServerAuthenticationOptions> ServerOptionsSelectionCallback(SslStream stream, SslClientHelloInfo clientHelloInfo, object? state, CancellationToken cancellationToken);
And we'll need to be able to constructSslClientHelloInfostruct. Otherwise, we'll be left with reflection here as well. -
Current SslStreamCertificateContext takes list of additional certificates. But when
Createmethod is called it is not clear if X509Chain build succeeds and how the actual list looks.
Exposing theIntermediateCertificateswould make any investigations easier -
SslStreamCertificateContextis notiDisposable(unfortunately).
Exposing the inner certificates possibly allows some cleanup if needed. (also creates risk of mishandling)
API Proposal
- SslStreamCertificateContext
// Public ctors, but getters are internal.
public class SslStreamCertificateContext
{
- internal readonly X509Certificate2 Certificate;
+ public readonly X509Certificate2 TargetCertificate { get; }
+ public ReadOnlyMemory<X509Certificate2> IntermediateCertificates { get; }
// public ctors
}Create method calls the certificate target and that seems too ambiguous. The proposal is to call it TaregtCertificate to make it somewhat closer or we can use the current internal Certificate (that is pretty generic as well)
We also talk about ReadOnlySpan<X509Certificate2> for the IntermediateCertificates but the consensus was to use Memory as more flexible. Current implementation use array under the cover.
- SslClientHelloInfo
// It has public getters, but cannot be constructed with values outside of System.Net.Security.
public readonly struct SslClientHelloInfo
{
public readonly string ServerName { get; }
public readonly SslProtocols SslProtocols { get; }
- internal SslClientHelloInfo(string serverName, SslProtocols sslProtocols);
+ public SslClientHelloInfo(string serverName, SslProtocols sslProtocols);
}API Usage
- SslStreamCertificateContext
if (sslOptions.ServerCertificateContext is not null)
{
certificate = sslOptions.ServerCertificateContext.ContextCertificate;
intermediaries = sslOptions.ServerCertificateContext.ContextChain;
}- SslClientHelloInfo
SslClientHelloInfo sslClientHelloInfo = new SslClientHelloInfo(targetHost, SslProtocols.Tls13);Alternative Designs
-
SslStreamCertificateContext
Move the whole logic around certificates to System.Net.Security and get out of it only ASN1 blob (which we need for msquic). -
SslClientHelloInfo
Not much options here, possibly static create method.
For both, InternalsVisibleTo. I'm aware it's "forbidden" in runtime, but I don't know why.
For both, merging System.Net.Security with System.Net.Quic. This was voted against in our team discussion, unless we want to merge whole lot more and create omnipotent System.Net.dll.
-
Use reflection
That was done in early 7.0. Not great for obvious reason. -
Use semi-public methods (current state)
Needed properties and methods are public but not in ref assembly.
Noted in SslStreamCertificateContext has public fields in its implementation that aren't in the contract #72226 .
Risks
Not much. We'd be only exposing readonly getters and struct ctor.