improve quic client cert validation options on server (#56491)
authorTomas Weinfurt <tweinfurt@yahoo.com>
Thu, 29 Jul 2021 20:53:40 +0000 (13:53 -0700)
committerGitHub <noreply@github.com>
Thu, 29 Jul 2021 20:53:40 +0000 (13:53 -0700)
src/libraries/System.Net.Quic/ref/System.Net.Quic.cs
src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/Mock/MockConnection.cs
src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/MsQuic/MsQuicConnection.cs
src/libraries/System.Net.Quic/src/System/Net/Quic/Implementations/QuicConnectionProvider.cs
src/libraries/System.Net.Quic/src/System/Net/Quic/QuicConnection.cs
src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs

index 28d4332..4c7e48c 100644 (file)
@@ -31,6 +31,7 @@ namespace System.Net.Quic
         public int GetRemoteAvailableUnidirectionalStreamCount() { throw null; }
         public System.Net.Quic.QuicStream OpenBidirectionalStream() { throw null; }
         public System.Net.Quic.QuicStream OpenUnidirectionalStream() { throw null; }
+        public System.Security.Cryptography.X509Certificates.X509Certificate? RemoteCertificate { get { throw null; } }
         public System.Threading.Tasks.ValueTask WaitForAvailableBidirectionalStreamsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
         public System.Threading.Tasks.ValueTask WaitForAvailableUnidirectionalStreamsAsync(System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; }
     }
index 1e8fd20..c2594cf 100644 (file)
@@ -2,8 +2,8 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 
 using System.Diagnostics;
-using System.Net;
 using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
 using System.Runtime.ExceptionServices;
 using System.Threading;
 using System.Threading.Channels;
@@ -29,6 +29,8 @@ namespace System.Net.Quic.Implementations.Mock
         internal PeerStreamLimit? LocalStreamLimit => _isClient ? _state?._clientStreamLimit : _state?._serverStreamLimit;
         internal PeerStreamLimit? RemoteStreamLimit => _isClient ? _state?._serverStreamLimit : _state?._clientStreamLimit;
 
+        internal override X509Certificate? RemoteCertificate => null;
+
         // Constructor for outbound connections
         internal MockConnection(EndPoint? remoteEndPoint, SslClientAuthenticationOptions? sslClientAuthenticationOptions, IPEndPoint? localEndPoint = null, int maxUnidirectionalStreams = 100, int maxBidirectionalStreams = 100)
         {
index 37998df..d909514 100644 (file)
@@ -63,6 +63,7 @@ namespace System.Net.Quic.Implementations.MsQuic
             private bool _closing;
 
             // Certificate validation properties
+            public X509Certificate? RemoteCertificate;
             public bool RemoteCertificateRequired;
             public X509RevocationMode RevocationMode = X509RevocationMode.Offline;
             public RemoteCertificateValidationCallback? RemoteCertificateValidationCallback;
@@ -208,6 +209,8 @@ namespace System.Net.Quic.Implementations.MsQuic
 
         internal override EndPoint RemoteEndPoint => _remoteEndPoint;
 
+        internal override X509Certificate? RemoteCertificate => _state.RemoteCertificate;
+
         internal override SslApplicationProtocol NegotiatedApplicationProtocol => _negotiatedAlpnProtocol;
 
         internal override bool Connected => _state.Connected;
@@ -395,6 +398,8 @@ namespace System.Net.Quic.Implementations.MsQuic
                     sslPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNotAvailable;
                 }
 
+                state.RemoteCertificate = certificate;
+
                 if (state.RemoteCertificateValidationCallback != null)
                 {
                     bool success = state.RemoteCertificateValidationCallback(state, certificate, chain, sslPolicyErrors);
index e5153c3..38c0e27 100644 (file)
@@ -32,6 +32,8 @@ namespace System.Net.Quic.Implementations
 
         internal abstract System.Net.Security.SslApplicationProtocol NegotiatedApplicationProtocol { get; }
 
+        internal abstract System.Security.Cryptography.X509Certificates.X509Certificate? RemoteCertificate { get ; }
+
         internal abstract ValueTask CloseAsync(long errorCode, CancellationToken cancellationToken = default);
 
         public abstract void Dispose();
index e91913f..f94ee81 100644 (file)
@@ -3,6 +3,7 @@
 
 using System.Net.Quic.Implementations;
 using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
 using System.Threading;
 using System.Threading.Tasks;
 
@@ -58,6 +59,8 @@ namespace System.Net.Quic
 
         public EndPoint RemoteEndPoint => _provider.RemoteEndPoint;
 
+        public X509Certificate? RemoteCertificate => _provider.RemoteCertificate;
+
         public SslApplicationProtocol NegotiatedApplicationProtocol => _provider.NegotiatedApplicationProtocol;
 
         /// <summary>
index 0adb638..c6f85e1 100644 (file)
@@ -111,6 +111,8 @@ namespace System.Net.Quic.Tests
             Task<QuicConnection> serverTask = listener.AcceptConnectionAsync().AsTask();
             await TaskTimeoutExtensions.WhenAllOrAnyFailed(clientConnection.ConnectAsync().AsTask(), serverTask, PassingTestTimeoutMilliseconds);
             using QuicConnection serverConnection = serverTask.Result;
+            Assert.Equal(certificate, clientConnection.RemoteCertificate);
+            Assert.Null(serverConnection.RemoteCertificate);
         }
 
         [Fact]
@@ -344,6 +346,7 @@ namespace System.Net.Quic.Tests
             await PingPong(clientConnection, serverConnection);
             // check we completed the client certificate verification.
             Assert.True(clientCertificateOK);
+            Assert.Equal(ClientCertificate, serverConnection.RemoteCertificate);
 
             await serverConnection.CloseAsync(0);
         }