๐Ÿ“– TIL - ํƒ๋ฐฐ ๋ณด๋‚ด๋“ฏ ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”ํ•˜๊ธฐ: AES + RSA = ๋ฌด์ ์กฐํ•ฉ!

์Š˜ยท2025๋…„ 9์›” 13์ผ

๐Ÿ“– TIL

๋ชฉ๋ก ๋ณด๊ธฐ
89/90

๐Ÿ“Œ ๋ฌธ์ œ ์„ค๋ช…

์ƒํ™ฉ

  • ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ๋ฏผ๊ฐํ•œ ๊ฐœ์ธ์ •๋ณด๋ฅผ ์„œ๋ฒ„๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์ „์†กํ•ด์•ผ ํ•จ
  • ๊ฐœ์ธ์ •๋ณด(์ฃผ๋ฏผ๋ฒˆํ˜ธ, ์นด๋“œ๋ฒˆํ˜ธ ๋“ฑ)๋Š” ํ•ดํ‚น ์œ„ํ—˜์ด ๋†’์•„์„œ ๋ฐ˜๋“œ์‹œ ์•”ํ˜ธํ™” ํ•„์š”
  • ๋‹จ์ˆœํ•œ ์•”ํ˜ธํ™” ๋ฐฉ์‹์œผ๋กœ๋Š” ๋ณด์•ˆ์ด ์•ฝํ•˜๊ฑฐ๋‚˜ ์„ฑ๋Šฅ์ด ๋А๋ฆผ

์š”๊ตฌ์‚ฌํ•ญ

  • ๋ณด์•ˆ: ํ•ด์ปค๊ฐ€ ์ค‘๊ฐ„์— ๊ฐ€๋กœ์ฑ„๋„ ํ•ด๋… ๋ถˆ๊ฐ€๋Šฅํ•ด์•ผ ํ•จ
  • ์„ฑ๋Šฅ: ์•”ํ˜ธํ™” ์†๋„๊ฐ€ ๋นจ๋ผ์•ผ ํ•จ (์‚ฌ์šฉ์ž ๊ฒฝํ—˜)
  • ์•ˆ์ „์„ฑ: ์•”ํ˜ธํ™” ํ‚ค ์ „๋‹ฌ ๊ณผ์ •๋„ ์•ˆ์ „ํ•ด์•ผ ํ•จ

๐Ÿค” ๋ฌธ์ œ ์ ‘๊ทผ

๊ธฐ์กด ์•”ํ˜ธํ™” ๋ฐฉ์‹๋“ค์˜ ๋ฌธ์ œ์ 

1๏ธโƒฃ ๋Œ€์นญํ‚ค(AES)๋งŒ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ:

  • โœ… ์žฅ์ : ์†๋„ ๋น ๋ฆ„ (๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ ์ข‹์Œ)
  • โŒ ๋ฌธ์ œ: ํด๋ผ์ด์–ธํŠธ์™€ ์„œ๋ฒ„๊ฐ€ ๊ฐ™์€ ํ‚ค๋ฅผ ๊ณต์œ ํ•ด์•ผ ํ•˜๋Š”๋ฐ, ์ด ํ‚ค๋ฅผ ์–ด๋–ป๊ฒŒ ์•ˆ์ „ํ•˜๊ฒŒ ์ „๋‹ฌํ•  ๊ฒƒ์ธ๊ฐ€?

2๏ธโƒฃ ๋น„๋Œ€์นญํ‚ค(RSA)๋งŒ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ:

  • โœ… ์žฅ์ : ํ‚ค ์ „๋‹ฌ ๋ฌธ์ œ ํ•ด๊ฒฐ (๊ณต๊ฐœํ‚ค๋Š” ๋ˆ„๊ฐ€ ๋ด๋„ OK)
  • โŒ ๋ฌธ์ œ: ์†๋„ ๋А๋ฆผ (ํฐ ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”์‹œ ์„ฑ๋Šฅ ์ €ํ•˜)

3๏ธโƒฃ ๊ทธ๋ ‡๋‹ค๋ฉด?
โ†’ ๋‘ ๋ฐฉ์‹์˜ ์žฅ์ ๋งŒ ํ•ฉ์น˜์ž! = ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์•”ํ˜ธํ™”


๐Ÿ’ก ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

๐Ÿ” ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์•”ํ˜ธํ™” ๋ฐฉ์‹

= ๋Œ€์นญํ‚ค๋กœ ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™” + ๋น„๋Œ€์นญํ‚ค๋กœ ๋Œ€์นญํ‚ค ์•”ํ˜ธํ™”

๐Ÿ“ฆ ํƒ๋ฐฐ ๋น„์œ ๋กœ ์ดํ•ดํ•˜๊ธฐ

๐ŸŽ ํฐ ๋ณด๋ฌผ์ƒ์ž(๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ) โ†’ ๐Ÿ“ฆ ํƒ๋ฐฐ์ƒ์ž(AES)๋กœ ํฌ์žฅ (๋น ๋ฆ„!)
๐Ÿ”‘ ํƒ๋ฐฐ์ƒ์ž ์—ด์‡ (AESํ‚ค) โ†’ ๐Ÿ”’ ํŠน์ˆ˜๊ธˆ๊ณ (RSA)๋กœ ํฌ์žฅ (์•ˆ์ „!)

๊ฒฐ๊ณผ: ๋น ๋ฅด๋ฉด์„œ๋„ ์•ˆ์ „ํ•œ ์ „๋‹ฌ! โœจ

๐Ÿ”„ ์‹ค์ œ ์ฒ˜๋ฆฌ ๊ณผ์ •

  1. ํด๋ผ์ด์–ธํŠธ: ์ƒˆ๋กœ์šด AES ํ‚ค ์ƒ์„ฑ ("randomKey123")
  2. ํด๋ผ์ด์–ธํŠธ: AESํ‚ค๋กœ ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”
    • "๊ฐœ์ธ์ •๋ณด๋ฐ์ดํ„ฐ" + "randomKey123" โ†’ "encrypted_data_xyz"
  3. ํด๋ผ์ด์–ธํŠธ: RSA ๊ณต๊ฐœํ‚ค๋กœ AESํ‚ค ์•”ํ˜ธํ™”
    • "randomKey123" + RSA๊ณต๊ฐœํ‚ค โ†’ "encrypted_key_abc"
  4. ์„œ๋ฒ„ ์ „์†ก:
    • "encrypted_data_xyz" (AES๋กœ ์•”ํ˜ธํ™”๋œ ๋ฐ์ดํ„ฐ)
    • "encrypted_key_abc" (RSA๋กœ ์•”ํ˜ธํ™”๋œ AESํ‚ค)
  5. ์„œ๋ฒ„: RSA ๊ฐœ์ธํ‚ค๋กœ AESํ‚ค ๋ณต์›
    • RSA๊ฐœ์ธํ‚ค + "encrypted_key_abc" โ†’ "randomKey123"
  6. ์„œ๋ฒ„: AESํ‚ค๋กœ ์›๋ณธ ๋ฐ์ดํ„ฐ ๋ณต์›
    • "randomKey123" + "encrypted_data_xyz" โ†’ "๊ฐœ์ธ์ •๋ณด๋ฐ์ดํ„ฐ"

๐Ÿ” ์ฝ”๋“œ ์„ค๋ช…

API ํ˜ธ์ถœ ํ”Œ๋กœ์šฐ ์˜ˆ์‹œ

// 1๋‹จ๊ณ„: ์„œ๋ฒ„์— ์•”ํ˜ธํ™” ํ‚ค ์š”์ฒญ
const response = await fetch('/api/crypto/generate-keys', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    userId: currentUser.id,
    purpose: 'data-encryption'
  })
});
const { publicKey } = await response.json();
// 2๋‹จ๊ณ„: ํด๋ผ์ด์–ธํŠธ์—์„œ ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์•”ํ˜ธํ™”
// AES ํ‚ค ์ƒ์„ฑ (๋งค๋ฒˆ ์ƒˆ๋กœ์šด ๋žœ๋ค ํ‚ค!)
const aesKey = await crypto.subtle.generateKey(
  { name: 'AES-GCM', length: 256 },
  true, ['encrypt', 'decrypt']
);

// ๋ฏผ๊ฐํ•œ ๋ฐ์ดํ„ฐ AES ์•”ํ˜ธํ™”
const iv = crypto.getRandomValues(new Uint8Array(12));
const encryptedData = await crypto.subtle.encrypt(
  { name: 'AES-GCM', iv: iv },
  aesKey, 
  new TextEncoder().encode(sensitiveData)
);

// AES ํ‚ค๋ฅผ RSA ๊ณต๊ฐœํ‚ค๋กœ ์•”ํ˜ธํ™”
const rsaKey = await importRsaPublicKey(publicKey);
const encryptedAesKey = await crypto.subtle.encrypt(
  { name: 'RSA-OAEP' },
  rsaKey,
  await crypto.subtle.exportKey('raw', aesKey)
);
// 3๋‹จ๊ณ„: ์•”ํ˜ธํ™”๋œ ๋ฐ์ดํ„ฐ ์ „์†ก
await fetch('/api/secure-data/save', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    encryptedData: arrayBufferToBase64(encryptedData),
    encryptedKey: arrayBufferToBase64(encryptedAesKey),
    iv: arrayBufferToBase64(iv)
  })
});

โœจ ์ƒˆ๋กญ๊ฒŒ ๋ฐฐ์šด ์ 

๐Ÿ”‘ ํ•ต์‹ฌ ๊ฐœ๋…๋“ค

์šฉ์–ด์‰ฌ์šด ์„ค๋ช…์—ญํ• 
๋Œ€์นญํ‚ค (AES)๊ฐ™์€ ์—ด์‡ ๋กœ ์ž ๊ทธ๊ณ  ์—ด๊ธฐ๋น ๋ฅธ ๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”
๋น„๋Œ€์นญํ‚ค (RSA)๋‹ค๋ฅธ ์—ด์‡ ๋กœ ์ž ๊ทธ๊ณ  ์—ด๊ธฐ์•ˆ์ „ํ•œ ํ‚ค ์ „๋‹ฌ
๊ณต๊ฐœํ‚ค๋ชจ๋“  ์‚ฌ๋žŒ์ด ์•Œ์•„๋„ ๋˜๋Š” ํ‚ค๋ฐ์ดํ„ฐ ์•”ํ˜ธํ™”์šฉ
๊ฐœ์ธํ‚ค์ ˆ๋Œ€ ๋น„๋ฐ€์ธ ํ‚ค๋ฐ์ดํ„ฐ ๋ณตํ˜ธํ™”์šฉ
ํ•˜์ด๋ธŒ๋ฆฌ๋“œ๋‘ ๋ฐฉ์‹ ์กฐํ•ฉ๋น ๋ฆ„ + ์•ˆ์ „ํ•จ
IV์•”ํ˜ธํ™” ๋ณด์กฐ ์ •๋ณด๊ฐ™์€ ๋ฐ์ดํ„ฐ๋„ ๋‹ค๋ฅด๊ฒŒ ์•”ํ˜ธํ™”

๐Ÿง  ์ค‘์š”ํ•œ ๊นจ๋‹ฌ์Œ๋“ค

  1. ๋ณด์•ˆ != ๋ณต์žกํ•จ: ๋ณต์žกํ•œ ๊ฒŒ ํ•ญ์ƒ ์ข‹์€ ๊ฑด ์•„๋‹ˆ์•ผ. ์ ์žฌ์ ์†Œ์— ๋งž๋Š” ๋ฐฉ์‹ ์„ ํƒ์ด ์ค‘์š”!

  2. ํ‚ค๋Š” ๋งค๋ฒˆ ์ƒˆ๋กœ ๋งŒ๋“ ๋‹ค:

    • AESํ‚ค๋Š” ๋งค ์š”์ฒญ๋งˆ๋‹ค ์ƒˆ๋กœ ์ƒ์„ฑ (์ผํšŒ์šฉ!)
    • RSAํ‚ค๋Š” ์‚ฌ์šฉ์ž๋ณ„๋กœ ๊ด€๋ฆฌํ•˜์ง€๋งŒ ์ •๊ธฐ์  ๊ฐฑ์‹ 
  3. ํ”„๋ก ํŠธ vs ๋ฐฑ์—”๋“œ ์—ญํ•  ๋ถ„๋‹ด:

    • ํ”„๋ก ํŠธ: ๋™์ผํ•œ ์•”ํ˜ธํ™” ์š”์ฒญ
    • ๋ฐฑ์—”๋“œ: ๋งค๋ฒˆ ์ƒˆ๋กœ์šด ํ‚ค ์ƒ์„ฑํ•ด์„œ ์‘๋‹ต
  4. ํƒ๋ฐฐ ๋น„์œ ๊ฐ€ ์ฐฐ๋–ก: ํฐ ๋ฌผ๊ฑด(๋ฐ์ดํ„ฐ)์€ ์ผ๋ฐ˜ํƒ๋ฐฐ(AES), ์ž‘์€ ์—ด์‡ (ํ‚ค)๋งŒ ํŠน์ˆ˜๋ฐฐ์†ก(RSA)


๐Ÿš€ ์ด๋Ÿฐ ์ ์ด ํšจ์œจ์ ์ด์—์š”

โšก ์„ฑ๋Šฅ ์ตœ์ ํ™”

  • AES: ๋Œ€์šฉ๋Ÿ‰ ๋ฐ์ดํ„ฐ๋„ ๋น ๋ฅด๊ฒŒ ์•”ํ˜ธํ™” (๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„)
  • RSA: ์ž‘์€ AESํ‚ค๋งŒ ์•”ํ˜ธํ™”ํ•ด์„œ ์†๋„ ๋ถ€๋‹ด ์ตœ์†Œํ™”
  • ์ „์ฒด์ ์œผ๋กœ: ๋น ๋ฅธ ์†๋„ + ๊ฐ•ํ•œ ๋ณด์•ˆ ๋™์‹œ ๋‹ฌ์„ฑ!

๐Ÿ›ก๏ธ ๋ณด์•ˆ ๊ฐ•ํ™”

  • ํ‚ค ๋…ธ์ถœ ์ตœ์†Œํ™”: AESํ‚ค๊ฐ€ ๋งค๋ฒˆ ๋‹ฌ๋ผ์„œ ํ•˜๋‚˜ ๋šซ๋ ค๋„ ๋‹ค๋ฅธ ๋ฐ์ดํ„ฐ๋Š” ์•ˆ์ „
  • ์ด์ค‘ ๋ณด์•ˆ: AES + RSA ๋‘ ๊ฒน์œผ๋กœ ์•”ํ˜ธํ™”
  • ํ‚ค ์ „๋‹ฌ ์•ˆ์ „: ๊ณต๊ฐœํ‚ค๋Š” ๋ˆ„๊ฐ€ ๋ด๋„ OK, ๊ฐœ์ธํ‚ค๋Š” ์„œ๋ฒ„์—์„œ๋งŒ ๊ด€๋ฆฌ

๐Ÿ”„ ํ™•์žฅ์„ฑ

  • ์‚ฌ์šฉ์ž๋ณ„ ๊ด€๋ฆฌ: ๊ฐ ์‚ฌ์šฉ์ž๋งˆ๋‹ค ๋‹ค๋ฅธ RSAํ‚ค ์‚ฌ์šฉ ๊ฐ€๋Šฅ
  • ์„ธ์…˜๋ณ„ ๋ณด์•ˆ: ๊ฐ ์š”์ฒญ๋งˆ๋‹ค ๋…๋ฆฝ์ ์ธ ์•”ํ˜ธํ™”
  • ํ‘œ์ค€ ํ˜ธํ™˜: ์›น ํ‘œ์ค€ ์•”ํ˜ธํ™” ๋ฐฉ์‹์ด๋ผ ๋‹ค๋ฅธ ์‹œ์Šคํ…œ๊ณผ ํ˜ธํ™˜์„ฑ ์ข‹์Œ

โญ๏ธ ์ฐธ๊ณ  ์ž๋ฃŒ

๐Ÿ”— ๊ด€๋ จ ๊ธฐ์ˆ  ์Šคํƒ

  • Web Crypto API: ๋ธŒ๋ผ์šฐ์ € ๋‚ด์žฅ ์•”ํ˜ธํ™” ๊ธฐ๋Šฅ
  • AES-GCM: ๋ฌด๊ฒฐ์„ฑ ๊ฒ€์ฆ๊นŒ์ง€ ์ง€์›ํ•˜๋Š” ๋Œ€์นญํ‚ค ์•”ํ˜ธํ™”
  • RSA-OAEP: ํ˜„๋Œ€ ํ‘œ์ค€ ๋น„๋Œ€์นญํ‚ค ์•”ํ˜ธํ™” (ํŒจ๋”ฉ ํฌํ•จ)
  • Base64 ์ธ์ฝ”๋”ฉ: ๋ฐ”์ด๋„ˆ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ํ…์ŠคํŠธ๋กœ ์•ˆ์ „ํ•˜๊ฒŒ ์ „์†ก

๐Ÿ’ญ ์ถ”๊ฐ€ ํ•™์Šต ํ‚ค์›Œ๋“œ

  • crypto.subtle API ์ƒ์„ธ ์‚ฌ์šฉ๋ฒ•
  • ์•”ํ˜ธํ™” ํ‚ค ์ƒ๋ช…์ฃผ๊ธฐ ๊ด€๋ฆฌ
  • HTTPS vs ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋ ˆ๋ฒจ ์•”ํ˜ธํ™” ์ฐจ์ด์ 
  • ๋ธŒ๋ผ์šฐ์ €๋ณ„ Web Crypto API ์ง€์› ํ˜„ํ™ฉ

๐Ÿ› ๏ธ ์œ ์šฉํ•œ ์œ ํ‹ธ๋ฆฌํ‹ฐ ํ•จ์ˆ˜๋“ค

// ArrayBuffer๋ฅผ Base64๋กœ ๋ณ€ํ™˜
function arrayBufferToBase64(buffer) {
  const bytes = new Uint8Array(buffer);
  return btoa(String.fromCharCode(...bytes));
}

// RSA ๊ณต๊ฐœํ‚ค Import
async function importRsaPublicKey(pemKey) {
  const keyData = pemToArrayBuffer(pemKey);
  return await crypto.subtle.importKey(
    'spki', keyData, 
    { name: 'RSA-OAEP', hash: 'SHA-256' },
    false, ['encrypt']
  );
}

๐ŸŽฏ ํ•œ ์ค„ ์š”์•ฝ

"ํฐ ๋ฐ์ดํ„ฐ๋Š” ๋น ๋ฅธ AES๋กœ, ์ž‘์€ ํ‚ค๋Š” ์•ˆ์ „ํ•œ RSA๋กœ!" - ์ด๊ฒŒ ํ•˜์ด๋ธŒ๋ฆฌ๋“œ ์•”ํ˜ธํ™”์˜ ํ•ต์‹ฌ! ๐Ÿ”โœจ

๐ŸŒŸ ์‹ค๋ฌด์—์„œ ํ™œ์šฉํ•  ๋•Œ

  • ๊ธˆ์œต ๋ฐ์ดํ„ฐ, ๊ฐœ์ธ์ •๋ณด, ๋ฏผ๊ฐํ•œ ๋ฌธ์„œ ๋“ฑ ๋ชจ๋“  ์ค‘์š” ๋ฐ์ดํ„ฐ ์ „์†ก์‹œ ํ™œ์šฉ ๊ฐ€๋Šฅ
  • ํ”„๋ก ํŠธ์—”๋“œ์—์„œ๋„ ์ถฉ๋ถ„ํžˆ ๊ตฌํ˜„ ๊ฐ€๋Šฅํ•œ ํ˜„์‹ค์ ์ธ ๋ณด์•ˆ ๋ฐฉ๋ฒ•
  • ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์„ ํ•ด์น˜์ง€ ์•Š์œผ๋ฉด์„œ๋„ ๋†’์€ ์ˆ˜์ค€์˜ ๋ณด์•ˆ ์ œ๊ณต
profile
์ฃผ๋‹ˆ์–ด ํ”„๋ก ํŠธ์—”๋“œ ์„ฑ์žฅ๊ธฐ ๊ธฐ๋ก๊ธฐ๋ก

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