package com.billpaymentservice.bbps.util; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.zip.CRC32; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component public class EncryptionUtil { private static final Logger LOGGER = LoggerFactory.getLogger(EncryptionUtil.class); /** * Encrypts plaintext using AES/CBC/PKCS5Padding. * If the provided key length is not 16/24/32 bytes it will be adjusted to 16 bytes (padded/truncated). */ public String encryptMData(String plaintext, String encryptKey) { try { byte[] keyBytes = normalizeKey(encryptKey); SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); byte[] ivBytes = cipher.getParameters().getParameterSpec(IvParameterSpec.class).getIV(); byte[] encryptedData = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8)); byte[] combinedBytes = new byte[ivBytes.length + encryptedData.length]; System.arraycopy(ivBytes, 0, combinedBytes, 0, ivBytes.length); System.arraycopy(encryptedData, 0, combinedBytes, ivBytes.length, encryptedData.length); return Base64.getEncoder().encodeToString(combinedBytes); } catch (Exception e) { LOGGER.error("Exception in encrypt() : encryptMData- {} : Message- {}", encryptKey, e.getMessage()); } return null; } private byte[] normalizeKey(String encryptKey) { if (encryptKey == null) { encryptKey = ""; } byte[] bytes = encryptKey.getBytes(StandardCharsets.UTF_8); int targetLen = 16; // AES-128 if (bytes.length == targetLen || bytes.length == 24 || bytes.length == 32) { return bytes; } byte[] normalized = new byte[targetLen]; for (int i = 0; i < targetLen; i++) { if (i < bytes.length) { normalized[i] = bytes[i]; } else { normalized[i] = 0; // pad with zeros } } return normalized; } /** * Decrypts data encrypted by encryptMData: Base64(iv + ciphertext) with AES/CBC/PKCS5Padding */ public String decryptMData(String base64Payload, String encryptKey) { if (base64Payload == null) return null; try { byte[] combined = Base64.getDecoder().decode(base64Payload); // IV is first 16 bytes for AES if (combined.length < 16) { LOGGER.warn("Encrypted payload too short to contain IV"); return null; } byte[] iv = new byte[16]; System.arraycopy(combined, 0, iv, 0, 16); byte[] cipherText = new byte[combined.length - 16]; System.arraycopy(combined, 16, cipherText, 0, cipherText.length); byte[] keyBytes = normalizeKey(encryptKey); SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES"); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, new IvParameterSpec(iv)); byte[] plain = cipher.doFinal(cipherText); return new String(plain, StandardCharsets.UTF_8); } catch (Exception ex) { LOGGER.error("Error decrypting payload: {}", ex.getMessage()); return null; } } /** * Compute CRC32 checksum value for the provided input string and return the numeric value as String */ public String getChecksumCRC32(String input) { if (input == null) input = ""; try (InputStream is = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8))) { CRC32 crc = new CRC32(); byte[] buffer = new byte[1024]; int len; while ((len = is.read(buffer)) != -1) { crc.update(buffer, 0, len); } long value = crc.getValue(); LOGGER.debug("Computed CRC32: {} for input: {}", value, input); return String.valueOf(value); } catch (IOException e) { LOGGER.error("Error computing CRC32: {}", e.getMessage()); return null; } } }