private async Task InnerOnConnectionAsync(ConnectionContext context) { var feature = new TlsConnectionFeature(); context.Features.Set<ITlsConnectionFeature>(feature); context.Features.Set<ITlsHandshakeFeature>(feature); var memoryPool = context.Features.Get<IMemoryPoolFeature>()?.MemoryPool; var inputPipeOptions = new StreamPipeReaderOptions ( pool: memoryPool, bufferSize: memoryPool.GetMinimumSegmentSize(), minimumReadSize: memoryPool.GetMinimumAllocSize(), leaveOpen: true ); var outputPipeOptions = new StreamPipeWriterOptions ( pool: memoryPool, leaveOpen: true ); TlsDuplexPipe tlsDuplexPipe = null; if (_options.RemoteCertificateMode == RemoteCertificateMode.NoCertificate) { tlsDuplexPipe = new TlsDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions); } else { tlsDuplexPipe = new TlsDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions, s => new SslStream( s, leaveInnerStreamOpen: false, userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) => { if (certificate == null) { return _options.RemoteCertificateMode != RemoteCertificateMode.RequireCertificate; } if (_options.RemoteCertificateValidation == null) { if (sslPolicyErrors != SslPolicyErrors.None) { return false; } } var certificate2 = ConvertToX509Certificate2(certificate); if (certificate2 == null) { return false; } if (_options.RemoteCertificateValidation != null) { if (!_options.RemoteCertificateValidation(certificate2, chain, sslPolicyErrors)) { return false; } } return true; })); } var sslStream = tlsDuplexPipe.Stream; using (var cancellationTokeSource = new CancellationTokenSource(_options.HandshakeTimeout)) using (cancellationTokeSource.Token.UnsafeRegisterCancellation(state => ((ConnectionContext)state).Abort(), context)) { try { var sslOptions = new TlsClientAuthenticationOptions { ClientCertificates = new X509CertificateCollection(new[] { _certificate }), EnabledSslProtocols = _options.SslProtocols, }; _options.OnAuthenticateAsClient?.Invoke(context, sslOptions); #if NETCOREAPP await sslStream.AuthenticateAsClientAsync(sslOptions.Value, cancellationTokeSource.Token); #else await sslStream.AuthenticateAsClientAsync( sslOptions.TargetHost, sslOptions.ClientCertificates, sslOptions.EnabledSslProtocols, sslOptions.CertificateRevocationCheckMode == X509RevocationMode.Online); #endif } catch (OperationCanceledException) { _logger?.LogDebug(2, "Authentication timed out"); #if NETCOREAPP await sslStream.DisposeAsync(); #else sslStream.Dispose(); #endif return; } catch (Exception ex) when (ex is IOException || ex is AuthenticationException) { _logger?.LogDebug(1, ex, "Authentication failed"); #if NETCOREAPP await sslStream.DisposeAsync(); #else sslStream.Dispose(); #endif return; } } #if NETCOREAPP feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.Protocol; #endif context.Features.Set<ITlsApplicationProtocolFeature>(feature); feature.LocalCertificate = ConvertToX509Certificate2(sslStream.LocalCertificate); feature.RemoteCertificate = ConvertToX509Certificate2(sslStream.RemoteCertificate); feature.CipherAlgorithm = sslStream.CipherAlgorithm; feature.CipherStrength = sslStream.CipherStrength; feature.HashAlgorithm = sslStream.HashAlgorithm; feature.HashStrength = sslStream.HashStrength; feature.KeyExchangeAlgorithm = sslStream.KeyExchangeAlgorithm; feature.KeyExchangeStrength = sslStream.KeyExchangeStrength; feature.Protocol = sslStream.SslProtocol; var originalTransport = context.Transport; try { context.Transport = tlsDuplexPipe; // Disposing the stream will dispose the tlsDuplexPipe #if NETCOREAPP await using (sslStream) await using (tlsDuplexPipe) #else using (sslStream) using (tlsDuplexPipe) #endif { await _next(context); // Dispose the inner stream (tlsDuplexPipe) before disposing the SslStream // as the duplex pipe can hit an ODE as it still may be writing. } } finally { // Restore the original so that it gets closed appropriately context.Transport = originalTransport; } }
private async Task InnerOnConnectionAsync(ConnectionContext context) { bool certificateRequired; var feature = new TlsConnectionFeature(); context.Features.Set <ITlsConnectionFeature>(feature); context.Features.Set <ITlsHandshakeFeature>(feature); var memoryPool = context.Features.Get <IMemoryPoolFeature>()?.MemoryPool; var inputPipeOptions = new StreamPipeReaderOptions ( pool: memoryPool, bufferSize: memoryPool.GetMinimumSegmentSize(), minimumReadSize: memoryPool.GetMinimumAllocSize(), leaveOpen: true ); var outputPipeOptions = new StreamPipeWriterOptions ( pool: memoryPool, leaveOpen: true ); TlsDuplexPipe tlsDuplexPipe = null; if (_options.RemoteCertificateMode == RemoteCertificateMode.NoCertificate) { tlsDuplexPipe = new TlsDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions); certificateRequired = false; } else { tlsDuplexPipe = new TlsDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions, s => new SslStream( s, leaveInnerStreamOpen: false, userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) => { if (certificate == null) { return(_options.RemoteCertificateMode != RemoteCertificateMode.RequireCertificate); } if (_options.RemoteCertificateValidation == null) { if (sslPolicyErrors != SslPolicyErrors.None) { return(false); } } var certificate2 = ConvertToX509Certificate2(certificate); if (certificate2 == null) { return(false); } if (_options.RemoteCertificateValidation != null) { if (!_options.RemoteCertificateValidation(certificate2, chain, sslPolicyErrors)) { return(false); } } return(true); })); certificateRequired = true; } var sslStream = tlsDuplexPipe.Stream; using (var cancellationTokeSource = new CancellationTokenSource(_options.HandshakeTimeout)) using (cancellationTokeSource.Token.UnsafeRegister(state => ((ConnectionContext)state).Abort(), context)) { try { // Adapt to the SslStream signature ServerCertificateSelectionCallback selector = null; if (_certificateSelector != null) { selector = (sender, name) => { context.Features.Set(sslStream); var cert = _certificateSelector(context, name); if (cert != null) { EnsureCertificateIsAllowedForServerAuth(cert); } return(cert); }; } var sslOptions = new TlsServerAuthenticationOptions { ServerCertificate = _certificate, ServerCertificateSelectionCallback = selector, ClientCertificateRequired = certificateRequired, EnabledSslProtocols = _options.SslProtocols, CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck, }; _options.OnAuthenticateAsServer?.Invoke(context, sslOptions); await sslStream.AuthenticateAsServerAsync(sslOptions.Value, cancellationTokeSource.Token); } catch (OperationCanceledException ex) { _logger?.LogWarning(2, ex, "Authentication timed out"); await sslStream.DisposeAsync(); return; } catch (Exception ex) { _logger?.LogWarning(1, ex, "Authentication failed"); await sslStream.DisposeAsync(); return; } } feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.Protocol; context.Features.Set <ITlsApplicationProtocolFeature>(feature); feature.LocalCertificate = ConvertToX509Certificate2(sslStream.LocalCertificate); feature.RemoteCertificate = ConvertToX509Certificate2(sslStream.RemoteCertificate); feature.CipherAlgorithm = sslStream.CipherAlgorithm; feature.CipherStrength = sslStream.CipherStrength; feature.HashAlgorithm = sslStream.HashAlgorithm; feature.HashStrength = sslStream.HashStrength; feature.KeyExchangeAlgorithm = sslStream.KeyExchangeAlgorithm; feature.KeyExchangeStrength = sslStream.KeyExchangeStrength; feature.Protocol = sslStream.SslProtocol; var originalTransport = context.Transport; try { context.Transport = tlsDuplexPipe; // Disposing the stream will dispose the TlsDuplexPipe await using (sslStream) await using (tlsDuplexPipe) { await _next(context); // Dispose the inner stream (TlsDuplexPipe) before disposing the SslStream // as the duplex pipe can hit an ODE as it still may be writing. } } finally { // Restore the original so that it gets closed appropriately context.Transport = originalTransport; } }