Dokumentasi Enkripsi

Pelajari cara kerja enkripsi AES-256-GCM kami. Dokumentasi lengkap dengan contoh kode untuk mengimplementasikan enkripsi sisi klien.

Cara Kerjanya

Penurunan Kunci

Kata sandi Anda digunakan untuk menurunkan kunci AES 256-bit menggunakan PBKDF2 dengan SHA-256 dan 600.000 iterasi. Ini membuat serangan brute-force mahal secara komputasi.

Nilai Acak

Garam 128-bit acak dan IV (Vektor Inisialisasi) 96-bit dihasilkan menggunakan pembuatan angka acak yang aman secara kriptografis untuk setiap enkripsi.

Enkripsi AES-256-GCM

Data dienkripsi menggunakan AES-256-GCM (Galois/Counter Mode), yang menyediakan kerahasiaan dan verifikasi keaslian.

Format Output

Output diformat sebagai v1:salt:iv:ciphertext di mana semua komponen dikodekan base64.

Fungsi Enkripsi

Turunkan Kunci

Turunkan kunci AES dari kata sandi
Menggunakan PBKDF2 dengan SHA-256 dan 600.000 iterasi untuk menurunkan kunci AES-GCM 256-bit dari kata sandi.
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']
    );
}

Pembantu

Fungsi pembantu
Utilitas pengkodean dan penguraian kode Base64 untuk array byte.
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;
}

Enkripsi

Enkripsi data
Mengenkripsi teks biasa menggunakan AES-256-GCM dengan garam dan IV acak. Mengembalikan payload yang diformat.
/**
 * 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(':');
}

Dekripsi

Dekripsi data
Mendekripsi payload v1 kembali ke teks biasa menggunakan kata sandi asli.
/**
 * 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);
}

Contoh Lengkap

Kode siap pakai
Salin dan tempel contoh lengkap ini ke konsol browser Anda atau ke file JavaScript Anda.
/**
 * 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);
})();

Output yang Diharapkan

Output konsol yang diharapkan
Asli: Hello World
Terenkripsi: v1:aBcDeFgHiJkLmNoP...:qRsTuVwXyZ...:encrypted_data...
Terdekripsi: Hello World

Catatan: Output terenkripsi akan berbeda setiap kali karena pembuatan garam dan IV acak, tetapi dekripsi akan selalu mengembalikan pesan asli.

Siap mencoba?

Gunakan alat enkripsi interaktif kami untuk melihat enkripsi ini beraksi.

Coba Alat Enkripsi

Kami hampir tidak menggunakan cookie

Kami hanya menggunakan cookie yang sangat diperlukan, yang meningkatkan keamanan situs web dan memperbaiki pengalaman pengguna. Kami tidak melacak tindakan atau aktivitas Anda.