예제 #1
0
    public void WildcardOnlyMatchesNullServerNameDueToNoAlpn()
    {
        var sniDictionary = new Dictionary <string, SniConfig>
        {
            {
                "*",
                new SniConfig
                {
                    Certificate = new CertificateConfig
                    {
                        Path = "WildcardOnly"
                    }
                }
            }
        };

        var mockCertificateConfigLoader = new MockCertificateConfigLoader();
        var pathDictionary = mockCertificateConfigLoader.CertToPathDictionary;

        var sniOptionsSelector = new SniOptionsSelector(
            "TestEndpointName",
            sniDictionary,
            mockCertificateConfigLoader,
            fallbackHttpsOptions: new HttpsConnectionAdapterOptions(),
            fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
            logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

        var(options, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), null);
        Assert.Equal("WildcardOnly", pathDictionary[options.ServerCertificate]);
    }
예제 #2
0
    public void PrefersClientCertificateModeDefinedInSniConfig()
    {
        var sniDictionary = new Dictionary <string, SniConfig>
        {
            {
                "www.example.org",
                new SniConfig
                {
                    ClientCertificateMode = ClientCertificateMode.DelayCertificate,
                    Certificate           = new CertificateConfig()
                }
            }
        };

        var sniOptionsSelector = new SniOptionsSelector(
            "TestEndpointName",
            sniDictionary,
            new MockCertificateConfigLoader(),
            new HttpsConnectionAdapterOptions
        {
            ClientCertificateMode = ClientCertificateMode.AllowCertificate
        },
            fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
            logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

        var(options, certMode) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "www.example.org");

        Assert.Equal(ClientCertificateMode.DelayCertificate, certMode);
        Assert.False(options.ClientCertificateRequired);

        Assert.NotNull(options.RemoteCertificateValidationCallback);
        // The RemoteCertificateValidationCallback should first check if the certificate is null and return true since it's optional.
        Assert.True(options.RemoteCertificateValidationCallback(sender: null, certificate: null, chain: null, SslPolicyErrors.None));
    }
예제 #3
0
    public void CachesSslServerAuthenticationOptions()
    {
        var sniDictionary = new Dictionary <string, SniConfig>
        {
            {
                "www.example.org",
                new SniConfig
                {
                    Certificate = new CertificateConfig()
                }
            }
        };

        var sniOptionsSelector = new SniOptionsSelector(
            "TestEndpointName",
            sniDictionary,
            new MockCertificateConfigLoader(),
            fallbackHttpsOptions: new HttpsConnectionAdapterOptions(),
            fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
            logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

        var(options1, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "www.example.org");
        var(options2, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "www.example.org");
        Assert.Same(options1, options2);
    }
예제 #4
0
    public void PrefersSslProtocolsDefinedInSniConfig()
    {
        var sniDictionary = new Dictionary <string, SniConfig>
        {
            {
                "www.example.org",
                new SniConfig
                {
#pragma warning disable SYSLIB0039 // TLS 1.0 and 1.1 are obsolete
                    SslProtocols = SslProtocols.Tls13 | SslProtocols.Tls11,
#pragma warning restore SYSLIB0039
                    Certificate = new CertificateConfig()
                }
            }
        };

        var sniOptionsSelector = new SniOptionsSelector(
            "TestEndpointName",
            sniDictionary,
            new MockCertificateConfigLoader(),
            new HttpsConnectionAdapterOptions
        {
            SslProtocols = SslProtocols.Tls13
        },
            fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
            logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

        var(options, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "www.example.org");
#pragma warning disable SYSLIB0039 // TLS 1.0 and 1.1 are obsolete
        Assert.Equal(SslProtocols.Tls13 | SslProtocols.Tls11, options.EnabledSslProtocols);
#pragma warning restore SYSLIB0039
    }
예제 #5
0
    public void FallsBackToFallbackSslProtocols()
    {
        var sniDictionary = new Dictionary <string, SniConfig>
        {
            {
                "www.example.org",
                new SniConfig
                {
                    Certificate = new CertificateConfig()
                }
            }
        };

        var sniOptionsSelector = new SniOptionsSelector(
            "TestEndpointName",
            sniDictionary,
            new MockCertificateConfigLoader(),
            new HttpsConnectionAdapterOptions
        {
            SslProtocols = SslProtocols.Tls13
        },
            fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
            logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

        var(options, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "www.example.org");
        Assert.Equal(SslProtocols.Tls13, options.EnabledSslProtocols);
    }
예제 #6
0
    public void ConfiguresAlpnBasedOnConfiguredHttpProtocols()
    {
        var sniDictionary = new Dictionary <string, SniConfig>
        {
            {
                "www.example.org",
                new SniConfig
                {
                    // I'm not using Http1AndHttp2 or Http2 because I don't want to account for
                    // validation and normalization. Other tests cover that.
                    Protocols   = HttpProtocols.Http1,
                    Certificate = new CertificateConfig()
                }
            }
        };

        var sniOptionsSelector = new SniOptionsSelector(
            "TestEndpointName",
            sniDictionary,
            new MockCertificateConfigLoader(),
            new HttpsConnectionAdapterOptions(),
            fallbackHttpProtocols: HttpProtocols.None,
            logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

        var(options, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "www.example.org");
        var alpnList = options.ApplicationProtocols;

        Assert.NotNull(alpnList);
        var protocol = Assert.Single(alpnList);

        Assert.Equal(SslApplicationProtocol.Http11, protocol);
    }
예제 #7
0
    public void FallsBackToFallbackHttpProtocols()
    {
        var sniDictionary = new Dictionary <string, SniConfig>
        {
            {
                "www.example.org",
                new SniConfig
                {
                    Certificate = new CertificateConfig()
                }
            }
        };

        var sniOptionsSelector = new SniOptionsSelector(
            "TestEndpointName",
            sniDictionary,
            new MockCertificateConfigLoader(),
            new HttpsConnectionAdapterOptions(),
            fallbackHttpProtocols: HttpProtocols.Http1,
            logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

        var mockConnectionContext = new MockConnectionContext();

        sniOptionsSelector.GetOptions(mockConnectionContext, "www.example.org");

        var httpProtocolsFeature = mockConnectionContext.Features.Get <HttpProtocolsFeature>();

        Assert.NotNull(httpProtocolsFeature);
        Assert.Equal(HttpProtocols.Http1, httpProtocolsFeature.HttpProtocols);
    }
예제 #8
0
    public void FallsBackToFallbackClientCertificateMode()
    {
        var sniDictionary = new Dictionary <string, SniConfig>
        {
            {
                "www.example.org",
                new SniConfig
                {
                    Certificate = new CertificateConfig()
                }
            }
        };

        var sniOptionsSelector = new SniOptionsSelector(
            "TestEndpointName",
            sniDictionary,
            new MockCertificateConfigLoader(),
            new HttpsConnectionAdapterOptions
        {
            ClientCertificateMode = ClientCertificateMode.AllowCertificate
        },
            fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
            logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

        var(options, certMode) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "www.example.org");

        Assert.Equal(ClientCertificateMode.AllowCertificate, certMode);
        // Despite the confusing name, ClientCertificateRequired being true simply requests a certificate from the client, but doesn't require it.
        Assert.True(options.ClientCertificateRequired);

        Assert.NotNull(options.RemoteCertificateValidationCallback);
        // The RemoteCertificateValidationCallback should see we're in the AllowCertificate mode and return true.
        Assert.True(options.RemoteCertificateValidationCallback(sender: null, certificate: null, chain: null, SslPolicyErrors.None));
    }
예제 #9
0
    public void FallsBackToHttpsConnectionAdapterServerCertificateSelectorOverServerCertificate()
    {
        var sniDictionary = new Dictionary <string, SniConfig>
        {
            { "www.example.org", new SniConfig() }
        };

        var selectorCertificate = _x509Certificate2;

        var fallbackOptions = new HttpsConnectionAdapterOptions
        {
            ServerCertificate         = new X509Certificate2(Array.Empty <byte>()),
            ServerCertificateSelector = (context, serverName) => selectorCertificate
        };

        var sniOptionsSelector = new SniOptionsSelector(
            "TestEndpointName",
            sniDictionary,
            new MockCertificateConfigLoader(),
            fallbackOptions,
            fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
            logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

        var(options, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "www.example.org");
        Assert.Same(selectorCertificate, options.ServerCertificate);
    }
예제 #10
0
        public void ClonesSslServerAuthenticationOptionsIfTheFallbackServerCertificateSelectorIsUsed()
        {
            var sniDictionary = new Dictionary <string, SniConfig>
            {
                {
                    "selector.example.org",
                    new SniConfig()
                },
                {
                    "config.example.org",
                    new SniConfig
                    {
                        Certificate = new CertificateConfig()
                    }
                }
            };

            var selectorCertificate = _x509Certificate2;

            var fallbackOptions = new HttpsConnectionAdapterOptions
            {
                ServerCertificate         = new X509Certificate2(),
                ServerCertificateSelector = (context, serverName) => selectorCertificate
            };

            var sniOptionsSelector = new SniOptionsSelector(
                "TestEndpointName",
                sniDictionary,
                new MockCertificateConfigLoader(),
                fallbackOptions,
                fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
                logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

            var selectorOptions1 = sniOptionsSelector.GetOptions(new MockConnectionContext(), "selector.example.org");

            Assert.Same(selectorCertificate, selectorOptions1.ServerCertificate);

            var selectorOptions2 = sniOptionsSelector.GetOptions(new MockConnectionContext(), "selector.example.org");

            Assert.Same(selectorCertificate, selectorOptions2.ServerCertificate);

            // The SslServerAuthenticationOptions were cloned because the cert came from the ServerCertificateSelector fallback.
            Assert.NotSame(selectorOptions1, selectorOptions2);

            var configOptions1 = sniOptionsSelector.GetOptions(new MockConnectionContext(), "config.example.org");

            Assert.NotSame(selectorCertificate, configOptions1.ServerCertificate);

            var configOptions2 = sniOptionsSelector.GetOptions(new MockConnectionContext(), "config.example.org");

            Assert.NotSame(selectorCertificate, configOptions2.ServerCertificate);

            // The SslServerAuthenticationOptions don't need to be cloned if a static cert is defined in config for the given server name.
            Assert.Same(configOptions1, configOptions2);
        }
예제 #11
0
        public void ServerNameMatchingIsCaseInsensitive()
        {
            var sniDictionary = new Dictionary <string, SniConfig>
            {
                {
                    "Www.Example.Org",
                    new SniConfig
                    {
                        Certificate = new CertificateConfig
                        {
                            Path = "Exact"
                        }
                    }
                },
                {
                    "*.Example.Org",
                    new SniConfig
                    {
                        Certificate = new CertificateConfig
                        {
                            Path = "WildcardPrefix"
                        }
                    }
                }
            };

            var mockCertificateConfigLoader = new MockCertificateConfigLoader();
            var pathDictionary = mockCertificateConfigLoader.CertToPathDictionary;

            var sniOptionsSelector = new SniOptionsSelector(
                "TestEndpointName",
                sniDictionary,
                mockCertificateConfigLoader,
                fallbackHttpsOptions: new HttpsConnectionAdapterOptions(),
                fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
                logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

            var wwwSubdomainOptions = sniOptionsSelector.GetOptions(new MockConnectionContext(), "wWw.eXample.oRg");

            Assert.Equal("Exact", pathDictionary[wwwSubdomainOptions.ServerCertificate]);

            var baSubdomainOptions = sniOptionsSelector.GetOptions(new MockConnectionContext(), "B.a.eXample.oRg");

            Assert.Equal("WildcardPrefix", pathDictionary[baSubdomainOptions.ServerCertificate]);

            var aSubdomainOptions = sniOptionsSelector.GetOptions(new MockConnectionContext(), "A.eXample.oRg");

            Assert.Equal("WildcardPrefix", pathDictionary[aSubdomainOptions.ServerCertificate]);
        }
예제 #12
0
        public void PerfersLongerWildcardPrefixOverShorterWildcardPrefix()
        {
            var sniDictionary = new Dictionary <string, SniConfig>
            {
                {
                    "*.a.example.org",
                    new SniConfig
                    {
                        Certificate = new CertificateConfig
                        {
                            Path = "Long"
                        }
                    }
                },
                {
                    "*.example.org",
                    new SniConfig
                    {
                        Certificate = new CertificateConfig
                        {
                            Path = "Short"
                        }
                    }
                }
            };

            var mockCertificateConfigLoader = new MockCertificateConfigLoader();
            var pathDictionary = mockCertificateConfigLoader.CertToPathDictionary;

            var sniOptionsSelector = new SniOptionsSelector(
                "TestEndpointName",
                sniDictionary,
                mockCertificateConfigLoader,
                fallbackHttpsOptions: new HttpsConnectionAdapterOptions(),
                fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
                logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

            var baSubdomainOptions = sniOptionsSelector.GetOptions(new MockConnectionContext(), "b.a.example.org");

            Assert.Equal("Long", pathDictionary[baSubdomainOptions.ServerCertificate]);

            // "*.a.example.org" is preferred over "*.example.org", but "a.example.org" doesn't match "*.a.example.org".
            var aSubdomainOptions = sniOptionsSelector.GetOptions(new MockConnectionContext(), "a.example.org");

            Assert.Equal("Short", pathDictionary[aSubdomainOptions.ServerCertificate]);
        }
예제 #13
0
    public void GetOptionsThrowsAnAuthenticationExceptionIfThereIsNoMatchingSniSection()
    {
        var sniOptionsSelector = new SniOptionsSelector(
            "TestEndpointName",
            new Dictionary <string, SniConfig>(),
            new MockCertificateConfigLoader(),
            fallbackHttpsOptions: new HttpsConnectionAdapterOptions(),
            fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
            logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

        var authExWithServerName = Assert.Throws <AuthenticationException>(() => sniOptionsSelector.GetOptions(new MockConnectionContext(), "example.org"));

        Assert.Equal(CoreStrings.FormatSniNotConfiguredForServerName("example.org", "TestEndpointName"), authExWithServerName.Message);

        var authExWithoutServerName = Assert.Throws <AuthenticationException>(() => sniOptionsSelector.GetOptions(new MockConnectionContext(), null));

        Assert.Equal(CoreStrings.FormatSniNotConfiguredToAllowNoServerName("TestEndpointName"), authExWithoutServerName.Message);
    }
예제 #14
0
    public void MultipleWildcardPrefixServerNamesOfSameLengthAreAllowed()
    {
        var sniDictionary = new Dictionary <string, SniConfig>
        {
            {
                "*.a.example.org",
                new SniConfig
                {
                    Certificate = new CertificateConfig
                    {
                        Path = "a"
                    }
                }
            },
            {
                "*.b.example.org",
                new SniConfig
                {
                    Certificate = new CertificateConfig
                    {
                        Path = "b"
                    }
                }
            }
        };

        var mockCertificateConfigLoader = new MockCertificateConfigLoader();
        var pathDictionary = mockCertificateConfigLoader.CertToPathDictionary;

        var sniOptionsSelector = new SniOptionsSelector(
            "TestEndpointName",
            sniDictionary,
            mockCertificateConfigLoader,
            fallbackHttpsOptions: new HttpsConnectionAdapterOptions(),
            fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
            logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

        var(aSubdomainOptions, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "c.a.example.org");
        Assert.Equal("a", pathDictionary[aSubdomainOptions.ServerCertificate]);

        var(bSubdomainOptions, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "c.b.example.org");
        Assert.Equal("b", pathDictionary[bSubdomainOptions.ServerCertificate]);
    }
예제 #15
0
        public void ClonesSslServerAuthenticationOptionsIfAnOnAuthenticateCallbackIsDefined()
        {
            var sniDictionary = new Dictionary <string, SniConfig>
            {
                {
                    "www.example.org",
                    new SniConfig
                    {
                        Certificate = new CertificateConfig()
                    }
                }
            };

            SslServerAuthenticationOptions lastSeenSslOptions = null;

            var fallbackOptions = new HttpsConnectionAdapterOptions
            {
                OnAuthenticate = (context, sslOptions) =>
                {
                    lastSeenSslOptions = sslOptions;
                }
            };

            var sniOptionsSelector = new SniOptionsSelector(
                "TestEndpointName",
                sniDictionary,
                new MockCertificateConfigLoader(),
                fallbackOptions,
                fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
                logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

            var options1 = sniOptionsSelector.GetOptions(new MockConnectionContext(), "www.example.org");

            Assert.Same(lastSeenSslOptions, options1);

            var options2 = sniOptionsSelector.GetOptions(new MockConnectionContext(), "www.example.org");

            Assert.Same(lastSeenSslOptions, options2);

            Assert.NotSame(options1, options2);
        }
예제 #16
0
    public void FallsBackToHttpsConnectionAdapterCertificate()
    {
        var sniDictionary = new Dictionary <string, SniConfig>
        {
            { "www.example.org", new SniConfig() }
        };
        var fallbackOptions = new HttpsConnectionAdapterOptions
        {
            ServerCertificate = new X509Certificate2(TestResources.GetCertPath("aspnetdevcert.pfx"), "testPassword")
        };

        var sniOptionsSelector = new SniOptionsSelector(
            "TestEndpointName",
            sniDictionary,
            new MockCertificateConfigLoader(),
            fallbackOptions,
            fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
            logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

        var(options, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "www.example.org");
        Assert.Same(fallbackOptions.ServerCertificate, options.ServerCertificate);
    }
예제 #17
0
    public void PrefersExactMatchOverWildcardPrefixOverWildcardOnly()
    {
        var sniDictionary = new Dictionary <string, SniConfig>
        {
            {
                "www.example.org",
                new SniConfig
                {
                    Certificate = new CertificateConfig
                    {
                        Path = "Exact"
                    }
                }
            },
            {
                "*.example.org",
                new SniConfig
                {
                    Certificate = new CertificateConfig
                    {
                        Path = "WildcardPrefix"
                    }
                }
            },
            {
                "*",
                new SniConfig
                {
                    Certificate = new CertificateConfig
                    {
                        Path = "WildcardOnly"
                    }
                }
            }
        };

        var mockCertificateConfigLoader = new MockCertificateConfigLoader();
        var pathDictionary = mockCertificateConfigLoader.CertToPathDictionary;

        var sniOptionsSelector = new SniOptionsSelector(
            "TestEndpointName",
            sniDictionary,
            mockCertificateConfigLoader,
            fallbackHttpsOptions: new HttpsConnectionAdapterOptions(),
            fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
            logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

        var(wwwSubdomainOptions, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "www.example.org");
        Assert.Equal("Exact", pathDictionary[wwwSubdomainOptions.ServerCertificate]);

        var(baSubdomainOptions, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "b.a.example.org");
        Assert.Equal("WildcardPrefix", pathDictionary[baSubdomainOptions.ServerCertificate]);

        var(aSubdomainOptions, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "a.example.org");
        Assert.Equal("WildcardPrefix", pathDictionary[aSubdomainOptions.ServerCertificate]);

        // "*.example.org" is preferred over "*", but "*.example.org" doesn't match "example.org".
        // REVIEW: Are we OK with "example.org" matching "*" instead of "*.example.org"? It feels annoying to me to have to configure example.org twice.
        // Unfortunately, the alternative would have "a.example.org" match "*.a.example.org" before "*.example.org", and that just seems wrong.
        var(noSubdomainOptions, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "example.org");
        Assert.Equal("WildcardOnly", pathDictionary[noSubdomainOptions.ServerCertificate]);

        var(anotherTldOptions, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "dot.net");
        Assert.Equal("WildcardOnly", pathDictionary[anotherTldOptions.ServerCertificate]);
    }
예제 #18
0
    public void FullChainCertsCanBeLoaded()
    {
        var sniDictionary = new Dictionary <string, SniConfig>
        {
            {
                "Www.Example.Org",
                new SniConfig
                {
                    Certificate = new CertificateConfig
                    {
                        Path = "Exact"
                    }
                }
            },
            {
                "*.Example.Org",
                new SniConfig
                {
                    Certificate = new CertificateConfig
                    {
                        Path = "WildcardPrefix"
                    }
                }
            }
        };

        var mockCertificateConfigLoader = new MockCertificateConfigLoader();
        var pathDictionary      = mockCertificateConfigLoader.CertToPathDictionary;
        var fullChainDictionary = mockCertificateConfigLoader.CertToFullChain;

        var sniOptionsSelector = new SniOptionsSelector(
            "TestEndpointName",
            sniDictionary,
            mockCertificateConfigLoader,
            fallbackHttpsOptions: new HttpsConnectionAdapterOptions(),
            fallbackHttpProtocols: HttpProtocols.Http1AndHttp2,
            logger: Mock.Of <ILogger <HttpsConnectionMiddleware> >());

        var(wwwSubdomainOptions, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "wWw.eXample.oRg");
        Assert.Equal("Exact", pathDictionary[wwwSubdomainOptions.ServerCertificate]);

        var(baSubdomainOptions, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "B.a.eXample.oRg");
        Assert.Equal("WildcardPrefix", pathDictionary[baSubdomainOptions.ServerCertificate]);

        var(aSubdomainOptions, _) = sniOptionsSelector.GetOptions(new MockConnectionContext(), "A.eXample.oRg");
        Assert.Equal("WildcardPrefix", pathDictionary[aSubdomainOptions.ServerCertificate]);

        /*
         * Chain test certs were created using smallstep cli: https://github.com/smallstep/cli
         * root_ca(pwd: testroot) ->
         * intermediate_ca 1(pwd: inter) ->
         * intermediate_ca 2(pwd: inter) ->
         * leaf.com(pwd: leaf) (bundled)
         */
        var fullChain = fullChainDictionary[aSubdomainOptions.ServerCertificate];

        // Expect intermediate 2 cert and leaf.com
        Assert.Equal(2, fullChain.Count);
        Assert.Equal("CN=leaf.com", fullChain[0].Subject);
        Assert.Equal("CN=Test Intermediate CA 2", fullChain[0].IssuerName.Name);
        Assert.Equal("CN=Test Intermediate CA 2", fullChain[1].Subject);
        Assert.Equal("CN=Test Intermediate CA 1", fullChain[1].IssuerName.Name);
    }
예제 #19
0
    public void CloneSslOptionsClonesAllProperties()
    {
        var propertyNames = typeof(SslServerAuthenticationOptions).GetProperties().Select(property => property.Name).ToList();

        CipherSuitesPolicy cipherSuitesPolicy = null;

        if (!OperatingSystem.IsWindows())
        {
            try
            {
                // The CipherSuitesPolicy ctor throws a PlatformNotSupportedException on Windows.
                cipherSuitesPolicy = new CipherSuitesPolicy(new[] { TlsCipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 });
            }
            catch (PlatformNotSupportedException)
            {
                // The CipherSuitesPolicy ctor throws a PlatformNotSupportedException on Ubuntu 16.04.
                // I don't know exactly which other distros/versions throw PNEs, but it isn't super relevant to this test,
                // so let's just swallow this exception.
            }
        }

        // Set options properties to non-default values to verify they're copied.
        var options = new SslServerAuthenticationOptions
        {
            // Defaults to true
            AllowRenegotiation = false,
            // Defaults to null
            ApplicationProtocols = new List <SslApplicationProtocol> {
                SslApplicationProtocol.Http2
            },
            // Defaults to X509RevocationMode.NoCheck
            CertificateRevocationCheckMode = X509RevocationMode.Offline,
            // Defaults to null
            CipherSuitesPolicy = cipherSuitesPolicy,
            // Defaults to false
            ClientCertificateRequired = true,
            // Defaults to SslProtocols.None
#pragma warning disable SYSLIB0039 // TLS 1.0 and 1.1 are obsolete
            EnabledSslProtocols = SslProtocols.Tls13 | SslProtocols.Tls11,
#pragma warning restore SYSLIB0039
#pragma warning disable SYSLIB0040 // EncryptionPolicy.NoEncryption is obsolete
            // Defaults to EncryptionPolicy.RequireEncryption
            EncryptionPolicy = EncryptionPolicy.NoEncryption,
#pragma warning restore SYSLIB0040
            // Defaults to null
            RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true,
            // Defaults to null
            ServerCertificate = new X509Certificate2(Array.Empty <byte>()),
            // Defaults to null
            ServerCertificateContext = SslStreamCertificateContext.Create(_x509Certificate2, additionalCertificates: null, offline: true),
            // Defaults to null
            ServerCertificateSelectionCallback = (sender, serverName) => null,
            // Defaults to null
            CertificateChainPolicy = new X509ChainPolicy(),
        };

        var clonedOptions = SniOptionsSelector.CloneSslOptions(options);

        Assert.NotSame(options, clonedOptions);

        Assert.Equal(options.AllowRenegotiation, clonedOptions.AllowRenegotiation);
        Assert.True(propertyNames.Remove(nameof(options.AllowRenegotiation)));

        // Ensure the List<SslApplicationProtocol> is also cloned since it could be modified by a user callback.
        Assert.NotSame(options.ApplicationProtocols, clonedOptions.ApplicationProtocols);
        Assert.Equal(Assert.Single(options.ApplicationProtocols), Assert.Single(clonedOptions.ApplicationProtocols));
        Assert.True(propertyNames.Remove(nameof(options.ApplicationProtocols)));

        Assert.Equal(options.CertificateRevocationCheckMode, clonedOptions.CertificateRevocationCheckMode);
        Assert.True(propertyNames.Remove(nameof(options.CertificateRevocationCheckMode)));

        Assert.Same(options.CipherSuitesPolicy, clonedOptions.CipherSuitesPolicy);
        Assert.True(propertyNames.Remove(nameof(options.CipherSuitesPolicy)));

        Assert.Equal(options.ClientCertificateRequired, clonedOptions.ClientCertificateRequired);
        Assert.True(propertyNames.Remove(nameof(options.ClientCertificateRequired)));

        Assert.Equal(options.EnabledSslProtocols, clonedOptions.EnabledSslProtocols);
        Assert.True(propertyNames.Remove(nameof(options.EnabledSslProtocols)));

        Assert.Equal(options.EncryptionPolicy, clonedOptions.EncryptionPolicy);
        Assert.True(propertyNames.Remove(nameof(options.EncryptionPolicy)));

        Assert.Same(options.RemoteCertificateValidationCallback, clonedOptions.RemoteCertificateValidationCallback);
        Assert.True(propertyNames.Remove(nameof(options.RemoteCertificateValidationCallback)));

        // Technically the ServerCertificate could be reset/reimported, but I'm hoping this is uncommon. Trying to clone the certificate and/or context seems risky.
        Assert.Same(options.ServerCertificate, clonedOptions.ServerCertificate);
        Assert.True(propertyNames.Remove(nameof(options.ServerCertificate)));

        Assert.Same(options.ServerCertificateContext, clonedOptions.ServerCertificateContext);
        Assert.True(propertyNames.Remove(nameof(options.ServerCertificateContext)));

        Assert.Same(options.ServerCertificateSelectionCallback, clonedOptions.ServerCertificateSelectionCallback);
        Assert.True(propertyNames.Remove(nameof(options.ServerCertificateSelectionCallback)));

        Assert.Same(options.CertificateChainPolicy, clonedOptions.CertificateChainPolicy);
        Assert.True(propertyNames.Remove(nameof(options.CertificateChainPolicy)));

        // Ensure we've checked every property. When new properties get added, we'll have to update this test along with the CloneSslOptions implementation.
        Assert.Empty(propertyNames);
    }
예제 #20
0
    // Adds endpoints from config to KestrelServerOptions.ConfigurationBackedListenOptions and configures some other options.
    // Any endpoints that were removed from the last time endpoints were loaded are returned.
    internal (List <ListenOptions>, List <ListenOptions>) Reload()
    {
        var endpointsToStop  = Options.ConfigurationBackedListenOptions.ToList();
        var endpointsToStart = new List <ListenOptions>();

        Options.ConfigurationBackedListenOptions.Clear();
        DefaultCertificateConfig = null;

        ConfigurationReader = new ConfigurationReader(Configuration);

        LoadDefaultCert();

        foreach (var endpoint in ConfigurationReader.Endpoints)
        {
            var listenOptions = AddressBinder.ParseAddress(endpoint.Url, out var https);

            if (!https)
            {
                ConfigurationReader.ThrowIfContainsHttpsOnlyConfiguration(endpoint);
            }

            Options.ApplyEndpointDefaults(listenOptions);

            if (endpoint.Protocols.HasValue)
            {
                listenOptions.Protocols = endpoint.Protocols.Value;
            }
            else
            {
                // Ensure endpoint is reloaded if it used the default protocol and the protocol changed.
                // listenOptions.Protocols should already be set to this by ApplyEndpointDefaults.
                endpoint.Protocols = ConfigurationReader.EndpointDefaults.Protocols;
            }

            // Compare to UseHttps(httpsOptions => { })
            var httpsOptions = new HttpsConnectionAdapterOptions();

            if (https)
            {
                // Defaults
                Options.ApplyHttpsDefaults(httpsOptions);

                if (endpoint.SslProtocols.HasValue)
                {
                    httpsOptions.SslProtocols = endpoint.SslProtocols.Value;
                }
                else
                {
                    // Ensure endpoint is reloaded if it used the default protocol and the SslProtocols changed.
                    endpoint.SslProtocols = ConfigurationReader.EndpointDefaults.SslProtocols;
                }

                if (endpoint.ClientCertificateMode.HasValue)
                {
                    httpsOptions.ClientCertificateMode = endpoint.ClientCertificateMode.Value;
                }
                else
                {
                    // Ensure endpoint is reloaded if it used the default mode and the ClientCertificateMode changed.
                    endpoint.ClientCertificateMode = ConfigurationReader.EndpointDefaults.ClientCertificateMode;
                }

                // A cert specified directly on the endpoint overrides any defaults.
                httpsOptions.ServerCertificate = CertificateConfigLoader.LoadCertificate(endpoint.Certificate, endpoint.Name)
                                                 ?? httpsOptions.ServerCertificate;

                if (httpsOptions.ServerCertificate == null && httpsOptions.ServerCertificateSelector == null)
                {
                    // Fallback
                    Options.ApplyDefaultCert(httpsOptions);

                    // Ensure endpoint is reloaded if it used the default certificate and the certificate changed.
                    endpoint.Certificate = DefaultCertificateConfig;
                }
            }

            // Now that defaults have been loaded, we can compare to the currently bound endpoints to see if the config changed.
            // There's no reason to rerun an EndpointConfigurations callback if nothing changed.
            var matchingBoundEndpoints = endpointsToStop.Where(o => o.EndpointConfig == endpoint).ToList();

            if (matchingBoundEndpoints.Count > 0)
            {
                endpointsToStop.RemoveAll(o => o.EndpointConfig == endpoint);
                Options.ConfigurationBackedListenOptions.AddRange(matchingBoundEndpoints);
                continue;
            }

            if (EndpointConfigurations.TryGetValue(endpoint.Name, out var configureEndpoint))
            {
                var endpointConfig = new EndpointConfiguration(https, listenOptions, httpsOptions, endpoint.ConfigSection);
                configureEndpoint(endpointConfig);
            }

            // EndpointDefaults or configureEndpoint may have added an https adapter.
            if (https && !listenOptions.IsTls)
            {
                if (endpoint.Sni.Count == 0)
                {
                    if (httpsOptions.ServerCertificate == null && httpsOptions.ServerCertificateSelector == null)
                    {
                        throw new InvalidOperationException(CoreStrings.NoCertSpecifiedNoDevelopmentCertificateFound);
                    }

                    listenOptions.UseHttps(httpsOptions);
                }
                else
                {
                    var sniOptionsSelector = new SniOptionsSelector(endpoint.Name, endpoint.Sni, CertificateConfigLoader,
                                                                    httpsOptions, listenOptions.Protocols, HttpsLogger);
                    var tlsCallbackOptions = new TlsHandshakeCallbackOptions()
                    {
                        OnConnection      = SniOptionsSelector.OptionsCallback,
                        HandshakeTimeout  = httpsOptions.HandshakeTimeout,
                        OnConnectionState = sniOptionsSelector,
                    };

                    listenOptions.UseHttps(tlsCallbackOptions);
                }
            }

            listenOptions.EndpointConfig = endpoint;

            endpointsToStart.Add(listenOptions);
            Options.ConfigurationBackedListenOptions.Add(listenOptions);
        }

        return(endpointsToStop, endpointsToStart);
    }