package com.owrawww.util; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.crypto.Cipher; import javax.crypto.SecretKey; import javax.crypto.spec.GCMParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.SecureRandom; import java.util.Base64; /** * AES-256-GCM 암호화/복호화 유틸리티 * - IV(12 bytes) + 암호문을 Base64로 인코딩하여 저장 */ @Component public class AesUtil { private static final String ALGORITHM = "AES/GCM/NoPadding"; private static final int IV_LENGTH = 12; // GCM 권장 IV 크기 private static final int TAG_BIT_LEN = 128; // GCM 인증 태그 크기 private final SecretKey secretKey; public AesUtil(@Value("${app.encryption.key}") String base64Key) { byte[] keyBytes = Base64.getDecoder().decode(base64Key); if (keyBytes.length != 32) { throw new IllegalArgumentException("암호화 키는 Base64 인코딩된 32바이트(256bit)여야 합니다."); } this.secretKey = new SecretKeySpec(keyBytes, "AES"); } /** * 평문 → AES-256-GCM 암호화 → Base64 문자열 */ public String encrypt(String plainText) { if (plainText == null || plainText.isEmpty()) return plainText; try { byte[] iv = new byte[IV_LENGTH]; new SecureRandom().nextBytes(iv); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(TAG_BIT_LEN, iv)); byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8")); // IV + 암호문 결합 후 Base64 인코딩 byte[] combined = new byte[IV_LENGTH + encrypted.length]; System.arraycopy(iv, 0, combined, 0, IV_LENGTH); System.arraycopy(encrypted, 0, combined, IV_LENGTH, encrypted.length); return Base64.getEncoder().encodeToString(combined); } catch (Exception e) { throw new RuntimeException("암호화 실패", e); } } /** * Base64 암호문 → AES-256-GCM 복호화 → 평문 */ public String decrypt(String encryptedText) { if (encryptedText == null || encryptedText.isEmpty()) return encryptedText; try { byte[] combined = Base64.getDecoder().decode(encryptedText); byte[] iv = new byte[IV_LENGTH]; byte[] cipherText = new byte[combined.length - IV_LENGTH]; System.arraycopy(combined, 0, iv, 0, IV_LENGTH); System.arraycopy(combined, IV_LENGTH, cipherText, 0, cipherText.length); Cipher cipher = Cipher.getInstance(ALGORITHM); cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(TAG_BIT_LEN, iv)); return new String(cipher.doFinal(cipherText), "UTF-8"); } catch (Exception e) { throw new RuntimeException("복호화 실패", e); } } }