Пример #1
0
        private static SecurityStatusPal HandshakeInternal(
            SafeFreeCredentials credential,
            ref SafeDeleteSslContext?context,
            ReadOnlySpan <byte> inputBuffer,
            ref byte[]?outputBuffer,
            SslAuthenticationOptions sslAuthenticationOptions,
            SelectClientCertificate?clientCertificateSelectionCallback)
        {
            try
            {
                SafeDeleteSslContext?sslContext = ((SafeDeleteSslContext?)context);

                if ((null == context) || context.IsInvalid)
                {
                    sslContext = new SafeDeleteSslContext(sslAuthenticationOptions);
                    context    = sslContext;
                }

                if (inputBuffer.Length > 0)
                {
                    sslContext !.Write(inputBuffer);
                }

                SafeSslHandle     sslHandle = sslContext !.SslContext;
                SecurityStatusPal status    = PerformHandshake(sslHandle);
                if (status.ErrorCode == SecurityStatusPalErrorCode.CredentialsNeeded && clientCertificateSelectionCallback != null)
                {
                    X509Certificate2?clientCertificate = clientCertificateSelectionCallback(out bool _);
                    if (clientCertificate != null)
                    {
                        sslAuthenticationOptions.CertificateContext = SslStreamCertificateContext.Create(clientCertificate);
                        SafeDeleteSslContext.SetCertificate(sslContext.SslContext, sslAuthenticationOptions.CertificateContext);
                    }

                    // We either got certificate or we can proceed without it. It is up to the server to decide if either is OK.
                    status = PerformHandshake(sslHandle);
                }

                outputBuffer = sslContext.ReadPendingWrites();
                return(status);
            }
            catch (Exception exc)
            {
                return(new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, exc));
            }
        }
Пример #2
0
        private static SecurityStatusPal HandshakeInternal(
            SafeFreeCredentials credential,
            ref SafeDeleteSslContext?context,
            ReadOnlySpan <byte> inputBuffer,
            ref byte[]?outputBuffer,
            SslAuthenticationOptions sslAuthenticationOptions)
        {
            Debug.Assert(!credential.IsInvalid);

            try
            {
                SafeDeleteSslContext?sslContext = ((SafeDeleteSslContext?)context);

                if ((null == context) || context.IsInvalid)
                {
                    sslContext = new SafeDeleteSslContext((credential as SafeFreeSslCredentials) !, sslAuthenticationOptions);
                    context    = sslContext;

                    if (!string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost))
                    {
                        Debug.Assert(!sslAuthenticationOptions.IsServer, "targetName should not be set for server-side handshakes");
                        Interop.AppleCrypto.SslSetTargetName(sslContext.SslContext, sslAuthenticationOptions.TargetHost);
                    }

                    if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.RemoteCertRequired)
                    {
                        Interop.AppleCrypto.SslSetAcceptClientCert(sslContext.SslContext);
                    }
                }

                if (inputBuffer.Length > 0)
                {
                    sslContext !.Write(inputBuffer);
                }

                SafeSslHandle     sslHandle = sslContext !.SslContext;
                SecurityStatusPal status    = PerformHandshake(sslHandle);

                outputBuffer = sslContext.ReadPendingWrites();
                return(status);
            }
            catch (Exception exc)
            {
                return(new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, exc));
            }
        }
Пример #3
0
        public static SecurityStatusPal AcceptSecurityContext(
            ref SafeFreeCredentials credential,
            ref SafeDeleteContext context,
            SecurityBuffer[] inputBuffers,
            SecurityBuffer outputBuffer,
            SslAuthenticationOptions sslAuthenticationOptions)
        {
            if (inputBuffers != null)
            {
                Debug.Assert(inputBuffers.Length == 2);
                Debug.Assert(inputBuffers[1].token == null);

                return(HandshakeInternal(credential, ref context, inputBuffers[0], outputBuffer, sslAuthenticationOptions));
            }
            else
            {
                return(HandshakeInternal(credential, ref context, inputBuffer: null, outputBuffer, sslAuthenticationOptions));
            }
        }
Пример #4
0
        private static SecurityStatusPal HandshakeInternal(
            SafeFreeCredentials credential,
            ref SafeDeleteSslContext?context,
            ReadOnlySpan <byte> inputBuffer,
            ref byte[]?outputBuffer,
            SslAuthenticationOptions sslAuthenticationOptions)
        {
            Debug.Assert(!credential.IsInvalid);

            try
            {
                SafeDeleteSslContext?sslContext = ((SafeDeleteSslContext?)context);

                if ((context == null) || context.IsInvalid)
                {
                    context    = new SafeDeleteSslContext((credential as SafeFreeSslCredentials) !, sslAuthenticationOptions);
                    sslContext = context;
                }

                if (inputBuffer.Length > 0)
                {
                    sslContext !.Write(inputBuffer);
                }

                SafeSslHandle sslHandle = sslContext !.SslContext;

                PAL_SSLStreamStatus        ret        = Interop.AndroidCrypto.SSLStreamHandshake(sslHandle);
                SecurityStatusPalErrorCode statusCode = ret switch
                {
                    PAL_SSLStreamStatus.OK => SecurityStatusPalErrorCode.OK,
                    PAL_SSLStreamStatus.NeedData => SecurityStatusPalErrorCode.ContinueNeeded,
                    _ => SecurityStatusPalErrorCode.InternalError
                };

                outputBuffer = sslContext.ReadPendingWrites();

                return(new SecurityStatusPal(statusCode));
            }
            catch (Exception exc)
            {
                return(new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, exc));
            }
        }
Пример #5
0
        public SafeDeleteSslContext(SafeFreeSslCredentials credential, SslAuthenticationOptions sslAuthenticationOptions)
            : base(credential)
        {
            Debug.Assert((null != credential) && !credential.IsInvalid, "Invalid credential used in SafeDeleteSslContext");

            try
            {
                _sslContext = Interop.OpenSsl.AllocateSslContext(
                    credential.Protocols,
                    credential.CertHandle,
                    credential.CertKeyHandle,
                    credential.Policy,
                    sslAuthenticationOptions);
            }
            catch (Exception ex)
            {
                Debug.Write("Exception Caught. - " + ex);
                Dispose();
                throw;
            }
        }
Пример #6
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);
        }
Пример #7
0
        public static SecurityBuffer[] GetIncomingSecurityBuffers(SslAuthenticationOptions options, ref SecurityBuffer incomingSecurity)
        {
            SecurityBuffer alpnBuffer = null;

            SecurityBuffer[] incomingSecurityBuffers = null;

            if (options.ApplicationProtocols != null && options.ApplicationProtocols.Count != 0)
            {
                byte[] alpnBytes = SslStreamPal.ConvertAlpnProtocolListToByteArray(options.ApplicationProtocols);
                alpnBuffer = new SecurityBuffer(alpnBytes, 0, alpnBytes.Length, SecurityBufferType.SECBUFFER_APPLICATION_PROTOCOLS);
            }

            if (incomingSecurity != null)
            {
                if (alpnBuffer != null)
                {
                    incomingSecurityBuffers = new SecurityBuffer[]
                    {
                        incomingSecurity,
                        new SecurityBuffer(null, 0, 0, SecurityBufferType.SECBUFFER_EMPTY),
                        alpnBuffer
                    };
                }
                else
                {
                    incomingSecurityBuffers = new SecurityBuffer[]
                    {
                        incomingSecurity,
                        new SecurityBuffer(null, 0, 0, SecurityBufferType.SECBUFFER_EMPTY)
                    };
                }
            }
            else if (alpnBuffer != null)
            {
                incomingSecurity = alpnBuffer;
            }

            return(incomingSecurityBuffers);
        }
Пример #8
0
        public static SecurityStatusPal InitializeSecurityContext(SafeFreeCredentials credential, ref SafeDeleteContext context, string targetName, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
        {
            Debug.Assert(inputBuffers.Length == 2);
            Debug.Assert(inputBuffers[1].token == null);

            return(HandshakeInternal(credential, ref context, inputBuffers[0], outputBuffer, sslAuthenticationOptions));
        }
Пример #9
0
        private static SecurityStatusPal HandshakeInternal(SafeFreeCredentials credential, ref SafeDeleteContext context, SecurityBuffer inputBuffer,
                                                           SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
        {
            Debug.Assert(!credential.IsInvalid);

            try
            {
                if ((null == context) || context.IsInvalid)
                {
                    context = new SafeDeleteSslContext(credential as SafeFreeSslCredentials, sslAuthenticationOptions);
                }

                byte[] output = null;
                int    outputSize;
                bool   done;

                if (null == inputBuffer)
                {
                    done = Interop.OpenSsl.DoSslHandshake(((SafeDeleteSslContext)context).SslContext, null, 0, 0, out output, out outputSize);
                }
                else
                {
                    done = Interop.OpenSsl.DoSslHandshake(((SafeDeleteSslContext)context).SslContext, inputBuffer.token, inputBuffer.offset, inputBuffer.size, out output, out outputSize);
                }

                outputBuffer.size   = outputSize;
                outputBuffer.offset = 0;
                outputBuffer.token  = outputSize > 0 ? output : null;

                return(new SecurityStatusPal(done ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.ContinueNeeded));
            }
            catch (Exception exc)
            {
                return(new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, exc));
            }
        }
Пример #10
0
 public static SecurityStatusPal InitializeSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteContext context,
                                                           string targetName, SecurityBuffer inputBuffer, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
 {
     return(HandshakeInternal(credential, ref context, inputBuffer, outputBuffer, sslAuthenticationOptions));
 }
Пример #11
0
 public static SecurityStatusPal AcceptSecurityContext(ref SafeFreeCredentials?credential, ref SafeDeleteSslContext?context,
                                                       ReadOnlySpan <byte> inputBuffer, ref byte[]?outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
 {
     return(HandshakeInternal(credential !, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions));
 }
Пример #12
0
 public static SecurityStatusPal Renegotiate(ref SafeFreeCredentials?credentialsHandle, ref SafeDeleteSslContext?context, SslAuthenticationOptions sslAuthenticationOptions, out byte[]?outputBuffer)
 {
     throw new PlatformNotSupportedException();
 }
Пример #13
0
 internal void ValidateCreateContext(SslAuthenticationOptions sslAuthenticationOptions)
 {
 }
Пример #14
0
        public static SecurityStatusPal AcceptSecurityContext(ref SafeFreeCredentials credentialsHandle, ref SafeDeleteSslContext context, byte[] inputBuffer, int offset, int count, ref byte[] outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
        {
            Interop.SspiCli.ContextFlags unusedAttributes = default;
            ArraySegment <byte>          input            = inputBuffer != null ? new ArraySegment <byte>(inputBuffer, offset, count) : default;

            ThreeSecurityBuffers threeSecurityBuffers = default;
            SecurityBuffer?      incomingSecurity     = input.Array != null ?
                                                        new SecurityBuffer(input.Array, input.Offset, input.Count, SecurityBufferType.SECBUFFER_TOKEN) :
                                                        (SecurityBuffer?)null;
            Span <SecurityBuffer> inputBuffers = MemoryMarshal.CreateSpan(ref threeSecurityBuffers._item0, 3);

            GetIncomingSecurityBuffers(sslAuthenticationOptions, in incomingSecurity, ref inputBuffers);

            var resultBuffer = new SecurityBuffer(outputBuffer, SecurityBufferType.SECBUFFER_TOKEN);

            int errorCode = SSPIWrapper.AcceptSecurityContext(
                GlobalSSPI.SSPISecureChannel,
                credentialsHandle,
                ref context,
                ServerRequiredFlags | (sslAuthenticationOptions.RemoteCertRequired ? Interop.SspiCli.ContextFlags.MutualAuth : Interop.SspiCli.ContextFlags.Zero),
                Interop.SspiCli.Endianness.SECURITY_NATIVE_DREP,
                inputBuffers,
                ref resultBuffer,
                ref unusedAttributes);

            outputBuffer = resultBuffer.token;
            return(SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode));
        }
Пример #15
0
 public static SecurityStatusPal AcceptSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteContext context,
                                                       ArraySegment <byte> inputBuffer, ref byte[] outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
 {
     return(HandshakeInternal(credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions));
 }
Пример #16
0
 private static void GetIncomingSecurityBuffers(SslAuthenticationOptions options, in SecurityBuffer?incomingSecurity, ref Span <SecurityBuffer> incomingSecurityBuffers)
Пример #17
0
        private static SecurityStatusPal HandshakeInternal(SafeFreeCredentials credential, ref SafeDeleteSslContext context,
                                                           ReadOnlySpan <byte> inputBuffer, ref byte[] outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
        {
            Debug.Assert(!credential.IsInvalid);

            byte[] output     = null;
            int    outputSize = 0;

            try
            {
                if ((null == context) || context.IsInvalid)
                {
                    context = new SafeDeleteSslContext(credential as SafeFreeSslCredentials, sslAuthenticationOptions);
                }

                bool done = Interop.OpenSsl.DoSslHandshake(((SafeDeleteSslContext)context).SslContext, inputBuffer, out output, out outputSize);

                // When the handshake is done, and the context is server, check if the alpnHandle target was set to null during ALPN.
                // If it was, then that indicates ALPN failed, send failure.
                // We have this workaround, as openssl supports terminating handshake only from version 1.1.0,
                // whereas ALPN is supported from version 1.0.2.
                SafeSslHandle sslContext = ((SafeDeleteSslContext)context).SslContext;
                if (done && sslAuthenticationOptions.IsServer && sslAuthenticationOptions.ApplicationProtocols != null && sslContext.AlpnHandle.IsAllocated && sslContext.AlpnHandle.Target == null)
                {
                    return(new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, Interop.OpenSsl.CreateSslException(SR.net_alpn_failed)));
                }

                outputBuffer =
                    outputSize == 0 ? null :
                    outputSize == output.Length ? output :
                    new Span <byte>(output, 0, outputSize).ToArray();

                return(new SecurityStatusPal(done ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.ContinueNeeded));
            }
            catch (Exception exc)
            {
                // Even if handshake failed we may have Alert to sent.
                if (outputSize > 0)
                {
                    outputBuffer = outputSize == output.Length ? output : new Span <byte>(output, 0, outputSize).ToArray();
                }

                return(new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, exc));
            }
        }
Пример #18
0
        private static SecurityStatusPal HandshakeInternal(SafeFreeCredentials credential, ref SafeDeleteContext context, SecurityBuffer inputBuffer,
                                                           SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
        {
            Debug.Assert(!credential.IsInvalid);

            try
            {
                if ((null == context) || context.IsInvalid)
                {
                    context = new SafeDeleteSslContext(credential as SafeFreeSslCredentials, sslAuthenticationOptions);
                }

                byte[] output = null;
                int    outputSize;
                bool   done;

                if (null == inputBuffer)
                {
                    done = Interop.OpenSsl.DoSslHandshake(((SafeDeleteSslContext)context).SslContext, null, 0, 0, out output, out outputSize);
                }
                else
                {
                    done = Interop.OpenSsl.DoSslHandshake(((SafeDeleteSslContext)context).SslContext, inputBuffer.token, inputBuffer.offset, inputBuffer.size, out output, out outputSize);
                }

                // When the handshake is done, and the context is server, check if the alpnHandle target was set to null during ALPN.
                // If it was, then that indiciates ALPN failed, send failure.
                // We have this workaround, as openssl supports terminating handshake only from version 1.1.0,
                // whereas ALPN is supported from version 1.0.2.
                SafeSslHandle sslContext = ((SafeDeleteSslContext)context).SslContext;
                if (done && sslAuthenticationOptions.IsServer && sslAuthenticationOptions.ApplicationProtocols != null && sslContext.AlpnHandle.IsAllocated && sslContext.AlpnHandle.Target == null)
                {
                    return(new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, Interop.OpenSsl.CreateSslException(SR.net_alpn_failed)));
                }

                outputBuffer.size   = outputSize;
                outputBuffer.offset = 0;
                outputBuffer.token  = outputSize > 0 ? output : null;

                return(new SecurityStatusPal(done ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.ContinueNeeded));
            }
            catch (Exception exc)
            {
                return(new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, exc));
            }
        }
Пример #19
0
        public static SecurityStatusPal AcceptSecurityContext(ref SafeFreeCredentials?credentialsHandle, ref SafeDeleteSslContext?context, ReadOnlySpan <byte> inputBuffer, ref byte[]?outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
        {
            Interop.SspiCli.ContextFlags unusedAttributes = default;

            InputSecurityBuffers inputBuffers = default;

            inputBuffers.SetNextBuffer(new InputSecurityBuffer(inputBuffer, SecurityBufferType.SECBUFFER_TOKEN));
            inputBuffers.SetNextBuffer(new InputSecurityBuffer(default, SecurityBufferType.SECBUFFER_EMPTY));
Пример #20
0
 public static SecurityStatusPal InitializeSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteSslContext context, string targetName,
                                                           ReadOnlySpan <byte> inputBuffer, ref byte[] outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
 {
     return(HandshakeInternal(credential, ref context, inputBuffer, ref outputBuffer, sslAuthenticationOptions));
 }
Пример #21
0
        public static SecurityStatusPal AcceptSecurityContext(ref SafeFreeCredentials credentialsHandle, ref SafeDeleteContext context, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
        {
            Interop.SspiCli.ContextFlags unusedAttributes = default(Interop.SspiCli.ContextFlags);

            int errorCode = SSPIWrapper.AcceptSecurityContext(
                GlobalSSPI.SSPISecureChannel,
                credentialsHandle,
                ref context,
                ServerRequiredFlags | (sslAuthenticationOptions.RemoteCertRequired ? Interop.SspiCli.ContextFlags.MutualAuth : Interop.SspiCli.ContextFlags.Zero),
                Interop.SspiCli.Endianness.SECURITY_NATIVE_DREP,
                inputBuffers,
                outputBuffer,
                ref unusedAttributes);

            return(SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode));
        }
Пример #22
0
        public static SecurityStatusPal InitializeSecurityContext(ref SafeFreeCredentials credentialsHandle, ref SafeDeleteContext context, string targetName, ArraySegment <byte> input, ref byte[] outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
        {
            Interop.SspiCli.ContextFlags unusedAttributes = default;

            ThreeSecurityBuffers threeSecurityBuffers = default;
            SecurityBuffer?      incomingSecurity     = input.Array != null ?
                                                        new SecurityBuffer(input.Array, input.Offset, input.Count, SecurityBufferType.SECBUFFER_TOKEN) :
                                                        (SecurityBuffer?)null;
            Span <SecurityBuffer> inputBuffers = MemoryMarshal.CreateSpan(ref threeSecurityBuffers._item0, 3);

            GetIncomingSecurityBuffers(sslAuthenticationOptions, in incomingSecurity, ref inputBuffers);

            var resultBuffer = new SecurityBuffer(outputBuffer, SecurityBufferType.SECBUFFER_TOKEN);

            int errorCode = SSPIWrapper.InitializeSecurityContext(
                GlobalSSPI.SSPISecureChannel,
                ref credentialsHandle,
                ref context,
                targetName,
                RequiredFlags | Interop.SspiCli.ContextFlags.InitManualCredValidation,
                Interop.SspiCli.Endianness.SECURITY_NATIVE_DREP,
                inputBuffers,
                ref resultBuffer,
                ref unusedAttributes);

            outputBuffer = resultBuffer.token;
            return(SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode));
        }
Пример #23
0
        public static SecurityStatusPal InitializeSecurityContext(SafeFreeCredentials credentialsHandle, ref SafeDeleteContext context, string targetName, SecurityBuffer[] inputBuffers, SecurityBuffer outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
        {
            Interop.SspiCli.ContextFlags unusedAttributes = default(Interop.SspiCli.ContextFlags);

            int errorCode = SSPIWrapper.InitializeSecurityContext(
                GlobalSSPI.SSPISecureChannel,
                credentialsHandle,
                ref context,
                targetName,
                RequiredFlags | Interop.SspiCli.ContextFlags.InitManualCredValidation,
                Interop.SspiCli.Endianness.SECURITY_NATIVE_DREP,
                inputBuffers,
                outputBuffer,
                ref unusedAttributes);

            return(SecurityStatusAdapterPal.GetSecurityStatusPalFromNativeInt(errorCode));
        }
Пример #24
0
 public static SecurityStatusPal AcceptSecurityContext(ref SafeFreeCredentials credential, ref SafeDeleteSslContext context,
                                                       byte[] inputBuffer, int offset, int count, ref byte[] outputBuffer, SslAuthenticationOptions sslAuthenticationOptions)
 {
     return(HandshakeInternal(credential, ref context, new ReadOnlySpan <byte>(inputBuffer, offset, count), ref outputBuffer, sslAuthenticationOptions));
 }
Пример #25
0
 public static SafeFreeCredentials?AcquireCredentialsHandle(SslAuthenticationOptions sslAuthenticationOptions)
 {
     return(null);
 }
Пример #26
0
 private void ValidateCreateContext(SslAuthenticationOptions sslAuthenticationOptions)
 {
 }
Пример #27
0
        private static SecurityStatusPal HandshakeInternal(
            SecureChannel secureChannel,
            SafeFreeCredentials credential,
            ref SafeDeleteSslContext?context,
            ReadOnlySpan <byte> inputBuffer,
            ref byte[]?outputBuffer,
            SslAuthenticationOptions sslAuthenticationOptions)
        {
            Debug.Assert(!credential.IsInvalid);

            try
            {
                SafeDeleteSslContext?sslContext = ((SafeDeleteSslContext?)context);

                if ((null == context) || context.IsInvalid)
                {
                    sslContext = new SafeDeleteSslContext((credential as SafeFreeSslCredentials) !, sslAuthenticationOptions);
                    context    = sslContext;

                    if (!string.IsNullOrEmpty(sslAuthenticationOptions.TargetHost) && !sslAuthenticationOptions.IsServer)
                    {
                        Interop.AppleCrypto.SslSetTargetName(sslContext.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.SslContext, true);
                    }

                    if (sslAuthenticationOptions.IsServer && sslAuthenticationOptions.RemoteCertRequired)
                    {
                        Interop.AppleCrypto.SslSetAcceptClientCert(sslContext.SslContext);
                    }
                }

                if (inputBuffer.Length > 0)
                {
                    sslContext !.Write(inputBuffer);
                }

                SafeSslHandle     sslHandle = sslContext !.SslContext;
                SecurityStatusPal status    = PerformHandshake(sslHandle);
                if (status.ErrorCode == SecurityStatusPalErrorCode.CredentialsNeeded)
                {
                    X509Certificate2?clientCertificate = secureChannel.SelectClientCertificate(out _);
                    if (clientCertificate != null)
                    {
                        sslAuthenticationOptions.CertificateContext = SslStreamCertificateContext.Create(clientCertificate);
                        SafeDeleteSslContext.SetCertificate(sslContext.SslContext, sslAuthenticationOptions.CertificateContext);
                    }

                    // We either got certificate or we can proceed without it. It is up to the server to decide if either is OK.
                    status = PerformHandshake(sslHandle);
                }

                outputBuffer = sslContext.ReadPendingWrites();
                return(status);
            }
            catch (Exception exc)
            {
                return(new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, exc));
            }
        }