เอกสารการเข้ารหัส

เรียนรู้วิธีการทำงานของการเข้ารหัส AES-256-GCM ของเรา เอกสารฉบับสมบูรณ์พร้อมตัวอย่างโค้ดสำหรับการใช้การเข้ารหัสฝั่งไคลเอนต์

วิธีการทำงาน

การรับคีย์

รหัสผ่านของคุณใช้เพื่อรับคีย์ AES 256 บิตโดยใช้ PBKDF2 กับ SHA-256 และ 600,000 รอบ สิ่งนี้ทำให้การโจมตีแบบ brute-force มีค่าใช้จ่ายในการคำนวณสูง

ค่าสุ่ม

Salt แบบสุ่ม 128 บิตและ IV (Initialization Vector) 96 บิตถูกสร้างขึ้นโดยใช้การสร้างตัวเลขสุ่มที่ปลอดภัยทางเข้ารหัสสำหรับการเข้ารหัสแต่ละครั้ง

การเข้ารหัส AES-256-GCM

ข้อมูลถูกเข้ารหัสโดยใช้ AES-256-GCM (Galois/Counter Mode) ซึ่งให้ทั้งความลับและการตรวจสอบความถูกต้อง

รูปแบบผลลัพธ์

ผลลัพธ์ถูกจัดรูปแบบเป็น v1:salt:iv:ciphertext โดยที่ส่วนประกอบทั้งหมดถูกเข้ารหัส base64

ฟังก์ชันการเข้ารหัส

รับคีย์

รับคีย์ AES จากรหัสผ่าน
ใช้ PBKDF2 กับ SHA-256 และ 600,000 รอบเพื่อรับคีย์ AES-GCM 256 บิตจากรหัสผ่าน
async function deriveAesGcmKey(password, saltBytes) {
    const te = new TextEncoder();
    const baseKey = await crypto.subtle.importKey(
        'raw',
        te.encode(password),
        { name: 'PBKDF2' },
        false,
        ['deriveKey']
    );

    return crypto.subtle.deriveKey(
        {
            name: 'PBKDF2',
            hash: 'SHA-256',
            salt: saltBytes,
            iterations: 600000, // OWASP 2025 recommended minimum
        },
        baseKey,
        { name: 'AES-GCM', length: 256 },
        false,
        ['encrypt', 'decrypt']
    );
}

ตัวช่วย

ฟังก์ชันตัวช่วย
ยูทิลิตี้การเข้ารหัสและถอดรหัส Base64 สำหรับอาร์เรย์ไบต์
function bytesToBase64(bytes) {
    let binary = '';
    const len = bytes.byteLength;
    for (let i = 0; i < len; i++) {
        binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
}

function base64ToBytes(base64) {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
        bytes[i] = binary.charCodeAt(i);
    }
    return bytes;
}

เข้ารหัส

เข้ารหัสข้อมูล
เข้ารหัสข้อความธรรมดาโดยใช้ AES-256-GCM พร้อม salt และ IV แบบสุ่ม ส่งคืนเพย์โหลดที่จัดรูปแบบแล้ว
/**
 * Build combined key from password and salt key
 * Format: "password:saltKey"
 */
function buildCombinedKey(password, saltKey) {
    if (saltKey) {
        return password + ':' + saltKey;
    }
    return password;
}

async function encrypt(plainText, password, saltKey = '') {
    const te = new TextEncoder();
    const salt = crypto.getRandomValues(new Uint8Array(16));
    const iv = crypto.getRandomValues(new Uint8Array(12));
    const combinedKey = buildCombinedKey(password, saltKey);
    const key = await deriveAesGcmKey(combinedKey, salt);

    const cipherBuf = await crypto.subtle.encrypt(
        { name: 'AES-GCM', iv },
        key,
        te.encode(plainText)
    );

    return [
        'v1',
        bytesToBase64(salt),
        bytesToBase64(iv),
        bytesToBase64(new Uint8Array(cipherBuf)),
    ].join(':');
}

ถอดรหัส

ถอดรหัสข้อมูล
ถอดรหัสเพย์โหลด v1 กลับเป็นข้อความธรรมดาโดยใช้รหัสผ่านเดิม
/**
 * Build combined key from password and salt key
 * Format: "password:saltKey"
 */
function buildCombinedKey(password, saltKey) {
    if (saltKey) {
        return password + ':' + saltKey;
    }
    return password;
}

async function decrypt(payload, password, saltKey = '') {
    const td = new TextDecoder();
    const [version, saltB64, ivB64, cipherB64] = payload.split(':');

    if (version !== 'v1') {
        throw new Error('Unsupported payload version');
    }

    const salt = base64ToBytes(saltB64);
    const iv = base64ToBytes(ivB64);
    const ciphertext = base64ToBytes(cipherB64);
    const combinedKey = buildCombinedKey(password, saltKey);
    const key = await deriveAesGcmKey(combinedKey, salt);

    const plainBuf = await crypto.subtle.decrypt(
        { name: 'AES-GCM', iv },
        key,
        ciphertext
    );

    return td.decode(plainBuf);
}

ตัวอย่างที่สมบูรณ์

โค้ดพร้อมใช้งาน
คัดลอกและวางตัวอย่างที่สมบูรณ์นี้ลงในคอนโซลเบราว์เซอร์หรือไฟล์ JavaScript ของคุณ
/**
 * AES-256-GCM Encryption/Decryption Example
 * Encrypts "Hello World" and decrypts it back
 */

// Salt key (RK) - used to strengthen password-based encryption
const SALT_KEY = '9x=1KO2tUFw#G:ARZd>Ff)s(^H+DWY4MpgJ:Cp_pCUU|og$>6a.bS.;ij9Wnw';

// Helper functions
function bytesToBase64(bytes) {
    let binary = '';
    const len = bytes.byteLength;
    for (let i = 0; i < len; i++) {
        binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
}

function base64ToBytes(base64) {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
        bytes[i] = binary.charCodeAt(i);
    }
    return bytes;
}

/**
 * Build combined key from password and salt key
 * Format: "password:saltKey"
 */
function buildCombinedKey(password, saltKey) {
    if (saltKey) {
        return password + ':' + saltKey;
    }
    return password;
}

/**
 * Version configuration for encryption payloads
 * v1: 150,000 iterations (legacy)
 * v2: 600,000 iterations (current, OWASP 2025 recommended)
 */
const VERSION_CONFIG = {
    v1: { iterations: 150000 },
    v2: { iterations: 600000 },
};
const CURRENT_VERSION = 'v2';

// Derive AES-256 key from password using PBKDF2
async function deriveAesGcmKey(password, saltBytes, iterations) {
    const te = new TextEncoder();
    const baseKey = await crypto.subtle.importKey(
        'raw',
        te.encode(password),
        { name: 'PBKDF2' },
        false,
        ['deriveKey']
    );

    return crypto.subtle.deriveKey(
        {
            name: 'PBKDF2',
            hash: 'SHA-256',
            salt: saltBytes,
            iterations: iterations,
        },
        baseKey,
        { name: 'AES-GCM', length: 256 },
        false,
        ['encrypt', 'decrypt']
    );
}

// Encrypt function (uses v2 with 600,000 iterations)
async function encrypt(plainText, password, saltKey = '') {
    const te = new TextEncoder();
    const salt = crypto.getRandomValues(new Uint8Array(16));
    const iv = crypto.getRandomValues(new Uint8Array(12));
    const combinedKey = buildCombinedKey(password, saltKey);
    const iterations = VERSION_CONFIG[CURRENT_VERSION].iterations;
    const key = await deriveAesGcmKey(combinedKey, salt, iterations);

    const cipherBuf = await crypto.subtle.encrypt(
        { name: 'AES-GCM', iv },
        key,
        te.encode(plainText)
    );

    return [
        CURRENT_VERSION,
        bytesToBase64(salt),
        bytesToBase64(iv),
        bytesToBase64(new Uint8Array(cipherBuf)),
    ].join(':');
}

// Decrypt function (supports both v1 and v2)
async function decrypt(payload, password, saltKey = '') {
    const td = new TextDecoder();
    const [version, saltB64, ivB64, cipherB64] = payload.split(':');

    const versionConfig = VERSION_CONFIG[version];
    if (!versionConfig) {
        throw new Error('Unsupported payload version: ' + version);
    }

    const salt = base64ToBytes(saltB64);
    const iv = base64ToBytes(ivB64);
    const ciphertext = base64ToBytes(cipherB64);
    const combinedKey = buildCombinedKey(password, saltKey);
    const key = await deriveAesGcmKey(combinedKey, salt, versionConfig.iterations);

    const plainBuf = await crypto.subtle.decrypt(
        { name: 'AES-GCM', iv },
        key,
        ciphertext
    );

    return td.decode(plainBuf);
}

// Example usage
(async () => {
    const message = 'Hello World';
    const password = 'my-secret-password';

    console.log('Original:', message);

    // Encrypt with salt key (uses v2)
    const encrypted = await encrypt(message, password, SALT_KEY);
    console.log('Encrypted:', encrypted);

    // Decrypt with same salt key (supports v1 and v2)
    const decrypted = await decrypt(encrypted, password, SALT_KEY);
    console.log('Decrypted:', decrypted);
})();

ผลลัพธ์ที่คาดหวัง

ผลลัพธ์คอนโซลที่คาดหวัง
ต้นฉบับ: Hello World
เข้ารหัสแล้ว: v1:aBcDeFgHiJkLmNoP...:qRsTuVwXyZ...:encrypted_data...
ถอดรหัสแล้ว: Hello World

หมายเหตุ: ผลลัพธ์ที่เข้ารหัสจะแตกต่างกันทุกครั้งเนื่องจากการสร้าง salt และ IV แบบสุ่ม แต่การถอดรหัสจะส่งคืนข้อความเดิมเสมอ

พร้อมที่จะลองหรือยัง?

ใช้เครื่องมือเข้ารหัสแบบโต้ตอบของเราเพื่อดูการเข้ารหัสนี้ในการทำงาน

ลองเครื่องมือเข้ารหัส

เราแทบไม่ใช้คุกกี้

เราใช้เฉพาะคุกกี้ที่จำเป็นอย่างยิ่งซึ่งเพิ่มความปลอดภัยของเว็บไซต์และปรับปรุงประสบการณ์ผู้ใช้ของคุณ เราไม่ติดตามพฤติกรรมหรือกิจกรรมของคุณ