예제 #1
0
        public ServerHandshakeSession(SecurityParameters securityParameters)
            : base(securityParameters)
        {
            if (securityParameters.ServerCertificateSelectionCallback != null)
            {
                _certificateSelectionCallback = securityParameters.ServerCertificateSelectionCallback;
            }
            else
            {
                _certificateSelectionCallback = new ServerCertificateSelectionCallback(DefaultCertificateSelectionCallback);
            }

            _availableCertificates = new List <X509CertificateCollection>();
            _availableCertificates.AddRange(securityParameters.AvailableCertificates);

            _availablePrivateKeys = new List <CertificatePrivateKey>();
            _availablePrivateKeys.AddRange(securityParameters.AvailablePrivateKeys);

            if (securityParameters.ClientCertificateTypes.Count > 0)
            {
                _certificateRequest = new HandshakeCertificateRequest(_version);
                _certificateRequest.CertificateTypes.AddRange(securityParameters.ClientCertificateTypes);
                _certificateRequest.SignatureAndHashAlgorithms.AddRange(_pluginManager.GetSupportedSignatureAndHashAlgorithms());
                _certificateRequest.CertificateAuthorities.AddRange(securityParameters.ClientCertificateAuthorities);
            }
        }
        private void BeginHandshake(IChannelHandlerContext ctx)
        {
            if (_isServer)
            {
#if NETCOREAPP_2_0_GREATER || NETSTANDARD_2_0_GREATER
                // Adapt to the SslStream signature
                ServerCertificateSelectionCallback selector = null;
                if (_serverCertificateSelector is object)
                {
                    X509Certificate LocalServerCertificateSelection(object sender, string name)
                    {
                        ctx.GetAttribute(SslStreamAttrKey).Set(_sslStream);
                        return(_serverCertificateSelector(ctx, name));
                    }

                    selector = new ServerCertificateSelectionCallback(LocalServerCertificateSelection);
                }

                var sslOptions = new SslServerAuthenticationOptions()
                {
                    ServerCertificate = _serverCertificate,
                    ServerCertificateSelectionCallback = selector,
                    ClientCertificateRequired          = _serverSettings.NegotiateClientCertificate,
                    EnabledSslProtocols            = _serverSettings.EnabledProtocols,
                    CertificateRevocationCheckMode = _serverSettings.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
                    ApplicationProtocols           = _serverSettings.ApplicationProtocols // ?? new List<SslApplicationProtocol>()
                };
                _serverSettings.OnAuthenticate?.Invoke(ctx, _serverSettings, sslOptions);

                var cts = new CancellationTokenSource(_serverSettings.HandshakeTimeout);
                _sslStream.AuthenticateAsServerAsync(sslOptions, cts.Token)
                .ContinueWith(
예제 #3
0
        internal static MSI.MonoServerCertificateSelectionCallback PublicToMono(ServerCertificateSelectionCallback callback)
        {
            if (callback == null)
            {
                return(null);
            }

            return((s, h) => callback(s, h));
        }
        private static CipherSuite SelectCipherSuite(CipherSuitePluginManager pluginManager,
            ProtocolVersion clientVersion, ProtocolVersion minVersion, ProtocolVersion maxVersion,
            List<CipherSuiteId> clientSuites, List<CipherSuiteId> serverSuites,
            ServerCertificateSelectionCallback certificateSelectionCallback,
            List<X509CertificateCollection> availableCertificates)
        {
            if (clientVersion < minVersion)
            {
                throw new AlertException(AlertDescription.ProtocolVersion,
                                         "Offered client version " + clientVersion +
                                         " lower than minimum supported version " + minVersion);
            }

            // Initialize our return value as null
            CipherSuite selectedCipherSuite = null;

            // Run as long as we either select a cipher suite or run out of versions
            ProtocolVersion selectedVersion = clientVersion < maxVersion ? clientVersion : maxVersion;
            while (selectedCipherSuite == null)
            {
                foreach (CipherSuiteId id in clientSuites)
                {
                    if (!serverSuites.Contains(id))
                        continue;

                    // Try initializing the cipher suite based on ID
                    selectedCipherSuite = pluginManager.GetCipherSuite(selectedVersion, (ushort)id);
                    if (selectedCipherSuite == null)
                        continue;

                    // Try selecting a suitable certificate for this cipher suite
                    int certificateIndex = certificateSelectionCallback(selectedCipherSuite, availableCertificates.ToArray());
                    if (certificateIndex >= 0 && certificateIndex < availableCertificates.Count)
                    {
                        // We finally found the valid suite, break out from the loop
                        break;
                    }
                    // No certificate was found for the suite, ignore
                    selectedCipherSuite = null;
                }

                if (selectedCipherSuite != null) break;
                if (selectedVersion == minVersion) break;
                selectedVersion = selectedVersion.PreviousProtocolVersion;
            }

            if (selectedCipherSuite == null)
            {
                throw new AlertException(AlertDescription.HandshakeFailure,
                                         "None of the cipher suites offered by client is accepted");
            }
            return selectedCipherSuite;
        }
예제 #5
0
        private SslAuthenticationOptions CreateAuthenticationOptions(SslServerAuthenticationOptions sslServerAuthenticationOptions)
        {
            if (sslServerAuthenticationOptions.ServerCertificate == null && sslServerAuthenticationOptions.ServerCertificateSelectionCallback == null)
            {
                throw new ArgumentNullException(nameof(sslServerAuthenticationOptions.ServerCertificate));
            }

            if (sslServerAuthenticationOptions.ServerCertificate != null && sslServerAuthenticationOptions.ServerCertificateSelectionCallback != null)
            {
                throw new InvalidOperationException(SR.Format(SR.net_conflicting_options, nameof(ServerCertificateSelectionCallback)));
            }

            var authOptions = new SslAuthenticationOptions(sslServerAuthenticationOptions);

            _userServerCertificateSelectionCallback = sslServerAuthenticationOptions.ServerCertificateSelectionCallback;
            authOptions.ServerCertSelectionDelegate = _userServerCertificateSelectionCallback == null ? null : new ServerCertSelectionCallback(ServerCertSelectionCallbackWrapper);

            authOptions.CertValidationDelegate = _certValidationDelegate;

            return(authOptions);
        }
예제 #6
0
        public ServerHandshakeSession(SecurityParameters securityParameters)
            : base(securityParameters)
        {
            if (securityParameters.ServerCertificateSelectionCallback != null) {
                _certificateSelectionCallback = securityParameters.ServerCertificateSelectionCallback;
            } else {
                _certificateSelectionCallback = new ServerCertificateSelectionCallback(DefaultCertificateSelectionCallback);
            }

            _availableCertificates = new List<X509CertificateCollection>();
            _availableCertificates.AddRange(securityParameters.AvailableCertificates);

            _availablePrivateKeys = new List<CertificatePrivateKey>();
            _availablePrivateKeys.AddRange(securityParameters.AvailablePrivateKeys);

            if (securityParameters.ClientCertificateTypes.Count > 0) {
                _certificateRequest = new HandshakeCertificateRequest(_version);
                _certificateRequest.CertificateTypes.AddRange(securityParameters.ClientCertificateTypes);
                _certificateRequest.SignatureAndHashAlgorithms.AddRange(_pluginManager.GetSupportedSignatureAndHashAlgorithms());
                _certificateRequest.CertificateAuthorities.AddRange(securityParameters.ClientCertificateAuthorities);
            }
        }
        private Task DoOptionsBasedHandshakeAsync(ConnectionContext context, SslStream sslStream, Core.Internal.TlsConnectionFeature feature, CancellationToken cancellationToken)
        {
            // Adapt to the SslStream signature
            ServerCertificateSelectionCallback selector = null;

            if (_serverCertificateSelector != null)
            {
                selector = (sender, name) =>
                {
                    feature.HostName = name;
                    context.Features.Set(sslStream);
                    var cert = _serverCertificateSelector(context, name);
                    if (cert != null)
                    {
                        EnsureCertificateIsAllowedForServerAuth(cert);
                    }
                    return(cert);
                };
            }

            var sslOptions = new SslServerAuthenticationOptions
            {
                ServerCertificate                  = _serverCertificate,
                ServerCertificateContext           = _serverCertificateContext,
                ServerCertificateSelectionCallback = selector,
                ClientCertificateRequired          = _options.ClientCertificateMode != ClientCertificateMode.NoCertificate,
                EnabledSslProtocols                = _options.SslProtocols,
                CertificateRevocationCheckMode     = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
            };

            ConfigureAlpn(sslOptions, _options.HttpProtocols);

            _options.OnAuthenticate?.Invoke(context, sslOptions);

            KestrelEventSource.Log.TlsHandshakeStart(context, sslOptions);

            return(sslStream.AuthenticateAsServerAsync(sslOptions, cancellationToken));
        }
        private async Task InnerOnConnectionAsync(ConnectionContext context)
        {
            bool certificateRequired;
            var  feature = new Core.Internal.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
                                    );

            SslDuplexPipe sslDuplexPipe = null;

            if (_options.ClientCertificateMode == ClientCertificateMode.NoCertificate)
            {
                sslDuplexPipe       = new SslDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions);
                certificateRequired = false;
            }
            else
            {
                sslDuplexPipe = new SslDuplexPipe(context.Transport, inputPipeOptions, outputPipeOptions, s => new SslStream(s,
                                                                                                                             leaveInnerStreamOpen: false,
                                                                                                                             userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) =>
                {
                    if (certificate == null)
                    {
                        return(_options.ClientCertificateMode != ClientCertificateMode.RequireCertificate);
                    }

                    if (_options.ClientCertificateValidation == null)
                    {
                        if (sslPolicyErrors != SslPolicyErrors.None)
                        {
                            return(false);
                        }
                    }

                    var certificate2 = ConvertToX509Certificate2(certificate);
                    if (certificate2 == null)
                    {
                        return(false);
                    }

                    if (_options.ClientCertificateValidation != null)
                    {
                        if (!_options.ClientCertificateValidation(certificate2, chain, sslPolicyErrors))
                        {
                            return(false);
                        }
                    }

                    return(true);
                }));

                certificateRequired = true;
            }

            var sslStream = sslDuplexPipe.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 (_serverCertificateSelector != null)
                        {
                            selector = (sender, name) =>
                            {
                                context.Features.Set(sslStream);
                                var cert = _serverCertificateSelector(context, name);
                                if (cert != null)
                                {
                                    EnsureCertificateIsAllowedForServerAuth(cert);
                                }
                                return(cert);
                            };
                        }

                        var sslOptions = new SslServerAuthenticationOptions
                        {
                            ServerCertificate = _serverCertificate,
                            ServerCertificateSelectionCallback = selector,
                            ClientCertificateRequired          = certificateRequired,
                            EnabledSslProtocols            = _options.SslProtocols,
                            CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
                            ApplicationProtocols           = new List <SslApplicationProtocol>()
                        };

                        // This is order sensitive
                        if ((_options.HttpProtocols & HttpProtocols.Http2) != 0)
                        {
                            sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http2);
                            // https://tools.ietf.org/html/rfc7540#section-9.2.1
                            sslOptions.AllowRenegotiation = false;
                        }

                        if ((_options.HttpProtocols & HttpProtocols.Http1) != 0)
                        {
                            sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http11);
                        }

                        _options.OnAuthenticate?.Invoke(context, sslOptions);

                        await sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None);
                    }
                    catch (OperationCanceledException)
                    {
                        _logger?.LogDebug(2, CoreStrings.AuthenticationTimedOut);
                        await sslStream.DisposeAsync();

                        return;
                    }
                    catch (Exception ex) when(ex is IOException || ex is AuthenticationException)
                    {
                        _logger?.LogDebug(1, ex, CoreStrings.AuthenticationFailed);
                        await sslStream.DisposeAsync();

                        return;
                    }
                }

            feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.Protocol;
            context.Features.Set <ITlsApplicationProtocolFeature>(feature);
            feature.ClientCertificate    = 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 = sslDuplexPipe;

                // Disposing the stream will dispose the sslDuplexPipe
                await using (sslStream)
                    await using (sslDuplexPipe)
                    {
                        await _next(context);

                        // Dispose the inner stream (SslDuplexPipe) 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;
            }
        }
예제 #9
0
        private async Task <IAdaptedConnection> InnerOnConnectionAsync(ConnectionAdapterContext context)
        {
            SslStream sslStream;
            bool      certificateRequired;
            var       feature = new TlsConnectionFeature();

            context.Features.Set <ITlsConnectionFeature>(feature);

            if (_options.ClientCertificateMode == ClientCertificateMode.NoCertificate)
            {
                sslStream           = new SslStream(context.ConnectionStream);
                certificateRequired = false;
            }
            else
            {
                sslStream = new SslStream(context.ConnectionStream,
                                          leaveInnerStreamOpen: false,
                                          userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) =>
                {
                    if (certificate == null)
                    {
                        return(_options.ClientCertificateMode != ClientCertificateMode.RequireCertificate);
                    }

                    if (_options.ClientCertificateValidation == null)
                    {
                        if (sslPolicyErrors != SslPolicyErrors.None)
                        {
                            return(false);
                        }
                    }

                    var certificate2 = ConvertToX509Certificate2(certificate);
                    if (certificate2 == null)
                    {
                        return(false);
                    }

                    if (_options.ClientCertificateValidation != null)
                    {
                        if (!_options.ClientCertificateValidation(certificate2, chain, sslPolicyErrors))
                        {
                            return(false);
                        }
                    }

                    return(true);
                });

                certificateRequired = true;
            }

            var timeoutFeature = context.Features.Get <IConnectionTimeoutFeature>();

            timeoutFeature.SetTimeout(_options.HandshakeTimeout);

            try
            {
#if NETCOREAPP2_1
                // Adapt to the SslStream signature
                ServerCertificateSelectionCallback selector = null;
                if (_serverCertificateSelector != null)
                {
                    selector = (sender, name) =>
                    {
                        context.Features.Set(sslStream);
                        var cert = _serverCertificateSelector(context.ConnectionContext, name);
                        if (cert != null)
                        {
                            EnsureCertificateIsAllowedForServerAuth(cert);
                        }
                        return(cert);
                    };
                }

                var sslOptions = new SslServerAuthenticationOptions()
                {
                    ServerCertificate = _serverCertificate,
                    ServerCertificateSelectionCallback = selector,
                    ClientCertificateRequired          = certificateRequired,
                    EnabledSslProtocols            = _options.SslProtocols,
                    CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
                    ApplicationProtocols           = new List <SslApplicationProtocol>()
                };

                // This is order sensitive
                if ((_options.HttpProtocols & HttpProtocols.Http2) != 0)
                {
                    sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http2);
                }

                if ((_options.HttpProtocols & HttpProtocols.Http1) != 0)
                {
                    sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http11);
                }

                await sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None);
#else
                var serverCert = _serverCertificate;
                if (_serverCertificateSelector != null)
                {
                    context.Features.Set(sslStream);
                    serverCert = _serverCertificateSelector(context.ConnectionContext, null);
                    if (serverCert != null)
                    {
                        EnsureCertificateIsAllowedForServerAuth(serverCert);
                    }
                }
                await sslStream.AuthenticateAsServerAsync(serverCert, certificateRequired,
                                                          _options.SslProtocols, _options.CheckCertificateRevocation);
#endif
            }
            catch (OperationCanceledException)
            {
                _logger?.LogDebug(2, CoreStrings.AuthenticationTimedOut);
                sslStream.Dispose();
                return(_closedAdaptedConnection);
            }
            catch (IOException ex)
            {
                _logger?.LogDebug(1, ex, CoreStrings.AuthenticationFailed);
                sslStream.Dispose();
                return(_closedAdaptedConnection);
            }
            finally
            {
                timeoutFeature.CancelTimeout();
            }

#if NETCOREAPP2_1
            feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.Protocol;
            context.Features.Set <ITlsApplicationProtocolFeature>(feature);
#endif
            feature.ClientCertificate = ConvertToX509Certificate2(sslStream.RemoteCertificate);

            return(new HttpsAdaptedConnection(sslStream));
        }
예제 #10
0
        private async Task <IAdaptedConnection> InnerOnConnectionAsync(ConnectionAdapterContext context)
        {
            SslStream sslStream;
            bool      certificateRequired;
            var       feature = new Core.Internal.TlsConnectionFeature();

            context.Features.Set <ITlsConnectionFeature>(feature);
            context.Features.Set <ITlsHandshakeFeature>(feature);

            if (_options.ClientCertificateMode == ClientCertificateMode.NoCertificate)
            {
                sslStream           = new SslStream(context.ConnectionStream);
                certificateRequired = false;
            }
            else
            {
                sslStream = new SslStream(context.ConnectionStream,
                                          leaveInnerStreamOpen: false,
                                          userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) =>
                {
                    if (certificate == null)
                    {
                        return(_options.ClientCertificateMode != ClientCertificateMode.RequireCertificate);
                    }

                    if (_options.ClientCertificateValidation == null)
                    {
                        if (sslPolicyErrors != SslPolicyErrors.None)
                        {
                            return(false);
                        }
                    }

                    var certificate2 = ConvertToX509Certificate2(certificate);
                    if (certificate2 == null)
                    {
                        return(false);
                    }

                    if (_options.ClientCertificateValidation != null)
                    {
                        if (!_options.ClientCertificateValidation(certificate2, chain, sslPolicyErrors))
                        {
                            return(false);
                        }
                    }

                    return(true);
                });

                certificateRequired = true;
            }

            var timeoutFeature = context.Features.Get <IConnectionTimeoutFeature>();

            timeoutFeature.SetTimeout(_options.HandshakeTimeout);

            _options.OnHandshakeStarted?.Invoke();

            try
            {
                // Adapt to the SslStream signature
                ServerCertificateSelectionCallback selector = null;
                if (_serverCertificateSelector != null)
                {
                    selector = (sender, name) =>
                    {
                        context.Features.Set(sslStream);
                        var cert = _serverCertificateSelector(context.ConnectionContext, name);
                        if (cert != null)
                        {
                            EnsureCertificateIsAllowedForServerAuth(cert);
                        }
                        return(cert);
                    };
                }

                var sslOptions = new SslServerAuthenticationOptions
                {
                    ServerCertificate = _serverCertificate,
                    ServerCertificateSelectionCallback = selector,
                    ClientCertificateRequired          = certificateRequired,
                    EnabledSslProtocols            = _options.SslProtocols,
                    CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
                    ApplicationProtocols           = new List <SslApplicationProtocol>()
                };

                // This is order sensitive
                if ((_options.HttpProtocols & HttpProtocols.Http2) != 0)
                {
                    sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http2);
                    // https://tools.ietf.org/html/rfc7540#section-9.2.1
                    sslOptions.AllowRenegotiation = false;
                }

                if ((_options.HttpProtocols & HttpProtocols.Http1) != 0)
                {
                    sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http11);
                }

                await sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None);
            }
            catch (OperationCanceledException)
            {
                _logger?.LogDebug(2, CoreStrings.AuthenticationTimedOut);
                sslStream.Dispose();
                return(_closedAdaptedConnection);
            }
            catch (Exception ex) when(ex is IOException || ex is AuthenticationException)
            {
                _logger?.LogDebug(1, ex, CoreStrings.AuthenticationFailed);
                sslStream.Dispose();
                return(_closedAdaptedConnection);
            }
            finally
            {
                timeoutFeature.CancelTimeout();
            }

            feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.Protocol;
            context.Features.Set <ITlsApplicationProtocolFeature>(feature);
            feature.ClientCertificate    = 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;

            return(new HttpsAdaptedConnection(sslStream));
        }
예제 #11
0
        private bool EnsureAuthenticated(IChannelHandlerContext ctx)
        {
            var oldState = State;

            if (!oldState.HasAny(TlsHandlerState.AuthenticationStarted))
            {
                State = oldState | TlsHandlerState.Authenticating;
                if (_isServer)
                {
#if NETCOREAPP_2_0_GREATER || NETSTANDARD_2_0_GREATER
                    // Adapt to the SslStream signature
                    ServerCertificateSelectionCallback selector = null;
                    if (_serverCertificateSelector is object)
                    {
                        X509Certificate LocalServerCertificateSelection(object sender, string name)
                        {
                            ctx.GetAttribute(SslStreamAttrKey).Set(_sslStream);
                            var cert = _serverCertificateSelector(ctx, name);

                            if (cert is object)
                            {
                                EnsureCertificateIsAllowedForServerAuth(cert);
                            }
                            return(cert);
                        }

                        selector = new ServerCertificateSelectionCallback(LocalServerCertificateSelection);
                    }

                    var sslOptions = new SslServerAuthenticationOptions()
                    {
                        ServerCertificate = _serverCertificate,
                        ServerCertificateSelectionCallback = selector,
                        ClientCertificateRequired          = _serverSettings.NegotiateClientCertificate,
                        EnabledSslProtocols            = _serverSettings.EnabledProtocols,
                        CertificateRevocationCheckMode = _serverSettings.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
                        ApplicationProtocols           = _serverSettings.ApplicationProtocols // ?? new List<SslApplicationProtocol>()
                    };
                    if (_hasHttp2Protocol)
                    {
                        // https://tools.ietf.org/html/rfc7540#section-9.2.1
                        sslOptions.AllowRenegotiation = false;
                    }
                    _sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None)
                    .ContinueWith(s_handshakeCompletionCallback, this, TaskContinuationOptions.ExecuteSynchronously);
#else
                    var serverCert = _serverCertificate;
                    if (_serverCertificateSelector is object)
                    {
                        ctx.GetAttribute(SslStreamAttrKey).Set(_sslStream);
                        var serverCert2 = _serverCertificateSelector(ctx, null);
                        if (serverCert2 is object)
                        {
                            EnsureCertificateIsAllowedForServerAuth(serverCert2);
                            serverCert = serverCert2;
                        }
                    }
                    _sslStream.AuthenticateAsServerAsync(serverCert,
                                                         _serverSettings.NegotiateClientCertificate,
                                                         _serverSettings.EnabledProtocols,
                                                         _serverSettings.CheckCertificateRevocation)
                    .ContinueWith(s_handshakeCompletionCallback, this, TaskContinuationOptions.ExecuteSynchronously);
#endif
                }
                else
                {
#if NETCOREAPP_2_0_GREATER || NETSTANDARD_2_0_GREATER
                    LocalCertificateSelectionCallback selector = null;
                    if (_userCertSelector is object)
                    {
                        X509Certificate LocalCertificateSelection(object sender, string targetHost, X509CertificateCollection localCertificates, X509Certificate remoteCertificate, string[] acceptableIssuers)
                        {
                            ctx.GetAttribute(SslStreamAttrKey).Set(_sslStream);
                            return(_userCertSelector(ctx, targetHost, localCertificates, remoteCertificate, acceptableIssuers));
                        }

                        selector = new LocalCertificateSelectionCallback(LocalCertificateSelection);
                    }
                    var sslOptions = new SslClientAuthenticationOptions()
                    {
                        TargetHost                        = _clientSettings.TargetHost,
                        ClientCertificates                = _clientSettings.X509CertificateCollection,
                        EnabledSslProtocols               = _clientSettings.EnabledProtocols,
                        CertificateRevocationCheckMode    = _clientSettings.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
                        LocalCertificateSelectionCallback = selector,
                        ApplicationProtocols              = _clientSettings.ApplicationProtocols
                    };
                    if (_hasHttp2Protocol)
                    {
                        // https://tools.ietf.org/html/rfc7540#section-9.2.1
                        sslOptions.AllowRenegotiation = false;
                    }
                    _sslStream.AuthenticateAsClientAsync(sslOptions, CancellationToken.None)
                    .ContinueWith(s_handshakeCompletionCallback, this, TaskContinuationOptions.ExecuteSynchronously);
#else
                    _sslStream.AuthenticateAsClientAsync(_clientSettings.TargetHost,
                                                         _clientSettings.X509CertificateCollection,
                                                         _clientSettings.EnabledProtocols,
                                                         _clientSettings.CheckCertificateRevocation)
                    .ContinueWith(s_handshakeCompletionCallback, this, TaskContinuationOptions.ExecuteSynchronously);
#endif
                }
                return(false);
            }

            return(oldState.Has(TlsHandlerState.Authenticated));
        }
예제 #12
0
        private static CipherSuite SelectCipherSuite(CipherSuitePluginManager pluginManager,
                                                     ProtocolVersion clientVersion, ProtocolVersion minVersion, ProtocolVersion maxVersion,
                                                     List <UInt16> clientSuites, List <UInt16> serverSuites,
                                                     ServerCertificateSelectionCallback certificateSelectionCallback,
                                                     List <X509CertificateCollection> availableCertificates)
        {
            if (clientVersion < minVersion)
            {
                throw new AlertException(AlertDescription.ProtocolVersion,
                                         "Offered client version " + clientVersion +
                                         " lower than minimum supported version " + minVersion);
            }

            // Initialize our return value as null
            CipherSuite selectedCipherSuite = null;

            // Run as long as we either select a cipher suite or run out of versions
            ProtocolVersion selectedVersion = clientVersion < maxVersion ? clientVersion : maxVersion;

            while (selectedCipherSuite == null)
            {
                foreach (UInt16 id in clientSuites)
                {
                    if (!serverSuites.Contains(id))
                    {
                        continue;
                    }

                    // Try initializing the cipher suite based on ID
                    selectedCipherSuite = pluginManager.GetCipherSuite(selectedVersion, id);
                    if (selectedCipherSuite == null)
                    {
                        continue;
                    }

                    // Try selecting a suitable certificate for this cipher suite
                    int certificateIndex = certificateSelectionCallback(selectedCipherSuite, availableCertificates.ToArray());
                    if (certificateIndex >= 0 && certificateIndex < availableCertificates.Count)
                    {
                        // We finally found the valid suite, break out from the loop
                        break;
                    }
                    // No certificate was found for the suite, ignore
                    selectedCipherSuite = null;
                }

                if (selectedCipherSuite != null)
                {
                    break;
                }
                if (selectedVersion == minVersion)
                {
                    break;
                }
                selectedVersion = selectedVersion.PreviousProtocolVersion;
            }

            if (selectedCipherSuite == null)
            {
                throw new AlertException(AlertDescription.HandshakeFailure,
                                         "None of the cipher suites offered by client is accepted");
            }
            return(selectedCipherSuite);
        }
예제 #13
0
 public SslListenerBuilder WithServerCertificateSelectionCallback(ServerCertificateSelectionCallback callback)
 {
     _options.ServerCertificateSelectionCallback = callback;
     return(this);
 }
예제 #14
0
        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.UnsafeRegisterCancellation(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);

#if NETCOREAPP
                        await sslStream.AuthenticateAsServerAsync(sslOptions.Value, cancellationTokeSource.Token);
#else
                        await sslStream.AuthenticateAsServerAsync(
                            sslOptions.ServerCertificate,
                            sslOptions.ClientCertificateRequired,
                            sslOptions.EnabledSslProtocols,
                            sslOptions.CertificateRevocationCheckMode == X509RevocationMode.Online);
#endif
                    }
                    catch (OperationCanceledException ex)
                    {
                        _logger?.LogWarning(2, ex, "Authentication timed out");
#if NETCOREAPP
                        await sslStream.DisposeAsync();
#else
                        sslStream.Dispose();
#endif
                        return;
                    }
                    catch (Exception ex)
                    {
                        _logger?.LogWarning(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;
            }
        }
예제 #15
0
        private async Task InnerOnConnectionAsync(ConnectionContext context)
        {
            SslStream sslStream;
            bool      certificateRequired;
            var       feature = new Core.Internal.TlsConnectionFeature();

            context.Features.Set <ITlsConnectionFeature>(feature);
            context.Features.Set <ITlsHandshakeFeature>(feature);

            // TODO: Handle the cases where this can be null
            var memoryPoolFeature = context.Features.Get <IMemoryPoolFeature>();

            var inputPipeOptions = new PipeOptions
                                   (
                pool: memoryPoolFeature.MemoryPool,
                readerScheduler: _options.Scheduler,
                writerScheduler: PipeScheduler.Inline,
                pauseWriterThreshold: _options.MaxInputBufferSize ?? 0,
                resumeWriterThreshold: _options.MaxInputBufferSize / 2 ?? 0,
                useSynchronizationContext: false,
                minimumSegmentSize: memoryPoolFeature.MemoryPool.GetMinimumSegmentSize()
                                   );

            var outputPipeOptions = new PipeOptions
                                    (
                pool: memoryPoolFeature.MemoryPool,
                readerScheduler: PipeScheduler.Inline,
                writerScheduler: PipeScheduler.Inline,
                pauseWriterThreshold: _options.MaxOutputBufferSize ?? 0,
                resumeWriterThreshold: _options.MaxOutputBufferSize / 2 ?? 0,
                useSynchronizationContext: false,
                minimumSegmentSize: memoryPoolFeature.MemoryPool.GetMinimumSegmentSize()
                                    );

            // TODO: eventually make SslDuplexStream : Stream, IDuplexPipe to avoid RawStream allocation and pipe allocations
            var adaptedPipeline = new AdaptedPipeline(context.Transport, new Pipe(inputPipeOptions), new Pipe(outputPipeOptions), _logger, memoryPoolFeature.MemoryPool.GetMinimumAllocSize());
            var transportStream = adaptedPipeline.TransportStream;

            if (_options.ClientCertificateMode == ClientCertificateMode.NoCertificate)
            {
                sslStream           = new SslStream(transportStream);
                certificateRequired = false;
            }
            else
            {
                sslStream = new SslStream(transportStream,
                                          leaveInnerStreamOpen: false,
                                          userCertificateValidationCallback: (sender, certificate, chain, sslPolicyErrors) =>
                {
                    if (certificate == null)
                    {
                        return(_options.ClientCertificateMode != ClientCertificateMode.RequireCertificate);
                    }

                    if (_options.ClientCertificateValidation == null)
                    {
                        if (sslPolicyErrors != SslPolicyErrors.None)
                        {
                            return(false);
                        }
                    }

                    var certificate2 = ConvertToX509Certificate2(certificate);
                    if (certificate2 == null)
                    {
                        return(false);
                    }

                    if (_options.ClientCertificateValidation != null)
                    {
                        if (!_options.ClientCertificateValidation(certificate2, chain, sslPolicyErrors))
                        {
                            return(false);
                        }
                    }

                    return(true);
                });

                certificateRequired = true;
            }

            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 (_serverCertificateSelector != null)
                        {
                            selector = (sender, name) =>
                            {
                                context.Features.Set(sslStream);
                                var cert = _serverCertificateSelector(context, name);
                                if (cert != null)
                                {
                                    EnsureCertificateIsAllowedForServerAuth(cert);
                                }
                                return(cert);
                            };
                        }

                        var sslOptions = new SslServerAuthenticationOptions
                        {
                            ServerCertificate = _serverCertificate,
                            ServerCertificateSelectionCallback = selector,
                            ClientCertificateRequired          = certificateRequired,
                            EnabledSslProtocols            = _options.SslProtocols,
                            CertificateRevocationCheckMode = _options.CheckCertificateRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck,
                            ApplicationProtocols           = new List <SslApplicationProtocol>()
                        };

                        // This is order sensitive
                        if ((_options.HttpProtocols & HttpProtocols.Http2) != 0)
                        {
                            sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http2);
                            // https://tools.ietf.org/html/rfc7540#section-9.2.1
                            sslOptions.AllowRenegotiation = false;
                        }

                        if ((_options.HttpProtocols & HttpProtocols.Http1) != 0)
                        {
                            sslOptions.ApplicationProtocols.Add(SslApplicationProtocol.Http11);
                        }

                        _options.OnAuthenticate?.Invoke(context, sslOptions);

                        await sslStream.AuthenticateAsServerAsync(sslOptions, CancellationToken.None);
                    }
                    catch (OperationCanceledException)
                    {
                        _logger?.LogDebug(2, CoreStrings.AuthenticationTimedOut);
                        sslStream.Dispose();
                        return;
                    }
                    catch (Exception ex) when(ex is IOException || ex is AuthenticationException)
                    {
                        _logger?.LogDebug(1, ex, CoreStrings.AuthenticationFailed);
                        sslStream.Dispose();
                        return;
                    }
                }

            feature.ApplicationProtocol = sslStream.NegotiatedApplicationProtocol.Protocol;
            context.Features.Set <ITlsApplicationProtocolFeature>(feature);
            feature.ClientCertificate    = 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 original = context.Transport;

            try
            {
                context.Transport = adaptedPipeline;

                using (sslStream)
                {
                    try
                    {
                        adaptedPipeline.RunAsync(sslStream);

                        await _next(context);
                    }
                    finally
                    {
                        await adaptedPipeline.CompleteAsync();
                    }
                }
            }
            finally
            {
                // Restore the original so that it gets closed appropriately
                context.Transport = original;
            }
        }