Beispiel #1
0
        public void SslStream_SendCertificateTrust_ThrowsOnUnsupportedPlatform()
        {
            (X509Certificate2 certificate, X509Certificate2Collection caCerts) = TestHelper.GenerateCertificates(nameof(SslStream_SendCertificateTrust_CertificateCollection));

            using X509Store store = new X509Store("Root", StoreLocation.LocalMachine);

            Assert.Throws <PlatformNotSupportedException>(() => SslCertificateTrust.CreateForX509Collection(caCerts, sendTrustInHandshake: true));
            Assert.Throws <PlatformNotSupportedException>(() => SslCertificateTrust.CreateForX509Store(store, sendTrustInHandshake: true));
        }
        public async Task SslStream_SendCertificateTrust_CertificateCollection()
        {
            (X509Certificate2 certificate, X509Certificate2Collection caCerts) = TestHelper.GenerateCertificates(nameof(SslStream_SendCertificateTrust_CertificateCollection));

            SslCertificateTrust trust = SslCertificateTrust.CreateForX509Collection(caCerts, sendTrustInHandshake: true);

            string[] acceptableIssuers = await ConnectAndGatherAcceptableIssuers(trust);

            Assert.Equal(caCerts.Count, acceptableIssuers.Length);
            Assert.Equal(caCerts.Select(c => c.Subject), acceptableIssuers);
        }
        public async Task SslStream_SendCertificateTrust_CertificateStore()
        {
            using X509Store store = new X509Store("Root", StoreLocation.LocalMachine);

            SslCertificateTrust trust = SslCertificateTrust.CreateForX509Store(store, sendTrustInHandshake: true);

            string[] acceptableIssuers = await ConnectAndGatherAcceptableIssuers(trust);

            // don't assert individual ellements, just that some issuers were sent
            // we use Root cert store which should always contain at least some certs
            Assert.NotEmpty(acceptableIssuers);
        }
        private async Task <string[]> ConnectAndGatherAcceptableIssuers(SslCertificateTrust trust)
        {
            (SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams();
            using (client)
                using (server)
                    using (X509Certificate2 serverCertificate = Configuration.Certificates.GetServerCertificate())
                        using (X509Certificate2 clientCertificate = Configuration.Certificates.GetClientCertificate())
                        {
                            SslServerAuthenticationOptions serverOptions = new SslServerAuthenticationOptions
                            {
                                ServerCertificate                   = serverCertificate,
                                ClientCertificateRequired           = true,
                                RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true,
                                ServerCertificateContext            = SslStreamCertificateContext.Create(serverCertificate, null, false, trust)
                            };

                            string[] acceptableIssuers = Array.Empty <string>();
                            SslClientAuthenticationOptions clientOptions = new SslClientAuthenticationOptions
                            {
                                TargetHost = "localhost",
                                // Force Tls 1.2 to avoid issues with certain OpenSSL versions and Tls 1.3
                                // https://github.com/openssl/openssl/issues/7384
                                EnabledSslProtocols = SslProtocols.Tls12,
                                RemoteCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true,
                                LocalCertificateSelectionCallback   = (sender, targetHost, localCertificates, remoteCertificate, issuers) =>
                                {
                                    if (remoteCertificate == null)
                                    {
                                        // ignore the first call that is called before handshake
                                        return(null);
                                    }

                                    acceptableIssuers = issuers;
                                    return(clientCertificate);
                                },
                            };

                            await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
                                client.AuthenticateAsClientAsync(clientOptions),
                                server.AuthenticateAsServerAsync(serverOptions));

                            return(acceptableIssuers);
                        }
        }
Beispiel #5
0
        // This essentially wraps SSL* SSL_new()
        internal static SafeSslHandle AllocateSslHandle(SslAuthenticationOptions sslAuthenticationOptions)
        {
            SafeSslHandle?       sslHandle    = null;
            SafeSslContextHandle?sslCtxHandle = null;
            SafeSslContextHandle?newCtxHandle = null;
            SslProtocols         protocols    = CalculateEffectiveProtocols(sslAuthenticationOptions);
            bool hasAlpn         = sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0;
            bool cacheSslContext = !DisableTlsResume && sslAuthenticationOptions.EncryptionPolicy == EncryptionPolicy.RequireEncryption && sslAuthenticationOptions.CipherSuitesPolicy == null;

            if (cacheSslContext)
            {
                if (sslAuthenticationOptions.IsClient)
                {
                    // We don't support client resume on old OpenSSL versions.
                    // We don't want to try on empty TargetName since that is our key.
                    // And we don't want to mess up with client authentication. It may be possible
                    // but it seems safe to get full new session.
                    if (!Interop.Ssl.Capabilities.Tls13Supported ||
                        string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost) ||
                        sslAuthenticationOptions.CertificateContext != null ||
                        sslAuthenticationOptions.CertSelectionDelegate != null)
                    {
                        cacheSslContext = false;
                    }
                }
                else
                {
                    // Server should always have certificate
                    Debug.Assert(sslAuthenticationOptions.CertificateContext != null);
                    if (sslAuthenticationOptions.CertificateContext == null ||
                        sslAuthenticationOptions.CertificateContext.SslContexts == null)
                    {
                        cacheSslContext = false;
                    }
                }
            }

            if (cacheSslContext)
            {
                if (sslAuthenticationOptions.IsServer)
                {
                    sslAuthenticationOptions.CertificateContext !.SslContexts !.TryGetValue(protocols | (hasAlpn ? FakeAlpnSslProtocol : SslProtocols.None), out sslCtxHandle);
                }
                else
                {
                    s_clientSslContexts.TryGetValue(protocols, out sslCtxHandle);
                }
            }

            if (sslCtxHandle == null)
            {
                // We did not get SslContext from cache
                sslCtxHandle = newCtxHandle = AllocateSslContext(sslAuthenticationOptions, protocols, cacheSslContext);

                if (cacheSslContext)
                {
                    bool added = sslAuthenticationOptions.IsServer ?
                                 sslAuthenticationOptions.CertificateContext !.SslContexts !.TryAdd(protocols | (SslProtocols)(hasAlpn ? 1 : 0), newCtxHandle) :
                                 s_clientSslContexts.TryAdd(protocols, newCtxHandle);
                    if (added)
                    {
                        newCtxHandle = null;
                    }
                }
            }

            GCHandle alpnHandle = default;

            try
            {
                sslHandle = SafeSslHandle.Create(sslCtxHandle, sslAuthenticationOptions.IsServer);
                Debug.Assert(sslHandle != null, "Expected non-null return value from SafeSslHandle.Create");
                if (sslHandle.IsInvalid)
                {
                    sslHandle.Dispose();
                    throw CreateSslException(SR.net_allocate_ssl_context_failed);
                }

                if (sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0)
                {
                    if (sslAuthenticationOptions.IsServer)
                    {
                        Debug.Assert(Interop.Ssl.SslGetData(sslHandle) == IntPtr.Zero);
                        alpnHandle = GCHandle.Alloc(sslAuthenticationOptions.ApplicationProtocols);
                        Interop.Ssl.SslSetData(sslHandle, GCHandle.ToIntPtr(alpnHandle));
                        sslHandle.AlpnHandle = alpnHandle;
                    }
                    else
                    {
                        if (Interop.Ssl.SslSetAlpnProtos(sslHandle, sslAuthenticationOptions.ApplicationProtocols) != 0)
                        {
                            throw CreateSslException(SR.net_alpn_config_failed);
                        }
                    }
                }

                if (sslAuthenticationOptions.IsClient)
                {
                    // The IdnMapping converts unicode input into the IDNA punycode sequence.
                    string punyCode = string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost) ? string.Empty : s_idnMapping.GetAscii(sslAuthenticationOptions.TargetHost !);

                    // Similar to windows behavior, set SNI on openssl by default for client context, ignore errors.
                    if (!Ssl.SslSetTlsExtHostName(sslHandle, punyCode))
                    {
                        Crypto.ErrClearError();
                    }

                    if (cacheSslContext && !string.IsNullOrEmpty(punyCode))
                    {
                        sslCtxHandle.TrySetSession(sslHandle, punyCode);
                        bool ignored = false;
                        sslCtxHandle.DangerousAddRef(ref ignored);
                        sslHandle.SslContextHandle = sslCtxHandle;
                    }

                    // relevant to TLS 1.3 only: if user supplied a client cert or cert callback,
                    // advertise that we are willing to send the certificate post-handshake.
                    if (sslAuthenticationOptions.ClientCertificates?.Count > 0 ||
                        sslAuthenticationOptions.CertSelectionDelegate != null)
                    {
                        Ssl.SslSetPostHandshakeAuth(sslHandle, 1);
                    }

                    // Set client cert callback, this will interrupt the handshake with SecurityStatusPalErrorCode.CredentialsNeeded
                    // if server actually requests a certificate.
                    Ssl.SslSetClientCertCallback(sslHandle, 1);
                }
                else // sslAuthenticationOptions.IsServer
                {
                    if (sslAuthenticationOptions.RemoteCertRequired)
                    {
                        Ssl.SslSetVerifyPeer(sslHandle);
                    }

                    if (sslAuthenticationOptions.CertificateContext != null)
                    {
                        if (sslAuthenticationOptions.CertificateContext.Trust?._sendTrustInHandshake == true)
                        {
                            SslCertificateTrust        trust    = sslAuthenticationOptions.CertificateContext !.Trust !;
                            X509Certificate2Collection certList = (trust._trustList ?? trust._store !.Certificates);

                            Debug.Assert(certList != null, "certList != null");
                            Span <IntPtr> handles = certList.Count <= 256 ?
                                                    stackalloc IntPtr[256] :
                                                    new IntPtr[certList.Count];

                            for (int i = 0; i < certList.Count; i++)
                            {
                                handles[i] = certList[i].Handle;
                            }

                            if (!Ssl.SslAddClientCAs(sslHandle, handles.Slice(0, certList.Count)))
                            {
                                // The method can fail only when the number of cert names exceeds the maximum capacity
                                // supported by STACK_OF(X509_NAME) structure, which should not happen under normal
                                // operation.
                                Debug.Fail("Failed to add issuer to trusted CA list.");
                            }
                        }

                        byte[]? ocspResponse = sslAuthenticationOptions.CertificateContext.GetOcspResponseNoWaiting();

                        if (ocspResponse != null)
                        {
                            Ssl.SslStapleOcsp(sslHandle, ocspResponse);
                        }
                    }
                }
            }
            catch
            {
                if (alpnHandle.IsAllocated)
                {
                    alpnHandle.Free();
                }

                throw;
            }
            finally
            {
                newCtxHandle?.Dispose();
            }

            return(sslHandle);
        }
Beispiel #6
0
        public SafeDeleteSslContext(SafeFreeSslCredentials credential, SslAuthenticationOptions sslAuthenticationOptions)
            : base(credential)
        {
            Debug.Assert((null != credential) && !credential.IsInvalid, "Invalid credential used in SafeDeleteSslContext");

            try
            {
                int osStatus;

                _sslContext = CreateSslContext(credential, sslAuthenticationOptions.IsServer);

                // Make sure the class instance is associated to the session and is provided
                // in the Read/Write callback connection parameter
                SslSetConnection(_sslContext);

                unsafe
                {
                    osStatus = Interop.AppleCrypto.SslSetIoCallbacks(
                        _sslContext,
                        &ReadFromConnection,
                        &WriteToConnection);
                }

                if (osStatus != 0)
                {
                    throw Interop.AppleCrypto.CreateExceptionForOSStatus(osStatus);
                }

                if (sslAuthenticationOptions.CipherSuitesPolicy != null)
                {
                    uint[] tlsCipherSuites = sslAuthenticationOptions.CipherSuitesPolicy.Pal.TlsCipherSuites;

                    unsafe
                    {
                        fixed(uint *cipherSuites = tlsCipherSuites)
                        {
                            osStatus = Interop.AppleCrypto.SslSetEnabledCipherSuites(
                                _sslContext,
                                cipherSuites,
                                tlsCipherSuites.Length);

                            if (osStatus != 0)
                            {
                                throw Interop.AppleCrypto.CreateExceptionForOSStatus(osStatus);
                            }
                        }
                    }
                }

                if (sslAuthenticationOptions.ApplicationProtocols != null && sslAuthenticationOptions.ApplicationProtocols.Count != 0)
                {
                    // On OSX coretls supports only client side. For server, we will silently ignore the option.
                    if (!sslAuthenticationOptions.IsServer)
                    {
                        Interop.AppleCrypto.SslCtxSetAlpnProtos(_sslContext, sslAuthenticationOptions.ApplicationProtocols);
                    }
                }
            }
            catch (Exception ex)
            {
                Debug.Write("Exception Caught. - " + ex);
                Dispose();
                throw;
            }

            if (!string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost) && !sslAuthenticationOptions.IsServer)
            {
                Interop.AppleCrypto.SslSetTargetName(_sslContext, sslAuthenticationOptions.TargetHost);
            }

            if (sslAuthenticationOptions.CertificateContext == null && sslAuthenticationOptions.CertSelectionDelegate != null)
            {
                // certificate was not provided but there is user callback. We can break handshake if server asks for certificate
                // and we can try to get it based on remote certificate and trusted issuers.
                Interop.AppleCrypto.SslBreakOnCertRequested(_sslContext, true);
            }

            if (sslAuthenticationOptions.IsServer)
            {
                if (sslAuthenticationOptions.RemoteCertRequired)
                {
                    Interop.AppleCrypto.SslSetAcceptClientCert(_sslContext);
                }

                if (sslAuthenticationOptions.CertificateContext?.Trust?._sendTrustInHandshake == true)
                {
                    SslCertificateTrust        trust    = sslAuthenticationOptions.CertificateContext !.Trust !;
                    X509Certificate2Collection certList = (trust._trustList ?? trust._store !.Certificates);

                    Debug.Assert(certList != null, "certList != null");
                    Span <IntPtr> handles = certList.Count <= 256
                        ? stackalloc IntPtr[256]
                        : new IntPtr[certList.Count];

                    for (int i = 0; i < certList.Count; i++)
                    {
                        handles[i] = certList[i].Handle;
                    }

                    Interop.AppleCrypto.SslSetCertificateAuthorities(_sslContext, handles.Slice(0, certList.Count), true);
                }
            }
        }
Beispiel #7
0
        public void SslStream_SendCertificateTrust_CertificateCollection_ThrowsOnWindows()
        {
            (X509Certificate2 certificate, X509Certificate2Collection caCerts) = TestHelper.GenerateCertificates(nameof(SslStream_SendCertificateTrust_CertificateCollection));

            Assert.Throws <PlatformNotSupportedException>(() => SslCertificateTrust.CreateForX509Collection(caCerts, sendTrustInHandshake: true));
        }