/// <summary>
        /// Verify a request or response
        /// </summary>
        /// <param name="securityBuffers">Security buffers containing encrypted data.</param>
        /// <param name="sentFromClient">True if the token is a request, false is a response</param>
        /// <returns>True if verify succeed; otherwise, false.</returns>
        private bool Verify(SecurityBuffer[] securityBuffers, bool sentFromClient)
        {
            KileContext context = sentFromClient ? (KileContext)kileDecoder.serverContext : (KileContext)kileDecoder.clientContext;
            KilePdu     pdu;

            return(KileRole.GssVerifyMicEx(context, securityBuffers, out pdu));
        }
        /// <summary>
        /// Encrypts Message. User decides what SecBuffers are used.
        /// </summary>
        /// <param name="kileRole">Represents client or server</param>
        /// <param name="securityBuffers">The security buffers to encrypt.</param>
        /// <exception cref="System.ArgumentException">Thrown when the data or token is not valid.</exception>
        internal static void Encrypt(KileRole kileRole, params SecurityBuffer[] securityBuffers)
        {
            byte[] message = SspiUtility.ConcatenateReadWriteSecurityBuffers(securityBuffers, SecurityBufferType.Data);
            byte[] token   = SspiUtility.ConcatenateSecurityBuffers(securityBuffers, SecurityBufferType.Token);

            if (token.Length == 0)
            {
                throw new ArgumentException("Token buffer is not valid.");
            }

            SGN_ALG sgnAlg = GetSgnAlg(kileRole);
            KilePdu pdu    = kileRole.GssWrap(true, sgnAlg, message);

            byte[] cipherData = null;

            if (pdu.GetType() == typeof(Token4121))
            {
                cipherData = pdu.ToBytes();
            }
            else
            {
                byte[] allData     = pdu.ToBytes();
                byte[] paddingData = ((Token1964_4757)pdu).paddingData;
                cipherData = ArrayUtility.SubArray(allData, 0, allData.Length - paddingData.Length);
                SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Padding, paddingData);
            }
            SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Data,
                                              ArrayUtility.SubArray(cipherData, cipherData.Length - message.Length));
            SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Token,
                                              ArrayUtility.SubArray(cipherData, 0, cipherData.Length - message.Length));
        }
        /// <summary>
        /// Decrypt a request or response
        /// </summary>
        /// <param name="securityBuffers">Security buffers containing encrypted data.</param>
        /// <param name="sentFromClient">True if the token is a request, false is a response</param>
        /// <returns>Plain-text data.</returns>
        private byte[] Decrypt(SecurityBuffer[] securityBuffers, bool sentFromClient)
        {
            KileContext context = sentFromClient ? (KileContext)kileDecoder.serverContext : (KileContext)kileDecoder.clientContext;

            KileRole.GssUnWrapEx(context, securityBuffers);
            return(SspiUtility.ConcatenateReadWriteSecurityBuffers(securityBuffers, SecurityBufferType.Data));
        }
        /// <summary>
        /// This takes the given SecurityBuffer array, signs data part, and updates signature into token part
        /// </summary>
        /// <param name="kileRole">Represents client or server</param>
        /// <param name="securityBuffers">Data to sign and token to update.</param>
        /// <exception cref="System.ArgumentException">Thrown when the data or token is not valid.</exception>
        internal static void Sign(KileRole kileRole, params SecurityBuffer[] securityBuffers)
        {
            byte[] token = SspiUtility.ConcatenateReadWriteSecurityBuffers(securityBuffers, SecurityBufferType.Token);

            if (token.Length == 0)
            {
                throw new ArgumentException("No token can be updated for signature.");
            }
            byte[]  message = GetToBeSignedDataFromSecurityBuffers(securityBuffers);
            SGN_ALG sgnAlg  = GetSgnAlg(kileRole);
            KilePdu pdu     = kileRole.GssGetMic(sgnAlg, message);

            byte[] signature = pdu.ToBytes();

            SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Token, signature);
        }
        /// <summary>
        /// Get SGN_ALG for encryption and sign.
        /// </summary>
        /// <returns>The SGN_ALG got from context.</returns>
        /// <exception cref="System.FormatException">Thrown when the key is not valid.</exception>
        private static SGN_ALG GetSgnAlg(KileRole kileRole)
        {
            EncryptionKey key = kileRole.Context.ContextKey;

            if (key == null || key.keytype == null || key.keyvalue == null || key.keyvalue.Value == null)
            {
                throw new FormatException("Initialization is not complete successfully!");
            }

            SGN_ALG        sgnAlg = SGN_ALG.HMAC;
            EncryptionType type   = (EncryptionType)key.keytype.Value;

            if (type == EncryptionType.DES_CBC_MD5 || type == EncryptionType.DES_CBC_CRC)
            {
                sgnAlg = SGN_ALG.DES_MAC_MD5;
            }

            return(sgnAlg);
        }
        /// <summary>
        /// This takes the given byte array, decrypts it, and returns
        /// the original, unencrypted byte array.
        /// </summary>
        /// <param name="kileRole">Represents client or server</param>
        /// <param name="securityBuffers">The security buffers to decrypt.</param>
        /// <exception cref="System.ArgumentException">Thrown when the data or token is not valid.</exception>
        internal static bool Decrypt(KileRole kileRole, params SecurityBuffer[] securityBuffers)
        {
            KilePdu pdu = kileRole.GssUnWrapEx(securityBuffers);

            byte[] decryptedMessage = null;

            if (pdu.GetType() == typeof(Token4121))
            {
                decryptedMessage = ((Token4121)pdu).Data;
            }
            else if (pdu.GetType() == typeof(Token1964_4757))
            {
                Token1964_4757 tokenData = (Token1964_4757)pdu;
                decryptedMessage = tokenData.Data;
                SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Padding, tokenData.paddingData);
            }
            // else do nothing

            return(true);
        }
        /// <summary>
        /// Decrypt a request or response
        /// </summary>
        /// <param name="token">A token containing encrypted data.</param>
        /// <param name="sentFromClient">True if the token is a request, false is a response</param>
        /// <returns>Plain-text data.</returns>
        private byte[] Decrypt(byte[] token, bool sentFromClient)
        {
            KileContext context = sentFromClient ? (KileContext)kileDecoder.serverContext : (KileContext)kileDecoder.clientContext;
            KilePdu     pdu     = KileRole.GssUnWrap(context, token);

            Token4121 token4121Pdu = pdu as Token4121;

            if (token4121Pdu != null)
            {
                return(token4121Pdu.Data);
            }

            Token1964_4757 token1964or4757Pdu = pdu as Token1964_4757;

            if (token1964or4757Pdu != null)
            {
                return(token1964or4757Pdu.Data);
            }

            throw new InvalidOperationException("Token type is not supported.");
        }
        /// <summary>
        /// Create a KileDecrypter instance.
        /// User should call following methods in sequence to initialize: AsExchange, TgsExchange and ApExchange.
        /// After exchanges are done, call DecryptRequest or DecryptResponse from first encrypted message to last.
        /// Do not skip any request or response.
        /// </summary>
        /// <param name="domain">
        /// The realm part of the client's principal identifier.
        /// This argument cannot be null.
        /// </param>
        /// <param name="cName">
        /// The account to logon the remote machine. Either user account or computer account.
        /// This argument cannot be null.
        /// </param>
        /// <param name="password">
        /// The password of the user.
        /// This argument cannot be null.
        /// </param>
        /// <param name="accountType">
        /// The type of the logon account. User or Computer.
        /// </param>
        /// <param name="connectionType">
        /// The connection type, TCP or UDP.
        /// </param>
        /// <exception cref="System.ArgumentNullException">
        /// Thrown when any parameter is null.
        /// </exception>
        public KileDecrypter(
            string domain,
            string cName,
            string password,
            KileAccountType accountType,
            KileConnectionType connectionType)
        {
            if (domain == null)
            {
                throw new ArgumentNullException(nameof(domain));
            }
            if (cName == null)
            {
                throw new ArgumentNullException(nameof(cName));
            }
            if (password == null)
            {
                throw new ArgumentNullException(nameof(password));
            }

            kileDecoder = new KileDecoder();
            kileDecoder.connectionType = connectionType;

            string salt = KileRole.GenerateSalt(domain, cName, accountType);

            kileDecoder.clientContext               = new KileClientContext();
            kileDecoder.clientContext.Password      = password;
            kileDecoder.clientContext.TransportType = connectionType;
            kileDecoder.clientContext.Salt          = salt;

            kileDecoder.serverContext = new KileServerContext();
            kileDecoder.serverContext.TransportType = connectionType;
            kileDecoder.serverContext.Salt          = salt;
            kileDecoder.serverContextList           = new Dictionary <KileConnection, KileServerContext>(new KileServerContextComparer());
            kileDecoder.serverContextList.Add(new KileConnection(FAKE_ENDPOINT), kileDecoder.serverContext);
        }
        /// <summary>
        /// This takes the given byte array and verifies it using the SSPI VerifySignature method.
        /// </summary>
        /// <param name="kileRole">Represents client or server</param>
        /// <param name="securityBuffers">Data and token to verify</param>
        /// <returns>Success if true, Fail if false</returns>
        /// <exception cref="System.ArgumentException">Thrown when the data or token is not valid.</exception>
        internal static bool Verify(KileRole kileRole, params SecurityBuffer[] securityBuffers)
        {
            KilePdu pdu;

            return(kileRole.GssVerifyMicEx(securityBuffers, out pdu));
        }