using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace NewPosInstaller
{
    public class UserAES
    {
        /// <summary>
        /// 암호화 키 고정값("0001000100010001")
        /// </summary>
        private static string STATIC_KEY = "0001000100010001";

        #region 가변키 암호화/복호화
        /// <summary>
        /// 가변키 암호화
        /// </summary>
        /// <param name="pPosNo"></param>
        /// <param name="pSysYmdhms"></param>
        /// <param name="pPlainText"></param>
        /// <returns></returns>
        public static string DynamicKeyEncrypt(string pPosNo, string pSysYmdhms, string pPlainText)
        {
            string sEncryptText = string.Empty;

            try
            {
                if (pPlainText == "") return "";

                string sKey = string.Empty;
                string sPosNo = pPosNo;
                string sSysYmdhms = pSysYmdhms;

                sKey = sPosNo + sSysYmdhms;

                if (sKey.Length != 16)
                {
                    sEncryptText = pPlainText;
                    ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                         "UserAES.DynamicKeyEncrypt()", "가변키 오류=>" + sKey);
                }

                byte[] byteKey = Encoding.UTF8.GetBytes(sKey);
                byte[] bytePlainText = Encoding.UTF8.GetBytes(pPlainText);

                byte[] tmpKey = new byte[byteKey.Length];
                Array.Copy(byteKey, tmpKey, byteKey.Length);

                for (int i = 0; i < bytePlainText.Length; i++)
                {
                    if (bytePlainText[i] == '\x00')
                        bytePlainText[i] = (byte)'\x20';
                }

                byte[] tmp = new byte[80 * 1024];
                Array.Copy(bytePlainText, tmp, bytePlainText.Length);

                byte[] byteEncrypt = new byte[80 * 1024];

                int iRet = EncryptByAESAndBase64(tmpKey, tmpKey, tmp, ref byteEncrypt);

                if (iRet > 0)
                {
                    sEncryptText = Encoding.UTF8.GetString(byteEncrypt, 0, iRet);
                }
                else
                {
                    sEncryptText = pPlainText;
                    //ComLog.WriteAMSLog("APL", "ERR", "0000", "가변키 암호화 실패");
                    ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                    "UserAES.DynamicKeyEncrypt()", "가변키 암호화 실패=>" + pPlainText);
                }
            }
            catch (Exception ex)
            {
                ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                    "UserAES.DynamicKeyEncrypt()", ex.Message);
            }
            return sEncryptText;
        }

        /// <summary>
        /// 가변키 암호화
        /// </summary>
        /// <param name="pPosNo">POS번호</param>
        /// <param name="pTranNo">Tran번호</param>
        /// <param name="pPlainText">평문</param>
        /// <returns>암호화 실패하면 평문을 리턴</returns>
        public static byte[] DynamicKeyEncrypt(string pPosNo, string pSysYmdhms, byte[] pPlainText)
        {
            byte[] byteEncryptText = null;
            //int encLen = 0;

            try
            {
                string sKey = string.Empty;
                string sPosNo = pPosNo;
                string sSysYmdhms = pSysYmdhms;

                sKey = pPosNo + pSysYmdhms;

                if (sKey.Length != 16)
                {
                    byteEncryptText = pPlainText;
                    //ComLog.WriteAMSLog("APL", "ERR", "0000", "가변키 오류");
                    ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                        "UserAES.DynamicKeyEncrypt()", "가변키 오류=>" + sKey);
                }

                byte[] bytePlainText = pPlainText;

                byte[] byteKey = Encoding.UTF8.GetBytes(sKey);

                byte[] tmpKey = new byte[byteKey.Length];
                Array.Copy(byteKey, tmpKey, byteKey.Length);

                for (int i = 0; i < bytePlainText.Length; i++)
                {
                    if (bytePlainText[i] == '\x00')
                        bytePlainText[i] = (byte)'\x20';
                }

                byte[] tmp = new byte[80 * 1024];
                Array.Copy(bytePlainText, tmp, bytePlainText.Length);

                byte[] byteEncrypt = new byte[80 * 1024];

                int iRet = EncryptByAESAndBase64(tmpKey, tmpKey, tmp, ref byteEncrypt);

                if (iRet > 0)
                {
                    byteEncryptText = new byte[iRet];
                    Array.Copy(byteEncrypt, byteEncryptText, iRet);
                }
                else
                {
                    byteEncryptText = pPlainText;
                    //ComLog.WriteAMSLog("APL", "ERR", "0000", "가변키 암호화 실패");
                    ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                    "UserAES.DynamicKeyEncrypt()", "가변키 암호화 실패=>" + Encoding.Default.GetString(pPlainText, 0, pPlainText.Length));
                }
            }
            catch (Exception ex)
            {
                ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                    "UserAES.DynamicKeyEncrypt()", ex.Message);
            }
            return byteEncryptText;
        }


        /// <summary>
        /// 가변키 복호화
        /// </summary>
        /// <param name="pPosNo"></param>
        /// <param name="pSysYmdhms"></param>
        /// <param name="pEncryptText"></param>
        /// <returns></returns>
        public static string DynamicKeyDecrypt(string pPosNo, string pSysYmdhms, string pEncryptText)
        {
            string sDecryptData = string.Empty;

            try
            {
                if (pEncryptText == "") return "";

                string sKey = string.Empty;
                string sPosNo = pPosNo;
                string sSysYmdhms = pSysYmdhms;

                sKey = sPosNo + sSysYmdhms;

                if (sKey.Length != 16)
                {
                    sDecryptData = pEncryptText;
                    ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                        "UserAES.DynamicKeyDecrypt()", "가변키 오류=>" + sKey);
                }

                byte[] byteKey = Encoding.UTF8.GetBytes(sKey);

                byte[] byteEncryptText = Encoding.UTF8.GetBytes(pEncryptText);

                byte[] tmpKey = new byte[byteKey.Length];
                Array.Copy(byteKey, tmpKey, byteKey.Length);

                for (int i = 0; i < byteEncryptText.Length; i++)
                {
                    if (byteEncryptText[i] == '\x00')
                        byteEncryptText[i] = (byte)'\x20';
                }

                byte[] tmp = new byte[80 * 1024];
                Array.Copy(byteEncryptText, tmp, byteEncryptText.Length);

                byte[] byteTmpPlainText = new byte[80 * 1024];

                int iRet = DecryptByAESAndBase64(tmpKey, tmpKey, tmp, ref  byteTmpPlainText);

                if (iRet > 0)
                {
                    byte[] bytePlainText = new byte[iRet];

                    Array.Copy(byteTmpPlainText, bytePlainText, iRet);
                    sDecryptData = Encoding.UTF8.GetString(bytePlainText, 0, bytePlainText.Length);
                }
                else
                {
                    sDecryptData = pEncryptText;
                    ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                    "UserAES.DynamicKeyDecrypt()", "가변키 복호화 실패=>" + pEncryptText);
                }
            }
            catch (Exception ex)
            {
                ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                    "UserAES.DynamicKeyDecrypt()", ex.Message);
            }
            return sDecryptData;
        }

        /// <summary>
        /// 가변키 복호화
        /// </summary>
        /// <param name="pPosNo">POS번호</param>
        /// <param name="pTranNo">Tran번호</param>
        /// <param name="pEncryptText">암호문</param>
        /// <returns>복호화 실패하면 암호문 리턴</returns>
        public static byte[] DynamicKeyDecrypt(string pPosNo, string pSysYmdhms, byte[] pEncryptText)
        {
            byte[] byteDecryptData = null;

            try
            {
                string sKey = string.Empty;
                string sPosNo = pPosNo;
                string sSysYmdhms = pSysYmdhms;

                sKey = sPosNo + sSysYmdhms;

                if (sKey.Length != 16)
                {
                    byteDecryptData = pEncryptText;
                    //ComLog.WriteAMSLog("APL", "ERR", "0000", "가변키 오류");
                    ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                        "UserAES.DynamicKeyDecrypt()", "가변키 오류=>" + sKey);
                }

                byte[] byteKey = Encoding.UTF8.GetBytes(sKey);

                byte[] byteTmpEncrypt = pEncryptText;

                byte[] tmpKey = new byte[byteKey.Length];
                Array.Copy(byteKey, tmpKey, byteKey.Length);

                for (int i = 0; i < byteTmpEncrypt.Length; i++)
                {
                    if (byteTmpEncrypt[i] == '\x00')
                        byteTmpEncrypt[i] = (byte)'\x20';
                }

                byte[] tmp = new byte[80 * 1024];
                Array.Copy(byteTmpEncrypt, tmp, byteTmpEncrypt.Length);

                byte[] byteTmpPlainText = new byte[80 * 1024];

                int iRet = DecryptByAESAndBase64(tmpKey, tmpKey, tmp, ref byteTmpPlainText);

                if (iRet > 0)
                {
                    byte[] bytePlainText = new byte[iRet];
                    Array.Copy(byteTmpPlainText, bytePlainText, iRet);
                    byteDecryptData = bytePlainText;
                }
                else
                {
                    byteDecryptData = pEncryptText;
                    //ComLog.WriteAMSLog("APL", "ERR", "0000", "가변키 복호화 오류");
                    ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                    "UserAES.DynamicKeyDecrypt()", "가변키 복호화 오류=>" + Encoding.Default.GetString(pEncryptText, 0, pEncryptText.Length));
                }
            }

            catch (Exception ex)
            {
                ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                    "UserAES.DynamicKeyDecrypt()", ex.Message);
            }
            return byteDecryptData;
        }


        #endregion

        #region 고정키 암호화/복호화,  고정키 : 0001000100010001
        /// <summary>
        /// 고정키 암호화
        /// </summary>
        /// <param name="pPlainText">평문</param>
        /// <returns>암호화 실패하면 평문 리턴</returns>
        public static string StaticKeyEncrypt(string pPlainText)
        {
            string sEncryptText = string.Empty;

            try
            {
                if (pPlainText == "") return "";

                byte[] bytePlainText = Encoding.Default.GetBytes(pPlainText);

                byte[] byteKey = Encoding.Default.GetBytes(UserAES.STATIC_KEY);

                byte[] tmpKey = new byte[byteKey.Length];
                Array.Copy(byteKey, tmpKey, byteKey.Length);

                byte[] tmp = new byte[80 * 1024];
                Array.Copy(bytePlainText, tmp, bytePlainText.Length);

                byte[] byteEncrypt = new byte[80 * 1024];

                int iRet = EncryptByAESAndBase64(tmpKey, tmpKey, tmp, ref byteEncrypt);
                //int iRet = EncryptByAESAndBase64Ex(tmpKey, tmpKey, tmp, ref byteEncrypt);

                if (iRet > 0)
                {
                    sEncryptText = Encoding.Default.GetString(byteEncrypt, 0, iRet);
                }
                else
                {
                    sEncryptText = pPlainText;
                    //ComLog.WriteAMSLog("APL", "ERR", "0000", "고정키 암호화 실패");
                    ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                        "UserAES.StaticKeyEncrypt()", "고정키 암호화 오류=>" + pPlainText);
                }
            }
            catch (Exception ex)
            {
                sEncryptText = pPlainText;
                ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                    "UserAES.StaticKeyEncrypt()", ex.Message);
            }
            return sEncryptText;
        }

        /// <summary>
        /// 고정키 복호화
        /// </summary>
        /// <param name="pEncryptText">암호문</param>
        /// <returns>복호화 실패하면 평문 리턴</returns>
        public static string StaticKeyDecrypt(string pEncryptText)
        {
            string sPlainText = string.Empty;

            try
            {
                if (pEncryptText == "") return "";

                byte[] byteEncryptText = Encoding.Default.GetBytes(pEncryptText);

                byte[] byteKey = Encoding.Default.GetBytes(UserAES.STATIC_KEY);

                //#if WindowsCE
                //                byte[] tmpKey = new byte[byteKey.Length + 1];
                //                Array.Copy(byteKey, tmpKey, byteKey.Length);

                //                byte[] tmp = new byte[64 * 1024];
                //                Array.Copy(byteEncryptText, tmp, byteEncryptText.Length);

                //                byte[] byteTmpPlainText = new byte[64 * 1024];

                //                int iRet = AES_Base64_Decrypt(tmpKey, tmpKey, tmp, byteTmpPlainText);
                //#else
                byte[] tmpKey = new byte[byteKey.Length];
                Array.Copy(byteKey, tmpKey, byteKey.Length);

                byte[] tmp = new byte[80 * 1024];
                Array.Copy(byteEncryptText, tmp, byteEncryptText.Length);

                byte[] byteTmpPlainText = new byte[80 * 1024];

                int iRet = DecryptByAESAndBase64(tmpKey, tmpKey, tmp, ref byteTmpPlainText);
                //int iRet = DecryptByAESAndBase64Ex(tmpKey, tmpKey, tmp, ref byteTmpPlainText);

                //int iRet = AES_Base64_Decrypt(tmpKey, tmpKey, tmp, byteTmpPlainText);
                //m_Mutex.WaitOne();
                //byte[] byteTmpPlainText = new byte[byteEncryptText.Length];
                //int iRet = AES_Base64_Decrypt(byteKey, byteKey, byteEncryptText, byteTmpPlainText);
                //m_Mutex.ReleaseMutex();
                //#endif
                if (iRet > 0)
                {
                    byte[] bytePlainText = new byte[iRet];
                    Array.Copy(byteTmpPlainText, bytePlainText, iRet);
                    sPlainText = Encoding.Default.GetString(bytePlainText, 0, iRet);
                }
                else
                {
                    sPlainText = pEncryptText;
                    //ComLog.WriteAMSLog("APL", "ERR", "0000", "고정키 복호화 실패");
                    ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                        "UserAES.StaticKeyDecrypt()", "고정키 복호화 오류=>" + pEncryptText);
                }
            }
            catch (Exception ex)
            {
                sPlainText = pEncryptText;
                ComLog.WriteLogFile(ComLib.LOG_ERROR, System.Reflection.Assembly.GetExecutingAssembly().ManifestModule.Name,
                                    "UserAES.StaticKeyDecrypt()", ex.Message);
            }
            return sPlainText;
        }
        #endregion

        #region AES-BASE64 암호화/복호화(속도개선)
        /// <summary>
        /// AES-BASE64 암호화(속도개선)
        /// </summary>
        /// <param name="key">암호화KEY</param>
        /// <param name="iv">Initialize Vector</param>
        /// <param name="plainData">평문</param>
        /// <param name="encodedData">암호문</param>
        private static int EncryptByAESAndBase64Ex(byte[] key, byte[] iv, byte[] plainData, ref byte[] encodedData)
        {
            AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
            aes.BlockSize = 128;
            aes.KeySize = 128;
            aes.IV = iv;
            aes.Key = key;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;

            //Encryption
            using (ICryptoTransform encrypt = aes.CreateEncryptor())
            {
                byte[] dest = encrypt.TransformFinalBlock(plainData, 0, plainData.Length);

                // 암호화된 데이터를 Base64 인코딩된 문자열로 변환
                encodedData = Encoding.Default.GetBytes(Convert.ToBase64String(dest));
            }

            return encodedData.Length;
        }

        /// <summary>
        /// AES-BASE64 복호화(속도개선)
        /// </summary>
        /// <param name="key">암호화KEY</param>
        /// <param name="iv">Initialize Vector</param>
        /// <param name="encodedData">암호문</param>
        /// <param name="plainData">평문</param>
        private static int DecryptByAESAndBase64Ex(byte[] key, byte[] iv, byte[] encodedData, ref byte[] plainData)
        {
            AesCryptoServiceProvider aes = new AesCryptoServiceProvider();
            aes.BlockSize = 128;
            aes.KeySize = 128;
            aes.IV = iv;
            aes.Key = key;
            aes.Mode = CipherMode.CBC;
            aes.Padding = PaddingMode.PKCS7;

            byte[] encryptedData = Convert.FromBase64String(Encoding.Default.GetString(encodedData, 0, Array.IndexOf(encodedData, (byte)0x00)));

            //Decryption
            using (ICryptoTransform decrypt = aes.CreateDecryptor())
            {
                plainData = decrypt.TransformFinalBlock(encryptedData, 0, encryptedData.Length);
            }

            return plainData.Length;
        }
        #endregion

        #region AES-BASE64 암호화/복호화
        /// <summary>
        /// AES-BASE64 암호화
        /// </summary>
        /// <param name="key">암호화KEY</param>
        /// <param name="iv">Initialize Vector</param>
        /// <param name="plainData">평문</param>
        /// <param name="encodedData">암호문</param>
        private static int EncryptByAESAndBase64(byte[] key, byte[] iv, byte[] plainData, ref byte[] encodedData)
        {
            Array.Clear(encodedData, 0, encodedData.Length);
            RijndaelManaged rijndaelCipher = new RijndaelManaged();

            //// key에 salt 사용
            //byte[] salt = Encoding.Default.GetBytes(key.Length.ToString());

            //PasswordDeriveBytes secretKey = new PasswordDeriveBytes(key, salt);
            rijndaelCipher.Key = key;
            rijndaelCipher.IV = iv;

            //ICryptoTransform encryptor = rijndaelCipher.CreateEncryptor(secretKey.GetBytes(32), secretKey.GetBytes(16));
            ICryptoTransform encryptor = rijndaelCipher.CreateEncryptor(rijndaelCipher.Key, rijndaelCipher.IV);

            MemoryStream memoryStream = new MemoryStream();

            CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write);

            // 암호화 프로세스 진행
            cryptoStream.Write(plainData, 0, Array.IndexOf(plainData, (byte)0x00));

            // 암호화 종료
            cryptoStream.FlushFinalBlock();

            // 암호화된 데이터를 바이트 배열에 저장
            byte[] cipherBytes = memoryStream.ToArray();

            memoryStream.Close();
            cryptoStream.Close();

            // 암호화된 데이터를 Base64 인코딩된 문자열로 변환
            encodedData = Encoding.Default.GetBytes(Convert.ToBase64String(cipherBytes));

            return encodedData.Length;
        }

        /// <summary>
        /// AES-BASE64 복호화
        /// </summary>
        /// <param name="key">암호화KEY</param>
        /// <param name="iv">Initialize Vector</param>
        /// <param name="encodedData">암호문</param>
        /// <param name="plainData">평문</param>
        private static int DecryptByAESAndBase64(byte[] key, byte[] iv, byte[] encodedData, ref byte[] plainData)
        {
            RijndaelManaged rijndaelCipher = new RijndaelManaged();

            byte[] encryptedData = Convert.FromBase64String(Encoding.Default.GetString(encodedData, 0, Array.IndexOf(encodedData, (byte)0x00)));

            //// key에 salt 사용
            //byte[] salt = Encoding.Default.GetBytes(key.Length.ToString());

            //PasswordDeriveBytes secretKey = new PasswordDeriveBytes(key, salt);
            rijndaelCipher.Key = key;
            rijndaelCipher.IV = iv;

            ICryptoTransform decryptor = rijndaelCipher.CreateDecryptor(rijndaelCipher.Key, rijndaelCipher.IV);

            MemoryStream memoryStream = new MemoryStream(encryptedData);

            CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read);

            // 복호화 진행
            cryptoStream.Read(plainData, 0, encryptedData.Length);

            memoryStream.Close();
            cryptoStream.Close();

            return Array.IndexOf(plainData, (byte)0x00);
        }
        #endregion
    }
}
