/// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        public CommandAPDU CreateExternalAuthCommand()
        {
            MemoryStream memStream = new MemoryStream();

            memStream.Write(mInitUpdateResponse, 12, 8);
            memStream.Write(mHostChallenge, 0, mHostChallenge.Length);

            byte[] hostCryptogram = CryptoUtil.FullTripleDESMAC(mSessionKeys.RetrieveKey(Key.KEY_TYPE_ENC),
                                                                CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, CryptoUtil.DESPad(memStream.ToArray()));
            int P1 = mSecurityLevel;

            CommandAPDU externalAuth = new CommandAPDU(CLA_SECURE_GP, INS_EXT_AUTH, P1, 0x00, hostCryptogram);

            mSecureChannel = new SecureChannel(mSessionKeys, SecurityLevel.C_MAC, mSCPIdentifier,
                                               mSCPImplementationOption, CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK);
            externalAuth = mSecureChannel.wrap(externalAuth);
            return(externalAuth);
        }
        public CommandAPDU wrap(CommandAPDU command)
        {
            // Apply R-MAC
            if ((mSecurityLevel & net.SecurityLevel.R_MAC) != 0)
            {
                if(mRMACStream != null)
                    throw new Exception("There exists an unwrapped response while R-MAC security level set. Secure channel can only work correctly if " +
                                        "for each wrapped command the corresponding response be unwrapped immediately.");
                mRMACStream = new MemoryStream();

                //Clear 3 LSB of CLA
                mRMACStream.WriteByte((byte)(command.CLA & ~0x07));
                mRMACStream.WriteByte((byte) command.INS);
                mRMACStream.WriteByte((byte) command.P1);
                mRMACStream.WriteByte((byte) command.P2);
                if (command.LC > 0)
                {
                    mRMACStream.WriteByte((byte) command.LC);
                    mRMACStream.Write(command.Data, 0, command.Data.Length);
                }

            }

            if ((mSecurityLevel & (net.SecurityLevel.C_MAC | net.SecurityLevel.C_DECRYPTION)) == 0)
                return command;

            int secureCLA = command.CLA;
            byte[] wrappedData = null;
            int wrappedDataSize = command.LC;

            MemoryStream commandStream = new MemoryStream();

            int maxCommandSize = 255;
            if ((mSecurityLevel & net.SecurityLevel.C_MAC) != 0)
                maxCommandSize -= 8;
            if ((mSecurityLevel & net.SecurityLevel.C_DECRYPTION) != 0)
                maxCommandSize -= 8;
            if(command.LC > maxCommandSize)
                throw new Exception("APDU command too large. Max command length = 255 - 8(for C-MAC if present) - 8(for C-DECRYTPION padding if present).");

            if ((mSecurityLevel & net.SecurityLevel.C_MAC) != 0)
            {
                if (mFirstCommandInChain)
                    mFirstCommandInChain = false;
                else if(mICVEncryption)
                {
                    if (mSCPIdentifier == GlobalPlatform.SCP_01)
                    {
                        mICV = CryptoUtil.TripleDESECB(new Key(mSessionKeys.MacKey.BuildTripleDesKey()), mICV,
                            CryptoUtil.MODE_ENCRYPT);
                    }
                    else
                    {
                        mICV = CryptoUtil.DESECB(new Key(mSessionKeys.MacKey.BuildDesKey()), mICV,
                            CryptoUtil.MODE_ENCRYPT);
                    }
                } // If ICV Encryption

                if (mApplyToModifiedAPDU)
                {
                    secureCLA = command.CLA | 0x04;
                    wrappedDataSize += 8;
                }

                commandStream.WriteByte((byte) secureCLA);
                commandStream.WriteByte((byte) command.INS);
                commandStream.WriteByte((byte) command.P1);
                commandStream.WriteByte((byte) command.P2);
                commandStream.WriteByte((byte) wrappedDataSize);
                commandStream.Write(command.Data, 0, command.Data.Length);
                if (mSCPIdentifier == GlobalPlatform.SCP_01)
                {
                    mICV = CryptoUtil.FullTripleDESMAC(mSessionKeys.MacKey, mICV, CryptoUtil.DESPad(commandStream.ToArray()));
                }
                else
                {
                    mICV = CryptoUtil.SingleDESFullTripleDESMAC(mSessionKeys.MacKey, mICV, CryptoUtil.DESPad(commandStream.ToArray()));
                }

                if (!mApplyToModifiedAPDU)
                {
                    secureCLA = command.CLA | 0x04;
                    wrappedDataSize += 8;
                }
                wrappedData = command.Data;
                commandStream = new MemoryStream();
            } // If C-MAC

            if (((mSecurityLevel & net.SecurityLevel.C_DECRYPTION) != 0) && command.LC > 0)
            {
                if (mSCPIdentifier == GlobalPlatform.SCP_01)
                {
                    if ((command.LC + 1)%8 != 0)
                    {
                        commandStream.WriteByte((byte) command.LC);
                        commandStream.Write(command.Data, 0, command.Data.Length);
                        byte[] paddedData = CryptoUtil.DESPad(commandStream.ToArray());
                        commandStream = new MemoryStream();
                        commandStream.Write(paddedData, 0, paddedData.Length);
                    }
                    else
                    {
                        commandStream.WriteByte((byte)command.LC);
                        commandStream.Write(command.Data, 0, command.Data.Length);
                    }
                } // If SCP '01'
                else
                {
                    byte[] paddedData = CryptoUtil.DESPad(command.Data);
                    commandStream.Write(paddedData, 0, paddedData.Length);
                }
                wrappedDataSize += (int)(commandStream.Length - command.Data.Length);
                wrappedData = CryptoUtil.TripleDESCBC(new Key(mSessionKeys.EncKey.BuildTripleDesKey()),
                    CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, commandStream.ToArray(), CryptoUtil.MODE_ENCRYPT);
                commandStream = new MemoryStream();
            }  // If C-DECRYPTION
            commandStream.WriteByte((byte) secureCLA);
            commandStream.WriteByte((byte) command.INS);
            commandStream.WriteByte((byte) command.P1);
            commandStream.WriteByte((byte) command.P2);
            if (wrappedDataSize > 0)
            {
                commandStream.WriteByte((byte) wrappedDataSize);
                commandStream.Write(wrappedData, 0, wrappedData.Length);
            }

            if((mSecurityLevel & net.SecurityLevel.C_MAC) != 0)
                commandStream.Write(mICV, 0, mICV.Length);
            if(command.LE > 0)
                commandStream.WriteByte((byte) command.LE);
 
            return new CommandAPDU(commandStream.ToArray());
        }
        /// <summary>
        ///
        /// </summary>
        /// <param name="keys"></param>
        /// <param name="replaceExisting"></param>
        /// <param name="keyFormat"></param>
        /// <returns></returns>
        public CommandAPDU CreatePutKeyCommand(List <Key> keys, bool replaceExisting, bool addKCV, int keyFormat)
        {
            int p1;
            int p2;

            if (keyFormat == KEY_FORMAT_2)
            {
                throw new Exception("Format 2 is reserved for futrue use.");
            }
            if (keyFormat != KEY_FORMAT_1)
            {
                throw new Exception("Unknown format");
            }

            int prevId = -1;

            for (int i = 0; i < keys.Count; i++)
            {
                Key key = keys[i];
                if (i > 1)
                {
                    if (key.KeyId != prevId + 1)
                    {
                        throw new Exception("Key Identifiers must sequentially increment. See See Global Platform 2.1.1 Card Spec section 9.8.2.3.1");
                    }
                }
                prevId = key.KeyId;
            }

            if (replaceExisting)
            {
                p1 = keys[0].KeyVersion;
            }
            else
            {
                p1 = 0;
            }

            p2 = keys[0].KeyId;

            // Multiple keys
            if (keys.Count > 1)
            {
                p2 |= 0x80;
            }

            Key kek = null;

            if (mSCPIdentifier == SCP_01)
            {
                kek = new Key(mStaticKeys.KekKey.BuildTripleDesKey());
            }
            else if (mSCPIdentifier == SCP_02)
            {
                kek = new Key(mSessionKeys.KekKey.BuildTripleDesKey());
            }

            MemoryStream allKeyData = new MemoryStream();

            allKeyData.WriteByte((byte)keys[0].KeyVersion);
            for (int i = 0; i < keys.Count; i++)
            {
                Key    key          = keys[i];
                byte[] keyDataBytes = EncodeKeyData(key, kek, addKCV, keyFormat);
                allKeyData.Write(keyDataBytes, 0, keyDataBytes.Length);
            }
            CommandAPDU putKeyCommand = new CommandAPDU(CLA_GP, INS_PUT_KEY, p1, p2, allKeyData.ToArray(), 0x00);

            putKeyCommand = mSecureChannel.wrap(putKeyCommand);
            return(putKeyCommand);
        }
        /// <summary>
        /// Generates INITIALIZE UPDATE command with specified static key set.
        /// </summary>
        /// <param name="staticKeySet">Secure channel static key set</param>
        /// <param name="securityLevel">Security level. It must be a valid combination of
        /// security level bit pattern defined in <see cref="SecurityLevel"/>.</param>
        /// <param name="scpIdentifier">Secure Channel Identifier according to Global Platform 2.1.1 Card Spec section 8.6.
        /// Currently SCP '01' and SCP '02' is supported. Use <see cref="SCP_ANY"/> if you are not sure.</param>
        /// <param name="scpImplementationOption">Secure Channel Implementation Option according to
        /// Global Platform 2.1.1 Card Spec section D.1.1 for SCP '01' or section E.1.1 for SCP '02'. Use <see cref="IMPL_OPTION_ANY"/>
        /// along with <see cref="SCP_ANY"/> for Secure Channel Identifier, if you are not sure.</param>
        /// <returns>CommandAPDU for INITIALIZE UPDATE command for specified static key set</returns>
        public CommandAPDU CreateInitUpdateCommand(KeySet staticKeySet, int securityLevel, int scpIdentifier,
                                                   int scpImplementationOption)
        {
            // Validate Secure Channel Identifier
            if ((scpIdentifier != SCP_01) && (scpIdentifier != SCP_02) && (scpIdentifier != SCP_ANY))
            {
                throw new Exception(
                          "Invalid secure channel protocol identifier. Currently SCP 01 (0x01) and SCP 02 (0x02) are valid." +
                          " See Global Platform 2.1.1 Card Spec section 8.6.");
            }

            //Validate Secure Channel Implementation Option
            if (scpImplementationOption == IMPL_OPTION_ANY && scpIdentifier != SCP_ANY)
            {
                throw new Exception(
                          "Secure Channel Implementation Option IMPL_OPTION_ANY can only be used along with Secure Channel Identifier SCP_ANY.");
            }


            if (scpIdentifier == SCP_ANY)
            {
                if (scpImplementationOption != IMPL_OPTION_I_05 && scpImplementationOption != IMPL_OPTION_I_15 &&
                    scpImplementationOption != IMPL_OPTION_ANY)
                {
                    throw new Exception(
                              "Invalid implementation option. Only IMPL_OPTION_I_05, IMPL_OPTION_I_15 or IMPL_OPTION_ANY can be used along with SCP_ANY.");
                }
            }

            // Validate Secure Channel Implementation Option for SCP 01
            if (scpIdentifier == SCP_01)
            {
                if ((scpImplementationOption != IMPL_OPTION_I_05) && (scpImplementationOption != IMPL_OPTION_I_15))
                {
                    throw new Exception(
                              "Invalid implementation option for SCP 01. See Global Platform 2.1.1 Card Spec section D.1.1.");
                }
            }

            // Validate Secure Channel Implementation Option for SCP 02
            if (scpIdentifier == SCP_02)
            {
                if ((scpImplementationOption != IMPL_OPTION_I_04) && (scpImplementationOption != IMPL_OPTION_I_05) &&
                    (scpImplementationOption != IMPL_OPTION_I_0A) && (scpImplementationOption != IMPL_OPTION_I_0B) &&
                    (scpImplementationOption != IMPL_OPTION_I_14) && (scpImplementationOption != IMPL_OPTION_I_15) &&
                    (scpImplementationOption != IMPL_OPTION_I_1A) && (scpImplementationOption != IMPL_OPTION_I_1B))
                {
                    throw new Exception(
                              "Invalid implementation option for SCP 02. See Global Platform 2.1.1 Card Spec section E.1.1.");
                }

                if ((scpImplementationOption == IMPL_OPTION_I_0A) || (scpImplementationOption == IMPL_OPTION_I_0B) ||
                    (scpImplementationOption == IMPL_OPTION_I_1A) || (scpImplementationOption == IMPL_OPTION_I_1B))
                {
                    throw new Exception("Implicit secure channel can't be initialized explicitly.");
                }
            }


            mSCPIdentifier           = scpIdentifier;
            mSCPImplementationOption = scpImplementationOption;
            mStaticKeys    = staticKeySet;
            mSecurityLevel = securityLevel;


            // C-DECRYPTION allways come with C-MAC
            if ((securityLevel & SecurityLevel.C_DECRYPTION) != 0)
            {
                securityLevel |= SecurityLevel.C_MAC;
            }

            // Validate security level
            if (securityLevel != (SecurityLevel.NO_SECURITY_LEVEL) &&
                securityLevel != (SecurityLevel.C_DECRYPTION | SecurityLevel.C_MAC | SecurityLevel.R_MAC) &&
                securityLevel != (SecurityLevel.C_MAC | SecurityLevel.R_MAC) &&
                securityLevel != (SecurityLevel.R_MAC) &&
                securityLevel != (SecurityLevel.C_DECRYPTION | SecurityLevel.C_MAC) &&
                securityLevel != (SecurityLevel.C_MAC))
            {
                throw new Exception(
                          "Invalid security level. See Global Platform 2.1.1 Card Spec section E.5.2.3 or section D.4.2.3.");
            }

            // Host challenge
            mHostChallenge = new byte[8];
            RandomNumberGenerator rng = RandomNumberGenerator.Create();

            rng.GetBytes(mHostChallenge);

            // Build INITIALIZE UPDATE command
            CommandAPDU initUpdate = new CommandAPDU(CLA_GP, INS_INIT_UPDATE, staticKeySet.KeyVersion,
                                                     staticKeySet.KeyId, mHostChallenge, 0x00);

            return(initUpdate);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="keys"></param>
        /// <param name="replaceExisting"></param>
        /// <param name="keyFormat"></param>
        /// <returns></returns>
        public CommandAPDU CreatePutKeyCommand(List<Key> keys, bool replaceExisting, bool addKCV, int keyFormat)
        {
            int p1;
            int p2;
            if (keyFormat == KEY_FORMAT_2)
                throw new Exception("Format 2 is reserved for futrue use.");
            if (keyFormat != KEY_FORMAT_1)
                throw new Exception("Unknown format");

            int prevId = -1;
            for (int i = 0; i < keys.Count; i++)
            {
                Key key = keys[i];
                if (i > 1)
                    if (key.KeyId != prevId + 1)
                        throw new Exception("Key Identifiers must sequentially increment. See See Global Platform 2.1.1 Card Spec section 9.8.2.3.1");
                prevId = key.KeyId;
            }

            if (replaceExisting)
                p1 = keys[0].KeyVersion;
            else
            {
                p1 = 0;
            }

            p2 = keys[0].KeyId;

            // Multiple keys
            if (keys.Count > 1)
                p2 |= 0x80;

            Key kek = null;
            if (mSCPIdentifier == SCP_01)
                kek = new Key(mStaticKeys.KekKey.BuildTripleDesKey());
            else if(mSCPIdentifier == SCP_02)
            {
                kek = new Key(mSessionKeys.KekKey.BuildTripleDesKey());
            }

            MemoryStream allKeyData = new MemoryStream();
            allKeyData.WriteByte((byte)keys[0].KeyVersion);
            for (int i = 0; i < keys.Count; i++)
            {
                Key key = keys[i];
                byte[] keyDataBytes = EncodeKeyData(key, kek, addKCV, keyFormat);
                allKeyData.Write(keyDataBytes, 0, keyDataBytes.Length);
                
            }
            CommandAPDU putKeyCommand = new CommandAPDU(CLA_GP, INS_PUT_KEY, p1, p2, allKeyData.ToArray(), 0x00);
            putKeyCommand = mSecureChannel.wrap(putKeyCommand);
            return putKeyCommand;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public CommandAPDU CreateExternalAuthCommand()
        {
            MemoryStream memStream = new MemoryStream();
            memStream.Write(mInitUpdateResponse, 12, 8);
            memStream.Write(mHostChallenge, 0, mHostChallenge.Length);

            byte[] hostCryptogram = CryptoUtil.FullTripleDESMAC(mSessionKeys.RetrieveKey(Key.KEY_TYPE_ENC),
                CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, CryptoUtil.DESPad(memStream.ToArray()));
            int P1 = mSecurityLevel;

            CommandAPDU externalAuth = new CommandAPDU(CLA_SECURE_GP, INS_EXT_AUTH, P1, 0x00, hostCryptogram);
            mSecureChannel = new SecureChannel(mSessionKeys, SecurityLevel.C_MAC, mSCPIdentifier,
                mSCPImplementationOption, CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK);
            externalAuth = mSecureChannel.wrap(externalAuth);
            return externalAuth;
        }
        /// <summary>
        /// Generates INITIALIZE UPDATE command with specified static key set.
        /// </summary>
        /// <param name="staticKeySet">Secure channel static key set</param>
        /// <param name="securityLevel">Security level. It must be a valid combination of 
        /// security level bit pattern defined in <see cref="SecurityLevel"/>.</param>
        /// <param name="scpIdentifier">Secure Channel Identifier according to Global Platform 2.1.1 Card Spec section 8.6.
        /// Currently SCP '01' and SCP '02' is supported. Use <see cref="SCP_ANY"/> if you are not sure.</param>
        /// <param name="scpImplementationOption">Secure Channel Implementation Option according to
        /// Global Platform 2.1.1 Card Spec section D.1.1 for SCP '01' or section E.1.1 for SCP '02'. Use <see cref="IMPL_OPTION_ANY"/> 
        /// along with <see cref="SCP_ANY"/> for Secure Channel Identifier, if you are not sure.</param>
        /// <returns>CommandAPDU for INITIALIZE UPDATE command for specified static key set</returns>
        public CommandAPDU CreateInitUpdateCommand(KeySet staticKeySet, int securityLevel, int scpIdentifier,
            int scpImplementationOption)
        {
            // Validate Secure Channel Identifier
            if ((scpIdentifier != SCP_01) && (scpIdentifier != SCP_02) && (scpIdentifier != SCP_ANY))
                throw new Exception(
                    "Invalid secure channel protocol identifier. Currently SCP 01 (0x01) and SCP 02 (0x02) are valid." +
                    " See Global Platform 2.1.1 Card Spec section 8.6.");

            //Validate Secure Channel Implementation Option
            if (scpImplementationOption == IMPL_OPTION_ANY && scpIdentifier != SCP_ANY)
                throw new Exception(
                    "Secure Channel Implementation Option IMPL_OPTION_ANY can only be used along with Secure Channel Identifier SCP_ANY.");


            if (scpIdentifier == SCP_ANY)
            {
                if (scpImplementationOption != IMPL_OPTION_I_05 && scpImplementationOption != IMPL_OPTION_I_15 &&
                    scpImplementationOption != IMPL_OPTION_ANY)
                    throw new Exception(
                        "Invalid implementation option. Only IMPL_OPTION_I_05, IMPL_OPTION_I_15 or IMPL_OPTION_ANY can be used along with SCP_ANY.");
            }

            // Validate Secure Channel Implementation Option for SCP 01
            if (scpIdentifier == SCP_01)
                if ((scpImplementationOption != IMPL_OPTION_I_05) && (scpImplementationOption != IMPL_OPTION_I_15))
                    throw new Exception(
                        "Invalid implementation option for SCP 01. See Global Platform 2.1.1 Card Spec section D.1.1.");

            // Validate Secure Channel Implementation Option for SCP 02
            if (scpIdentifier == SCP_02)
            {
                if ((scpImplementationOption != IMPL_OPTION_I_04) && (scpImplementationOption != IMPL_OPTION_I_05) &&
                    (scpImplementationOption != IMPL_OPTION_I_0A) && (scpImplementationOption != IMPL_OPTION_I_0B) &&
                    (scpImplementationOption != IMPL_OPTION_I_14) && (scpImplementationOption != IMPL_OPTION_I_15) &&
                    (scpImplementationOption != IMPL_OPTION_I_1A) && (scpImplementationOption != IMPL_OPTION_I_1B))
                    throw new Exception(
                        "Invalid implementation option for SCP 02. See Global Platform 2.1.1 Card Spec section E.1.1.");

                if ((scpImplementationOption == IMPL_OPTION_I_0A) || (scpImplementationOption == IMPL_OPTION_I_0B) ||
                    (scpImplementationOption == IMPL_OPTION_I_1A) || (scpImplementationOption == IMPL_OPTION_I_1B))
                    throw new Exception("Implicit secure channel can't be initialized explicitly.");
            }


            mSCPIdentifier = scpIdentifier;
            mSCPImplementationOption = scpImplementationOption;
            mStaticKeys = staticKeySet;
            mSecurityLevel = securityLevel;


            // C-DECRYPTION allways come with C-MAC
            if ((securityLevel & SecurityLevel.C_DECRYPTION) != 0)
                securityLevel |= SecurityLevel.C_MAC;

            // Validate security level
            if (securityLevel != (SecurityLevel.NO_SECURITY_LEVEL) &&
                securityLevel != (SecurityLevel.C_DECRYPTION | SecurityLevel.C_MAC | SecurityLevel.R_MAC) &&
                securityLevel != (SecurityLevel.C_MAC | SecurityLevel.R_MAC) &&
                securityLevel != (SecurityLevel.R_MAC) &&
                securityLevel != (SecurityLevel.C_DECRYPTION | SecurityLevel.C_MAC) &&
                securityLevel != (SecurityLevel.C_MAC))

                throw new Exception(
                    "Invalid security level. See Global Platform 2.1.1 Card Spec section E.5.2.3 or section D.4.2.3.");

            // Host challenge
            mHostChallenge = new byte[8];
            RandomNumberGenerator rng = RandomNumberGenerator.Create();
            rng.GetBytes(mHostChallenge);

            // Build INITIALIZE UPDATE command
            CommandAPDU initUpdate = new CommandAPDU(CLA_GP, INS_INIT_UPDATE, staticKeySet.KeyVersion,
                staticKeySet.KeyId, mHostChallenge, 0x00);
            return initUpdate;
        }
        public CommandAPDU wrap(CommandAPDU command)
        {
            // Apply R-MAC
            if ((mSecurityLevel & net.SecurityLevel.R_MAC) != 0)
            {
                if (mRMACStream != null)
                {
                    throw new Exception("There exists an unwrapped response while R-MAC security level set. Secure channel can only work correctly if " +
                                        "for each wrapped command the corresponding response be unwrapped immediately.");
                }
                mRMACStream = new MemoryStream();

                //Clear 3 LSB of CLA
                mRMACStream.WriteByte((byte)(command.CLA & ~0x07));
                mRMACStream.WriteByte((byte)command.INS);
                mRMACStream.WriteByte((byte)command.P1);
                mRMACStream.WriteByte((byte)command.P2);
                if (command.LC > 0)
                {
                    mRMACStream.WriteByte((byte)command.LC);
                    mRMACStream.Write(command.Data, 0, command.Data.Length);
                }
            }

            if ((mSecurityLevel & (net.SecurityLevel.C_MAC | net.SecurityLevel.C_DECRYPTION)) == 0)
            {
                return(command);
            }

            int secureCLA = command.CLA;

            byte[] wrappedData     = null;
            int    wrappedDataSize = command.LC;

            MemoryStream commandStream = new MemoryStream();

            int maxCommandSize = 255;

            if ((mSecurityLevel & net.SecurityLevel.C_MAC) != 0)
            {
                maxCommandSize -= 8;
            }
            if ((mSecurityLevel & net.SecurityLevel.C_DECRYPTION) != 0)
            {
                maxCommandSize -= 8;
            }
            if (command.LC > maxCommandSize)
            {
                throw new Exception("APDU command too large. Max command length = 255 - 8(for C-MAC if present) - 8(for C-DECRYTPION padding if present).");
            }

            if ((mSecurityLevel & net.SecurityLevel.C_MAC) != 0)
            {
                if (mFirstCommandInChain)
                {
                    mFirstCommandInChain = false;
                }
                else if (mICVEncryption)
                {
                    if (mSCPIdentifier == GlobalPlatform.SCP_01)
                    {
                        mICV = CryptoUtil.TripleDESECB(new Key(mSessionKeys.MacKey.BuildTripleDesKey()), mICV,
                                                       CryptoUtil.MODE_ENCRYPT);
                    }
                    else
                    {
                        mICV = CryptoUtil.DESECB(new Key(mSessionKeys.MacKey.BuildDesKey()), mICV,
                                                 CryptoUtil.MODE_ENCRYPT);
                    }
                } // If ICV Encryption

                if (mApplyToModifiedAPDU)
                {
                    secureCLA        = command.CLA | 0x04;
                    wrappedDataSize += 8;
                }

                commandStream.WriteByte((byte)secureCLA);
                commandStream.WriteByte((byte)command.INS);
                commandStream.WriteByte((byte)command.P1);
                commandStream.WriteByte((byte)command.P2);
                commandStream.WriteByte((byte)wrappedDataSize);
                commandStream.Write(command.Data, 0, command.Data.Length);
                if (mSCPIdentifier == GlobalPlatform.SCP_01)
                {
                    mICV = CryptoUtil.FullTripleDESMAC(mSessionKeys.MacKey, mICV, CryptoUtil.DESPad(commandStream.ToArray()));
                }
                else
                {
                    mICV = CryptoUtil.SingleDESFullTripleDESMAC(mSessionKeys.MacKey, mICV, CryptoUtil.DESPad(commandStream.ToArray()));
                }

                if (!mApplyToModifiedAPDU)
                {
                    secureCLA        = command.CLA | 0x04;
                    wrappedDataSize += 8;
                }
                wrappedData   = command.Data;
                commandStream = new MemoryStream();
            } // If C-MAC

            if (((mSecurityLevel & net.SecurityLevel.C_DECRYPTION) != 0) && command.LC > 0)
            {
                if (mSCPIdentifier == GlobalPlatform.SCP_01)
                {
                    if ((command.LC + 1) % 8 != 0)
                    {
                        commandStream.WriteByte((byte)command.LC);
                        commandStream.Write(command.Data, 0, command.Data.Length);
                        byte[] paddedData = CryptoUtil.DESPad(commandStream.ToArray());
                        commandStream = new MemoryStream();
                        commandStream.Write(paddedData, 0, paddedData.Length);
                    }
                    else
                    {
                        commandStream.WriteByte((byte)command.LC);
                        commandStream.Write(command.Data, 0, command.Data.Length);
                    }
                } // If SCP '01'
                else
                {
                    byte[] paddedData = CryptoUtil.DESPad(command.Data);
                    commandStream.Write(paddedData, 0, paddedData.Length);
                }
                wrappedDataSize += (int)(commandStream.Length - command.Data.Length);
                wrappedData      = CryptoUtil.TripleDESCBC(new Key(mSessionKeys.EncKey.BuildTripleDesKey()),
                                                           CryptoUtil.BINARY_ZEROS_8_BYTE_BLOCK, commandStream.ToArray(), CryptoUtil.MODE_ENCRYPT);
                commandStream = new MemoryStream();
            }  // If C-DECRYPTION
            commandStream.WriteByte((byte)secureCLA);
            commandStream.WriteByte((byte)command.INS);
            commandStream.WriteByte((byte)command.P1);
            commandStream.WriteByte((byte)command.P2);
            if (wrappedDataSize > 0)
            {
                commandStream.WriteByte((byte)wrappedDataSize);
                commandStream.Write(wrappedData, 0, wrappedData.Length);
            }

            if ((mSecurityLevel & net.SecurityLevel.C_MAC) != 0)
            {
                commandStream.Write(mICV, 0, mICV.Length);
            }
            if (command.LE > 0)
            {
                commandStream.WriteByte((byte)command.LE);
            }

            return(new CommandAPDU(commandStream.ToArray()));
        }