๐Ÿ” AES-256-GCM ์™„์ „ ์ •๋ณต: finger-basic ๊ฐœ์ธ์ •๋ณด ์•”ํ˜ธํ™” ์ „์ฒด ํ๋ฆ„ (Encrypt + Decrypt)

์กด์Šค๋…ธ์šฐยท2025๋…„ 12์›” 3์ผ

๊ธฐํƒ€

๋ชฉ๋ก ๋ณด๊ธฐ
12/12

๐Ÿ“Œ ์š”์•ฝ

ํ•ญ๋ชฉ๊ฐ’
์•”ํ˜ธํ™” ์•Œ๊ณ ๋ฆฌ์ฆ˜AES-256-GCM
ํ‚ค ๊ธธ์ด256๋น„ํŠธ (32๋ฐ”์ดํŠธ)
IV ๊ธธ์ด12๋ฐ”์ดํŠธ
์ธ์ฆ ํƒœ๊ทธ(AuthTag)16๋ฐ”์ดํŠธ (128-bit)
์ถœ๋ ฅ ํฌ๋งทBase64(IV + ์•”ํ˜ธ๋ฌธ + AuthTag)
๋ณตํ˜ธํ™” ๊ฐ€๋Šฅ ์กฐ๊ฑด๋น„๋ฐ€ํ‚ค + IV + ์•”ํ˜ธ๋ฌธ + AuthTag

AES-GCM์€ ํ˜„์กด ์ตœ๊ณ  ์ˆ˜์ค€์˜ ์•ˆ์ „์„ฑ์„ ์ œ๊ณตํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ,
finger-basic์˜ ์•”ํ˜ธํ™” ๊ตฌํ˜„์€ ๊ตญ์ œ ํ‘œ์ค€์— ์™„์ „ํžˆ ๋ถ€ํ•ฉํ•ฉ๋‹ˆ๋‹ค.


๐Ÿงฑ ์ „์ฒด ๊ตฌ์กฐ (Encrypt + Decrypt ์š”์•ฝ)

์•”ํ˜ธํ™”(Encrypt)
  โ†“
์ž…๋ ฅ โ†’ IV ์ƒ์„ฑ โ†’ AES-GCM ์•”ํ˜ธํ™” โ†’ AuthTag ์ƒ์„ฑ
  โ†“
[IV + ์•”ํ˜ธ๋ฌธ + AuthTag] ๊ฒฐํ•ฉ
  โ†“
Base64 ๋ฌธ์ž์—ด๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ DB ์ €์žฅ
๋ณตํ˜ธํ™”(Decrypt)
  โ†“
Base64 โ†’ ๋ฐ”์ดํŠธ ๋ฐฐ์—ด ๋ณต์›
  โ†“
์•ž 12๋ฐ”์ดํŠธ = IV ์ถ”์ถœ
๋‚˜๋จธ์ง€ = ์•”ํ˜ธ๋ฌธ + AuthTag
  โ†“
AES-GCM ๋ณตํ˜ธํ™” โ†’ ์›๋ž˜ ๋ฌธ์ž์—ด ๋ฐ˜ํ™˜

1๏ธโƒฃ ์•”ํ˜ธํ™”(Encrypt) ์ƒ์„ธ ํ•ด์„ค

์•„๋ž˜๋Š” encrypt() ๋กœ์ง์„ ํ๋ฆ„๋Œ€๋กœ โ€œ๊ฐœ๋ฐœ์ž ๋ˆˆ๋†’์ดโ€์—์„œ ์„ค๋ช…ํ•œ ๊ฒƒ.


โ‘  ๋นˆ ๊ฐ’ ์ฒ˜๋ฆฌ

if (plainText == null || plainText.isEmpty()) {
  return plainText;
}
  • null โ†’ ์•”ํ˜ธํ™” ๋ถˆํ•„์š”
  • ๋นˆ ๋ฌธ์ž์—ด โ†’ ์•”ํ˜ธํ™”ํ•  ์˜๋ฏธ ์—†์Œ

๋ฏธ๋ฆฌ ๋ฐฉ์–ด ์ฝ”๋“œ(Null Safe)


โ‘ก ๋žœ๋ค IV ์ƒ์„ฑ (12๋ฐ”์ดํŠธ)

byte[] iv = new byte[GCM_IV_LENGTH];
SECURE_RANDOM.nextBytes(iv);
  • 12๋ฐ”์ดํŠธ ๋žœ๋ค ๊ฐ’ ์ƒ์„ฑ (AES-GCM ํ‘œ์ค€)
  • ๋งค๋ฒˆ ๋‹ค๋ฅธ IV โ†’ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋„ ๋‹ค๋ฅธ ์•”ํ˜ธ๋ฌธ ์ƒ์„ฑ

IV = Initial Vector = ์•”ํ˜ธํ™” "์‹œ์ž‘์ "

๐ŸŽฒ ๋งค๋ฒˆ ๋‹ค๋ฅธ ์Šคํƒ€ํŠธ ์ง€์ ์—์„œ ์•”ํ˜ธํ™”๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ์ฃผ์‚ฌ์œ„


โ‘ข GCM ํŒŒ๋ผ๋ฏธํ„ฐ ์„ค์ •

GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH, iv);
  • AuthTag = 128bit (๋ฐ์ดํ„ฐ ๋ณ€์กฐ ๊ฒ€์ฆ์šฉ ์„œ๋ช…)
  • AES-GCM์€ ๋ฐ์ดํ„ฐ๋ฅผ ์•”ํ˜ธํ™” + ๋ณ€์กฐ ๋ฐฉ์ง€๊นŒ์ง€ ์ฑ…์ž„์ง

๐Ÿ“ฆ ํƒ๋ฐฐ ๋ด‰์ธ ์Šคํ‹ฐ์ปค ๊ธฐ๋Šฅ์ด AuthTag


โ‘ฃ ๋น„๋ฐ€ํ‚ค ์ค€๋น„

SecretKeySpec keySpec =
    new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), KEY_ALGORITHM);
  • 32๋ฐ”์ดํŠธ ๋ฌธ์ž์—ด์„ ์‹ค์ œ AES ํ‚ค ๊ฐ์ฒด๋กœ ๋ณ€ํ™˜
  • AES๋Š” ํ‚ค ๊ธธ์ด๊ฐ€ ์ •ํ™•ํžˆ 32๋ฐ”์ดํŠธ์—ฌ์•ผ AES-256๋กœ ๋™์ž‘ํ•จ

๐Ÿ”‘ ์ง„์งœ ์ž๋ฌผ์‡  ์—ด์‡ 


โ‘ค Cipher ์ดˆ๊ธฐํ™”

Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, keySpec, spec);
  • AES/GCM/NoPadding ์ดˆ๊ธฐํ™”
  • key + IV ์กฐํ•ฉ์œผ๋กœ ์•”ํ˜ธํ™” ๊ธฐ๊ณ„ ์ค€๋น„

โ‘ฅ ์•”ํ˜ธํ™” ์‹คํ–‰

byte[] encrypted = cipher.doFinal(plainText.getBytes(UTF_8));

๊ฒฐ๊ณผ = ์•”ํ˜ธ๋ฌธ + AuthTag

AES-GCM์€ ๋‚ด๋ถ€์ ์œผ๋กœ AuthTag(16๋ฐ”์ดํŠธ)๋ฅผ ์•”ํ˜ธ๋ฌธ ๋’ค์— ๋ถ™์—ฌ์„œ ๋ฐ˜ํ™˜ํ•จ.


โ‘ฆ IV + ์•”ํ˜ธ๋ฌธ + AuthTag ๊ฒฐํ•ฉ

byte[] combined = new byte[12 + encrypted.length];
System.arraycopy(iv, 0, combined, 0, 12);
System.arraycopy(encrypted, 0, combined, 12, encrypted.length);

์ตœ์ข… ๊ตฌ์กฐ:

[IV][CipherText + AuthTag]
  • ๋ณตํ˜ธํ™”ํ•˜๋ ค๋ฉด ๋ฐ˜๋“œ์‹œ IV๊ฐ€ ํ•„์š”
  • ๊ทธ๋ž˜์„œ โ€œ์•ž์— ๋ถ™์—ฌ์„œโ€ DB์— ํ•จ๊ป˜ ์ €์žฅํ•˜๋Š” ๋ฐฉ์‹์ด ํ‘œ์ค€

โ‘ง Base64๋กœ ๋ณ€ํ™˜ โ†’ DB ์ €์žฅ

return Base64.getEncoder().encodeToString(combined);
  • ๋ฐ”์ด๋„ˆ๋ฆฌ๋Š” DB์— ์ €์žฅ ๋ถˆ๊ฐ€ํ•  ์ˆ˜ ์žˆ์Œ โ†’ Base64 ์ธ์ฝ”๋”ฉ

๐Ÿ”ฅ Encrypt ํ๋ฆ„ ์š”์•ฝ ๋‹ค์ด์–ด๊ทธ๋žจ

์ž…๋ ฅ โ†’ ๋žœ๋ค IV ์ƒ์„ฑ
       โ†“
AES-256-GCM ์•”ํ˜ธํ™” (AuthTag ํฌํ•จ)
       โ†“
[IV + ์•”ํ˜ธ๋ฌธ + AuthTag] ๊ฒฐํ•ฉ
       โ†“
Base64 ์ธ์ฝ”๋”ฉ โ†’ ์ €์žฅ

2๏ธโƒฃ ๋ณตํ˜ธํ™”(Decrypt) ์ƒ์„ธ ํ•ด์„ค

์•”ํ˜ธํ™” ๊ณผ์ •์ด ์ดํ•ด๋˜๋ฉด ๋ณตํ˜ธํ™”๋Š” ๋งค์šฐ ์ง๊ด€์ ์ž„.


โ‘  Base64 ๋ฌธ์ž์—ด โ†’ ๋ฐ”์ดํŠธ ๋ฐฐ์—ด

byte[] decoded = Base64.getDecoder().decode(cipherText);

โ‘ก ์•ž 12๋ฐ”์ดํŠธ = IV ์ถ”์ถœ

byte[] iv = Arrays.copyOfRange(decoded, 0, 12);

โ‘ข ๋‚˜๋จธ์ง€ = ์•”ํ˜ธ๋ฌธ + AuthTag ์ถ”์ถœ

byte[] encrypted = Arrays.copyOfRange(decoded, 12, decoded.length);

AES-GCM์€ CipherText ๋’ค์— ์ž๋™์œผ๋กœ AuthTag(16๋ฐ”์ดํŠธ)๋ฅผ ํฌํ•จํ•จ.


โ‘ฃ GCM ํŒŒ๋ผ๋ฏธํ„ฐ ๋ณต์›

GCMParameterSpec spec = new GCMParameterSpec(128, iv);

โ‘ค Cipher ์ดˆ๊ธฐํ™” + ๋ณตํ˜ธํ™” ์‹คํ–‰

cipher.init(Cipher.DECRYPT_MODE, keySpec, spec);
byte[] decrypted = cipher.doFinal(encrypted);
  • AuthTag ๊ฒ€์ฆ โ†’ ๋ณ€์กฐ ์‹œ ์˜ˆ์™ธ ๋ฐœ์ƒ
  • ์ •์ƒ ๋ฐ์ดํ„ฐ๋งŒ ๋ณตํ˜ธํ™” ์„ฑ๊ณต

๐Ÿ”ฅ Decrypt ํ๋ฆ„ ์š”์•ฝ ๋‹ค์ด์–ด๊ทธ๋žจ

Base64 ๋””์ฝ”๋“œ
     โ†“
[IV 12๋ฐ”์ดํŠธ][์•”ํ˜ธ๋ฌธ+AuthTag] ๋ถ„๋ฆฌ
     โ†“
AES-GCM ๋ณตํ˜ธํ™” (IV + Key)
     โ†“
์›๋ณธ ํ…์ŠคํŠธ ๋ฐ˜ํ™˜

3๏ธโƒฃ ์™œ IV๊ฐ€ ๊ณต๊ฐœ๋˜์–ด๋„ ์•ˆ์ „ํ•œ๊ฐ€?

AES ๋ณด์•ˆ์˜ ํ•ต์‹ฌ์€ "๋น„๋ฐ€ํ‚ค"์ด์ง€ IV๊ฐ€ ์•„๋‹ˆ๋‹ค.

์š”์†Œ์—ญํ• ๊ณต๊ฐœ ์—ฌ๋ถ€
๋น„๋ฐ€ํ‚ค๋ณตํ˜ธํ™” ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ๊ฒฐ์ •โŒ ๋น„๊ณต๊ฐœ
IV์•”ํ˜ธํ™” ๋ฌด์ž‘์œ„์„ฑ ์ œ๊ณตโœ… ๊ณต๊ฐœ ๊ฐ€๋Šฅ
AuthTag๋ณ€์กฐ ๊ฐ์ง€๊ณต๊ฐœ ๊ฐ€๋Šฅ

์ฆ‰:

  • IV๋ฅผ ์•Œ์•„๋„ ์ ˆ๋Œ€ ๋ณตํ˜ธํ™” ๋ถˆ๊ฐ€
  • IV๋งŒ์œผ๋กœ๋Š” ๊ณต๊ฒฉ์— ์•„๋ฌด ์˜๋ฏธ ์—†์Œ

4๏ธโƒฃ IV ์—†์œผ๋ฉด ์–ผ๋งˆ๋‚˜ ์œ„ํ—˜ํ•œ๊ฐ€?

IV ์—†์ด ์•”ํ˜ธํ™”ํ•˜๋ฉด?

์•”ํ˜ธํ™”("010-1234-5678") โ†’ ํ•ญ์ƒ ๊ฐ™์€ ์•”ํ˜ธ๋ฌธ

์ฆ‰ ํ•ด์ปค๋Š”:

  • โ€œ์ด ๋‘ ์œ ์ €์˜ ๋ฒˆํ˜ธ๋Š” ๊ฐ™๋‹คโ€
  • ์‚ฌ์ „ ๊ณต๊ฒฉ(precomputed table) ๊ฐ€๋Šฅ

์˜ˆ:

์•”ํ˜ธํ™”("010-1234-5678") โ†’ abc123
์•”ํ˜ธ๋ฌธ์ด abc123์ด๋ฉด โ†’ ์ด ๊ฐ’์ด๊ตฌ๋‚˜!

IV ์žˆ์œผ๋ฉด?

์•”ํ˜ธํ™”("010-1234-5678") โ†’ abc123
์•”ํ˜ธํ™”("010-1234-5678") โ†’ lp0aaK
์•”ํ˜ธํ™”("010-1234-5678") โ†’ Qj2mLx

๋™์ผํ•œ ๋ฐ์ดํ„ฐ๊ฐ€ ์™„์ „ํžˆ ๋‹ค๋ฅธ ์•”ํ˜ธ๋ฌธ โ†’ ํŒจํ„ด ๋ถ„์„ ๋ถˆ๊ฐ€๋Šฅ

์ด๊ฒŒ ๋ฐ”๋กœ ๋ณด์•ˆ.


5๏ธโƒฃ AES-GCM์˜ ์žฅ์ 

์žฅ์ ์„ค๋ช…
๋ณ€์กฐ ๋ฐฉ์ง€AuthTag ์ž๋™ ๊ฒ€์ฆ
Padding Attack ๋ฉด์—ญCBC ์ทจ์•ฝ์  ์ œ๊ฑฐ
๊ณ ์†์ŠคํŠธ๋ฆฌ๋ฐ ์ฒ˜๋ฆฌ ์„ฑ๋Šฅ ์šฐ์ˆ˜
TLS 1.3 ๊ธฐ๋ฐ˜HTTPS๊ฐ€ ๋‚ด๋ถ€์ ์œผ๋กœ ์“ฐ๋Š” ๋ชจ๋“œ

์ฆ‰, ํ˜„๋Œ€ ์•”ํ˜ธํ™”์˜ ํ‘œ์ค€


6๏ธโƒฃ ์ตœ์ข… ์ •๋ฆฌ

๐Ÿ” AES-256-GCM + ๋žœ๋ค IV + AuthTag ๊ตฌ์กฐ๋Š”

ํ˜„์žฌ ์‹ค์„œ๋น„์Šค์—์„œ ๊ฐ€์žฅ ๊ถŒ์žฅ๋˜๋Š” ์•”ํ˜ธํ™” ๋ฐฉ์‹

profile
์–ด์ œ์˜ ๋‚˜๋ณด๋‹ค ํ•œ๊ฑธ์Œ ๋”

0๊ฐœ์˜ ๋Œ“๊ธ€