package net.minecraft.util;

import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.spec.EncodedKeySpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.X509EncodedKeySpec;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class CryptManager {
   private static final Logger LOGGER = LogManager.getLogger();

   /**
    * Generate a new shared secret AES key from a secure random source
    */
   @OnlyIn(Dist.CLIENT)
   public static SecretKey createNewSharedKey() {
      try {
         KeyGenerator keygenerator = KeyGenerator.getInstance("AES");
         keygenerator.init(128);
         return keygenerator.generateKey();
      } catch (NoSuchAlgorithmException nosuchalgorithmexception) {
         throw new Error(nosuchalgorithmexception);
      }
   }

   /**
    * Generates RSA KeyPair
    */
   public static KeyPair generateKeyPair() {
      try {
         KeyPairGenerator keypairgenerator = KeyPairGenerator.getInstance("RSA");
         keypairgenerator.initialize(1024);
         return keypairgenerator.generateKeyPair();
      } catch (NoSuchAlgorithmException nosuchalgorithmexception) {
         nosuchalgorithmexception.printStackTrace();
         LOGGER.error("Key pair generation failed!");
         return null;
      }
   }

   /**
    * Compute a serverId hash for use by sendSessionRequest()
    */
   public static byte[] getServerIdHash(String serverId, PublicKey publicKey, SecretKey secretKey) {
      try {
         return digestOperation("SHA-1", serverId.getBytes("ISO_8859_1"), secretKey.getEncoded(), publicKey.getEncoded());
      } catch (UnsupportedEncodingException unsupportedencodingexception) {
         unsupportedencodingexception.printStackTrace();
         return null;
      }
   }

   /**
    * Compute a message digest on arbitrary byte[] data
    */
   private static byte[] digestOperation(String algorithm, byte[]... data) {
      try {
         MessageDigest messagedigest = MessageDigest.getInstance(algorithm);

         for(byte[] abyte : data) {
            messagedigest.update(abyte);
         }

         return messagedigest.digest();
      } catch (NoSuchAlgorithmException nosuchalgorithmexception) {
         nosuchalgorithmexception.printStackTrace();
         return null;
      }
   }

   /**
    * Create a new PublicKey from encoded X.509 data
    */
   public static PublicKey decodePublicKey(byte[] encodedKey) {
      try {
         EncodedKeySpec encodedkeyspec = new X509EncodedKeySpec(encodedKey);
         KeyFactory keyfactory = KeyFactory.getInstance("RSA");
         return keyfactory.generatePublic(encodedkeyspec);
      } catch (NoSuchAlgorithmException var3) {
         ;
      } catch (InvalidKeySpecException var4) {
         ;
      }

      LOGGER.error("Public key reconstitute failed!");
      return null;
   }

   /**
    * Decrypt shared secret AES key using RSA private key
    */
   public static SecretKey decryptSharedKey(PrivateKey key, byte[] secretKeyEncrypted) {
      return new SecretKeySpec(decryptData(key, secretKeyEncrypted), "AES");
   }

   /**
    * Encrypt byte[] data with RSA public key
    */
   @OnlyIn(Dist.CLIENT)
   public static byte[] encryptData(Key key, byte[] data) {
      return cipherOperation(1, key, data);
   }

   /**
    * Decrypt byte[] data with RSA private key
    */
   public static byte[] decryptData(Key key, byte[] data) {
      return cipherOperation(2, key, data);
   }

   /**
    * Encrypt or decrypt byte[] data using the specified key
    */
   private static byte[] cipherOperation(int opMode, Key key, byte[] data) {
      try {
         return createTheCipherInstance(opMode, key.getAlgorithm(), key).doFinal(data);
      } catch (IllegalBlockSizeException illegalblocksizeexception) {
         illegalblocksizeexception.printStackTrace();
      } catch (BadPaddingException badpaddingexception) {
         badpaddingexception.printStackTrace();
      }

      LOGGER.error("Cipher data failed!");
      return null;
   }

   /**
    * Creates the Cipher Instance.
    */
   private static Cipher createTheCipherInstance(int opMode, String transformation, Key key) {
      try {
         Cipher cipher = Cipher.getInstance(transformation);
         cipher.init(opMode, key);
         return cipher;
      } catch (InvalidKeyException invalidkeyexception) {
         invalidkeyexception.printStackTrace();
      } catch (NoSuchAlgorithmException nosuchalgorithmexception) {
         nosuchalgorithmexception.printStackTrace();
      } catch (NoSuchPaddingException nosuchpaddingexception) {
         nosuchpaddingexception.printStackTrace();
      }

      LOGGER.error("Cipher creation failed!");
      return null;
   }

   /**
    * Creates an Cipher instance using the AES/CFB8/NoPadding algorithm. Used for protocol encryption.
    */
   public static Cipher createNetCipherInstance(int opMode, Key key) {
      try {
         Cipher cipher = Cipher.getInstance("AES/CFB8/NoPadding");
         cipher.init(opMode, key, new IvParameterSpec(key.getEncoded()));
         return cipher;
      } catch (GeneralSecurityException generalsecurityexception) {
         throw new RuntimeException(generalsecurityexception);
      }
   }
}