// for Server side (IIS 6.0) see: \\netindex\Sources\inetsrv\iis\iisrearc\iisplus\ulw3\digestprovider.cxx
        // for Client side (HTTP.SYS) see: \\netindex\Sources\net\http\sys\ucauth.c
        internal string GetOutgoingDigestBlob(string incomingBlob, string requestMethod, string requestedUri, string realm, bool isClientPreAuth, bool throwOnError, out SecurityStatus statusCode)
        {
            GlobalLog.Enter("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", incomingBlob);

            // second time call with 3 incoming buffers to select HTTP client.
            // we should get back a SecurityStatus.OK and a non null outgoingBlob.
            SecurityBuffer[] inSecurityBuffers = null;
            SecurityBuffer outSecurityBuffer = new SecurityBuffer(m_TokenSize, isClientPreAuth ? BufferType.Parameters : BufferType.Token);

            bool firstTime = m_SecurityContext == null;
            try {
                if (!m_IsServer) {
                    // client session

                    if (!isClientPreAuth) {

                        if (incomingBlob != null) 
                        {
                            List<SecurityBuffer> list = new List<SecurityBuffer>(5);

                            list.Add(new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(incomingBlob), BufferType.Token));
                            list.Add(new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestMethod), BufferType.Parameters));
                            list.Add(new SecurityBuffer(null, BufferType.Parameters));
                            list.Add(new SecurityBuffer(Encoding.Unicode.GetBytes(m_Spn), BufferType.TargetHost));

                            if (m_ChannelBinding != null) {
                                list.Add(new SecurityBuffer(m_ChannelBinding));
                            }

                            inSecurityBuffers = list.ToArray();
                        }

                        statusCode = (SecurityStatus) SSPIWrapper.InitializeSecurityContext(
                            GlobalSSPI.SSPIAuth,
                            m_CredentialsHandle,
                            ref m_SecurityContext,
                            requestedUri, // this must match the Uri in the HTTP status line for the current request
                            m_RequestedContextFlags,
                            Endianness.Network,
                            inSecurityBuffers,
                            outSecurityBuffer,
                            ref m_ContextFlags );

                        GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.InitializeSecurityContext() returns statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
                    }
                    else {
#if WDIGEST_PREAUTH
                        inSecurityBuffers = new SecurityBuffer[] {
                            new SecurityBuffer(null, BufferType.Token),
                            new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestMethod), BufferType.Parameters),
                            new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestedUri), BufferType.Parameters),
                            new SecurityBuffer(null, BufferType.Parameters),
                            outSecurityBuffer,
                        };

                        statusCode = (SecurityStatus) SSPIWrapper.MakeSignature(GlobalSSPI.SSPIAuth, m_SecurityContext, inSecurityBuffers, 0);

                        GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.MakeSignature() returns statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
#else
                        statusCode = SecurityStatus.OK;
                        GlobalLog.Assert("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob()", "Invalid code path.");
#endif
                    }
                }
                else {
                    // server session
                    List<SecurityBuffer> list = new List<SecurityBuffer>(6);
                    
                    list.Add(incomingBlob == null ? new SecurityBuffer(0, BufferType.Token) : new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(incomingBlob), BufferType.Token));
                    list.Add(requestMethod == null ? new SecurityBuffer(0, BufferType.Parameters) : new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestMethod), BufferType.Parameters));
                    list.Add(requestedUri == null ? new SecurityBuffer(0, BufferType.Parameters) : new SecurityBuffer(WebHeaderCollection.HeaderEncoding.GetBytes(requestedUri), BufferType.Parameters));
                    list.Add(new SecurityBuffer(0, BufferType.Parameters));
                    list.Add(realm == null ? new SecurityBuffer(0, BufferType.Parameters) : new SecurityBuffer(Encoding.Unicode.GetBytes(realm), BufferType.Parameters));

                    if (m_ChannelBinding != null) {
                        list.Add(new SecurityBuffer(m_ChannelBinding));
                    }

                    inSecurityBuffers = list.ToArray();

                    statusCode = (SecurityStatus) SSPIWrapper.AcceptSecurityContext(
                        GlobalSSPI.SSPIAuth,
                        m_CredentialsHandle,
                        ref m_SecurityContext,
                        m_RequestedContextFlags,
                        Endianness.Network,
                        inSecurityBuffers,
                        outSecurityBuffer,
                        ref m_ContextFlags );

                    GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.AcceptSecurityContext() returns statusCode:0x" + ((int)statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");

                    if (statusCode == SecurityStatus.CompleteNeeded)
                    {
                        inSecurityBuffers[4] = outSecurityBuffer;

                        statusCode = (SecurityStatus) SSPIWrapper.CompleteAuthToken(
                                GlobalSSPI.SSPIAuth,
                                ref m_SecurityContext,
                                inSecurityBuffers );

                        GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() SSPIWrapper.CompleteAuthToken() returns statusCode:0x" + ((int)statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");

                        outSecurityBuffer.token = null;
                    }
                }
            }
            finally {
                //
                // Assuming the ISC or ASC has referenced the credential on the first successful call,
                // we want to decrement the effective ref count by "disposing" it.
                // The real dispose will happen when the security context is closed.
                // Note if the first call was not successfull the handle is physically destroyed here
                //
              if (firstTime && m_CredentialsHandle != null)
                  m_CredentialsHandle.Close();
            }


            if (((int) statusCode & unchecked((int) 0x80000000)) != 0)
            {
                CloseContext();
                if (throwOnError) {
                    Win32Exception exception = new Win32Exception((int) statusCode);
                    GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", "Win32Exception:" + exception);
                    throw exception;
                }
                GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", "null statusCode:0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
                return null;
            }
            else if (firstTime && m_CredentialsHandle != null)
            {
                // cache until it is pushed out by newly incoming handles
                SSPIHandleCache.CacheCredential(m_CredentialsHandle);
            }


            // the return value from SSPI will tell us correctly if the
            // handshake is over or not: http://msdn.microsoft.com/library/psdk/secspi/sspiref_67p0.htm
            if (statusCode == SecurityStatus.OK)
            {
                // we're done, cleanup
                m_IsCompleted = true;
            }
            else {
                // we need to continue
                GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() need continue statusCode:[0x" + ((int) statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + "] (" + statusCode.ToString() + ") m_SecurityContext#" + ValidationHelper.HashString(m_SecurityContext) + "::Handle:" + ValidationHelper.ToString(m_SecurityContext) + "]");
            }
            GlobalLog.Print("out token = " + outSecurityBuffer.ToString());
            GlobalLog.Dump(outSecurityBuffer.token);
            GlobalLog.Print("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob() IsCompleted:" + IsCompleted.ToString());

            byte[] decodedOutgoingBlob = outSecurityBuffer.token;
            string outgoingBlob = null;
            if (decodedOutgoingBlob!=null && decodedOutgoingBlob.Length>0) {
                outgoingBlob = WebHeaderCollection.HeaderEncoding.GetString(decodedOutgoingBlob, 0, outSecurityBuffer.size);
            }
            GlobalLog.Leave("NTAuthentication#" + ValidationHelper.HashString(this) + "::GetOutgoingDigestBlob", outgoingBlob);
            return outgoingBlob;
        }