/// <summary>
        /// Decrypts the encrypted message and returns decrypted message.
        /// Schannel is not supported.
        /// </summary>
        /// <param name="messageToBeDecrypted">Message to be decrypted.</param>
        /// <param name="signature">Signature of the message, for windows sspi, signature can't be null.</param>
        /// <returns>Decrypted message</returns>
        public byte[] Decrypt(byte[] messageToBeDecrypted, byte[] signature)
        {
            SecurityBuffer messageBuffer = new SecurityBuffer(SecurityBufferType.Data, messageToBeDecrypted);
            SecurityBuffer tokenBuffer = new SecurityBuffer(SecurityBufferType.Token, signature);

            Decrypt(messageBuffer, tokenBuffer);
            return messageBuffer.Buffer;
        }
        /// <summary>
        /// Encrypts the message and returns another byte array containing encrypted message.
        /// Schannel is not supported.
        /// </summary>
        /// <param name="messageToBeEncrypted">Message to be encrypted.</param>
        /// <param name="signature">Generated signature</param>
        /// <returns>Encrypted message</returns>
        public byte[] Encrypt(byte[] messageToBeEncrypted, out byte[] signature)
        {
            SecurityBuffer messageBuffer = new SecurityBuffer(SecurityBufferType.Data, messageToBeEncrypted);
            SecurityBuffer tokenBuffer = new SecurityBuffer(
                SecurityBufferType.Token,
                new byte[NativeMethods.MAX_TOKEN_SIZE]);

            Encrypt(messageBuffer, tokenBuffer);
            signature = tokenBuffer.Buffer;
            return messageBuffer.Buffer;
        }
        /// <summary>
        /// Writes a sequence of bytes to the current stream. The data is encrypted first before sending out.
        /// </summary>
        /// <param name="buffer">The buffer to be sent.</param>
        /// <param name="offset">The offset in buffer at which to begin writing to the stream.</param>
        /// <param name="count">The number of bytes to be written.</param>
        /// <exception cref="IOException">Raised when attempting to read from/write to a remote connection which 
        /// has been closed</exception>
        /// <exception cref="ArgumentOutOfRangeException">Raised when the offset incremented by count exceeds
        /// the length of buffer</exception>
        public override void Write(byte[] buffer, int offset, int count)
        {
            if (offset > buffer.Length - 1)
            {
                throw new ArgumentOutOfRangeException("offset");
            }

            if (offset + count > buffer.Length)
            {
                throw new ArgumentOutOfRangeException("count");
            }

            byte[] outBuffer = new byte[count];
            Array.Copy(buffer, offset, outBuffer, 0, count);

            // Encrypt message
            SecurityPackageContextStreamSizes streamSizes =
                (SecurityPackageContextStreamSizes)context.QueryContextAttributes("SECPKG_ATTR_STREAM_SIZES");
            SecurityBuffer messageBuffer = new SecurityBuffer(SecurityBufferType.Data, buffer);
            SecurityBuffer headerBuffer = new SecurityBuffer(
                SecurityBufferType.StreamHeader,
                new byte[streamSizes.Header]);
            SecurityBuffer trailerBuffer = new SecurityBuffer(
                SecurityBufferType.StreamTrailer,
                new byte[streamSizes.Trailer]);
            SecurityBuffer emptyBuffer = new SecurityBuffer(SecurityBufferType.Empty, null);

            context.Encrypt(headerBuffer, messageBuffer, trailerBuffer, emptyBuffer);
            byte[] encryptedMsg = ArrayUtility.ConcatenateArrays(
                headerBuffer.Buffer,
                messageBuffer.Buffer,
                trailerBuffer.Buffer);
            clientStream.Write(encryptedMsg, 0, encryptedMsg.Length);
        }
        /// <summary>
        /// Reads a sequence of bytes from the current stream.
        /// </summary>
        /// <param name="buffer">The buffer that contains decrypted data.</param>
        /// <param name="offset">The offset in buffer at which to begin storing the decrypted data.</param>
        /// <param name="count">The maximum number of bytes to get.</param>
        /// <exception cref="ArgumentOutOfRangeException">Raised when buffer or the internal decryptedBuffer doesn't
        /// contain enough space
        /// </exception>
        /// <exception cref="IOException">Raised when attempting to read from/write to a remote connection which 
        /// has been closed</exception>
        /// <returns>The actual number of bytes read into the buffer. Could be less than count</returns>
        public override int Read(byte[] buffer, int offset, int count)
        {
            if (offset > buffer.Length - 1)
            {
                throw new ArgumentOutOfRangeException("offset");
            }

            if (offset + count > buffer.Length)
            {
                throw new ArgumentOutOfRangeException("count");
            }

            if (!IsBufferEmpty())
            {
                if (CheckAvailableCount(count))
                {
                    Array.Copy(decryptedBuffer, this.startIndex, buffer, offset, count);
                    this.startIndex += count;

                    return count;
                }
                else
                {
                    int sizeRead = this.endIndex - this.startIndex;
                    Array.Copy(decryptedBuffer, this.startIndex, buffer, offset, sizeRead);
                    // All data is read, reset indices
                    this.startIndex = 0;
                    this.endIndex = 0;

                    return sizeRead;
                }
            }

            byte[] encryptedMsg = null;
            int bytesReceived = 0;
            byte[] decryptedMsg = null;

            while (decryptedMsg == null)
            {
                // decryptedMsg being null indicates incomplete data, so we continue reading and decrypting.
                bytesReceived = clientStream.Read(recvBuffer, 0, recvBuffer.Length);

                // The connection has been closed by remote server
                if (bytesReceived == 0)
                {
                    return 0;
                }

                // There's pooled data, concatenate the buffer together for decryption
                if (this.pooledBuffer != null && this.pooledBuffer.Length > 0)
                {
                    encryptedMsg = new byte[this.pooledBuffer.Length + bytesReceived];
                    Array.Copy(this.pooledBuffer, encryptedMsg, this.pooledBuffer.Length);
                    Array.Copy(recvBuffer, 0, encryptedMsg, this.pooledBuffer.Length, bytesReceived);

                    this.pooledBuffer = null;
                }
                else
                {
                    encryptedMsg = new byte[bytesReceived];
                    Array.Copy(recvBuffer, encryptedMsg, bytesReceived);
                }

                byte[] extraData = null;
                // Do decryption
                SecurityBuffer[] securityBuffers = new SecurityBuffer[]
                {
                    new SecurityBuffer(SecurityBufferType.Data, encryptedMsg),
                    new SecurityBuffer(SecurityBufferType.Empty, null),
                    new SecurityBuffer(SecurityBufferType.Empty, null),
                    new SecurityBuffer(SecurityBufferType.Empty, null)
                };

                context.Decrypt(securityBuffers);
                for (int i = 0; i < securityBuffers.Length; i++)
                {
                    if (securityBuffers[i].BufferType == SecurityBufferType.Data)
                    {
                        decryptedMsg = ArrayUtility.ConcatenateArrays(decryptedMsg, securityBuffers[i].Buffer);
                    }
                    else if (securityBuffers[i].BufferType == SecurityBufferType.Extra)
                    {
                        extraData = ArrayUtility.ConcatenateArrays(extraData, securityBuffers[i].Buffer);
                    }
                }

                if (extraData != null && extraData.Length > 0)
                {
                    this.pooledBuffer = extraData;
                }
            }

            Array.Copy(decryptedMsg, 0, this.decryptedBuffer, this.endIndex, decryptedMsg.Length);
            this.endIndex += decryptedMsg.Length;

            return Read(buffer, offset, count);
        }
        /// <summary>
        /// validate netlogon signature token when AES is not negotiated
        /// </summary>
        /// <param name="sequenceNumber">sequence number</param>
        /// <param name="sessionKey">session key</param>
        /// <param name="requestConfidentiality">confidentiality is required or not</param>
        /// <param name="isClientSideOutbound">Is client side outbound message.</param>
        /// <param name="securityBuffers">
        /// Security buffer, contains plain-text if requestConfidentiality is false; 
        /// or cipher-text if requestConfidentiality is true; 
        /// and signature.
        /// </param>
        /// <returns>true if validate success; otherwise, false.</returns>
        private static bool ValidateNetlogonSignatureTokenWhenAesIsNotNegotiated(
            ref ulong sequenceNumber,
            byte[] sessionKey,
            bool requestConfidentiality,
            bool isClientSideOutbound,
            SecurityBuffer[] securityBuffers)
        {
            byte[] plainText;
            byte[] cipherText;
            byte[] token;

            //If AES is not negotiated, a server receives a NL_AUTH_SIGNATURE structure.
            token = SspiUtility.ConcatenateSecurityBuffers(securityBuffers, SecurityBufferType.Token);
            NL_AUTH_SIGNATURE nlAuthSign = TypeMarshal.ToStruct<NL_AUTH_SIGNATURE>(token);

            //The SignatureAlgorithm bytes MUST be verified to ensure:
            //If AES is not negotiated, the first byte is set to 0x77.
            //The second byte is set to 0x00
            if (nlAuthSign.SignatureAlgorithm != SignatureAlgorithm_Values.HMACMD5)
            {
                return false;
            }

            if (requestConfidentiality)
            {
                //If the Confidentiality option is requested from the application,
                //then the SealAlgorithm MUST be verified to ensure that
                //if AES is not negotiated, the first byte is set to 0x7A.
                //The second byte is set to 0x00.
                if (nlAuthSign.SealAlgorithm != SealAlgorithm_Values.RC4)
                {
                    return false;
                }
            }
            else
            {
                //If the Confidentiality option is not requested,
                //then the SealAlgorithm MUST be verified to contain all 0xff bytes.
                if (nlAuthSign.SealAlgorithm != SealAlgorithm_Values.NotEncrypted)
                {
                    return false;
                }
            }

            //The Pad MUST be verified to contain all 0xff bytes.
            if (nlAuthSign.Pad != Pad_Values.V1)
            {
                return false;
            }

            //The Flags data MAY be<86> disregarded.

            //The SequenceNumber MUST be decrypted.
            //If AES is not negotiated, then the server MUST use the RC4 algorithm.
            //The RC4key MUST be derived as follows.
            //SET zeroes to 4 bytes of 0
            //CALL hmac_md5(zeroes, [4 bytes], SessionKey, size of SessionKey, TmpData)
            //CALL hmac_md5(Checksum, size of Checksum, TmpData, size of TmpData, DecryptionKey)
            byte[] tmpData;
            byte[] sequenceNumberDecryptionKey;
            using (HMACMD5 hmacMd5 = new HMACMD5(sessionKey))
            {
                tmpData = hmacMd5.ComputeHash(new byte[4]);
            }
            using (HMACMD5 hmacMd5 = new HMACMD5(tmpData))
            {
                sequenceNumberDecryptionKey = hmacMd5.ComputeHash(nlAuthSign.Checksum);
            }
            using (RC4 rc4 = RC4.Create())
            {
                rc4.Key = sequenceNumberDecryptionKey;
                nlAuthSign.SequenceNumber = rc4.CreateDecryptor().TransformFinalBlock(
                    nlAuthSign.SequenceNumber,
                    0,
                    nlAuthSign.SequenceNumber.Length);
            }

            //A local copy of SequenceNumber MUST be computed using the following algorithm.
            byte[] copySequenceNumber = ComputeCopySequenceNumber(sequenceNumber, isClientSideOutbound);

            //The SequenceNumber MUST be compared to CopySeqNumber.
            if (!ArrayUtility.CompareArrays(
                copySequenceNumber,
                nlAuthSign.SequenceNumber))
            {
                return false;
            }

            //ServerSequenceNumber MUST be incremented.
            sequenceNumber += 1;

            //If the Confidentiality option is requested,
            //the Confounder and the data MUST be decrypted.
            if (requestConfidentiality)
            {
                byte[] rc4DecryptionKey = ComputeEncryptionKeyWhenAesIsNotNegotiated(
                    sessionKey,
                    nlAuthSign.SequenceNumber);

                using (RC4 rc4 = RC4.Create())
                {
                    rc4.Key = rc4DecryptionKey;
                    nlAuthSign.Confounder = rc4.CreateDecryptor().TransformFinalBlock(
                        nlAuthSign.Confounder,
                        0,
                        nlAuthSign.Confounder.Length);

                    cipherText = SspiUtility.ConcatenateReadWriteSecurityBuffers(
                        securityBuffers,
                        SecurityBufferType.Data);
                    plainText = rc4.CreateDecryptor().TransformFinalBlock(
                        cipherText,
                        0,
                        cipherText.Length);
                }

                SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Data, plainText);
            }

            //Compute signature
            plainText = ConcatenateSecurityBuffersForChecksum(securityBuffers);
            byte[] checksum = ComputeSignatureWhenAesIsNotNegotiated(
                nlAuthSign,
                sessionKey,
                requestConfidentiality,
                plainText);

            //The first 8 bytes of the computed signature MUST be compared to the checksum.
            if (checksum != null
                && checksum.Length >= NL_AUTH_SIGNATURE_CHECKSUM_LENGTH
                && nlAuthSign.Checksum != null
                && nlAuthSign.Checksum.Length >= NL_AUTH_SIGNATURE_CHECKSUM_LENGTH)
            {
                for (int i = 0; i < NL_AUTH_SIGNATURE_CHECKSUM_LENGTH; i++)
                {
                    if (checksum[i] != nlAuthSign.Checksum[i])
                    {
                        return false;
                    }
                }
            }

            return true;
        }
        /// <summary>
        /// Decrypt data
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public byte[] Decrypt(byte[] data)
        {
            if (data == null || data.Length == 0)
            {
                return null;
            }

            byte[] dtlsDataBuffer = new byte[data.Length];
            Array.Copy(data, dtlsDataBuffer, data.Length);
            SecurityBuffer dataBuffer = new SecurityBuffer(SecurityBufferType.Data, dtlsDataBuffer);
            SecurityBuffer emptyBuffer1 = new SecurityBuffer(SecurityBufferType.Empty, null);
            SecurityBuffer emptyBuffer2 = new SecurityBuffer(SecurityBufferType.Empty, null);
            SecurityBuffer emptyBuffer3 = new SecurityBuffer(SecurityBufferType.Empty, null);
            if (dtlsServerContext != null)
            {
                dtlsServerContext.Decrypt(dataBuffer, emptyBuffer1, emptyBuffer2, emptyBuffer3);
            }
            else
            {
                dtlsClientContext.Decrypt(dataBuffer, emptyBuffer1, emptyBuffer2, emptyBuffer3);
            }
            SecurityBuffer decryptedDataBuffer =
                (dataBuffer.BufferType == SecurityBufferType.Data) ? dataBuffer :
                (emptyBuffer1.BufferType == SecurityBufferType.Data) ? emptyBuffer1 :
                (emptyBuffer2.BufferType == SecurityBufferType.Data) ? emptyBuffer2 :
                (emptyBuffer3.BufferType == SecurityBufferType.Data) ? emptyBuffer3 : null;
            if (decryptedDataBuffer != null)
            {
                return decryptedDataBuffer.Buffer;
            }
            else
            {
                return null;
            }
        }
        public override void Initialize(byte[] serverToken)
        {
            uint hResult;
            SecurityBuffer[] securityBuffers;
            if (this.packageType == SecurityPackageType.CredSsp)
            {
                //On calls to this function after the initial call, there must be two buffers.
                securityBuffers = new SecurityBuffer[2];
                //The first has type SECBUFFER_TOKEN and contains the token received from the server.
                securityBuffers[0] = new SecurityBuffer(SecurityBufferType.Token, serverToken);
                //The second buffer has type SECBUFFER_EMPTY; set both the pvBuffer and cbBuffer members to zero.
                securityBuffers[1] = new SecurityBuffer(SecurityBufferType.Empty, new byte[0]);
            }
            else
            {
                securityBuffers = new SecurityBuffer[] { new SecurityBuffer(SecurityBufferType.Token, serverToken) };
            }

            SecurityBuffer outTokenBuffer = new SecurityBuffer(
                SecurityBufferType.Token,
                new byte[NativeMethods.MAX_TOKEN_SIZE]);

            SecurityBufferDescWrapper serverTokenDescWrapper = new SecurityBufferDescWrapper(securityBuffers);
            SecurityBufferDescWrapper outBufferDescWrapper = new SecurityBufferDescWrapper(outTokenBuffer);
            uint outContextAttribute;
            SecurityInteger expiryTime = new SecurityInteger();

            if (serverToken == null)
            {
                hResult = NativeMethods.InitializeSecurityContext(
                    ref this.credentialHandle,
                    IntPtr.Zero,
                    this.serverPrincipalName,
                    (int)this.securityContextAttributes,
                    0,
                    (int)this.targetDataRepresentaion,
                    IntPtr.Zero,
                    0,
                    out this.contextHandle,
                    out outBufferDescWrapper.securityBufferDesc,
                    out outContextAttribute,
                    out expiryTime);
            }
            else
            {
                if (this.contextHandle.LowPart == IntPtr.Zero && this.contextHandle.HighPart == IntPtr.Zero)
                {
                    hResult = NativeMethods.InitializeSecurityContext(
                        ref this.credentialHandle,
                        IntPtr.Zero,
                        this.serverPrincipalName,
                        (int)this.securityContextAttributes,
                        0,
                        (int)this.targetDataRepresentaion,
                        ref serverTokenDescWrapper.securityBufferDesc,
                        0,
                        out this.contextHandle,
                        out outBufferDescWrapper.securityBufferDesc,
                        out outContextAttribute,
                        out expiryTime);
                }
                else
                {
                    hResult = NativeMethods.InitializeSecurityContext(
                        ref this.credentialHandle,
                        ref this.contextHandle,
                        this.serverPrincipalName,
                        (int)this.securityContextAttributes,
                        0,
                        (int)this.targetDataRepresentaion,
                        ref serverTokenDescWrapper.securityBufferDesc,
                        0,
                        out this.contextHandle,
                        out outBufferDescWrapper.securityBufferDesc,
                        out outContextAttribute,
                        out expiryTime);
                }
            }

            serverTokenDescWrapper.FreeSecurityBufferDesc();

            if (hResult == NativeMethods.SEC_E_OK)
            {
                this.needContinueProcessing = false;
            }
            else if (hResult == NativeMethods.SEC_I_CONTINUE_NEEDED)
            {
                this.needContinueProcessing = true;
            }
            else
            {
                throw new SspiException("Initialize failed.", hResult);
            }

            //Get token if success.
            this.token = null;
            SspiSecurityBuffer[] outBuffers = outBufferDescWrapper.securityBufferDesc.GetBuffers();
            for (int i = 0; i < outBuffers.Length; i++)
            {
                if (outBuffers[i].bufferType == (uint)SecurityBufferType.Token)
                {
                    if (outBuffers[i].bufferLength > 0)
                    {
                        this.token = new byte[outBuffers[i].bufferLength];
                        Marshal.Copy(outBuffers[i].pSecBuffer, this.token, 0, this.token.Length);
                    }
                    break;
                }
            }
            outBufferDescWrapper.FreeSecurityBufferDesc();
        }
        /// <summary>
        /// This takes the given byte array and verifies it using SSPI VerifySignature method.
        /// </summary>
        /// <param name="messageToBeVerified">Signed message to be verified.</param>
        /// <param name="signature">The signature of the message.</param>
        /// <exception cref="SspiException">If verify fail, this exception will be thrown.</exception>
        public bool Verify(byte[] messageToBeVerified, byte[] signature)
        {
            SecurityBuffer messageBuffer = new SecurityBuffer(SecurityBufferType.Data, messageToBeVerified);
            SecurityBuffer signatureBuffer = new SecurityBuffer(SecurityBufferType.Token, signature);

            return Verify(messageBuffer, signatureBuffer);
        }
 /// <summary>
 /// Concatenate security buffers for computing checksum.
 /// </summary>
 /// <param name="securityBuffers">The security buffers.</param>
 /// <returns>Concatenated security buffers.</returns>
 private static byte[] ConcatenateSecurityBuffersForChecksum(SecurityBuffer[] securityBuffers)
 {
     byte[] buf = new byte[0];
     for (int i = 0; i < securityBuffers.Length; i++)
     {
         SecurityBufferType securityBufferType = securityBuffers[i].BufferType;
         if ((securityBufferType & ~SecurityBufferType.AttrMask) == SecurityBufferType.Data
             && (securityBufferType & SecurityBufferType.ReadOnly) == 0
             && securityBuffers[i].Buffer != null)
         {
             buf = ArrayUtility.ConcatenateArrays(buf, securityBuffers[i].Buffer);
         }
     }
     return buf;
 }
        /// <summary>
        /// Concatenate all read-write (READONLY flag is not set) buffers of a specified type in the list.
        /// </summary>
        /// <param name="securityBuffers">Input security buffers.</param>
        /// <param name="targetTypes">Specified types.</param>
        /// <returns>A single byte array with all buffers concatenated.</returns>
        /// <exception cref="ArgumentNullException">
        /// Thrown when securityBuffers is null.
        /// </exception>
        public static byte[] ConcatenateReadWriteSecurityBuffers(
            SecurityBuffer[] securityBuffers,
            params SecurityBufferType[] targetTypes)
        {
            if (securityBuffers == null || securityBuffers.Length == 0)
            {
                throw new ArgumentNullException("securityBuffers");
            }
            for (int i = 0; i < securityBuffers.Length; i++)
            {
                if (securityBuffers[i] == null)
                {
                    throw new ArgumentNullException("securityBuffers");
                }
            }

            return ConcatenateSecurityBuffers(securityBuffers, targetTypes, false);
        }
        /// <summary>
        /// Update buffers of a specified type in the list. 
        /// Buffer will be separated automatically to fit the original length of a security buffer. 
        /// If Buffer field of an input security buffer is null, it means the length is unlimited 
        /// (that is all remaining data will be copied into it). 
        /// Only read-write (READONLY flag is not set) security buffer will be updated.
        /// </summary>
        /// <param name="securityBuffers">Input security buffers.</param>
        /// <param name="targetTypes">Specified types.</param>
        /// <param name="buffer">The buffer to be updated into security buffers.</param>
        /// <exception cref="ArgumentNullException">
        /// Thrown when securityBuffers or buffer is null.
        /// </exception>
        /// <exception cref="SspiException">
        /// Total length of security buffers is not enough.
        /// </exception>
        public static void UpdateSecurityBuffers(SecurityBuffer[] securityBuffers, SecurityBufferType[] targetTypes, byte[] buffer)
        {
            if (securityBuffers == null || securityBuffers.Length == 0)
            {
                throw new ArgumentNullException("securityBuffers");
            }
            for (int i = 0; i < securityBuffers.Length; i++)
            {
                if (securityBuffers[i] == null)
                {
                    throw new ArgumentNullException("securityBuffers");
                }
            }
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }

            int offset = 0;
            for (int i = 0; i < securityBuffers.Length; i++)
            {
                SecurityBufferType securityBufferType = (securityBuffers[i].BufferType & ~SecurityBufferType.AttrMask);
                bool isReadOnly = ((securityBuffers[i].BufferType & SecurityBufferType.ReadOnly) != 0)
                               || ((securityBuffers[i].BufferType & SecurityBufferType.ReadOnlyWithChecksum) != 0);
                bool typeMatch = false;
                for (int j = 0; j < targetTypes.Length; j++)
                {
                    if (securityBufferType == targetTypes[j])
                    {
                        typeMatch = true;
                        break;
                    }
                }
                if (typeMatch && !isReadOnly)
                {
                    int length = buffer.Length - offset;
                    if (securityBuffers[i].Buffer != null && securityBuffers[i].Buffer.Length < length)
                    {
                        length = securityBuffers[i].Buffer.Length;
                    }

                    securityBuffers[i].Buffer = ArrayUtility.SubArray(
                        buffer,
                        offset,
                        length);

                    offset += length;
                }
            }

            if (offset < buffer.Length)
            {
                throw new SspiException("Total length of security buffers is not enough.");
            }
            else if (offset > buffer.Length)
            {
                //Unlikely to happen
                throw new InvalidOperationException("Extra data were written to security buffers.");
            }
        }
 /// <summary>
 /// Update buffers of a specified type in the list. 
 /// Buffer will be separated automatically to fit the original length of a security buffer. 
 /// If Buffer field of an input security buffer is null, it means the length is unlimited 
 /// (that is all remaining data will be copied into it). 
 /// Only read-write (READONLY flag is not set) security buffer will be updated.
 /// </summary>
 /// <param name="securityBuffers">Input security buffers.</param>
 /// <param name="targetType">A specified type.</param>
 /// <param name="buffer">The buffer to be updated into security buffers.</param>
 /// <exception cref="ArgumentNullException">
 /// Thrown when securityBuffers or buffer is null.
 /// </exception>
 /// <exception cref="SspiException">
 /// Total length of security buffers is not enough.
 /// </exception>
 public static void UpdateSecurityBuffers(SecurityBuffer[] securityBuffers, SecurityBufferType targetType, byte[] buffer)
 {
     UpdateSecurityBuffers(securityBuffers, new SecurityBufferType[] { targetType }, buffer);
 }
        private static byte[] ConcatenateSecurityBuffers(
            SecurityBuffer[] securityBuffers,
            SecurityBufferType[] targetTypes,
            bool bothReadOnlyAndReadWrite)
        {
            byte[] buf = new byte[0];
            for (int i = 0; i < securityBuffers.Length; i++)
            {
                SecurityBufferType securityBufferType = (securityBuffers[i].BufferType & ~SecurityBufferType.AttrMask);
                bool typeMatch = false;
                for (int j = 0; j < targetTypes.Length; j++)
                {
                    if (securityBufferType == targetTypes[j])
                    {
                        typeMatch = true;
                        break;
                    }
                }
                if (typeMatch)
                {
                    bool skip = !bothReadOnlyAndReadWrite
                        && (((securityBuffers[i].BufferType & SecurityBufferType.ReadOnly) != 0)
                        || ((securityBuffers[i].BufferType & SecurityBufferType.ReadOnlyWithChecksum) != 0));

                    if (!skip && securityBuffers[i].Buffer != null)
                    {
                        buf = ArrayUtility.ConcatenateArrays(buf, securityBuffers[i].Buffer);
                    }
                }
            }
            return buf;
        }
        /// <summary>
        /// Convert SecurityBuffer to SspiSecurityBuffer.
        /// </summary>
        /// <param name="securityBuffer">SecurityBuffer</param>
        /// <exception cref="ArgumentNullException">If securityBuffer is null, this exception will be thrown.
        /// </exception>
        internal SspiSecurityBuffer(SecurityBuffer securityBuffer)
        {
            if (securityBuffer == null)
            {
                throw new ArgumentNullException("securityBuffer");
            }

            if (securityBuffer.Buffer == null || securityBuffer.Buffer.Length == 0)
            {
                this.bufferLength = 0;
                this.pSecBuffer = IntPtr.Zero;
            }
            else
            {
                this.bufferLength = (uint)securityBuffer.Buffer.Length;
                this.pSecBuffer = Marshal.AllocHGlobal((int)this.bufferLength);
                Marshal.Copy(securityBuffer.Buffer, 0, this.pSecBuffer, (int)this.bufferLength);
            }
            this.bufferType = (uint)securityBuffer.BufferType;
        }
        /// <summary>
        /// Send source Data to remote endpoint through DTLS transport.
        /// </summary>
        /// <param name="data">The sending data.</param>
        public List<byte[]> Encrypt(byte[] data)
        {
            if (data == null) return null;
            List<byte[]> encryptedDataList = new List<byte[]>();
            int consumedLen = 0;
            while (data.Length - consumedLen > 0)
            {
                int toSendLen = (int)Math.Min(data.Length - consumedLen, dtlsStreamSizes.MaximumMessage);
                byte[] dataToSend = new byte[toSendLen];
                Array.Copy(data, consumedLen, dataToSend, 0, toSendLen);

                SecurityBuffer streamHd = new SecurityBuffer(SecurityBufferType.StreamHeader, new byte[dtlsStreamSizes.Header]);
                SecurityBuffer dataBuffer = new SecurityBuffer(SecurityBufferType.Data, dataToSend);
                SecurityBuffer streamTl = new SecurityBuffer(SecurityBufferType.StreamTrailer, new byte[dtlsStreamSizes.Trailer]);
                SecurityBuffer emptyBuffer = new SecurityBuffer(SecurityBufferType.Empty, null);
                if (dtlsServerContext != null)
                {
                    dtlsServerContext.Encrypt(streamHd, dataBuffer, streamTl, emptyBuffer);
                }
                else
                {
                    dtlsClientContext.Encrypt(streamHd, dataBuffer, streamTl, emptyBuffer);
                }

                byte[] dtlsEncrptedData = new byte[streamHd.Buffer.Length + dataBuffer.Buffer.Length + streamTl.Buffer.Length];
                Array.Copy(streamHd.Buffer, dtlsEncrptedData, streamHd.Buffer.Length);
                Array.Copy(dataBuffer.Buffer, 0, dtlsEncrptedData, streamHd.Buffer.Length, dataBuffer.Buffer.Length);
                Array.Copy(streamTl.Buffer, 0, dtlsEncrptedData, streamHd.Buffer.Length + dataBuffer.Buffer.Length, streamTl.Buffer.Length);

                encryptedDataList.Add(dtlsEncrptedData);
                consumedLen += toSendLen;
            }

            return encryptedDataList;
        }
        /// <summary>
        /// This takes the given byte array, signs it, and returns the signature.
        /// </summary>
        /// <param name="messageToBeSigned">Message to be signed.</param>
        /// <returns>Signature of message</returns>
        /// <exception cref="SspiException">If sign fail, this exception will be thrown.</exception>
        public byte[] Sign(byte[] messageToBeSigned)
        {
            SecurityBuffer messageBuffer = new SecurityBuffer(SecurityBufferType.Data, messageToBeSigned);
            SecurityBuffer tokenBuffer = new SecurityBuffer(
                SecurityBufferType.Token,
                new byte[NativeMethods.MAX_TOKEN_SIZE]);

            Sign(messageBuffer, tokenBuffer);
            return tokenBuffer.Buffer;
        }
        /// <summary>
        /// This takes the given message, sign it and returns another byte array containing the original message
        /// and signature, the format of the returned byte array is as follow:
        /// |MESSAGE_LENGTH(4 bytes)|MESSAGE|SIGNATURE|
        /// </summary>
        /// <param name="messageToBeSigned">Message to be signed.</param>
        /// <returns>Signed message and signature, which contains the header.</returns>
        /// <exception cref="SspiException">If sign fail, this exception will be thrown.</exception>
        public byte[] SignMessage(byte[] messageToBeSigned)
        {
            SecurityBuffer messageBuffer = new SecurityBuffer(SecurityBufferType.Data, messageToBeSigned);
            SecurityBuffer signatureBuffer = new SecurityBuffer(
                SecurityBufferType.Token,
                new byte[NativeMethods.MAX_TOKEN_SIZE]);

            Sign(messageBuffer, signatureBuffer);

            int messageLength = messageBuffer.Buffer.Length;
            byte[] signedMessage = ArrayUtility.ConcatenateArrays(
                BitConverter.GetBytes(messageLength),
                messageBuffer.Buffer,
                signatureBuffer.Buffer);
            return signedMessage;
        }
        /// <summary>
        /// initial netlogon signature token when AES is negotiated
        /// </summary>
        /// <param name="sequenceNumber">sequence number</param>
        /// <param name="sessionKey">session key</param>
        /// <param name="requestConfidentiality">confidentiality is required or not</param>
        /// <param name="isClientSideOutbound">Is client side outbound message.</param>
        /// <param name="securityBuffers">
        /// Security buffers, contains input plain-text; output cipher-text and signature.
        /// </param>
        private static void InitialNetlogonSignatureTokenWhenAesIsNegotiated(
            ref ulong sequenceNumber,
            byte[] sessionKey,
            bool requestConfidentiality,
            bool isClientSideOutbound,
            SecurityBuffer[] securityBuffers)
        {
            byte[] plainText;
            byte[] cipherText;
            byte[] token;
            NL_AUTH_SHA2_SIGNATURE nlAuthSha2Sign = new NL_AUTH_SHA2_SIGNATURE();

            //The SignatureAlgorithm first byte MUST be set to 0x13,
            //and the second byte MUST be set to 0x00.
            nlAuthSha2Sign.SignatureAlgorithm =
                NL_AUTH_SHA2_SIGNATURE_SignatureAlgorithm_Values.HMACSHA256;

            if (requestConfidentiality)
            {
                //If the Confidentiality option (section 3.3.1) is
                //requested from the application, then the SealAlgorithm
                //first byte MUST be set to 0x1A, the second byte MUST
                //be set to 0x00, and the Confounder MUST be filled with
                //cryptographically random data.
                nlAuthSha2Sign.SealAlgorithm =
                    NL_AUTH_SHA2_SIGNATURE_SealAlgorithm_Values.AES128;
                nlAuthSha2Sign.Confounder = GenerateNonce(NL_AUTH_SIGNATURE_CONFOUNDER_LENGTH);
            }
            else
            {
                //If the Confidentiality option (section 3.3.1) is not
                //requested, then the SealAlgorithm MUST be filled with
                //two bytes of 0xff, and the Confounder is not included in the token.
                nlAuthSha2Sign.SealAlgorithm =
                    NL_AUTH_SHA2_SIGNATURE_SealAlgorithm_Values.NotEncrypted;
                nlAuthSha2Sign.Confounder = null;
            }

            //The Pad MUST be filled with 0xff bytes.
            nlAuthSha2Sign.Pad = NL_AUTH_SHA2_SIGNATURE_Pad_Values.V1;

            //The Flags MUST be filled with 0x00 bytes.
            nlAuthSha2Sign.Flags = Flags_Values.V1;

            //The SequenceNumber MUST be computed using the following algorithm.
            nlAuthSha2Sign.SequenceNumber = ComputeCopySequenceNumber(sequenceNumber, isClientSideOutbound);

            //The ClientSequenceNumber MUST be incremented by 1.
            sequenceNumber += 1;

            //Compute signature
            plainText = ConcatenateSecurityBuffersForChecksum(securityBuffers);
            nlAuthSha2Sign.Checksum = ComputeSignatureWhenAesIsNegotiated(
                nlAuthSha2Sign,
                sessionKey,
                requestConfidentiality,
                plainText);

            //If the Confidentiality option is requested, the data and the Confounder
            //field MUST be encrypted. If AES is negotiated then the server MUST use
            //the AES-128 algorithm using the SessionKey with an initialization
            //vector constructed by concatenating the sequence number with
            //itself twice (thus getting 16 bytes of data)
            if (requestConfidentiality)
            {
                byte[] aesEncryptionKey = ComputeEncryptionKeyWhenAesIsNegotiated(sessionKey);

                using (BCryptAlgorithm aes = new BCryptAlgorithm("AES"))
                {
                    aes.Mode = BCryptCipherMode.CFB;
                    aes.Key = aesEncryptionKey;
                    aes.IV = ArrayUtility.ConcatenateArrays(
                        nlAuthSha2Sign.SequenceNumber,
                        nlAuthSha2Sign.SequenceNumber);
                    nlAuthSha2Sign.Confounder = aes.Encrypt(nlAuthSha2Sign.Confounder);

                    plainText = SspiUtility.ConcatenateReadWriteSecurityBuffers(
                        securityBuffers,
                        SecurityBufferType.Data);
                    cipherText = aes.Encrypt(plainText);
                }

                SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Data, cipherText);
            }
            else
            {
                cipherText = null;
            }

            //The SequenceNumber MUST be encrypted.
            //If AES is negotiated, then the server MUST use the AES-128
            //algorithm using the SessionKey with an initialization vector
            //constructed by concatenating the first 8 bytes of the
            //checksum with itself twice (thus getting 16 bytes of data)
            using (BCryptAlgorithm aes = new BCryptAlgorithm("AES"))
            {
                aes.Mode = BCryptCipherMode.CFB;
                aes.Key = sessionKey;
                aes.IV = ArrayUtility.ConcatenateArrays(
                    nlAuthSha2Sign.Checksum, // Checksum is only 8 bytes
                    nlAuthSha2Sign.Checksum);
                nlAuthSha2Sign.SequenceNumber = aes.Encrypt(nlAuthSha2Sign.SequenceNumber);
            }

            nlAuthSha2Sign.Dummy = GenerateNonce(NL_AUTH_SHA2_SIGNATURE_DUMMY_LENGTH); // 24 == size of dummy

            token = TypeMarshal.ToBytes(nlAuthSha2Sign);
            SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Token, token);
        }
        /// <summary>
        /// initial netlogon signature token when AES is not negotiated
        /// </summary>
        /// <param name="sequenceNumber">sequence number</param>
        /// <param name="sessionKey">session key</param>
        /// <param name="requestConfidentiality">confidentiality is required or not</param>
        /// <param name="isClientSideOutbound">Is client side outbound message.</param>
        /// <param name="securityBuffers">
        /// Security buffers, contains input plain-text; output cipher-text and signature.
        /// </param>
        private static void InitialNetlogonSignatureTokenWhenAesIsNotNegotiated(
            ref ulong sequenceNumber,
            byte[] sessionKey,
            bool requestConfidentiality,
            bool isClientSideOutbound,
            SecurityBuffer[] securityBuffers)
        {
            byte[] plainText;
            byte[] cipherText;
            byte[] token;
            NL_AUTH_SIGNATURE nlAuthSign = new NL_AUTH_SIGNATURE();

            //The SignatureAlgorithm first byte MUST be set to 0x77
            //and the second byte MUST be set to 0x00.
            nlAuthSign.SignatureAlgorithm = SignatureAlgorithm_Values.HMACMD5;

            if (requestConfidentiality)
            {
                //If the Confidentiality option (section 3.3.1) is requested
                //from the application, then the SealAlgorithm first byte MUST
                //be set to 0x7A, the second byte MUST be set to 0x00, and
                //the Confounder MUST be filled with cryptographically random data.
                nlAuthSign.SealAlgorithm = SealAlgorithm_Values.RC4;
                nlAuthSign.Confounder = GenerateNonce(NL_AUTH_SIGNATURE_CONFOUNDER_LENGTH);
            }
            else
            {
                //If the Confidentiality option is not requested, then the
                //SealAlgorithm MUST be filled with two bytes of value 0xff,
                //and the Confounder is not included in the token.
                nlAuthSign.SealAlgorithm = SealAlgorithm_Values.NotEncrypted;
                nlAuthSign.Confounder = null;
            }

            //The Pad MUST be filled with 0xff bytes.
            nlAuthSign.Pad = Pad_Values.V1;

            //The Flags MUST be filled with 0x00 bytes.
            nlAuthSign.Flags = new byte[NL_AUTH_SIGNATURE_FLAGS_LENGTH];

            //The SequenceNumber MUST be computed using the following algorithm.
            nlAuthSign.SequenceNumber = ComputeCopySequenceNumber(sequenceNumber, isClientSideOutbound);

            //The ClientSequenceNumber MUST be incremented by 1.
            sequenceNumber += 1;

            //Compute signature
            plainText = ConcatenateSecurityBuffersForChecksum(securityBuffers);
            nlAuthSign.Checksum = ComputeSignatureWhenAesIsNotNegotiated(
                nlAuthSign,
                sessionKey,
                requestConfidentiality,
                plainText);

            //If the Confidentiality option is requested, the data and the Confounder
            //field MUST be encrypted. If AES is not negotiated, it MUST use the
            //RC4 algorithm.
            if (requestConfidentiality)
            {
                byte[] rc4EncryptionKey = ComputeEncryptionKeyWhenAesIsNotNegotiated(
                    sessionKey,
                    nlAuthSign.SequenceNumber);

                using (RC4 rc4 = RC4.Create())
                {
                    rc4.Key = rc4EncryptionKey;
                    nlAuthSign.Confounder = rc4.CreateEncryptor().TransformFinalBlock(
                        nlAuthSign.Confounder,
                        0,
                        nlAuthSign.Confounder.Length);

                    plainText = SspiUtility.ConcatenateReadWriteSecurityBuffers(
                        securityBuffers,
                        SecurityBufferType.Data);
                    cipherText = rc4.CreateEncryptor().TransformFinalBlock(
                        plainText,
                        0,
                        plainText.Length);
                }

                SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Data, cipherText);
            }
            else
            {
                cipherText = null;
            }

            //The SequenceNumber MUST be encrypted.
            //If AES is not negotiated, it MUST use the RC4 algorithm.
            //The RC4 key MUST be derived as follows:
            //SET zeroes to 4 bytes of 0
            //CALL hmac_md5(zeroes, [4 bytes], SessionKey, size of SessionKey, TmpData)
            //CALL hmac_md5(Checksum, size of Checksum, TmpData, size of TmpData, EncryptionKey)
            byte[] tmpData;
            byte[] sequenceNumberEncryptionKey;
            using (HMACMD5 hmacMd5 = new HMACMD5(sessionKey))
            {
                tmpData = hmacMd5.ComputeHash(new byte[4]);
            }
            using (HMACMD5 hmacMd5 = new HMACMD5(tmpData))
            {
                sequenceNumberEncryptionKey = hmacMd5.ComputeHash(nlAuthSign.Checksum);
            }
            using (RC4 rc4 = RC4.Create())
            {
                rc4.Key = sequenceNumberEncryptionKey;
                nlAuthSign.SequenceNumber = rc4.CreateEncryptor().TransformFinalBlock(
                    nlAuthSign.SequenceNumber,
                    0,
                    nlAuthSign.SequenceNumber.Length);
            }

            token = TypeMarshal.ToBytes(nlAuthSign);
            SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Token, token);
        }
        /// <summary>
        /// validate netlogon signature token when AES is negotiated
        /// </summary>
        /// <param name="sequenceNumber">sequence number</param>
        /// <param name="sessionKey">session key</param>
        /// <param name="requestConfidentiality">confidentiality is required or not</param>
        /// <param name="isClientSideOutbound">Is client side outbound message.</param>
        /// <param name="securityBuffers">
        /// Security buffer, contains plain-text if requestConfidentiality is false; 
        /// or cipher-text if requestConfidentiality is true; 
        /// and signature.
        /// </param>
        /// <returns>true if validate success; otherwise, false.</returns>
        private static bool ValidateNetlogonSignatureTokenWhenAesIsNegotiated(
            ref ulong sequenceNumber,
            byte[] sessionKey,
            bool requestConfidentiality,
            bool isClientSideOutbound,
            SecurityBuffer[] securityBuffers)
        {
            byte[] plainText;
            byte[] cipherText;
            byte[] token;

            //If AES is negotiated, a server receives an NL_AUTH_SHA2_SIGNATURE structure
            token = SspiUtility.ConcatenateSecurityBuffers(securityBuffers, SecurityBufferType.Token);
            NL_AUTH_SHA2_SIGNATURE nlAuthSha2Sign = TypeMarshal.ToStruct<NL_AUTH_SHA2_SIGNATURE>(token);

            //The SignatureAlgorithm bytes MUST be verified to ensure:
            //If AES is negotiated, the first byte is set to 0x13.
            //The second byte is set to 0x00
            if (nlAuthSha2Sign.SignatureAlgorithm != NL_AUTH_SHA2_SIGNATURE_SignatureAlgorithm_Values.HMACSHA256)
            {
                return false;
            }

            if (requestConfidentiality)
            {
                //If the Confidentiality option is requested from the application,
                //then the SealAlgorithm MUST be verified to ensure that
                //if AES is negotiated, the first byte is set to 0x1A;
                //The second byte is set to 0x00.
                if (nlAuthSha2Sign.SealAlgorithm != NL_AUTH_SHA2_SIGNATURE_SealAlgorithm_Values.AES128)
                {
                    return false;
                }
            }
            else
            {
                //If the Confidentiality option is not requested,
                //then the SealAlgorithm MUST be verified to contain all 0xff bytes.
                if (nlAuthSha2Sign.SealAlgorithm != NL_AUTH_SHA2_SIGNATURE_SealAlgorithm_Values.NotEncrypted)
                {
                    return false;
                }
            }

            //The Pad MUST be verified to contain all 0xff bytes.
            if (nlAuthSha2Sign.Pad != NL_AUTH_SHA2_SIGNATURE_Pad_Values.V1)
            {
                return false;
            }

            //The Flags data MAY be<86> disregarded.

            //The SequenceNumber MUST be decrypted.
            //If AES is negotiated, then the server MUST use the AES128 algorithm
            //and a key of SessionKey and an initialization vector constructed
            //by concatenating the checksum with itself (thus getting 16 bytes of data).
            using (BCryptAlgorithm aes = new BCryptAlgorithm("AES"))
            {
                aes.Mode = BCryptCipherMode.CFB;
                aes.Key = sessionKey;
                aes.IV = ArrayUtility.ConcatenateArrays(
                    nlAuthSha2Sign.Checksum,
                    nlAuthSha2Sign.Checksum);
                nlAuthSha2Sign.SequenceNumber = aes.Decrypt(nlAuthSha2Sign.SequenceNumber);
            }

            //A local copy of SequenceNumber MUST be computed using the following algorithm.
            byte[] copySequenceNumber = ComputeCopySequenceNumber(sequenceNumber, isClientSideOutbound);

            //The SequenceNumber MUST be compared to CopySeqNumber.
            if (!ArrayUtility.CompareArrays(
                    copySequenceNumber,
                    nlAuthSha2Sign.SequenceNumber))
            {
                return false;
            }

            //ServerSequenceNumber MUST be incremented.
            sequenceNumber += 1;

            if (requestConfidentiality)
            {
                //If the Confidentiality option is requested,
                //the Confounder and the data MUST be decrypted.
                byte[] aesEncryptionKey = ComputeEncryptionKeyWhenAesIsNegotiated(sessionKey);

                using (BCryptAlgorithm aes = new BCryptAlgorithm("AES"))
                {
                    aes.Mode = BCryptCipherMode.CFB;
                    aes.Key = aesEncryptionKey;
                    aes.IV = ArrayUtility.ConcatenateArrays(
                        nlAuthSha2Sign.SequenceNumber,
                        nlAuthSha2Sign.SequenceNumber);
                    nlAuthSha2Sign.Confounder = aes.Decrypt(nlAuthSha2Sign.Confounder);

                    cipherText = SspiUtility.ConcatenateReadWriteSecurityBuffers(
                        securityBuffers,
                        SecurityBufferType.Data);
                    plainText = aes.Decrypt(cipherText);
                }

                SspiUtility.UpdateSecurityBuffers(securityBuffers, SecurityBufferType.Data, plainText);
            }

            //Compute signature
            plainText = ConcatenateSecurityBuffersForChecksum(securityBuffers);
            byte[] checksum = ComputeSignatureWhenAesIsNegotiated(
                nlAuthSha2Sign,
                sessionKey,
                requestConfidentiality,
                plainText);

            //The first 8 bytes of the computed signature MUST be compared to the checksum.
            if (checksum != null
                && checksum.Length >= NL_AUTH_SIGNATURE_CHECKSUM_LENGTH
                && nlAuthSha2Sign.Checksum != null
                && nlAuthSha2Sign.Checksum.Length >= NL_AUTH_SIGNATURE_CHECKSUM_LENGTH)
            {
                for (int i = 0; i < NL_AUTH_SIGNATURE_CHECKSUM_LENGTH; i++)
                {
                    if (checksum[i] != nlAuthSha2Sign.Checksum[i])
                    {
                        return false;
                    }
                }
            }

            return true;
        }
        public override void Accept(byte[] clientToken)
        {
            SecurityBuffer[] inSecurityBuffers;
            //There must be two buffers.
            inSecurityBuffers = new SecurityBuffer[3];
            //The first buffer must be of type SECBUFFER_TOKEN and contain the security token
            //received from the client.
            inSecurityBuffers[0] = new SecurityBuffer(SecurityBufferType.Token, clientToken);
            //The second buffer should be of type SECBUFFER_EMPTY.
            inSecurityBuffers[1] = new SecurityBuffer(SecurityBufferType.Empty, new byte[0]);
            //The 3nd buffer should be of type Extra.
            inSecurityBuffers[2] = new SecurityBuffer(SecurityBufferType.Extra, new byte[0]);
            SecurityBufferDescWrapper inputBufferDescWrapper = new SecurityBufferDescWrapper(inSecurityBuffers);

            SecurityBuffer[] outSecurityBuffers;
            outSecurityBuffers = new SecurityBuffer[2];
            //1 token
            outSecurityBuffers[0] = new SecurityBuffer(SecurityBufferType.Token, new byte[NativeMethods.MAX_TOKEN_SIZE]);
            //2 alert
            outSecurityBuffers[1] = new SecurityBuffer(SecurityBufferType.Alert, new byte[NativeMethods.MAX_TOKEN_SIZE]);
            SecurityBufferDescWrapper outputBufferDescWrapper = new SecurityBufferDescWrapper(outSecurityBuffers);

            SecurityInteger timeStamp;
            uint contextAttribute;

            uint hResult = 0;

            if (this.contextHandle.LowPart == IntPtr.Zero || this.contextHandle.HighPart == IntPtr.Zero)
            {
                hResult = NativeMethods.AcceptSecurityContext(
                    ref this.credentialHandle,
                    IntPtr.Zero,
                    ref inputBufferDescWrapper.securityBufferDesc,
                    (uint)this.securityContextAttributes,
                    (uint)this.targetDataRepresentaion,
                    ref this.contextHandle,
                    out outputBufferDescWrapper.securityBufferDesc,
                    out contextAttribute,
                    out timeStamp);
            }
            else
            {
                hResult = NativeMethods.AcceptSecurityContext(
                    ref this.credentialHandle,
                    ref this.contextHandle,
                    ref inputBufferDescWrapper.securityBufferDesc,
                    (uint)this.securityContextAttributes,
                    (uint)this.targetDataRepresentaion,
                    ref this.contextHandle,
                    out outputBufferDescWrapper.securityBufferDesc,
                    out contextAttribute,
                    out timeStamp);
            }

            inputBufferDescWrapper.FreeSecurityBufferDesc();
            lastHResult = hResult;
            if (hResult == NativeMethods.SEC_E_OK)
            {
                this.needContinueProcessing = false;
                this.hasMoreFragments = false;
            }
            else if (hResult == NativeMethods.SEC_I_CONTINUE_NEEDED)
            {
                this.needContinueProcessing = true;
                this.hasMoreFragments = false;
            }
            else if (hResult == NativeMethods.SEC_I_MESSAGE_FRAGMENT)
            {
                this.needContinueProcessing = true;
                this.hasMoreFragments = true;
            }
            else
            {
                throw new SspiException("Accept failed.", hResult);
            }
            //Get token
            this.token = null;
            SspiSecurityBuffer[] buffers = outputBufferDescWrapper.securityBufferDesc.GetBuffers();
            for (int i = 0; i < buffers.Length; i++)
            {
                if (buffers[i].bufferType == (uint)SecurityBufferType.Token)
                {
                    if (buffers[i].bufferLength > 0)
                    {
                        this.token = new byte[buffers[i].bufferLength];
                        Marshal.Copy(buffers[i].pSecBuffer, this.token, 0, this.token.Length);
                    }
                    break;
                }
            }

            outputBufferDescWrapper.FreeSecurityBufferDesc();
        }
示例#22
0
        /// <summary>
        /// Accept client token.
        /// </summary>
        /// <param name="clientToken">Token of client</param>
        /// <exception cref="SspiException">If Accept fail, this exception will be thrown.</exception>
        public override void Accept(byte[] clientToken)
        {
            SecurityBuffer[] inSecurityBuffers;
            //There must be two buffers.
            inSecurityBuffers = new SecurityBuffer[3];
            //The first buffer must be of type SECBUFFER_TOKEN and contain the security token
            //received from the client.
            inSecurityBuffers[0] = new SecurityBuffer(SecurityBufferType.Token, clientToken);
            //The second buffer should be of type SECBUFFER_EMPTY.
            inSecurityBuffers[1] = new SecurityBuffer(SecurityBufferType.Empty, new byte[0]);
            //The 3nd buffer should be of type Extra.
            inSecurityBuffers[2] = new SecurityBuffer(SecurityBufferType.Extra, new byte[0]);
            SecurityBufferDescWrapper inputBufferDescWrapper = new SecurityBufferDescWrapper(inSecurityBuffers);

            SecurityBuffer[] outSecurityBuffers;
            outSecurityBuffers = new SecurityBuffer[2];
            //1 token
            outSecurityBuffers[0] = new SecurityBuffer(SecurityBufferType.Token, new byte[Consts.MAX_TOKEN_SIZE]);
            //2 alert
            outSecurityBuffers[1] = new SecurityBuffer(SecurityBufferType.Alert, new byte[Consts.MAX_TOKEN_SIZE]);
            SecurityBufferDescWrapper outputBufferDescWrapper = new SecurityBufferDescWrapper(outSecurityBuffers);

            SecurityInteger timeStamp;
            uint            contextAttribute;

            uint hResult = 0;

            if (this.contextHandle.LowPart == IntPtr.Zero || this.contextHandle.HighPart == IntPtr.Zero)
            {
                hResult = NativeMethods.AcceptSecurityContext(
                    ref this.credentialHandle,
                    IntPtr.Zero,
                    ref inputBufferDescWrapper.securityBufferDesc,
                    (uint)this.securityContextAttributes,
                    (uint)this.targetDataRepresentaion,
                    ref this.contextHandle,
                    out outputBufferDescWrapper.securityBufferDesc,
                    out contextAttribute,
                    out timeStamp);
            }
            else
            {
                hResult = NativeMethods.AcceptSecurityContext(
                    ref this.credentialHandle,
                    ref this.contextHandle,
                    ref inputBufferDescWrapper.securityBufferDesc,
                    (uint)this.securityContextAttributes,
                    (uint)this.targetDataRepresentaion,
                    ref this.contextHandle,
                    out outputBufferDescWrapper.securityBufferDesc,
                    out contextAttribute,
                    out timeStamp);
            }

            inputBufferDescWrapper.FreeSecurityBufferDesc();
            lastHResult = hResult;
            if (hResult == NativeMethods.SEC_E_OK)
            {
                this.needContinueProcessing = false;
                this.hasMoreFragments       = false;
            }
            else if (hResult == NativeMethods.SEC_I_CONTINUE_NEEDED)
            {
                this.needContinueProcessing = true;
                this.hasMoreFragments       = false;
            }
            else if (hResult == NativeMethods.SEC_I_MESSAGE_FRAGMENT)
            {
                this.needContinueProcessing = true;
                this.hasMoreFragments       = true;
            }
            else
            {
                throw new SspiException("Accept failed.", hResult);
            }
            //Get token
            this.token = null;
            SspiSecurityBuffer[] buffers = outputBufferDescWrapper.securityBufferDesc.GetBuffers();
            for (int i = 0; i < buffers.Length; i++)
            {
                if (buffers[i].bufferType == (uint)SecurityBufferType.Token)
                {
                    if (buffers[i].bufferLength > 0)
                    {
                        this.token = new byte[buffers[i].bufferLength];
                        Marshal.Copy(buffers[i].pSecBuffer, this.token, 0, this.token.Length);
                    }
                    break;
                }
            }

            outputBufferDescWrapper.FreeSecurityBufferDesc();
        }