public static byte[] Decrypt(byte[] bytes, Dictionary<ulong, Smb2CryptoInfo> cryptoInfoTable, Smb2Role role)
        {
            Transform_Header transformHeader = Smb2Utility.UnmarshalStructure<Transform_Header>(bytes);

            if (transformHeader.SessionId == 0 || !cryptoInfoTable.ContainsKey(transformHeader.SessionId))
                throw new InvalidOperationException("Invalid SessionId in TRANSFORM_HEADER.");

            Smb2CryptoInfo cryptoInfo = cryptoInfoTable[transformHeader.SessionId];

            using (var bcrypt = new BCryptAlgorithm("AES"))
            {
                int nonceLength = 0;
                BCryptCipherMode mode = BCryptCipherMode.NotAvailable;
                GetCryptoParams(cryptoInfo, CryptoOperationType.Decrypt, out mode, out nonceLength);
                bcrypt.Mode = mode;
                bcrypt.Key = role == Smb2Role.Server ? cryptoInfo.ServerInKey : cryptoInfo.ServerOutKey;
                return bcrypt.Decrypt(bytes.Skip(52).ToArray(), transformHeader.Nonce.ToByteArray().Take(nonceLength).ToArray(), bytes.Skip(20).Take(32).ToArray(), transformHeader.Signature);
            }
        }
        /// <summary>
        /// Decrypt a byte array. 
        /// Many data in NRPC struct is required to be encrypted.
        /// </summary>
        /// <param name="isAesNegotiated">
        /// Is AES negotiated during secure channel setup. 
        /// If AES is not negotiated, use RC4 to decrypt.
        /// </param>
        /// <param name="sessionKey">
        /// Session key.
        /// </param>
        /// <param name="buffer">
        /// Buffer to decrypt.
        /// </param>
        /// <returns>
        /// Decrypted buffer.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// Thrown when sessionKey or buffer is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// Thrown when session key length is incorrect.
        /// </exception>
        public static byte[] DecryptBuffer(
            bool isAesNegotiated,
            byte[] sessionKey,
            byte[] buffer)
        {
            if (sessionKey == null)
            {
                throw new ArgumentNullException("sessionKey");
            }
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            if (sessionKey.Length != NETLOGON_SESSION_KEY_LENGTH)
            {
                throw new ArgumentException("Session key length is incorrect.", "sessionKey");
            }

            if (isAesNegotiated)
            {
                using (BCryptAlgorithm aes = new BCryptAlgorithm("AES"))
                {
                    aes.Mode = BCryptCipherMode.CFB;
                    aes.Key = sessionKey;
                    aes.IV = new byte[aes.BlockSize];
                    return aes.Decrypt(buffer);
                }
            }
            else
            {
                using (RC4 rc4 = RC4.Create())
                {
                    rc4.Key = sessionKey;
                    return rc4.CreateDecryptor().TransformFinalBlock(buffer, 0, buffer.Length);
                }
            }
        }
        /// <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;
        }