package com.palacesun.masking.tools;

import com.palacesun.engine.gateway.EncryptionGatewayManager;
import com.palacesun.masking.adapter.StringUtils;
import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.SecureRandom;
import java.security.Security;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.logging.Logger;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;
import org.bouncycastle.crypto.engines.SM4Engine;
import org.bouncycastle.crypto.modes.CCMBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;

public class Sm4Util {
   protected static Logger logger = Logger.getLogger(Sm4Util.class.getName());
   private static final String ENCODING = "UTF-8";
   public static final String ALGORITHM_NAME = "SM4";
   public static final String ALGORITHM_NAME_ECB_PADDING = "SM4/ECB/PKCS5Padding";
   public static final String ALGORITHM_NAME_GCM_NOPADDING = "SM4/GCM/NoPadding";
   public static final String ALGORITHM_NAME_CBC_PADDING = "SM4/CBC/PKCS5Padding";
   public static final String ALGORITHM_NAME_OFB_PADDING = "SM4/OFB/NoPadding";
   public static final String ALGORITHM_NAME_CFB_PADDING = "SM4/CFB/NoPadding";
   public static final String ALGORITHM_NAME_CCM_PADDING = "SM4/CCM/NoPadding";
   public static final int DEFAULT_KEY_SIZE = 128;
   public static final int MAC_SIZE_BITS = 64;
   public static final String NOTICE = "1234567890abcdef1234";
   public static final CCMBlockCipher ccmCipher = new CCMBlockCipher(new SM4Engine());
   private static Properties properties;
   private static String ENCRYPT_LIKE_SPLIT = EncryptionGatewayManager.getParameterValue("encryption.like.split");
   private static String ENCRYPT_LABEL = EncryptionGatewayManager.getParameterValue("encryption.label");
   private static String ENCRYPT_SEPARATOR = EncryptionGatewayManager.getParameterValue("encryption.separator");
   private static final byte[] IV = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2};
   private static final String ENCRYPT_LIKE_OPEN = "1";

   public static String generateKey() throws Exception {
      return new String(Hex.encodeHex(generateKey(128), false));
   }

   public static byte[] generateKey(int keySize) throws Exception {
      KeyGenerator kg = KeyGenerator.getInstance("SM4", "BC");
      kg.init(keySize, new SecureRandom());
      return kg.generateKey().getEncoded();
   }

   private static Cipher generateEcbCipher(String algorithmName, int mode, byte[] key) throws Exception {
      Cipher cipher = Cipher.getInstance(algorithmName, "BC");
      Key sm4Key = new SecretKeySpec(key, "SM4");
      cipher.init(mode, sm4Key);
      return cipher;
   }

   private static Cipher generateGcmCipher(String algorithmName, int mode, byte[] key) throws Exception {
      Cipher cipher = Cipher.getInstance(algorithmName, "BC");
      Key sm4Key = new SecretKeySpec(key, "SM4");
      cipher.init(mode, sm4Key, new IvParameterSpec(IV));
      return cipher;
   }

   private static Cipher generateCcmCipher(String algorithmName, int mode, byte[] key) throws Exception {
      Cipher cipher = Cipher.getInstance(algorithmName, "BC");
      Key sm4Key = new SecretKeySpec(key, "SM4");
      cipher.init(mode, sm4Key, new IvParameterSpec(IV));
      return cipher;
   }

   private static Cipher generateCfbCipher(String algorithmName, int mode, byte[] key) throws Exception {
      Cipher cipher = Cipher.getInstance(algorithmName);
      Key sm4Key = new SecretKeySpec(key, "SM4");
      cipher.init(mode, sm4Key, new IvParameterSpec(IV));
      return cipher;
   }

   private static Cipher generateOfbCipher(String algorithmName, int mode, byte[] key) throws Exception {
      Cipher cipher = Cipher.getInstance(algorithmName);
      Key sm4Key = new SecretKeySpec(key, "SM4");
      cipher.init(mode, sm4Key, new IvParameterSpec(IV));
      return cipher;
   }

   public static String encryptEcb(String hexKey, String paramStr, String isLike, String keepfirst, String encdigit, String twoindex, String... encryptMode) throws Exception {
      try {
         String cipherText = "";
         byte[] keyData = ByteUtils.fromHexString(hexKey);
         String prefix = null;
         String enc = paramStr;
         String suffix = null;
         String encryptModeSwitch = "SM4";
         if (encryptMode.length > 0) {
            encryptModeSwitch = encryptMode[0];
         }

         if (!StringUtils.isBlank(keepfirst)) {
            if (!"0".equals(keepfirst) && !"1".equals(keepfirst)) {
               if ("2".equals(keepfirst)) {
                  int first = 0;
                  if (StringUtils.isNotBlank(encdigit)) {
                     first = Integer.parseInt(encdigit);
                  }

                  int last = 0;
                  if (StringUtils.isNotBlank(twoindex)) {
                     last = Integer.parseInt(twoindex);
                  }

                  if (first + last >= paramStr.length() || first >= paramStr.length() || last >= paramStr.length()) {
                     return paramStr;
                  }

                  prefix = paramStr.substring(0, first);
                  enc = paramStr.substring(first, paramStr.length() - last);
                  suffix = paramStr.substring(paramStr.length() - last);
               }
            } else if (StringUtils.isNotBlank(encdigit)) {
               if (Integer.parseInt(encdigit) >= paramStr.length()) {
                  return paramStr;
               }

               if (Integer.parseInt(encdigit) != 0) {
                  if ("0".equals(keepfirst)) {
                     prefix = paramStr.substring(0, Integer.parseInt(encdigit));
                     enc = paramStr.substring(Integer.parseInt(encdigit));
                  } else if ("1".equals(keepfirst)) {
                     enc = paramStr.substring(0, paramStr.length() - Integer.parseInt(encdigit));
                     suffix = paramStr.substring(paramStr.length() - Integer.parseInt(encdigit));
                  }
               }
            }
         }

         if ("1".equals(isLike)) {
            StringBuilder sb = new StringBuilder();
            List<String> subS = subS(enc);

            for(int i = 0; i < subS.size(); ++i) {
               String c = (String)subS.get(i);
               byte[] srcData = (c + "").getBytes("UTF-8");
               byte[] cipherArray = new byte[0];
               if (!"SM4".equals(encryptModeSwitch) && !"SM4_ECB".equals(encryptModeSwitch)) {
                  if ("SM4_GCM".equals(encryptModeSwitch)) {
                     cipherArray = encrypt_Gcm_Padding(keyData, srcData);
                  } else if ("SM4_CBC".equals(encryptModeSwitch)) {
                     cipherArray = encrypt_Cbc_Padding(keyData, srcData);
                  } else if ("SM4_OFB".equals(encryptModeSwitch)) {
                     cipherArray = encrypt_Ofb_Padding(keyData, srcData);
                  } else if ("SM4_CFB".equals(encryptModeSwitch)) {
                     cipherArray = encrypt_Cfb_Padding(keyData, srcData);
                  } else if ("SM4_CCM".equals(encryptModeSwitch)) {
                     cipherArray = encrypt_Ccm_Padding(keyData, srcData);
                  }
               } else {
                  cipherArray = encrypt_Ecb_Padding(keyData, srcData);
               }

               cipherText = ByteUtils.toHexString(cipherArray);
               sb.append(cipherText).append(ENCRYPT_LIKE_SPLIT);
            }

            cipherText = sb.deleteCharAt(sb.length() - 1).toString();
         } else {
            byte[] srcData = enc.getBytes("UTF-8");
            byte[] cipherArray = new byte[0];
            if (!"SM4".equals(encryptModeSwitch) && !"SM4_ECB".equals(encryptModeSwitch)) {
               if ("SM4_GCM".equals(encryptModeSwitch)) {
                  cipherArray = encrypt_Gcm_Padding(keyData, srcData);
               } else if ("SM4_CBC".equals(encryptModeSwitch)) {
                  cipherArray = encrypt_Cbc_Padding(keyData, srcData);
               } else if ("SM4_OFB".equals(encryptModeSwitch)) {
                  cipherArray = encrypt_Ofb_Padding(keyData, srcData);
               } else if ("SM4_CFB".equals(encryptModeSwitch)) {
                  cipherArray = encrypt_Cfb_Padding(keyData, srcData);
               } else if ("SM4_CCM".equals(encryptModeSwitch)) {
                  cipherArray = encrypt_Ccm_Padding(keyData, srcData);
               }
            } else {
               cipherArray = encrypt_Ecb_Padding(keyData, srcData);
            }

            cipherText = ByteUtils.toHexString(cipherArray);
         }

         if ("0".equals(keepfirst) && null != prefix) {
            cipherText = prefix + cipherText;
         } else if ("1".equals(keepfirst) && null != suffix) {
            cipherText = cipherText + suffix;
         } else if ("2".equals(keepfirst)) {
            if (null != prefix) {
               cipherText = prefix + cipherText;
            }

            if (null != suffix) {
               cipherText = cipherText + suffix;
            }
         }

         return cipherText;
      } catch (Exception var19) {
         return paramStr;
      }
   }

   public static List<String> subS(String str) {
      StringBuilder sb = new StringBuilder();
      List<String> a = new ArrayList();

      for(int i = 0; i < str.length(); ++i) {
         int codePoint = str.codePointAt(i);
         a.add(String.valueOf(Character.toChars(codePoint)));
         sb.append(Character.toChars(codePoint));
         if (Character.isSupplementaryCodePoint(codePoint)) {
            ++i;
         }
      }

      return a;
   }

   public static String modifyString(String input) {
      if (input.length() < 6) {
         throw new IllegalArgumentException("Input string must be at least 6 characters long.");
      } else {
         StringBuilder sb = new StringBuilder(input);
         sb.insert(1, 'a');
         sb.insert(4, 'b');
         sb.insert(6, 'c');
         return sb.toString();
      }
   }

   public static byte[] encrypt_Cbc_Padding(byte[] key, byte[] data) throws Exception {
      Cipher cipher = generateCbcCipher("SM4/CBC/PKCS5Padding", 1, key);
      return cipher.doFinal(data);
   }

   private static Cipher generateCbcCipher(String algorithmName, int mode, byte[] key) throws Exception {
      Cipher cipher = Cipher.getInstance(algorithmName, "BC");
      Key sm4Key = new SecretKeySpec(key, "SM4");
      cipher.init(mode, sm4Key, generateIV());
      return cipher;
   }

   public static AlgorithmParameters generateIV() throws Exception {
      byte[] iv = new byte[16];
      Arrays.fill(iv, (byte)0);
      AlgorithmParameters params = AlgorithmParameters.getInstance("SM4");
      params.init(new IvParameterSpec(iv));
      return params;
   }

   public static byte[] encrypt_Ccm_Padding(byte[] key, byte[] data) throws Exception {
      byte[] nonce = org.bouncycastle.util.encoders.Hex.decode("1234567890abcdef1234");
      ccmCipher.init(true, new ParametersWithIV(new KeyParameter(key), nonce));
      byte[] ciphertext = new byte[ccmCipher.getOutputSize(data.length)];
      int len = ccmCipher.processBytes(data, 0, data.length, ciphertext, 0);
      ccmCipher.doFinal(ciphertext, len);
      return ciphertext;
   }

   public static byte[] encrypt_Cfb_Padding(byte[] key, byte[] data) throws Exception {
      Cipher cipher = generateCfbCipher("SM4/CFB/NoPadding", 1, key);
      return cipher.doFinal(data);
   }

   public static byte[] encrypt_Ofb_Padding(byte[] key, byte[] data) throws Exception {
      Cipher cipher = generateOfbCipher("SM4/OFB/NoPadding", 1, key);
      return cipher.doFinal(data);
   }

   public static byte[] encrypt_Gcm_Padding(byte[] key, byte[] data) throws Exception {
      Cipher cipher = generateGcmCipher("SM4/GCM/NoPadding", 1, key);
      return cipher.doFinal(data);
   }

   public static byte[] encrypt_Ecb_Padding(byte[] key, byte[] data) throws Exception {
      Cipher cipher = generateEcbCipher("SM4/ECB/PKCS5Padding", 1, key);
      return cipher.doFinal(data);
   }

   public static String decryptEcb(String hexKey, String paramStr, String isLike, String keepfirst, String encdigit, String twoindex, String... encryptMode) throws Exception {
      String decryptStr = "";

      try {
         byte[] srcData = new byte[0];
         byte[] keyData = ByteUtils.fromHexString(hexKey);
         String prefix = null;
         String enc = paramStr;
         String suffix = null;
         String encryptModeSwitch = "SM4";
         if (encryptMode.length > 0) {
            encryptModeSwitch = encryptMode[0];
         }

         if (!StringUtils.isBlank(keepfirst)) {
            if (!"0".equals(keepfirst) && !"1".equals(keepfirst)) {
               if ("2".equals(keepfirst)) {
                  int first = 0;
                  if (StringUtils.isNotBlank(encdigit)) {
                     first = Integer.parseInt(encdigit);
                  }

                  int last = 0;
                  if (StringUtils.isNotBlank(twoindex)) {
                     last = Integer.parseInt(twoindex);
                  }

                  if (first + last >= paramStr.length() || first >= paramStr.length() || last >= paramStr.length()) {
                     return paramStr;
                  }

                  prefix = paramStr.substring(0, first);
                  enc = paramStr.substring(first, paramStr.length() - last);
                  suffix = paramStr.substring(paramStr.length() - last);
               }
            } else if (StringUtils.isNotBlank(encdigit)) {
               if (Integer.parseInt(encdigit) >= paramStr.length()) {
                  return paramStr;
               }

               if (Integer.parseInt(encdigit) != 0) {
                  if ("0".equals(keepfirst)) {
                     prefix = paramStr.substring(0, Integer.parseInt(encdigit));
                     enc = paramStr.substring(Integer.parseInt(encdigit));
                  } else if ("1".equals(keepfirst)) {
                     suffix = paramStr.substring(paramStr.length() - Integer.parseInt(encdigit));
                     enc = paramStr.substring(0, paramStr.length() - Integer.parseInt(encdigit));
                  }
               }
            }
         }

         if ("1".equals(isLike)) {
            StringBuilder sb = new StringBuilder();
            String[] split = enc.split(ENCRYPT_LIKE_SPLIT);

            for(String s : split) {
               byte[] cipherData = ByteUtils.fromHexString(s);

               try {
                  if (!"SM4".equals(encryptModeSwitch) && !"SM4_ECB".equals(encryptModeSwitch)) {
                     if ("SM4_GCM".equals(encryptModeSwitch)) {
                        srcData = decrypt_Gcm_Padding(keyData, cipherData);
                     } else if ("SM4_CBC".equals(encryptModeSwitch)) {
                        srcData = decrypt_Cbc_Padding(keyData, cipherData);
                     } else if ("SM4_OFB".equals(encryptModeSwitch)) {
                        srcData = decrypt_Ofb_Padding(keyData, cipherData);
                     } else if ("SM4_CFB".equals(encryptModeSwitch)) {
                        srcData = decrypt_Cfb_Padding(keyData, cipherData);
                     } else if ("SM4_CCM".equals(encryptModeSwitch)) {
                        srcData = decrypt_Ccm_Padding(keyData, cipherData);
                     }
                  } else {
                     srcData = decrypt_Ecb_Padding(keyData, cipherData);
                  }

                  decryptStr = new String(srcData, "UTF-8");
                  sb.append(decryptStr);
               } catch (Exception var22) {
                  return ENCRYPT_LABEL + paramStr.replaceAll(ENCRYPT_LIKE_SPLIT, ENCRYPT_SEPARATOR + ENCRYPT_LIKE_SPLIT + ENCRYPT_LABEL) + ENCRYPT_SEPARATOR;
               }
            }

            decryptStr = sb.toString();
         } else {
            byte[] cipherData = ByteUtils.fromHexString(enc);
            if (!"SM4".equals(encryptModeSwitch) && !"SM4_ECB".equals(encryptModeSwitch)) {
               if ("SM4_GCM".equals(encryptModeSwitch)) {
                  srcData = decrypt_Gcm_Padding(keyData, cipherData);
               } else if ("SM4_CBC".equals(encryptModeSwitch)) {
                  srcData = decrypt_Cbc_Padding(keyData, cipherData);
               } else if ("SM4_OFB".equals(encryptModeSwitch)) {
                  srcData = decrypt_Ofb_Padding(keyData, cipherData);
               } else if ("SM4_CFB".equals(encryptModeSwitch)) {
                  srcData = decrypt_Cfb_Padding(keyData, cipherData);
               } else if ("SM4_CCM".equals(encryptModeSwitch)) {
                  srcData = decrypt_Ccm_Padding(keyData, cipherData);
               }
            } else {
               srcData = decrypt_Ecb_Padding(keyData, cipherData);
            }

            decryptStr = new String(srcData, "UTF-8");
         }

         if ("0".equals(keepfirst) && null != prefix) {
            decryptStr = prefix + decryptStr;
         } else if ("1".equals(keepfirst) && null != suffix) {
            decryptStr = decryptStr + suffix;
         } else if ("2".equals(keepfirst)) {
            if (null != prefix) {
               decryptStr = prefix + decryptStr;
            }

            if (null != suffix) {
               decryptStr = decryptStr + suffix;
            }
         }

         return decryptStr;
      } catch (Exception var23) {
         return ENCRYPT_LABEL + paramStr + ENCRYPT_SEPARATOR;
      }
   }

   public static byte[] decrypt_Ccm_Padding(byte[] key, byte[] cipherText) throws Exception {
      byte[] nonce = org.bouncycastle.util.encoders.Hex.decode("1234567890abcdef1234");
      ccmCipher.init(false, new ParametersWithIV(new KeyParameter(key), nonce));
      byte[] decryptedText = new byte[ccmCipher.getOutputSize(cipherText.length)];
      int decryptedLen = ccmCipher.processBytes(cipherText, 0, cipherText.length, decryptedText, 0);
      ccmCipher.doFinal(decryptedText, decryptedLen);
      return decryptedText;
   }

   private static byte[] decrypt_Cfb_Padding(byte[] key, byte[] cipherText) throws Exception {
      Cipher cipher = generateCfbCipher("SM4/CFB/NoPadding", 2, key);
      return cipher.doFinal(cipherText);
   }

   private static byte[] decrypt_Ofb_Padding(byte[] key, byte[] cipherText) throws Exception {
      Cipher cipher = generateOfbCipher("SM4/OFB/NoPadding", 2, key);
      return cipher.doFinal(cipherText);
   }

   public static byte[] decrypt_Cbc_Padding(byte[] key, byte[] cipherText) throws Exception {
      Cipher cipher = generateCbcCipher("SM4/CBC/PKCS5Padding", 2, key);
      return cipher.doFinal(cipherText);
   }

   public static byte[] decrypt_Gcm_Padding(byte[] key, byte[] cipherText) throws Exception {
      Cipher cipher = generateGcmCipher("SM4/GCM/NoPadding", 2, key);
      return cipher.doFinal(cipherText);
   }

   public static byte[] decrypt_Ecb_Padding(byte[] key, byte[] cipherText) throws Exception {
      Cipher cipher = generateEcbCipher("SM4/ECB/PKCS5Padding", 2, key);
      return cipher.doFinal(cipherText);
   }

   public static boolean verifyEcb(String hexKey, String cipherText, String paramStr) throws Exception {
      boolean flag = false;
      byte[] keyData = ByteUtils.fromHexString(hexKey);
      byte[] cipherData = ByteUtils.fromHexString(cipherText);
      byte[] decryptData = decrypt_Ecb_Padding(keyData, cipherData);
      byte[] srcData = paramStr.getBytes("UTF-8");
      flag = Arrays.equals(decryptData, srcData);
      return flag;
   }

   public static void main(String[] args) {
      try {
         String json = "af91e8e7fd";
         String key = "4E829D9E2BEE7ECB79BFFA8C88A9937B";
         String cipher = encryptEcb(key, json, "0", "0", "0", "0");
         System.out.println("加密串---" + cipher);
         json = decryptEcb(key, "fefaf8c85fb3b82186f88b95c54b074b1", "0", "0", "0", "0");
         System.out.println("解密后数据---" + json);
      } catch (Exception e) {
         e.printStackTrace();
      }

   }

   static {
      Security.addProvider(new BouncyCastleProvider());
   }
}
