EncryptionUtil.java

package org.ondc;

import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
import java.util.logging.Level;
import java.util.logging.Logger;

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

import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;


/**
 * Utility class for encrypting and decrypting payload.
 */
public class EncryptionUtil {
	private static Logger log = Logger.getLogger("ErrorLogger");

    private EncryptionUtil() {
        
    }
	/**
	 * The standard Initialization Vector (IV) length (96 bits).
	 */
	public static final int IV_BIT_LENGTH = 96;
	
	/**
	 * The standard authentication tag length (128 bits).
	 */
    public static final int AUTH_TAG_BIT_LENGTH = 128;
    
    
    /**
     * 
     * The POJO class for Encrypted Data.
     *
     */
    public static class EncryptionPayload {
        
        /**
         * The Nonce / IV (Initialization Vector) created for encrypting the data.
         */
        private String nonce;
        
    	/**
    	 * The Encrypted Data in base64 encoded format.
    	 */
        @SerializedName("encrypted_data")
        private String encrypedData;
        
        /**
         * The HMAC
         */
        private String hmac;

        /**
         * Gets the Encrypted Data
         * @return The Encrypted Data.
         */
        public String getEncrypedData() {
            return encrypedData;
        }

        /**
         * Sets the Encrypted Data.
         * @param encrypedData The Encrypted Data.
         */
        private void setEncrypedData(String encrypedData) {
            this.encrypedData = encrypedData;
        }

        /**
         * Gets the Nonce.
         * @return The Nonce.
         */
        public String getNonce() {
            return nonce;
        }

        /**
         * Sets the Nonce.
         * @param nonce The Nonce.
         */
        private void setNonce(String nonce) {
            this.nonce = nonce;
        }

        /**
         * Gets the HMAC.
         * @return The HMAC.
         */
        public String getHmac() {
            return hmac;
        }

        /**
         * Sets the HMAC.
         * @param hmac The HMAC.
         */
        private void setHmac(String hmac) {
            this.hmac = hmac;
        }

        /**
         * A Method to convert this instance of a class to base64 string.
         * @return Base64 Encoded string of the object.
         */
        public String toBase64String() {
            return bytesToString(new Gson().toJson(this).getBytes());
        }

    }

    /**
     * Encrypts the specified plain text using AES/GCM/NoPadding.
     * @param key The Shared Key.
     * @param data The Raw Data to be Encrypted.
     * @return The Encrypted data in base64 encoded format.
     */
    public static String encryptData(String key, String data) {
    	
    	// Initialize a EncryptionPayload Object.
        String enryptionPayload = null;
        try {
        	
        	// Create a Cipher instance.
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            
            // De-serializing the Shared Key.
            SecretKey sKey = KeyUtil.sharedKeyFromString(key);
            SecretKeySpec keySpec = new SecretKeySpec(sKey.getEncoded(), "AES");
            
            // Randomly generate the IV / nonce.
            SecureRandom secureRandom = new SecureRandom();
            byte[] iv = new byte[IV_BIT_LENGTH / 8];
            secureRandom.nextBytes(iv);
            
            // Initialize AES/GCM cipher for encryption
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, new GCMParameterSpec(AUTH_TAG_BIT_LENGTH, iv));
            
            // Encrypt the raw data and get the cipher text and authentication tag.
            byte[] eData = cipher.doFinal(data.getBytes());
            EncryptionPayload encryptedData = new EncryptionPayload();
            int tagLengthBytes = AUTH_TAG_BIT_LENGTH / Byte.SIZE;
            
            
            byte[] encryptedText=Arrays.copyOfRange(eData, 0, eData.length - tagLengthBytes);
            byte[] authTag= Arrays.copyOfRange(eData, eData.length - tagLengthBytes, eData.length);
           
            // Set the values for the EncryptedData object.
            encryptedData.setEncrypedData(bytesToString(encryptedText));
            encryptedData.setNonce(bytesToString(iv));
            encryptedData.setHmac(bytesToString(authTag)); 
            
            enryptionPayload = encryptedData.toBase64String();
        } catch (Exception e) {
            log.log(Level.SEVERE, e.getMessage(), e);
        }
        
        // Return the Encrypted Data.
        return enryptionPayload;
    }

    /**
     * Decrypts the Encrypted Data using Shared Key.
     * @param key The Shared Key.
     * @param eData The Encrypted Data.
     * @return The Raw Decrypted data.
     */
    public static String decryptData(String key, String eData) {
    	
    	// Initialize a variable to store Decrypted Data.
        String rawData = null;
        
        // Decode the base64 string and De-serialize it as› EncryptionPayload Object.
        String json = stringToBytes(eData);
        EncryptionPayload encryptedData = new Gson().fromJson(json, EncryptionPayload.class);
        
        try {
        	
        	// Create a Cipher instance.
            Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
            
            // Decode the fields of encryptedData from base64 to bytes.
            byte[] nonce = Base64.getDecoder().decode(encryptedData.getNonce());
            byte[] authTagBytes = Base64.getDecoder().decode(encryptedData.getHmac());
            byte[] eBytes = Base64.getDecoder().decode(encryptedData.getEncrypedData());
            
            byte[] encryptedBytes= Arrays.copyOf(eBytes, eBytes.length + authTagBytes.length);
            System.arraycopy(authTagBytes,0,encryptedBytes,eBytes.length,authTagBytes.length);
            
            // De-serialize the Shared Key.
            SecretKey sKey = KeyUtil.sharedKeyFromString(key);
            SecretKeySpec keySpec = new SecretKeySpec(sKey.getEncoded(), "AES");
            
            // Initialize the AES/GCM Cipher instance for decryption.
            cipher.init(Cipher.DECRYPT_MODE, keySpec, new GCMParameterSpec(128, nonce));
            
            
            // Decrypt the data
            byte[] dData = cipher.doFinal(encryptedBytes);
            rawData = new String(dData);
            
        } catch (Exception e) {
            log.log(Level.SEVERE, e.getMessage(), e);
        }
        
        // Return the Decrypted Data.›
        return rawData;
        
    }
    

    /**
     * Converts a Byte Array into base64 string.
     * @param bytes The Byte Array to be encoded.
     * @return The base64 encoded string.
     */
    private static String bytesToString(byte[] bytes) {
        return Base64.getEncoder().encodeToString(bytes);
    }
    
    
    /**
     * Converts a base64 string into Byte Array.
     * @param data The base64 encoded string.
     * @return The decoded Byte Array.
     */
    private static String stringToBytes(String data) {
    	byte[] bytes = Base64.getDecoder().decode(data);
    	return new String(bytes);
    }
}