/// <summary>
        /// Construct a Kerberos test client for FAST
        /// </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>
        public KerberosFunctionalClient(string domain, string cName, string password, KerberosAccountType accountType, KerberosTicket armorTicket, EncryptionKey armorSessionKey, string kdcAddress, int kdcPort, TransportType transportType, KerberosConstValue.OidPkt omiPkt, ITestSite baseTestSite)
            : base(domain, cName, password, accountType, armorTicket, armorSessionKey, kdcAddress, kdcPort, transportType, omiPkt)
        {
            testSite = baseTestSite;
            if (accountType == KerberosAccountType.Device)
            {
                testSite.Log.Add(LogEntryKind.Debug, "Construct Kerberos client using computer account: {0}@{1}.",
                                    cName, domain);
            }
            else
            {
                testSite.Log.Add(LogEntryKind.Debug, "Construct Kerberos client using user account: {0}@{1}.",
                                    cName, domain);
            }
            EncryptionType[] encryptionTypes = new EncryptionType[]
            {
                EncryptionType.AES256_CTS_HMAC_SHA1_96,
                EncryptionType.AES128_CTS_HMAC_SHA1_96,
                EncryptionType.RC4_HMAC,
                EncryptionType.RC4_HMAC_EXP,
                EncryptionType.DES_CBC_CRC,
                EncryptionType.DES_CBC_MD5
            };

            KerbInt32[] etypes = new KerbInt32[encryptionTypes.Length];
            for (int i = 0; i < encryptionTypes.Length; i++)
            {
                etypes[i] = new KerbInt32((int)encryptionTypes[i]);
            }
            Asn1SequenceOf<KerbInt32> etype = new Asn1SequenceOf<KerbInt32>(etypes);

            Context.SupportedEType = etype;

            Context.Pvno = KerberosConstValue.KERBEROSV5;
        }
        /// <summary>
        /// Add a token header for AP request/response, wrap token or getmic token to make them a complete token.
        /// </summary>
        /// <param name="tokenBody">The AP request/response, wrap token or getmic token.
        /// This argument can be null.</param>
        /// <returns>The completed token.</returns>
        public static byte[] AddGssApiTokenHeader(byte[] tokenBody,
                                                  KerberosConstValue.OidPkt oidPkt     = KerberosConstValue.OidPkt.KerberosToken,
                                                  KerberosConstValue.GSSToken gssToken = KerberosConstValue.GSSToken.GSSSPNG)
        {
            List <byte> gssDataList = new List <byte>();

            gssDataList.Add(KerberosConstValue.KERBEROS_TAG);

            // kerberos oid (1.2.840.113554.1.2.2)
            byte[] oid;
            oid = KerberosConstValue.GetKerberosOid();

            if (tokenBody == null)
            {
                tokenBody = new byte[0];
            }

            int length = tokenBody.Length + oid.Length;

            if (length > 127)
            {
                // If the indicated value is 128 or more, it shall be represented in two or more octets,
                // with bit 8 of the first octet set to "1" and the remaining bits of the first octet
                // specifying the number of additional octets. The subsequent octets carry the value,
                // 8 bits per octet, most significant digit first. [rfc 2743]
                int         temp       = length;
                int         index      = 1;
                List <byte> lengthList = new List <byte>();
                lengthList.Add((byte)(temp & 0xFF));
                while ((temp >>= 8) != 0)
                {
                    index++;
                    lengthList.Add((byte)(temp & 0xFF));
                }

                gssDataList.Add((byte)(0x80 | index));
                lengthList.Reverse();
                gssDataList.AddRange(lengthList.ToArray());
            }
            else
            {
                // If the indicated value is less than 128, it shall be represented in a single octet with bit 8
                // (high order) set to "0" and the remaining bits representing the value. [rfc 2743]
                gssDataList.Add((byte)length);
            }

            gssDataList.AddRange(oid);
            gssDataList.AddRange(tokenBody);

            if (gssToken == KerberosConstValue.GSSToken.GSSAPI)
            {
                return(gssDataList.ToArray());
            }
            else
            {
                return(KerberosUtility.EncodeInitialNegToken(gssDataList.ToArray(), oidPkt));
            }
        }
 /// <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>
 /// <param name="kdcAddress">The IP address of the KDC.</param>
 /// <param name="kdcPort">The port of the KDC.</param>
 /// <param name="transportType">Whether the transport is TCP or UDP transport.</param>
 /// <exception cref="System.ArgumentNullException">Thrown when the input parameter is null.</exception>
 public KerberosClient(string domain, string cName, string password, KerberosAccountType accountType, KerberosTicket armorTicket, EncryptionKey armorSessionKey, string kdcAddress, int kdcPort, TransportType transportType, KerberosConstValue.OidPkt oidPkt = KerberosConstValue.OidPkt.KerberosToken, string salt = null)
 {
     TransportBufferSize = KerberosConstValue.TRANSPORT_BUFFER_SIZE;
     this.Context = new KerberosContext(domain, cName, password, accountType, salt, armorTicket, armorSessionKey);
     this.kdcAddress = kdcAddress;
     this.kdcPort = kdcPort;
     this.transportType = transportType;
     this.oidPkt = oidPkt;
     this.Context.TransportType = transportType;
 }
        /// <summary>
        /// Verify and remove a token header from AP request/response,
        /// wrap token or getmic token.
        /// </summary>
        /// <param name="completeToken">The complete token got from application message.
        /// This argument can be null. If it is null, null will be returned.</param>
        /// <returns>The token body without the header.</returns>
        /// <exception cref="System.FormatException">Throw FormatException if the token header is incorrect.</exception>
        public static byte[] VerifyGssApiTokenHeader(byte[] completeToken, KerberosConstValue.OidPkt oidPkt = KerberosConstValue.OidPkt.KerberosToken)
        {
            byte[] oid;
            oid = KerberosConstValue.GetKerberosOid();

            if (completeToken == null || completeToken.Length < KerberosConstValue.GetKerberosOid().Length)
            {
                throw new FormatException("The GSS-API token header is incomplete!");
            }

            if (completeToken[0] != KerberosConstValue.KERBEROS_TAG)
            {
                throw new FormatException("The GSS-API token header is incorrect!");
            }

            int length = 0;
            int index  = 2;  // the tag and length fields

            // If the length value is 128 or more.
            if ((completeToken[1] & 0x80) == 0x80)
            {
                // If the indicated value is 128 or more, it shall be represented in two or more octets,
                // with bit 8 of the first octet set to "1" and the remaining bits of the first octet
                // specifying the number of additional octets. The subsequent octets carry the value,
                // 8 bits per octet, most significant digit first.
                int num = completeToken[1] & 0x7F;
                index += num;

                if (num < 1 || num > 4)
                {
                    throw new FormatException("The GSS-API token length is incorrect!");
                }

                for (int i = 0; i < num; ++i)
                {
                    length = (length << 8) | completeToken[2 + i];
                }
            }
            else
            {
                // If the indicated value is less than 128, it shall be represented in a single octet with bit 8
                // (high order) set to "0" and the remaining bits representing the value. [rfc 2743]
                length = completeToken[1];
            }

            if (!ArrayUtility.CompareArrays(oid, ArrayUtility.SubArray(completeToken, index, oid.Length)))
            {
                throw new FormatException("The GSS-API token oid is incorrect!");
            }

            byte[] tokenBody = ArrayUtility.SubArray(completeToken, index + oid.Length);
            return(tokenBody);
        }
        public static byte[] EncodeInitialNegToken(byte[] token,
                                                   KerberosConstValue.OidPkt oidPkt)
        {
            int[] oidInt;
            if (oidPkt == KerberosConstValue.OidPkt.KerberosToken)
            {
                oidInt = KerberosConstValue.GetKerberosOidInt();
            }
            else if (oidPkt == KerberosConstValue.OidPkt.MSKerberosToken)
            {
                oidInt = KerberosConstValue.GetMsKerberosOidInt();
            }
            else
            {
                throw new NotSupportedException("oid not support");
            }

            MechTypeList mechTypeList = new MechTypeList(
                new MechType[]
            {
                new MechType(oidInt)
            }
                );

            Asn1OctetString octetString = new Asn1OctetString(token);
            NegTokenInit    init        = new NegTokenInit(mechTypeList, null, new Asn1OctetString(octetString.ByteArrayValue), new Asn1OctetString((byte[])null));

            NegotiationToken negToken = new NegotiationToken(NegotiationToken.negTokenInit, init);

            MechType        spnegoMech = new MechType(KerberosConstValue.GetSpngOidInt());
            InitialNegToken initToken  = new InitialNegToken(spnegoMech, negToken);

            Asn1BerEncodingBuffer buffer = new Asn1BerEncodingBuffer();

            initToken.BerEncode(buffer);

            return(buffer.Data);
        }
        /// <summary>
        /// Construct a Kerberos test client
        /// </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 logged on account. User or Computer</param>
        public KerberosTestClient(string domain, string cName, string password, KerberosAccountType accountType, string kdcAddress, int kdcPort, TransportType transportType, KerberosConstValue.OidPkt oidPkt, string salt = null)
            : base(domain, cName, password, accountType, kdcAddress, kdcPort, transportType, oidPkt, salt)
        {
            testSite = TestClassBase.BaseTestSite;
            if (accountType == KerberosAccountType.Device)
            {
                testSite.Log.Add(LogEntryKind.Debug, "Construct Kerberos client using computer account: {0}@{1}.",
                                    cName, domain);
            }
            else
            {
                testSite.Log.Add(LogEntryKind.Debug, "Construct Kerberos client using user account: {0}@{1}.",
                                    cName, domain);
            }
            EncryptionType[] encryptionTypes = new EncryptionType[]
            {
                EncryptionType.AES256_CTS_HMAC_SHA1_96,
                EncryptionType.AES128_CTS_HMAC_SHA1_96,
                EncryptionType.RC4_HMAC,
                EncryptionType.RC4_HMAC_EXP,
                EncryptionType.DES_CBC_CRC,
                EncryptionType.DES_CBC_MD5
            };

            Microsoft.Protocols.TestTools.StackSdk.Security.KerberosLib.KerbInt32[] etypes =
                new Microsoft.Protocols.TestTools.StackSdk.Security.KerberosLib.KerbInt32[encryptionTypes.Length];
            for (int i = 0; i < encryptionTypes.Length; i++)
            {
                etypes[i] = new Microsoft.Protocols.TestTools.StackSdk.Security.KerberosLib.KerbInt32((int)encryptionTypes[i]);
            }
            Asn1SequenceOf<KerbInt32> etype = new Asn1SequenceOf<KerbInt32>(etypes);

            Context.SupportedEType = etype;

            Context.Pvno = KerberosConstValue.KERBEROSV5;
        }
        /// <summary>
        /// Decode GSSAPI token to AP-REP
        /// </summary>
        /// <param name="token">GSSAPI token</param>
        /// <returns></returns>
        public KerberosApResponse GetApResponseFromToken(byte[] token, KerberosConstValue.GSSToken gssToken = KerberosConstValue.GSSToken.GSSSPNG)
        {
            if (gssToken == KerberosConstValue.GSSToken.GSSSPNG)
                token = KerberosUtility.DecodeNegotiationToken(token);

            if (token[0] == KerberosConstValue.KERBEROS_TAG)
            {
                byte[] apData = KerberosUtility.VerifyGssApiTokenHeader(token, this.oidPkt);

                // Check if it has a two-byte tok_id
                if (null == apData || apData.Length <= sizeof(TOK_ID))
                {
                    throw new FormatException(
                        "Data length is shorter than a valid AP Response data length.");
                }

                // verify TOK_ID
                byte[] tokenID = ArrayUtility.SubArray<byte>(apData, 0, sizeof(TOK_ID));
                Array.Reverse(tokenID);
                TOK_ID id = (TOK_ID)BitConverter.ToUInt16(tokenID, 0);

                this.testSite.Assert.AreEqual(TOK_ID.KRB_AP_REP, id, "The Token ID should be KRB_AP_REP");

                // Get apBody
                token = ArrayUtility.SubArray(apData, sizeof(TOK_ID));
            }

            KerberosApResponse response = new KerberosApResponse();
            response.FromBytes(token);

            return response;
        }