/// <summary>
        /// Create a KileClient instance.
        /// </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>
        /// <exception cref="System.ArgumentNullException">Thrown when the input parameter is null.</exception>
        public KileClient(string domain, string cName, string password, KileAccountType accountType)
        {
            if (domain == null)
            {
                throw new ArgumentNullException("domain");
            }
            if (cName == null)
            {
                throw new ArgumentNullException("cName");
            }
            if (password == null)
            {
                throw new ArgumentNullException("password");
            }

            context             = new KileClientContext();
            decoder             = new KileDecoder(context);
            transportBufferSize = ConstValue.TRANSPORT_BUFFER_SIZE;
            this.domain         = domain;
            this.userName       = cName;
            this.password       = password;

            context.Password = password;
            context.Salt     = GenerateSalt(domain, cName, accountType);
        }
        /// <summary>
        /// Set up the TCP/UDP transport connection with KDC.
        /// </summary>
        /// <param name="localPort">The server port</param>
        /// <param name="transportType">Whether the transport is TCP or UDP transport.</param>
        /// <param name="ipType">Ip Version</param>
        /// <param name="transportSize">The buffer size of transport stack. </param>
        /// <exception cref="System.ArgumentException">Thrown when the transportType is neither TCP nor UDP.</exception>
        public void Start(ushort localPort, KileConnectionType transportType, KileIpType ipType, int transportSize)
        {
            SocketTransportConfig transportConfig = new SocketTransportConfig();

            transportConfig.Role           = Role.Server;
            transportConfig.MaxConnections = ConstValue.MAX_CONNECTIONS;
            transportConfig.BufferSize     = transportSize;

            if (ipType == KileIpType.Ipv4)
            {
                transportConfig.LocalIpAddress = IPAddress.Any;
            }
            else
            {
                transportConfig.LocalIpAddress = IPAddress.IPv6Any;
            }
            transportConfig.LocalIpPort = localPort;

            if (transportType == KileConnectionType.TCP)
            {
                transportConfig.Type = StackTransportType.Tcp;
            }
            else if (transportType == KileConnectionType.UDP)
            {
                transportConfig.Type = StackTransportType.Udp;
            }
            else
            {
                throw new ArgumentException("ConnectionType can only be TCP or UDP.");
            }
            decoder   = new KileDecoder(contextList, transportType);
            transport = new TransportStack(transportConfig, decoder.DecodePacketCallback);
            transport.Start();
        }
        /// <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);
        }