package com.chenyang.nse.bussiness.tools.encryption;

import com.chenyang.nse.bussiness.config.PropertiesLoaderUtils;
import java.io.IOException;
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 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.apache.commons.lang3.StringUtils;
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 {
   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 String ENCRYPT_LABEL;
   private static String ENCRYPT_LABEL_SUFFIX;
   private static Properties properties;
   private static String ENCRYPT_LIKE_SPLIT;
   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;
   }

   public static byte[] encryptEcbBlob(String hexKey, byte[] paramStr, String... encryptMode) {
      try {
         byte[] keyData = ByteUtils.fromHexString(hexKey);
         String encryptModeSwitch = "SM4";
         byte[] cipherArray = new byte[0];
         if (!"SM4".equals(encryptModeSwitch) && !"SM4_ECB".equals(encryptModeSwitch)) {
            if ("SM4_GCM".equals(encryptModeSwitch)) {
               cipherArray = encrypt_Gcm_Padding(keyData, paramStr);
            } else if ("SM4_CBC".equals(encryptModeSwitch)) {
               cipherArray = encrypt_Cbc_Padding(keyData, paramStr);
            } else if ("SM4_OFB".equals(encryptModeSwitch)) {
               cipherArray = encrypt_Ofb_Padding(keyData, paramStr);
            } else if ("SM4_CFB".equals(encryptModeSwitch)) {
               cipherArray = encrypt_Cfb_Padding(keyData, paramStr);
            } else if ("SM4_CCM".equals(encryptModeSwitch)) {
               cipherArray = encrypt_Ccm_Padding(keyData, paramStr);
            }
         } else {
            cipherArray = encrypt_Ecb_Padding(keyData, paramStr);
         }

         return cipherArray;
      } catch (Exception var8) {
         return paramStr;
      }
   }

   public static byte[] decryptEcbBlob(String hexKey, byte[] paramStr, String... encryptMode) throws Exception {
      byte[] keyData = ByteUtils.fromHexString(hexKey);
      byte[] a = new byte[0];
      byte[] slicedArray = new byte[0];
      if (paramStr[0] != -1 && paramStr[1] != -1) {
         return a;
      } else {
         slicedArray = Arrays.copyOfRange(paramStr, 2, paramStr.length);

         try {
            return decryptEcbFBlob(hexKey, slicedArray, (String[])null);
         } catch (Exception var7) {
            return a;
         }
      }
   }

   public static byte[] decryptEcbFBlob(String hexKey, byte[] paramStr, String... encryptMode) throws Exception {
      byte[] srcData = new byte[0];
      byte[] keyData = ByteUtils.fromHexString(hexKey);
      String encryptModeSwitch = "SM4";
      if (!"SM4".equals(encryptModeSwitch) && !"SM4_ECB".equals(encryptModeSwitch)) {
         if ("SM4_GCM".equals(encryptModeSwitch)) {
            srcData = decrypt_Gcm_Padding(keyData, paramStr);
         } else if ("SM4_CBC".equals(encryptModeSwitch)) {
            srcData = decrypt_Cbc_Padding(keyData, paramStr);
         } else if ("SM4_OFB".equals(encryptModeSwitch)) {
            srcData = decrypt_Ofb_Padding(keyData, paramStr);
         } else if ("SM4_CFB".equals(encryptModeSwitch)) {
            srcData = decrypt_Cfb_Padding(keyData, paramStr);
         } else if ("SM4_CCM".equals(encryptModeSwitch)) {
            srcData = decrypt_Ccm_Padding(keyData, paramStr);
         }
      } else {
         srcData = decrypt_Ecb_Padding(keyData, paramStr);
      }

      return srcData;
   }

   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) {
      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];
               switch (encryptModeSwitch) {
                  case "SM4":
                  case "SM4_ECB":
                     cipherArray = encrypt_Ecb_Padding(keyData, srcData);
                     break;
                  case "SM4_GCM":
                     cipherArray = encrypt_Gcm_Padding(keyData, srcData);
                     break;
                  case "SM4_CBC":
                     cipherArray = encrypt_Cbc_Padding(keyData, srcData);
                     break;
                  case "SM4_OFB":
                     cipherArray = encrypt_Ofb_Padding(keyData, srcData);
                     break;
                  case "SM4_CFB":
                     cipherArray = encrypt_Cfb_Padding(keyData, srcData);
                     break;
                  case "SM4_CCM":
                     cipherArray = encrypt_Ccm_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];
            switch (encryptModeSwitch) {
               case "SM4":
               case "SM4_ECB":
                  cipherArray = encrypt_Ecb_Padding(keyData, srcData);
                  break;
               case "SM4_GCM":
                  cipherArray = encrypt_Gcm_Padding(keyData, srcData);
                  break;
               case "SM4_CBC":
                  cipherArray = encrypt_Cbc_Padding(keyData, srcData);
                  break;
               case "SM4_OFB":
                  cipherArray = encrypt_Ofb_Padding(keyData, srcData);
                  break;
               case "SM4_CFB":
                  cipherArray = encrypt_Cfb_Padding(keyData, srcData);
                  break;
               case "SM4_CCM":
                  cipherArray = encrypt_Ccm_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 var21) {
         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 encryptEcb(String hexKey, String paramStr) {
      try {
         byte[] keyData = ByteUtils.fromHexString(hexKey);
         byte[] srcData = paramStr.getBytes("UTF-8");
         byte[] cipherArray = encrypt_Ecb_Padding(keyData, srcData);
         String cipherText = ByteUtils.toHexString(cipherArray);
         return cipherText;
      } catch (Exception var7) {
         return paramStr;
      }
   }

   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)) {
            byte[] cipherData = ByteUtils.fromHexString(enc);

            try {
               switch (encryptModeSwitch) {
                  case "SM4":
                  case "SM4_ECB":
                     srcData = decrypt_Ecb_Padding(keyData, cipherData);
                     break;
                  case "SM4_GCM":
                     srcData = decrypt_Gcm_Padding(keyData, cipherData);
                     break;
                  case "SM4_CBC":
                     srcData = decrypt_Cbc_Padding(keyData, cipherData);
                     break;
                  case "SM4_OFB":
                     srcData = decrypt_Ofb_Padding(keyData, cipherData);
                     break;
                  case "SM4_CFB":
                     srcData = decrypt_Cfb_Padding(keyData, cipherData);
                     break;
                  case "SM4_CCM":
                     srcData = decrypt_Ccm_Padding(keyData, cipherData);
               }

               decryptStr = new String(srcData, "UTF-8");
            } catch (Exception var23) {
               decryptStr = ENCRYPT_LABEL + enc + ENCRYPT_LABEL_SUFFIX;
            }
         } else {
            StringBuilder sb = new StringBuilder();
            String[] split = enc.split(ENCRYPT_LIKE_SPLIT);

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

               try {
                  switch (encryptModeSwitch) {
                     case "SM4":
                     case "SM4_ECB":
                        srcData = decrypt_Ecb_Padding(keyData, cipherData);
                        break;
                     case "SM4_GCM":
                        srcData = decrypt_Gcm_Padding(keyData, cipherData);
                        break;
                     case "SM4_CBC":
                        srcData = decrypt_Cbc_Padding(keyData, cipherData);
                        break;
                     case "SM4_OFB":
                        srcData = decrypt_Ofb_Padding(keyData, cipherData);
                        break;
                     case "SM4_CFB":
                        srcData = decrypt_Cfb_Padding(keyData, cipherData);
                        break;
                     case "SM4_CCM":
                        srcData = decrypt_Ccm_Padding(keyData, cipherData);
                  }

                  decryptStr = new String(srcData, "UTF-8");
               } catch (Exception var24) {
                  decryptStr = ENCRYPT_LABEL + s + ENCRYPT_LABEL_SUFFIX;
               }

               sb.append(decryptStr);
            }

            if (sb.length() > 0) {
               decryptStr = sb.toString();
            } else {
               decryptStr = enc;
            }
         }

         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;
            }
         }
      } catch (Exception e) {
         decryptStr = paramStr;
         e.printStackTrace();
      }

      return decryptStr;
   }

   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) throws Exception {
      String str = "李四";
      System.out.println(str);
      String key = "55063E16011839DE9DAE178ACC45AFD41";
      System.out.println(key);
      String s = encryptEcb(key, str, "0", "0", "0", "0", "SM4_ECB");
      System.out.println("加密后的数据：" + s);
      String s1 = decryptEcb(key, "jm_8ae37f9ec52c7659bc83e18cc628e1361#jm_da51376869b8ca018b7f2e4391b02cc11", "0", "0", "0", "0", "SM4_ECB");
      System.out.println("解密后的数据：" + s1);
   }

   static {
      Security.removeProvider("SunEC");

      try {
         properties = PropertiesLoaderUtils.loadAllProperties("config.properties");
         ENCRYPT_LIKE_SPLIT = properties.get("encryption.like.split").toString();
         ENCRYPT_LABEL = properties.get("encryption.label").toString();
         ENCRYPT_LABEL_SUFFIX = properties.get("encryption.label.suffix").toString();
      } catch (IOException e) {
         e.printStackTrace();
      }

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