Web Crypto API는 브라우저에서 암호학적 연산을 수행하는 표준 API입니다. 해시 생성, AES 암호화, RSA 서명 등을 외부 라이브러리 없이 네이티브로 사용할 수 있습니다.

해시 생성 (SHA-256)

JS
async function sha256(message) {
  const encoder = new TextEncoder();
  const data = encoder.encode(message);
  const hash = await crypto.subtle.digest("SHA-256", data);

  // ArrayBuffer → hex 문자열 변환
  return Array.from(new Uint8Array(hash))
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
}

console.log(await sha256("hello")); // "2cf24dba..."

// 파일 해시 (무결성 검증)
async function fileHash(file) {
  const buffer = await file.arrayBuffer();
  const hash = await crypto.subtle.digest("SHA-256", buffer);
  return Array.from(new Uint8Array(hash))
    .map((b) => b.toString(16).padStart(2, "0"))
    .join("");
}

랜덤 값 생성

JS
// 암호학적으로 안전한 랜덤 값
const randomBytes = crypto.getRandomValues(new Uint8Array(32));

// UUID 생성
const uuid = crypto.randomUUID();
console.log(uuid); // "550e8400-e29b-41d4-a716-446655440000"

// 랜덤 토큰 생성
function generateToken(length = 32) {
  const bytes = crypto.getRandomValues(new Uint8Array(length));
  return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
}

AES 대칭 암호화

JS
// 키 생성
async function generateAESKey() {
  return crypto.subtle.generateKey(
    { name: "AES-GCM", length: 256 },
    true,      // extractable
    ["encrypt", "decrypt"]
  );
}

// 암호화
async function encrypt(key, plaintext) {
  const encoder = new TextEncoder();
  const iv = crypto.getRandomValues(new Uint8Array(12)); // 초기화 벡터
  const encrypted = await crypto.subtle.encrypt(
    { name: "AES-GCM", iv },
    key,
    encoder.encode(plaintext)
  );
  return { iv, encrypted };
}

// 복호화
async function decrypt(key, iv, encrypted) {
  const decrypted = await crypto.subtle.decrypt(
    { name: "AES-GCM", iv },
    key,
    encrypted
  );
  return new TextDecoder().decode(decrypted);
}

// 사용
const key = await generateAESKey();
const { iv, encrypted } = await encrypt(key, "비밀 메시지");
const decrypted = await decrypt(key, iv, encrypted);
console.log(decrypted); // "비밀 메시지"

키 내보내기/가져오기

JS
// 키 → JSON Web Key (JWK)
const jwk = await crypto.subtle.exportKey("jwk", key);
console.log(jwk); // { kty: "oct", k: "...", alg: "A256GCM", ... }

// JWK → 키
const importedKey = await crypto.subtle.importKey(
  "jwk", jwk,
  { name: "AES-GCM" },
  true,
  ["encrypt", "decrypt"]
);

// 비밀번호 기반 키 유도 (PBKDF2)
async function deriveKey(password, salt) {
  const encoder = new TextEncoder();
  const keyMaterial = await crypto.subtle.importKey(
    "raw",
    encoder.encode(password),
    "PBKDF2",
    false,
    ["deriveKey"]
  );

  return crypto.subtle.deriveKey(
    { name: "PBKDF2", salt, iterations: 100000, hash: "SHA-256" },
    keyMaterial,
    { name: "AES-GCM", length: 256 },
    true,
    ["encrypt", "decrypt"]
  );
}

RSA 비대칭 암호화

JS
// 키 쌍 생성
const keyPair = await crypto.subtle.generateKey(
  {
    name: "RSA-OAEP",
    modulusLength: 2048,
    publicExponent: new Uint8Array([1, 0, 1]),
    hash: "SHA-256",
  },
  true,
  ["encrypt", "decrypt"]
);

// 공개키로 암호화
const encrypted = await crypto.subtle.encrypt(
  { name: "RSA-OAEP" },
  keyPair.publicKey,
  new TextEncoder().encode("비밀 메시지")
);

// 개인키로 복호화
const decrypted = await crypto.subtle.decrypt(
  { name: "RSA-OAEP" },
  keyPair.privateKey,
  encrypted
);

HMAC — 메시지 인증

JS
async function createHMAC(key, message) {
  const cryptoKey = await crypto.subtle.importKey(
    "raw",
    new TextEncoder().encode(key),
    { name: "HMAC", hash: "SHA-256" },
    false,
    ["sign", "verify"]
  );

  const signature = await crypto.subtle.sign(
    "HMAC",
    cryptoKey,
    new TextEncoder().encode(message)
  );

  return new Uint8Array(signature);
}

주의사항

JS
// 1. HTTPS 필수 (localhost 제외)
// crypto.subtle은 Secure Context에서만 사용 가능

// 2. 키 저장 — IndexedDB에 CryptoKey 직접 저장 가능
// localStorage에는 저장 불가 (문자열만 가능)

// 3. Web Crypto는 저수준 API
// 실무에서는 libsodium-wrappers 같은 래퍼 라이브러리 고려

**기억하기 **: Web Crypto API는 브라우저 내장 암호화 라이브러리입니다. crypto.subtle.digest로 해시, generateKey + encrypt/decrypt로 AES 암호화, sign/verify로 서명을 수행합니다. HTTPS 환경에서만 동작하고, 암호학적으로 안전한 랜덤은 crypto.getRandomValues를 사용합니다.

댓글 로딩 중...