Web Crypto API — 해시, 암호화, 서명을 브라우저에서
Web Crypto API는 브라우저에서 암호학적 연산을 수행하는 표준 API입니다. 해시 생성, AES 암호화, RSA 서명 등을 외부 라이브러리 없이 네이티브로 사용할 수 있습니다.
해시 생성 (SHA-256)
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("");
}
랜덤 값 생성
// 암호학적으로 안전한 랜덤 값
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 대칭 암호화
// 키 생성
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); // "비밀 메시지"
키 내보내기/가져오기
// 키 → 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 비대칭 암호화
// 키 쌍 생성
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 — 메시지 인증
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);
}
주의사항
// 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를 사용합니다.
댓글 로딩 중...