1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Net.Http;
6 using System.Net.Test.Common;
7 using System.Security.Cryptography.X509Certificates;
8 using System.Security.Authentication;
9 using System.Threading.Tasks;
10 
11 using Xunit;
12 
13 namespace System.Net.Security.Tests
14 {
15     using Configuration = System.Net.Test.Common.Configuration;
16 
17     public abstract class SslStreamSystemDefaultTest
18     {
19         protected readonly SslStream _clientStream;
20         protected readonly SslStream _serverStream;
21 
SslStreamSystemDefaultTest()22         public SslStreamSystemDefaultTest()
23         {
24             var network = new VirtualNetwork();
25             var clientNet = new VirtualNetworkStream(network, isServer:false);
26             var serverNet = new VirtualNetworkStream(network, isServer: true);
27 
28             _clientStream = new SslStream(clientNet, false, ClientCertCallback);
29             _serverStream = new SslStream(serverNet, false, ServerCertCallback);
30         }
31 
32         public static bool IsNotWindows7 => !PlatformDetection.IsWindows7;
33 
AuthenticateClientAsync(string targetHost, X509CertificateCollection clientCertificates, bool checkCertificateRevocation, SslProtocols? protocols = null)34         protected abstract Task AuthenticateClientAsync(string targetHost, X509CertificateCollection clientCertificates, bool checkCertificateRevocation, SslProtocols? protocols = null);
AuthenticateServerAsync(X509Certificate serverCertificate, bool clientCertificateRequired, bool checkCertificateRevocation, SslProtocols? protocols = null)35         protected abstract Task AuthenticateServerAsync(X509Certificate serverCertificate, bool clientCertificateRequired, bool checkCertificateRevocation, SslProtocols? protocols = null);
36 
37         [ConditionalTheory(nameof(IsNotWindows7))]
38         [InlineData(null, null)]
39         [InlineData(SslProtocols.None, null)]
40         [InlineData(null, SslProtocols.None)]
41         [InlineData(SslProtocols.None, SslProtocols.None)]
42         [InlineData(null, SslProtocols.Tls11)]
43         [InlineData(SslProtocols.Tls11, null)]
44         [InlineData(null, SslProtocols.Tls12)]
45         [InlineData(SslProtocols.Tls12, null)]
46         [InlineData(SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12, null)]
47         [InlineData(null, SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12)]
48         public async Task ClientAndServer_OneOrBothUseDefault_Ok(SslProtocols? clientProtocols, SslProtocols? serverProtocols)
49         {
50             const int SEC_E_BUFFER_TOO_SMALL = unchecked((int)0x80090321);
51 
52             X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate();
53             string serverHost = serverCertificate.GetNameInfo(X509NameType.SimpleName, false);
54             var clientCertificates = new X509CertificateCollection();
55             clientCertificates.Add(Configuration.Certificates.GetClientCertificate());
56 
57             var tasks = new Task[2];
58             tasks[0] = AuthenticateClientAsync(serverHost, clientCertificates, checkCertificateRevocation: false, protocols: clientProtocols);
59             tasks[1] = AuthenticateServerAsync(serverCertificate, clientCertificateRequired: true, checkCertificateRevocation: false, protocols: serverProtocols);
60 
61             try
62             {
63                 await TestConfiguration.WhenAllOrAnyFailedWithTimeout(tasks);
64                 if (PlatformDetection.IsWindows && PlatformDetection.WindowsVersion >= 10)
65                 {
66                     Assert.True(
67                         (_clientStream.SslProtocol == SslProtocols.Tls11 && _clientStream.HashAlgorithm == HashAlgorithmType.Sha1) ||
68                         _clientStream.HashAlgorithm == HashAlgorithmType.Sha256 ||
69                         _clientStream.HashAlgorithm == HashAlgorithmType.Sha384 ||
70                         _clientStream.HashAlgorithm == HashAlgorithmType.Sha512);
71                 }
72             }
73             catch (HttpRequestException e) when (e.InnerException?.GetType().Name == "WinHttpException" &&
74                 e.InnerException.HResult == SEC_E_BUFFER_TOO_SMALL &&
75                 !PlatformDetection.IsWindows10Version1607OrGreater)
76             {
77                 // Testing on old Windows versions can hit https://github.com/dotnet/corefx/issues/7812
78                 // Ignore SEC_E_BUFFER_TOO_SMALL error on such cases.
79             }
80         }
81 
ClientCertCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)82         private bool ClientCertCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
83         {
84             switch (sslPolicyErrors)
85             {
86                 case SslPolicyErrors.None:
87                 case SslPolicyErrors.RemoteCertificateChainErrors:
88                 case SslPolicyErrors.RemoteCertificateNameMismatch:
89                     return true;
90                 case SslPolicyErrors.RemoteCertificateNotAvailable:
91                 default:
92                     return false;
93             }
94         }
95 
ServerCertCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)96         private bool ServerCertCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
97         {
98             switch (sslPolicyErrors)
99             {
100                 case SslPolicyErrors.None:
101                 case SslPolicyErrors.RemoteCertificateChainErrors:
102                 case SslPolicyErrors.RemoteCertificateNameMismatch:
103                     return true;
104                 case SslPolicyErrors.RemoteCertificateNotAvailable:
105                     // https://technet.microsoft.com/en-us/library/hh831771.aspx#BKMK_Changes2012R2
106                     // Starting with Windows 8, the "Management of trusted issuers for client authentication" has changed:
107                     // The behavior to send the Trusted Issuers List by default is off.
108                     //
109                     // In Windows 7 the Trusted Issuers List is sent within the Server Hello TLS record. This list is built
110                     // by the server using certificates from the Trusted Root Authorities certificate store.
111                     // The client side will use the Trusted Issuers List, if not empty, to filter proposed certificates.
112                     return PlatformDetection.IsWindows7 && !Capability.IsTrustedRootCertificateInstalled();
113                 default:
114                     return false;
115             }
116         }
117 
Dispose()118         public void Dispose()
119         {
120             _clientStream?.Dispose();
121             _serverStream?.Dispose();
122         }
123     }
124 
125     public sealed class SyncSslStreamSystemDefaultTest : SslStreamSystemDefaultTest
126     {
AuthenticateClientAsync(string targetHost, X509CertificateCollection clientCertificates, bool checkCertificateRevocation, SslProtocols? protocols)127         protected override Task AuthenticateClientAsync(string targetHost, X509CertificateCollection clientCertificates, bool checkCertificateRevocation, SslProtocols? protocols) =>
128             Task.Run(() =>
129             {
130                 if (protocols.HasValue)
131                 {
132                     _clientStream.AuthenticateAsClient(targetHost, clientCertificates, protocols.Value, checkCertificateRevocation);
133                 }
134                 else
135                 {
136                     _clientStream.AuthenticateAsClient(targetHost, clientCertificates, checkCertificateRevocation);
137                 }
138             });
139 
AuthenticateServerAsync(X509Certificate serverCertificate, bool clientCertificateRequired, bool checkCertificateRevocation, SslProtocols? protocols)140         protected override Task AuthenticateServerAsync(X509Certificate serverCertificate, bool clientCertificateRequired, bool checkCertificateRevocation, SslProtocols? protocols) =>
141             Task.Run(() =>
142             {
143                 if (protocols.HasValue)
144                 {
145                     _serverStream.AuthenticateAsServer(serverCertificate, clientCertificateRequired, protocols.Value, checkCertificateRevocation);
146                 }
147                 else
148                 {
149                     _serverStream.AuthenticateAsServer(serverCertificate, clientCertificateRequired, checkCertificateRevocation);
150                 }
151             });
152     }
153 
154     public sealed class ApmSslStreamSystemDefaultTest : SslStreamSystemDefaultTest
155     {
AuthenticateClientAsync(string targetHost, X509CertificateCollection clientCertificates, bool checkCertificateRevocation, SslProtocols? protocols)156         protected override Task AuthenticateClientAsync(string targetHost, X509CertificateCollection clientCertificates, bool checkCertificateRevocation, SslProtocols? protocols) =>
157             Task.Factory.FromAsync(
158                 (callback, state) => protocols.HasValue ?
159                     _clientStream.BeginAuthenticateAsClient(targetHost, clientCertificates, protocols.Value, checkCertificateRevocation, callback, state) :
160                     _clientStream.BeginAuthenticateAsClient(targetHost, clientCertificates, checkCertificateRevocation, callback, state),
161                 _clientStream.EndAuthenticateAsClient,
162                 state: null);
163 
AuthenticateServerAsync(X509Certificate serverCertificate, bool clientCertificateRequired, bool checkCertificateRevocation, SslProtocols? protocols)164         protected override Task AuthenticateServerAsync(X509Certificate serverCertificate, bool clientCertificateRequired, bool checkCertificateRevocation, SslProtocols? protocols) =>
165             Task.Factory.FromAsync(
166                 (callback, state) => protocols.HasValue ?
167                     _serverStream.BeginAuthenticateAsServer(serverCertificate, clientCertificateRequired, protocols.Value, checkCertificateRevocation, callback, state) :
168                     _serverStream.BeginAuthenticateAsServer(serverCertificate, clientCertificateRequired, checkCertificateRevocation, callback, state),
169                 _serverStream.EndAuthenticateAsServer,
170                 state: null);
171     }
172 
173     public sealed class AsyncSslStreamSystemDefaultTest : SslStreamSystemDefaultTest
174     {
AuthenticateClientAsync(string targetHost, X509CertificateCollection clientCertificates, bool checkCertificateRevocation, SslProtocols? protocols)175         protected override Task AuthenticateClientAsync(string targetHost, X509CertificateCollection clientCertificates, bool checkCertificateRevocation, SslProtocols? protocols) =>
176             protocols.HasValue ?
177             _clientStream.AuthenticateAsClientAsync(targetHost, clientCertificates, protocols.Value, checkCertificateRevocation) :
178             _clientStream.AuthenticateAsClientAsync(targetHost, clientCertificates, checkCertificateRevocation);
179 
AuthenticateServerAsync(X509Certificate serverCertificate, bool clientCertificateRequired, bool checkCertificateRevocation, SslProtocols? protocols)180         protected override Task AuthenticateServerAsync(X509Certificate serverCertificate, bool clientCertificateRequired, bool checkCertificateRevocation, SslProtocols? protocols) =>
181             protocols.HasValue ?
182             _serverStream.AuthenticateAsServerAsync(serverCertificate, clientCertificateRequired, protocols.Value, checkCertificateRevocation) :
183             _serverStream.AuthenticateAsServerAsync(serverCertificate, clientCertificateRequired, checkCertificateRevocation);
184     }
185 }
186