encrypt(String plain, String aad) 와 decrypt(String token, String aad) 를 추가하세요. AAD 는 암호화하지 않지만 무결성 검증에 포함됩니다. 힌트: Cipher.updateAAD(byte[]) 를 init 직후, doFinal 이전에 호출합니다.
public String encrypt(String plain, String aad) {
try {
SecretKey key = keys.get(currentVersion);
byte[] iv = new byte[IV_LEN];
RANDOM.nextBytes(iv);
Cipher cipher = Cipher.getInstance(CIPHER_ALGO);
cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(TAG_BITS, iv));
cipher.updateAAD(aad.getBytes("UTF-8"));
byte[] ct = cipher.doFinal(plain.getBytes("UTF-8"));
byte[] ivAndCt = new byte[iv.length + ct.length];
System.arraycopy(iv, 0, ivAndCt, 0, iv.length);
System.arraycopy(ct, 0, ivAndCt, iv.length, ct.length);
return currentVersion + ":" + Base64.getEncoder().encodeToString(ivAndCt);
} catch (Exception e) {
throw new RuntimeException("암호화 실패", e);
}
}decrypt 도 같은 방식으로 updateAAD 를 호출해야 하며, 암호화할 때와 다른 AAD 를 넘기면 AEADBadTagException 이 발생합니다. AAD 는 보통 테이블명·컬럼명처럼 컨텍스트를 고정하는 값으로 씁니다.
searchHash 를 확장해, 값 앞에 애플리케이션 전역 고정 문자열(pepper) 을 붙인 뒤 해시하도록 바꾸세요. 힌트: HMAC 은 이미 키가 있어 pepper 효과가 있지만, 값 자체를 변형하는 방식도 가능합니다.
private static final String PEPPER = "app-fixed-context-v1";
public String searchHash(String value) {
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(hmacKey);
byte[] out = mac.doFinal((PEPPER + value).getBytes("UTF-8"));
return Base64.getEncoder().encodeToString(out);
} catch (Exception e) {
throw new RuntimeException("해시 실패", e);
}
}HMAC 은 키 자체가 비밀이므로 pepper 없이도 안전하지만, 키 유출 시 이중 방어선이 될 수 있습니다. 다만 PEPPER 를 바꾸면 기존에 저장된 모든 해시가 무효화되므로 신중하게 결정합니다.