use new API on new windows to get TLS13 (#37888)
authorTomas Weinfurt <tweinfurt@yahoo.com>
Wed, 15 Jul 2020 22:07:32 +0000 (15:07 -0700)
committerGitHub <noreply@github.com>
Wed, 15 Jul 2020 22:07:32 +0000 (15:07 -0700)
* use new API on new windows to get TLS13

* use unmanaged ref to avoid copy

* feedback from review

* feedback from review

* feedback from review

* kick the build

14 files changed:
src/libraries/Common/src/Interop/Windows/SspiCli/ISSPIInterface.cs
src/libraries/Common/src/Interop/Windows/SspiCli/Interop.SSPI.cs
src/libraries/Common/src/Interop/Windows/SspiCli/SSPIAuthType.cs
src/libraries/Common/src/Interop/Windows/SspiCli/SSPISecureChannelType.cs
src/libraries/Common/src/Interop/Windows/SspiCli/SSPIWrapper.cs
src/libraries/Common/src/Interop/Windows/SspiCli/SecuritySafeHandles.cs
src/libraries/System.Net.Http/src/System.Net.Http.csproj
src/libraries/System.Net.HttpListener/src/System.Net.HttpListener.csproj
src/libraries/System.Net.Mail/src/System.Net.Mail.csproj
src/libraries/System.Net.Mail/tests/Unit/System.Net.Mail.Unit.Tests.csproj
src/libraries/System.Net.Security/src/System.Net.Security.csproj
src/libraries/System.Net.Security/src/System/Net/Security/SslStreamPal.Windows.cs
src/libraries/System.Net.Security/tests/FunctionalTests/SslStreamAlertsTest.cs
src/libraries/System.Net.Security/tests/FunctionalTests/TestHelper.cs

index b1d82a7..62daefd 100644 (file)
@@ -14,6 +14,7 @@ namespace System.Net
         int EnumerateSecurityPackages(out int pkgnum, out SafeFreeContextBuffer pkgArray);
         int AcquireCredentialsHandle(string moduleName, Interop.SspiCli.CredentialUse usage, ref SafeSspiAuthDataHandle authdata, out SafeFreeCredentials outCredential);
         int AcquireCredentialsHandle(string moduleName, Interop.SspiCli.CredentialUse usage, ref Interop.SspiCli.SCHANNEL_CRED authdata, out SafeFreeCredentials outCredential);
+        unsafe int AcquireCredentialsHandle(string moduleName, Interop.SspiCli.CredentialUse usage, Interop.SspiCli.SCH_CREDENTIALS* authdata, out SafeFreeCredentials outCredential);
         int AcquireDefaultCredential(string moduleName, Interop.SspiCli.CredentialUse usage, out SafeFreeCredentials outCredential);
         int AcceptSecurityContext(SafeFreeCredentials? credential, ref SafeDeleteSslContext? context, InputSecurityBuffers inputBuffers, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, ref SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags);
         int InitializeSecurityContext(ref SafeFreeCredentials? credential, ref SafeDeleteSslContext? context, string? targetName, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, InputSecurityBuffers inputBuffers, ref SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags);
index 9db80f3..9556c50 100644 (file)
@@ -215,6 +215,92 @@ internal static partial class Interop
         }
 
         [StructLayout(LayoutKind.Sequential)]
+        internal unsafe struct SCH_CREDENTIALS
+        {
+            public const int CurrentVersion = 0x5;
+
+            public int dwVersion;
+            public int dwCredformat;
+            public int cCreds;
+
+            // This is pointer to arry of CERT_CONTEXT*
+            // We do not use it directly in .NET. Instead, we wrap returned OS pointer in safe handle.
+            public void* paCred;
+
+            public IntPtr hRootStore;               // == always null, OTHERWISE NOT RELIABLE
+            public int cMappers;
+            public IntPtr aphMappers;               // == always null, OTHERWISE NOT RELIABLE
+
+            public int dwSessionLifespan;
+            public SCH_CREDENTIALS.Flags dwFlags;
+            public int cTlsParameters;
+            public TLS_PARAMETERS* pTlsParameters;
+
+            [Flags]
+            public enum Flags
+            {
+                Zero = 0,
+                SCH_CRED_NO_SYSTEM_MAPPER = 0x02,
+                SCH_CRED_NO_SERVERNAME_CHECK = 0x04,
+                SCH_CRED_MANUAL_CRED_VALIDATION = 0x08,
+                SCH_CRED_NO_DEFAULT_CREDS = 0x10,
+                SCH_CRED_AUTO_CRED_VALIDATION = 0x20,
+                SCH_CRED_USE_DEFAULT_CREDS = 0x40,
+                SCH_DISABLE_RECONNECTS = 0x80,
+                SCH_CRED_REVOCATION_CHECK_END_CERT = 0x100,
+                SCH_CRED_REVOCATION_CHECK_CHAIN = 0x200,
+                SCH_CRED_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT = 0x400,
+                SCH_CRED_IGNORE_NO_REVOCATION_CHECK = 0x800,
+                SCH_CRED_IGNORE_REVOCATION_OFFLINE = 0x1000,
+                SCH_CRED_CACHE_ONLY_URL_RETRIEVAL_ON_CREATE = 0x2000,
+                SCH_SEND_ROOT_CERT = 0x40000,
+                SCH_SEND_AUX_RECORD =   0x00200000,
+                SCH_USE_STRONG_CRYPTO = 0x00400000,
+                SCH_USE_PRESHAREDKEY_ONLY = 0x800000,
+                SCH_ALLOW_NULL_ENCRYPTION = 0x02000000,
+            }
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal unsafe struct TLS_PARAMETERS
+        {
+            public int cAlpnIds;                        // Valid for server applications only. Must be zero otherwise. Number of ALPN IDs in rgstrAlpnIds; set to 0 if applies to all.
+            public IntPtr rgstrAlpnIds;                 // Valid for server applications only. Must be NULL otherwise. Array of ALPN IDs that the following settings apply to; set to NULL if applies to all.
+            public uint grbitDisabledProtocols;         // List protocols you DO NOT want negotiated.
+            public int cDisabledCrypto;                 // Number of CRYPTO_SETTINGS structures; set to 0 if there are none.
+            public CRYPTO_SETTINGS* pDisabledCrypto;    // Array of CRYPTO_SETTINGS structures; set to NULL if there are none;
+            public TLS_PARAMETERS.Flags dwFlags;        // Optional flags to pass; set to 0 if there are none.
+
+            [Flags]
+            public enum Flags
+            {
+                Zero = 0,
+                TLS_PARAMS_OPTIONAL = 0x01,     // Valid for server applications only. Must be zero otherwise.
+                                                // TLS_PARAMETERS that will only be honored if they do not cause this server to terminate the handshake.
+            }
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal unsafe struct CRYPTO_SETTINGS
+        {
+            public TlsAlgorithmUsage eAlgorithmUsage;   // How this algorithm is being used.
+            public UNICODE_STRING* strCngAlgId;         // CNG algorithm identifier.
+            public int cChainingModes;                  // Set to 0 if CNG algorithm does not have a chaining mode.
+            public UNICODE_STRING* rgstrChainingModes;  // Set to NULL if CNG algorithm does not have a chaining mode.
+            public int dwMinBitLength;                  // Blacklist key sizes less than this. Set to 0 if not defined or CNG algorithm implies bit length.
+            public int dwMaxBitLength;                  // Blacklist key sizes greater than this. Set to 0 if not defined or CNG algorithm implies bit length.
+
+            public enum TlsAlgorithmUsage
+            {
+                TlsParametersCngAlgUsageKeyExchange,    // Key exchange algorithm. RSA, ECHDE, DHE, etc.
+                TlsParametersCngAlgUsageSignature,      // Signature algorithm. RSA, DSA, ECDSA, etc.
+                TlsParametersCngAlgUsageCipher,         // Encryption algorithm. AES, DES, RC4, etc.
+                TlsParametersCngAlgUsageDigest,         // Digest of cipher suite. SHA1, SHA256, SHA384, etc.
+                TlsParametersCngAlgUsageCertSig         // Signature and/or hash used to sign certificate. RSA, DSA, ECDSA, SHA1, SHA256, etc.
+            }
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
         internal unsafe struct SecBuffer
         {
             public int cbBuffer;
@@ -344,6 +430,20 @@ internal static partial class Interop
                   [Out] out long timeStamp
                   );
 
+        [DllImport(Interop.Libraries.SspiCli, ExactSpelling = true, CharSet = CharSet.Unicode, SetLastError = true)]
+        internal static extern unsafe int AcquireCredentialsHandleW(
+          [In] string? principal,
+          [In] string moduleName,
+          [In] int usage,
+          [In] void* logonID,
+          [In] SCH_CREDENTIALS* authData,
+          [In] void* keyCallback,
+          [In] void* keyArgument,
+          ref CredHandle handlePtr,
+          [Out] out long timeStamp
+          );
+
+
         [DllImport(Interop.Libraries.SspiCli, ExactSpelling = true, SetLastError = true)]
         internal static extern unsafe int InitializeSecurityContextW(
                   ref CredHandle credentialHandle,
index a29658d..7d35979 100644 (file)
@@ -45,6 +45,11 @@ namespace System.Net
             return SafeFreeCredentials.AcquireCredentialsHandle(moduleName, usage, ref authdata, out outCredential);
         }
 
+        public unsafe int AcquireCredentialsHandle(string moduleName, Interop.SspiCli.CredentialUse usage, Interop.SspiCli.SCH_CREDENTIALS* authdata, out SafeFreeCredentials outCredential)
+        {
+            return SafeFreeCredentials.AcquireCredentialsHandle(moduleName, usage, authdata, out outCredential);
+        }
+
         public int AcceptSecurityContext(SafeFreeCredentials? credential, ref SafeDeleteSslContext? context, InputSecurityBuffers inputBuffers, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, ref SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags)
         {
             return SafeDeleteContext.AcceptSecurityContext(ref credential, ref context, inFlags, endianness, inputBuffers, ref outputBuffer, ref outFlags);
index 0e9bc62..e30e53d 100644 (file)
@@ -45,6 +45,11 @@ namespace System.Net
             return SafeFreeCredentials.AcquireCredentialsHandle(moduleName, usage, ref authdata, out outCredential);
         }
 
+        public unsafe int AcquireCredentialsHandle(string moduleName, Interop.SspiCli.CredentialUse usage, Interop.SspiCli.SCH_CREDENTIALS* authdata, out SafeFreeCredentials outCredential)
+        {
+            return SafeFreeCredentials.AcquireCredentialsHandle(moduleName, usage, authdata, out outCredential);
+        }
+
         public int AcceptSecurityContext(SafeFreeCredentials? credential, ref SafeDeleteSslContext? context, InputSecurityBuffers inputBuffers, Interop.SspiCli.ContextFlags inFlags, Interop.SspiCli.Endianness endianness, ref SecurityBuffer outputBuffer, ref Interop.SspiCli.ContextFlags outFlags)
         {
             return SafeDeleteContext.AcceptSecurityContext(ref credential, ref context, inFlags, endianness, inputBuffers, ref outputBuffer, ref outFlags);
index 7477241..eaf9126 100644 (file)
@@ -110,14 +110,28 @@ namespace System.Net
 
         public static SafeFreeCredentials AcquireCredentialsHandle(ISSPIInterface secModule, string package, Interop.SspiCli.CredentialUse intent, Interop.SspiCli.SCHANNEL_CRED scc)
         {
-            if (NetEventSource.Log.IsEnabled()) NetEventSource.Log.AcquireCredentialsHandle(package, intent, scc);
-
-            SafeFreeCredentials? outCredential = null;
             int errorCode = secModule.AcquireCredentialsHandle(
                                             package,
                                             intent,
                                             ref scc,
-                                            out outCredential);
+                                            out SafeFreeCredentials outCredential);
+
+            if (errorCode != 0)
+            {
+                if (NetEventSource.IsEnabled) NetEventSource.Error(null, SR.Format(SR.net_log_operation_failed_with_error, nameof(AcquireCredentialsHandle), $"0x{errorCode:X}"));
+                throw new Win32Exception(errorCode);
+            }
+
+            return outCredential;
+        }
+
+        public static unsafe SafeFreeCredentials AcquireCredentialsHandle(ISSPIInterface secModule, string package, Interop.SspiCli.CredentialUse intent, Interop.SspiCli.SCH_CREDENTIALS* scc)
+        {
+            int errorCode = secModule.AcquireCredentialsHandle(
+                                            package,
+                                            intent,
+                                            scc,
+                                            out SafeFreeCredentials outCredential);
 
             if (errorCode != 0)
             {
index b4f3eb5..bef2693 100644 (file)
@@ -301,6 +301,38 @@ namespace System.Net.Security
 
             return errorCode;
         }
+
+        public static unsafe int AcquireCredentialsHandle(
+            string package,
+            Interop.SspiCli.CredentialUse intent,
+            Interop.SspiCli.SCH_CREDENTIALS* authdata,
+            out SafeFreeCredentials outCredential)
+        {
+            long timeStamp;
+
+            outCredential = new SafeFreeCredential_SECURITY();
+
+            int errorCode = Interop.SspiCli.AcquireCredentialsHandleW(
+                                null,
+                                package,
+                                (int)intent,
+                                null,
+                                authdata,
+                                null,
+                                null,
+                                ref outCredential._handle,
+                                out timeStamp);
+
+            if (NetEventSource.IsEnabled) NetEventSource.Verbose(null, $"{nameof(Interop.SspiCli.AcquireCredentialsHandleW)} returns 0x{errorCode:x}, handle = {outCredential}");
+
+            if (errorCode != 0)
+            {
+                outCredential.SetHandleAsInvalid();
+            }
+
+            return errorCode;
+        }
+
     }
 
     //
index ace5d2b..65d63c0 100644 (file)
     <Compile Include="System\Net\Http\SocketsHttpHandler\CurrentUserIdentityProvider.Windows.cs" />
     <Compile Include="$(CommonPath)\Interop\Windows\Interop.Libraries.cs"
              Link="Common\Interop\Windows\Interop.Libraries.cs" />
+    <Compile Include="$(CommonPath)Interop\Windows\Interop.UNICODE_STRING.cs"
+            Link="Common\Interop\Windows\Interop.UNICODE_STRING.cs" />
     <Compile Include="$(CommonPath)\Interop\Windows\Crypt32\Interop.CertEnumCertificatesInStore.cs"
              Link="Common\Interop\Windows\Crypt32\Interop.CertEnumCertificatesInStore.cs" />
     <Compile Include="$(CommonPath)\Interop\Windows\Crypt32\Interop.certificates_types.cs"
index e1c6dbf..e14f7fc 100644 (file)
     <Compile Include="System\Net\Windows\WebSockets\WebSocketProtocolComponent.cs" />
     <Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
              Link="Common\Interop\Windows\Interop.Libraries.cs" />
+    <Compile Include="$(CommonPath)Interop\Windows\Interop.UNICODE_STRING.cs"
+             Link="Common\Interop\Windows\Interop.UNICODE_STRING.cs" />
     <Compile Include="$(CommonPath)Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs"
              Link="Common\Interop\Windows\SChannel\Interop.SecPkgContext_ApplicationProtocol.cs" />
     <Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs"
index f88af75..27a9633 100644 (file)
              Link="Common\System\Net\Security\NetEventSource.Security.cs" />
     <Compile Include="$(CommonPath)System\HexConverter.cs"
              Link="Common\System\HexConverter.cs" />
+    <Compile Include="$(CommonPath)Interop\Windows\Interop.UNICODE_STRING.cs"
+             Link="Common\Interop\Windows\Interop.UNICODE_STRING.cs" />
   </ItemGroup>
   <!-- Unix specific files -->
   <ItemGroup Condition="'$(TargetsUnix)' == 'true'">
index 70ee694..6b9e7bd 100644 (file)
              Link="Common\Interop\Windows\SspiCli\SecuritySafeHandles.cs" />
     <Compile Include="$(CommonPath)Interop\Windows\SspiCli\SSPIWrapper.cs"
              Link="Common\Interop\Windows\SspiCli\SSPIWrapper.cs" />
+    <Compile Include="$(CommonPath)Interop\Windows\Interop.UNICODE_STRING.cs"
+             Link="Common\Interop\Windows\Interop.UNICODE_STRING.cs" />
   </ItemGroup>
 </Project>
index cce9afa..1c423af 100644 (file)
     <!-- Interop -->
     <Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
              Link="Common\Interop\Windows\Interop.Libraries.cs" />
+    <Compile Include="$(CommonPath)Interop\Windows\Interop.UNICODE_STRING.cs"
+             Link="Common\Interop\Windows\Interop.UNICODE_STRING.cs" />
     <Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.certificates.cs"
              Link="Common\Interop\Windows\Crypt32\Interop.certificates.cs" />
     <Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.certificates_types.cs"
              Link="Common\Interop\Windows\Crypt32\Interop.CertEnumCertificatesInStore.cs" />
     <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.CloseHandle.cs"
              Link="Common\Interop\Windows\Kernel32\Interop.CloseHandle.cs" />
+    <Compile Include="$(CommonPath)Interop\Windows\NtDll\Interop.RtlGetVersion.cs"
+             Link="Common\Interop\Windows\NtDll\Interop.RtlGetVersion.cs" />
     <Compile Include="$(CommonPath)Interop\Windows\SChannel\Interop.Alerts.cs"
              Link="Common\Interop\Windows\SChannel\Interop.Alerts.cs" />
     <Compile Include="$(CommonPath)Interop\Windows\SChannel\Interop.SchProtocols.cs"
index 673af81..1056c21 100644 (file)
@@ -15,6 +15,11 @@ namespace System.Net.Security
 {
     internal static class SslStreamPal
     {
+        private static readonly bool UseNewCryptoApi =
+            // On newer Windows version we use new API to get TLS1.3.
+            // API is supported since Windows 10 1809 (17763) but there is no reason to use at the moment.
+            Environment.OSVersion.Version.Major >= 10 && Environment.OSVersion.Version.Build >= 18836;
+
         private const string SecurityPackage = "Microsoft Unified Security Protocol Provider";
 
         private const Interop.SspiCli.ContextFlags RequiredFlags =
@@ -107,6 +112,16 @@ namespace System.Net.Security
 
         public static SafeFreeCredentials AcquireCredentialsHandle(X509Certificate? certificate, SslProtocols protocols, EncryptionPolicy policy, bool isServer)
         {
+            // New crypto API supports TLS1.3 but it does not allow to force NULL encryption.
+            return !UseNewCryptoApi || policy == EncryptionPolicy.NoEncryption ?
+                        AcquireCredentialsHandleSchannelCred(certificate, protocols, policy, isServer) :
+                        AcquireCredentialsHandleSchCredentials(certificate, protocols, policy, isServer);
+        }
+
+        // This is legacy crypto API used on .NET Framework and older Windows versions.
+        // It only supports TLS up to 1.2
+        public static SafeFreeCredentials AcquireCredentialsHandleSchannelCred(X509Certificate? certificate, SslProtocols protocols, EncryptionPolicy policy, bool isServer)
+        {
             int protocolFlags = GetProtocolFlagsFromSslProtocols(protocols, isServer);
             Interop.SspiCli.SCHANNEL_CRED.Flags flags;
             Interop.SspiCli.CredentialUse direction;
@@ -144,6 +159,72 @@ namespace System.Net.Security
             return AcquireCredentialsHandle(direction, secureCredential);
         }
 
+        // This function uses new crypto API to support TLS 1.3 and beyond.
+        public static unsafe SafeFreeCredentials AcquireCredentialsHandleSchCredentials(X509Certificate? certificate, SslProtocols protocols, EncryptionPolicy policy, bool isServer)
+        {
+            int protocolFlags = GetProtocolFlagsFromSslProtocols(protocols, isServer);
+            Interop.SspiCli.SCH_CREDENTIALS.Flags flags;
+            Interop.SspiCli.CredentialUse direction;
+
+            if (isServer)
+            {
+                direction = Interop.SspiCli.CredentialUse.SECPKG_CRED_INBOUND;
+                flags = Interop.SspiCli.SCH_CREDENTIALS.Flags.SCH_SEND_AUX_RECORD;
+            }
+            else
+            {
+                direction = Interop.SspiCli.CredentialUse.SECPKG_CRED_OUTBOUND;
+                flags =
+                    Interop.SspiCli.SCH_CREDENTIALS.Flags.SCH_CRED_MANUAL_CRED_VALIDATION |
+                    Interop.SspiCli.SCH_CREDENTIALS.Flags.SCH_CRED_NO_DEFAULT_CREDS |
+                    Interop.SspiCli.SCH_CREDENTIALS.Flags.SCH_SEND_AUX_RECORD;
+            }
+
+            if (policy == EncryptionPolicy.RequireEncryption)
+            {
+                // Always opt-in SCH_USE_STRONG_CRYPTO for TLS.
+                if (!isServer && ((protocolFlags & Interop.SChannel.SP_PROT_SSL3) == 0))
+                {
+                    flags |= Interop.SspiCli.SCH_CREDENTIALS.Flags.SCH_USE_STRONG_CRYPTO;
+                }
+            }
+            else if (policy == EncryptionPolicy.AllowNoEncryption)
+            {
+                // Allow null encryption cipher in addition to other ciphers.
+                flags |= Interop.SspiCli.SCH_CREDENTIALS.Flags.SCH_ALLOW_NULL_ENCRYPTION;
+            }
+            else
+            {
+                throw new ArgumentException(SR.Format(SR.net_invalid_enum, "EncryptionPolicy"), nameof(policy));
+            }
+
+            Interop.SspiCli.SCH_CREDENTIALS credential = default;
+            credential.dwVersion = Interop.SspiCli.SCH_CREDENTIALS.CurrentVersion;
+            credential.dwFlags = flags;
+
+            IntPtr certificateHandle = IntPtr.Zero;
+            if (certificate != null)
+            {
+                credential.cCreds = 1;
+                certificateHandle = certificate.Handle;
+                credential.paCred = &certificateHandle;
+            }
+
+            if (NetEventSource.IsEnabled) NetEventSource.Info($"flags=({flags}), ProtocolFlags=({protocolFlags}), EncryptionPolicy={policy}");
+
+            if (protocolFlags != 0)
+            {
+                // If we were asked to do specific protocol we need to fill TLS_PARAMETERS.
+                Interop.SspiCli.TLS_PARAMETERS tlsParameters = default;
+                tlsParameters.grbitDisabledProtocols = (uint)protocolFlags ^ uint.MaxValue;
+
+                credential.cTlsParameters = 1;
+                credential.pTlsParameters = &tlsParameters;
+            }
+
+            return AcquireCredentialsHandle(direction, &credential);
+        }
+
         internal static byte[]? GetNegotiatedApplicationProtocol(SafeDeleteContext context)
         {
             Interop.SecPkgContext_ApplicationProtocol alpnContext = default;
@@ -436,5 +517,26 @@ namespace System.Net.Security
                 return SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPISecureChannel, SecurityPackage, credUsage, secureCredential);
             }
         }
+
+        private static unsafe SafeFreeCredentials AcquireCredentialsHandle(Interop.SspiCli.CredentialUse credUsage, Interop.SspiCli.SCH_CREDENTIALS* secureCredential)
+        {
+            // First try without impersonation, if it fails, then try the process account.
+            // I.E. We don't know which account the certificate context was created under.
+            try
+            {
+                //
+                // For app-compat we want to ensure the credential are accessed under >>process<< account.
+                //
+                return WindowsIdentity.RunImpersonated<SafeFreeCredentials>(SafeAccessTokenHandle.InvalidHandle, () =>
+                {
+                    return SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPISecureChannel, SecurityPackage, credUsage, secureCredential);
+                });
+            }
+            catch
+            {
+                return SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPISecureChannel, SecurityPackage, credUsage, secureCredential);
+            }
+        }
+
     }
 }
index 3cb3f5c..96f293c 100644 (file)
@@ -87,13 +87,14 @@ namespace System.Net.Security.Tests
             }
         }
 
-        [Fact]
-        public async Task SslStream_StreamToStream_ClientInitiatedCloseNotify_Ok()
+        [Theory]
+        [InlineData(false)]
+        [InlineData(true)]
+        public async Task SslStream_StreamToStream_ClientInitiatedCloseNotify_Ok(bool sendData)
         {
-            VirtualNetwork network = new VirtualNetwork();
-
-            using (var clientStream = new VirtualNetworkStream(network, isServer: false))
-            using (var serverStream = new VirtualNetworkStream(network, isServer: true))
+            (Stream clientStream, Stream serverStream) = TestHelper.GetConnectedStreams();
+            using (clientStream)
+            using (serverStream)
             using (var client = new SslStream(clientStream, true, AllowAnyServerCertificate))
             using (var server = new SslStream(serverStream))
             using (X509Certificate2 certificate = Configuration.Certificates.GetServerCertificate())
@@ -105,13 +106,21 @@ namespace System.Net.Security.Tests
 
                 await Task.WhenAll(handshake).TimeoutAfter(TestConfiguration.PassingTestTimeoutMilliseconds);
 
+
                 var readBuffer = new byte[1024];
 
+                if (sendData)
+                {
+                    // Send some data before shutting down. This may matter for TLS13.
+                    handshake[0] = server.WriteAsync(readBuffer, 0, 1);
+                    handshake[1] = client.ReadAsync(readBuffer, 0, 1);
+                    await Task.WhenAll(handshake).TimeoutAfter(TestConfiguration.PassingTestTimeoutMilliseconds);
+                }
+
                 await client.ShutdownAsync();
                 int bytesRead = await server.ReadAsync(readBuffer, 0, readBuffer.Length);
                 // close_notify received by the server.
                 Assert.Equal(0, bytesRead);
-
                 await server.ShutdownAsync();
                 bytesRead = await client.ReadAsync(readBuffer, 0, readBuffer.Length);
                 // close_notify received by the client.
index b89ecd6..4e9a9a8 100644 (file)
@@ -39,6 +39,7 @@ namespace System.Net.Security.Tests
         {
             if (Capability.SecurityForceSocketStreams())
             {
+                // DOTNET_TEST_NET_SECURITY_FORCE_SOCKET_STREAMS is set.
                 return GetConnectedTcpStreams();
             }