실행 방법입니다. 외부 jar 는 필요 없습니다.
cd java-src\extension\13_crypto
C:\project\jdk-21.0.8\bin\javac -encoding UTF-8 *.java
C:\project\jdk-21.0.8\bin\java -Dstdout.encoding=UTF-8 Main # 약 1초Crypto — 암호화·복호화·검색용 해시public String encrypt(String plain) {
SecretKey key = keys.get(currentVersion);
byte[] iv = new byte[IV_LEN];
RANDOM.nextBytes(iv); // IV 는 매번 새로 무작위 생성, 재사용 금지
Cipher cipher = Cipher.getInstance(CIPHER_ALGO);
cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(TAG_BITS, iv));
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);
}decrypt 는 접두어로 키 버전을 읽어 알맞은 키를 고른 뒤 IV 와 암호문을 분리해 복호화합니다. 태그 검증에 실패하면 AEADBadTagException 이 발생해 변조나 잘못된 키를 즉시 알 수 있습니다.
1) 암호화 1회차: v1:M/IJdOxcBftSfTd/EFUXPnJXSdOdxJureibO6OeMrMkC6qUeaS/C3Sc=
1) 암호화 2회차: v1:yHicg0yubBgIz2h+aF1yGybF17q6dOaLFGrBzK9B/+siWlyQtu5R0Dk=
1) 서로 다른가? true
2) 복호화 결과: 8501011234567
3) 변조 감지: 변조되었거나 키가 맞지 않습니다
4) 다른 키 복호화 실패: 변조되었거나 키가 맞지 않습니다
5) HMAC 1회차: zV8waLkgarLLDtow9KQkiJx1XSas9JdGdp4uhcHMTuM=
5) HMAC 2회차: zV8waLkgarLLDtow9KQkiJx1XSas9JdGdp4uhcHMTuM=
5) 동일한가? true
6) 기존(v1) 토큰: v1:M/IJdOxcBftSfTd/EFUXPnJXSdOdxJureibO6OeMrMkC6qUeaS/C3Sc=
6) 재암호화(v2): v2:LOOL9zCTkJlBuPMLUj8I49P15S8MVbszTjfISz2u3V+8jbRCQ9fdK9Q=
6) v1 복호화: 8501011234567
6) v2 복호화: 8501011234567
7) 주민번호 마스킹: 850101-*******
7) 전화번호 마스킹: 010-****-5678
8) 평문 길이(byte): 13
8) 접두어 포함 저장 길이: 59같은 평문(1번)이 두 번 암호화될 때마다 결과가 달라지는 이유는 IV 가 매번 무작위로 바뀌기 때문입니다. 6번은 옛 키(v1)로 저장된 값을 읽어 새 키(v2)로 재암호화하는 키 교체 흐름을 보여줍니다.