/// <summary>
        /// Initialize method is not used for NRPC SSPI.<para/>
        /// NRPC SSPI will negotiate security context in its own RPC call. 
        /// </summary>
        /// <param name="inToken">
        /// A token returned from server SSPI; 
        /// if it's set to null, indicates to initialize a new client token.
        /// </param>
        /// <exception cref="SspiException">
        /// Thrown when server returned token is invalid.
        /// </exception>
        public override void Initialize(byte[] inToken)
        {
            if (inToken == null)
            {
                //Initialize a new token.
                if (nrpc.Context.SessionKey == null)
                {
                    //Negotiate a session key.
                    NrpcNegotiateFlags clientCapabilities = nrpc.Context.NegotiateFlags;

                    ushort[] nrpcTcpEndpoints = NrpcUtility.QueryNrpcTcpEndpoint(nrpc.Context.PrimaryName);
                    if (nrpcTcpEndpoints == null || nrpcTcpEndpoints.Length == 0)
                    {
                        throw new InvalidOperationException("Server doesn't support NRPC protocol.");
                    }

                    nrpc.BindOverTcp(
                        nrpc.Context.PrimaryName,
                        nrpcTcpEndpoints[0],
                        null,
                        timeout);
                    nrpc.NetrServerReqChallenge(credential.MachineName);
                    nrpc.NetrServerAuthenticate3(
                        credential.AccountName,
                        this.secureChannelType,
                        ref clientCapabilities,
                        credential.Password);
                }

                NL_AUTH_MESSAGE nlAuthMessage = nrpc.CreateNlAuthMessage();
                this.token = ArrayUtility.ConcatenateArrays(
                    BitConverter.GetBytes((uint)nlAuthMessage.MessageType),
                    BitConverter.GetBytes((uint)nlAuthMessage.Flags),
                    nlAuthMessage.Buffer);
                this.needContinueProcessing = true;
            }
            else
            {
                this.token = null;
                this.needContinueProcessing = false;

                NL_AUTH_MESSAGE nlAuthMessage = new NL_AUTH_MESSAGE();
                int offset = 0;
                nlAuthMessage.MessageType = (MessageType_Values)BitConverter.ToInt32(inToken, offset);
                offset += sizeof(int); // cannot call: Marshal.SizeOf(nlAuthMessage.MessageType);
                nlAuthMessage.Flags = (NL_AUTH_MESSAGE_Flags_Value)BitConverter.ToUInt32(inToken, offset);
                offset += sizeof(NL_AUTH_MESSAGE_Flags_Value);
                nlAuthMessage.Buffer = ArrayUtility.SubArray(inToken, offset, inToken.Length - offset);
                if (!nrpc.ValidateNlAuthMessage(nlAuthMessage))
                {
                    //validate server returned token failed.
                    throw new SspiException("Server returned token is invalid.");
                }
            }
        }
        /// <summary>
        /// Validate NL_AUTH_MESSAGE when the server receives token from client
        /// MessageType is not set to 0x00000000. 
        /// contains at least one domain name and one computer name
        /// </summary>
        /// <param name="inToken">token from client</param>
        /// <returns>True if validate pass; otherwise, false</returns>
        /// <exception cref="ArgumentException">Thrown when length of inToken is not large enough.</exception>
        private bool ValidateNlAuthMessage(byte[] inToken)
        {
            if (inToken.Length <= (sizeof(MessageType_Values) + sizeof(NL_AUTH_MESSAGE_Flags_Value)))
            {
                throw new ArgumentException("The token is invalid", "inToken");
            }

            NL_AUTH_MESSAGE nlAuthMessage = new NL_AUTH_MESSAGE();

            // convert inToken to a NL_AUTH_MESSAGE structure
            int offset = 0;
            nlAuthMessage.MessageType = (MessageType_Values)BitConverter.ToInt32(inToken, offset);
            offset += sizeof(MessageType_Values);
            nlAuthMessage.Flags = (NL_AUTH_MESSAGE_Flags_Value)BitConverter.ToUInt32(inToken, offset);
            offset += sizeof(NL_AUTH_MESSAGE_Flags_Value);
            nlAuthMessage.Buffer = ArrayUtility.SubArray(inToken, offset, inToken.Length - offset);

            // check message type
            if (nlAuthMessage.MessageType != MessageType_Values.NegotiateRequest)
            {
                return false;
            }

            // check domain name and computer name, must present both
            string domainName = null;
            string computerName = null;
            List<string> nameList = new List<string>();

            foreach (string name in Encoding.ASCII.GetString(nlAuthMessage.Buffer).Split(NULL))
            {
                if (!string.IsNullOrEmpty(name))
                {
                    nameList.Add(name);
                }
            }

            if (nameList.Count == 0)
            {
                return false;
            }

            int index = 0;
            if ((nlAuthMessage.Flags & NL_AUTH_MESSAGE_Flags_Value.NetbiosOemDomainName) != 0
                && index < nameList.Count)
            {
                domainName = nameList[index++];
            }
            if ((nlAuthMessage.Flags & NL_AUTH_MESSAGE_Flags_Value.NetbiosOemComputerName) != 0
                && index < nameList.Count)
            {
                computerName = nameList[index++];
            }
            if ((nlAuthMessage.Flags & NL_AUTH_MESSAGE_Flags_Value.DnsCompressedDomainName) != 0
                && index < nameList.Count)
            {
                domainName =
                    Rfc1035Utility.FromCompressedUtf8String(Encoding.ASCII.GetBytes(nameList[index++]));
            }
            if ((nlAuthMessage.Flags & NL_AUTH_MESSAGE_Flags_Value.DnsCompressedHostName) != 0
                && index < nameList.Count)
            {
                computerName =
                    Rfc1035Utility.FromCompressedUtf8String(Encoding.ASCII.GetBytes(nameList[index++]));
            }
            if ((nlAuthMessage.Flags & NL_AUTH_MESSAGE_Flags_Value.NetbiosCompressedComputerName) != 0
                && index < nameList.Count)
            {
                computerName =
                    Rfc1035Utility.FromCompressedUtf8String(Encoding.ASCII.GetBytes(nameList[index++]));
            }

            if (domainName == null || computerName == null)
            {
                return false;
            }

            return true;
        }
        /// <summary>
        /// Create an instance of NL_AUTH_MESSAGE.
        /// </summary>
        /// <returns>Created NL_AUTH_MESSAGE structure.</returns>
        /// <exception cref="InvalidOperationException">
        /// Thrown when the method is called before establishing a NRPC secure channel.
        /// </exception>
        public NL_AUTH_MESSAGE CreateNlAuthMessage()
        {
            const char NULL = '\0';

            ValidateSecureChannelExists();

            NL_AUTH_MESSAGE nlAuthMessage = new NL_AUTH_MESSAGE();

            nlAuthMessage.MessageType = MessageType_Values.NegotiateRequest;

            if (context.DomainName.Contains("."))
            {
                // context.DomainName is a DNS name.
                nlAuthMessage.Flags =
                    NL_AUTH_MESSAGE_Flags_Value.NetbiosOemComputerName
                    | NL_AUTH_MESSAGE_Flags_Value.DnsCompressedDomainName;
                nlAuthMessage.Buffer = ArrayUtility.ConcatenateArrays(
                    Encoding.ASCII.GetBytes(context.ClientComputerName + NULL),
                    Rfc1035Utility.ToCompressedUtf8String(context.DomainName));
            }
            else
            {
                // context.DomainName is a NetBIOS name.
                nlAuthMessage.Flags =
                    NL_AUTH_MESSAGE_Flags_Value.NetbiosOemDomainName
                    | NL_AUTH_MESSAGE_Flags_Value.NetbiosOemComputerName;
                nlAuthMessage.Buffer = ArrayUtility.ConcatenateArrays(
                    Encoding.ASCII.GetBytes(context.DomainName + NULL),
                    Encoding.ASCII.GetBytes(context.ClientComputerName + NULL));
            }

            return nlAuthMessage;
        }
        /// <summary>
        /// Create an instance of NL_AUTH_MESSAGE.
        /// </summary>
        /// <returns>Created NL_AUTH_MESSAGE structure</returns>
        private NL_AUTH_MESSAGE CreateNlAuthMessage()
        {
            NL_AUTH_MESSAGE nlAuthMessage = new NL_AUTH_MESSAGE();
            nlAuthMessage.MessageType = MessageType_Values.NegotiateResponse;
            nlAuthMessage.Flags = (NL_AUTH_MESSAGE_Flags_Value)0;
            nlAuthMessage.Buffer = BitConverter.GetBytes(NULL);

            return nlAuthMessage;
        }
        /// <summary>
        /// Validate NL_AUTH_MESSAGE when the client receives the return token.
        /// </summary>
        /// <param name="nlAuthMessage">NL_AUTH_MESSAGE token.</param>
        /// <returns>True if validate pass; otherwise, false.</returns>
        /// <exception cref="InvalidOperationException">
        /// Thrown when the method is called before establishing a NRPC secure channel.
        /// </exception>
        public bool ValidateNlAuthMessage(NL_AUTH_MESSAGE nlAuthMessage)
        {
            ValidateSecureChannelExists();

            //When the client receives the return token, it verifies that:
            //the NL_AUTH_MESSAGE token is at least 12 bytes in length, and
            //the MessageType is set to 1.
            if (nlAuthMessage.MessageType == MessageType_Values.NegotiateResponse)
            {
                //NL_AUTH_MESSAGE is always at least 12 bytes.

                //the client initializes ClientSequenceNumber to 0,
                //which is used to detect out-of-order messages.
                context.SequenceNumber = 0;
                return true;
            }
            else
            {
                return false;
            }
        }