package com.rjconsultores.ventaboletos.web.utilerias.security;

import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;

public class AESGSMHelper {
	private final String SECRET_KEY = "RJ@2019#c0n5ul10r35";
	private final String SALT = "HrqoFr44GtkAhhYN+jP8Ag==";
    private final String ENCRYPT_ALGO = "AES/GCM/NoPadding";
    private final int TAG_LENGTH_BIT = 128;
    private final int IV_LENGTH_BYTE = 12;
	
    private final Charset UTF_8 = StandardCharsets.UTF_8;

    public String encrypt(String value) throws Exception {
    	SecretKey secret = getAESKeyFromPassword(SECRET_KEY.toCharArray());
    	
    	byte[] pText = value.getBytes(StandardCharsets.UTF_8);
    	byte[] iv = getRandomNonce(12);
    	byte[] cipherText = encrypt(pText, secret, iv);

        byte[] cipherTextWithIv = ByteBuffer.allocate(iv.length + cipherText.length)
                .put(iv)
                .put(cipherText)
                .array();

        return hex(cipherTextWithIv);
    }

    private byte[] encrypt(byte[] pText, SecretKey secret, byte[] iv) throws Exception {
        Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
        cipher.init(Cipher.ENCRYPT_MODE, secret, new GCMParameterSpec(TAG_LENGTH_BIT, iv));
        
        byte[] encryptedText = cipher.doFinal(pText);

        return encryptedText;

    }

    public String decrypt(String value) throws Exception {
    	SecretKey secret = getAESKeyFromPassword(SECRET_KEY.toCharArray());

    	byte[] cText = unhex(value);
    			
        ByteBuffer bb = ByteBuffer.wrap(cText);

        byte[] iv = new byte[IV_LENGTH_BYTE];
        bb.get(iv);

        byte[] cipherText = new byte[bb.remaining()];
        bb.get(cipherText);

        String plainText = decrypt(cipherText, secret, iv);

        return plainText;
    }

    private String decrypt(byte[] cText, SecretKey secret, byte[] iv) throws Exception {
        Cipher cipher = Cipher.getInstance(ENCRYPT_ALGO);
        cipher.init(Cipher.DECRYPT_MODE, secret, new GCMParameterSpec(TAG_LENGTH_BIT, iv));

        byte[] plainText = cipher.doFinal(cText);
        
        return new String(plainText, UTF_8);
    }

    private byte[] getRandomNonce(int numBytes) {
        byte[] nonce = new byte[numBytes];
        new SecureRandom().nextBytes(nonce);
        
        return nonce;
    }

    private SecretKey getAESKeyFromPassword(char[] password) throws NoSuchAlgorithmException, InvalidKeySpecException {
		SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");

		KeySpec spec = new PBEKeySpec(password, SALT.getBytes(), 65536, 256);
		SecretKeySpec secret = new SecretKeySpec(factory.generateSecret(spec).getEncoded(), "AES");

		return secret;
    }
    
    private String hex(byte[] bytes) {
        char[] result = Hex.encodeHex(bytes);
        return new String(result);
    }

    private byte[] unhex(String hex) throws DecoderException {
        return Hex.decodeHex(hex.toCharArray());
    }    
}
