正在研究js-md5

分享和讨论Javascript相关的话题
头像
ejsoon
圈圈精英
圈圈精英
帖子: 3645
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 152 次
被圈友点赞: 173 次
联系:

Re: 正在研究js-md5

帖子 ejsoon »

gemini, SJCL:

代码: 全选

<!DOCTYPE html>
<html>
<head>
    <title>SJCL PBKDF2 範例</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/js-sjcl/1.0.3/sjcl.min.js"></script>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        label { display: block; margin-bottom: 5px; font-weight: bold; }
        input[type="text"], input[type="password"] { width: 300px; padding: 8px; margin-bottom: 10px; border: 1px solid #ccc; border-radius: 4px; }
        button { padding: 10px 15px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; }
        button:hover { background-color: #0056b3; }
        .output-container { margin-top: 20px; padding: 15px; border: 1px solid #e0e0e0; background-color: #f9f9f9; border-radius: 5px; }
        pre { white-space: pre-wrap; word-break: break-all; background-color: #eee; padding: 10px; border-radius: 4px; }
    </style>
</head>
<body>

    <h1>SJCL PBKDF2 密碼派生</h1>

    <p>輸入一個密碼,SJCL 將使用 PBKDF2 算法從中派生出一個金鑰(密文)。</p>

    <div>
        <label for="password">密碼:</label>
        <input type="password" id="password" value="MyStrongPassword123!" />
    </div>
    <div>
        <label for="salt">鹽 (Salt):</label>
        <input type="text" id="salt" value="some_random_salt_string" />
    </div>
    <div>
        <label for="iterations">迭代次數 (Iterations):</label>
        <input type="number" id="iterations" value="10000" min="1000" />
    </div>
    <button onclick="deriveKey()">派生金鑰</button>

    <div class="output-container">
        <h2>結果:</h2>
        <p>派生出的金鑰 (十六進制表示):</p>
        <pre id="derivedKeyOutput"></pre>
        <p>錯誤訊息:</p>
        <pre id="errorOutput" style="color: red;"></pre>
    </div>

    <script>
        function deriveKey() {
            const password = document.getElementById('password').value;
            const salt = document.getElementById('salt').value;
            const iterations = parseInt(document.getElementById('iterations').value, 10);
            const keyOutput = document.getElementById('derivedKeyOutput');
            const errorOutput = document.getElementById('errorOutput');

            keyOutput.textContent = '';
            errorOutput.textContent = '';

            if (!password || !salt || isNaN(iterations) || iterations < 1) {
                errorOutput.textContent = '請輸入有效的密碼、鹽和迭代次數。';
                return;
            }

            try {
                // 將密碼和鹽轉換為 SJCL 位陣列格式
                const passwordBits = sjcl.codec.utf8String.toBits(password);
                const saltBits = sjcl.codec.utf8String.toBits(salt);

                // 使用 PBKDF2 派生金鑰
                // sjcl.misc.pbkdf2(密碼, 鹽, 迭代次數, 輸出金鑰的位元數 (可選), 雜湊函數 (可選))
                // 預設雜湊函數為 SHA-256
                const derivedKeyBits = sjcl.misc.pbkdf2(passwordBits, saltBits, iterations, 256); // 256 位元輸出金鑰

                // 將派生出的金鑰從位陣列轉換為十六進制字串以便顯示
                const derivedKeyHex = sjcl.codec.hex.fromBits(derivedKeyBits);

                keyOutput.textContent = derivedKeyHex;

            } catch (e) {
                errorOutput.textContent = '金鑰派生失敗: ' + e.message;
                console.error('PBKDF2 Error:', e);
            }
        }
    </script>

</body>
</html>
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 3645
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 152 次
被圈友点赞: 173 次
联系:

Re: 正在研究js-md5

帖子 ejsoon »

gemini, SJCL,PBKDF2,隨機鹽:

代码: 全选

<!DOCTYPE html>
<html>
<head>
    <title>SJCL PBKDF2 範例 (含隨機鹽,Hex & Base64)</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/js-sjcl/1.0.3/sjcl.min.js"></script>
    <style>
        body { font-family: Arial, sans-serif; margin: 20px; }
        label { display: block; margin-bottom: 5px; font-weight: bold; }
        input[type="password"] { width: 300px; padding: 8px; margin-bottom: 10px; border: 1px solid #ccc; border-radius: 4px; }
        button { padding: 10px 15px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer; }
        button:hover { background-color: #0056b3; }
        .output-container { margin-top: 20px; padding: 15px; border: 1px solid #e0e0e0; background-color: #f9f9f9; border-radius: 5px; }
        pre { white-space: pre-wrap; word-break: break-all; background-color: #eee; padding: 10px; border-radius: 4px; }
    </style>
</head>
<body>

    <h1>SJCL PBKDF2 密碼派生 (含隨機鹽,Hex & Base64)</h1>

    <p>輸入一個密碼,SJCL 將使用 PBKDF2 算法從中派生出一個金鑰。每次派生都會生成一個新的隨機鹽,並以十六進制和 Base64 格式顯示。</p>

    <div>
        <label for="password">密碼:</label>
        <input type="password" id="password" value="MyStrongPassword123!" />
    </div>
    <div>
        <label for="iterations">迭代次數 (Iterations):</label>
        <input type="number" id="iterations" value="10000" min="1000" />
    </div>
    <button onclick="deriveKey()">派生金鑰</button>

    <div class="output-container">
        <h2>結果:</h2>
        <p>生成的鹽 (十六進制表示):</p>
        <pre id="generatedSaltHexOutput"></pre>
        <p>生成的鹽 (Base64 表示):</p>
        <pre id="generatedSaltBase64Output"></pre>
        <p>派生出的金鑰 (十六進制表示):</p>
        <pre id="derivedKeyOutput"></pre>
        <p>錯誤訊息:</p>
        <pre id="errorOutput" style="color: red;"></pre>
    </div>

    <script>
        function deriveKey() {
            const password = document.getElementById('password').value;
            const iterations = parseInt(document.getElementById('iterations').value, 10);
            const generatedSaltHexOutput = document.getElementById('generatedSaltHexOutput');
            const generatedSaltBase64Output = document.getElementById('generatedSaltBase64Output');
            const keyOutput = document.getElementById('derivedKeyOutput');
            const errorOutput = document.getElementById('errorOutput');

            generatedSaltHexOutput.textContent = '';
            generatedSaltBase64Output.textContent = '';
            keyOutput.textContent = '';
            errorOutput.textContent = '';

            if (!password || isNaN(iterations) || iterations < 1) {
                errorOutput.textContent = '請輸入有效的密碼和迭代次數。';
                return;
            }

            try {
                // 1. 生成一個隨機的鹽 (128 位元 / 16 位元組)
                const saltBits = sjcl.random.randomWords(128 / 32); 
                
                // 將鹽轉換為十六進制和 Base64 格式
                const generatedSaltHex = sjcl.codec.hex.fromBits(saltBits);
                const generatedSaltBase64 = sjcl.codec.base64.fromBits(saltBits);

                generatedSaltHexOutput.textContent = generatedSaltHex;
                generatedSaltBase64Output.textContent = generatedSaltBase64;

                // 將密碼轉換為 SJCL 位陣列格式
                const passwordBits = sjcl.codec.utf8String.toBits(password);

                // 2. 使用 PBKDF2 派生金鑰
                const derivedKeyBits = sjcl.misc.pbkdf2(passwordBits, saltBits, iterations, 256); // 256 位元輸出金鑰

                // 將派生出的金鑰從位陣列轉換為十六進制字串以便顯示
                const derivedKeyHex = sjcl.codec.hex.fromBits(derivedKeyBits);

                keyOutput.textContent = derivedKeyHex;

            } catch (e) {
                errorOutput.textContent = '金鑰派生失敗: ' + e.message;
                console.error('PBKDF2 Error:', e);
            }
        }
    </script>

</body>
</html>
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 3645
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 152 次
被圈友点赞: 173 次
联系:

Re: 正在研究js-md5

帖子 ejsoon »

grok0058:

代码: 全选

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>加密與解密</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
    <script src="https://bitwiseshiftleft.github.io/sjcl/sjcl.js"></script>
    <style>
        body {
            font-family: Arial, sans-serif;
            padding: 20px;
            max-width: 600px;
            margin: 0 auto;
            background-color: #f4f4f4;
        }
        h2, h4 {
            color: #333;
        }
        textarea, input, button {
            display: block;
            width: 100%;
            margin-bottom: 10px;
            padding: 10px;
            font-size: 16px;
        }
        textarea {
            height: 100px;
            resize: vertical;
        }
        input[type="password"] {
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        button {
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        button:hover {
            background-color: #0056b3;
        }
        pre {
            background-color: #fff;
            border: 1px solid #ddd;
            padding: 10px;
            white-space: pre-wrap;
            word-wrap: break-word;
            cursor: pointer;
            margin-bottom: 10px;
            border-radius: 4px;
        }
        pre:hover {
            background-color: #f0f0f0;
        }
        @media (max-width: 600px) {
            body {
                padding: 10px;
            }
            textarea, input, button {
                font-size: 14px;
            }
        }
    </style>
</head>
<body>
    <h2>加密</h2>
    <h4>輸入明文與密碼</h4>
    <textarea id="ejc_text" placeholder="輸入明文"></textarea>
    <input id="ejc_encryptpw" type="password" placeholder="輸入加密密碼">
    <button type="button" id="ejc_encrypt">加密</button>

    <h4>CryptoJS AES 加密結果</h4>
    <pre id="ejc_aes_ecb">ECB 模式結果</pre>
    <pre id="ejc_aes_cbc">CBC 模式結果</pre>
    <pre id="ejc_aes_cfb">CFB 模式結果</pre>
    <pre id="ejc_aes_ctr">CTR 模式結果</pre>
    <pre id="ejc_aes_ofb">OFB 模式結果</pre>

    <h4>SJCL AES 加密結果</h4>
    <pre id="sjcl_mode_cbc">CBC 模式結果</pre>
    <pre id="sjcl_mode_ccm">CCM 模式結果</pre>
    <pre id="sjcl_mode_ctr">CTR 模式結果</pre>
    <pre id="sjcl_mode_gcm">GCM 模式結果</pre>

    <h2>解密</h2>
    <textarea id="ejc_encrypttext" placeholder="輸入加密結果(JSON格式)"></textarea>
    <input id="ejc_decryptpw" type="password" placeholder="輸入解密密碼">
    <button type="button" id="ejc_decrypt">解密</button>
    <textarea id="ejc_decryptresult" placeholder="解密結果" readonly></textarea>

    <script>
        // CryptoJS 模式映射
        const cryptoModes = {
            ecb: CryptoJS.mode.ECB,
            cbc: CryptoJS.mode.CBC,
            cfb: CryptoJS.mode.CFB,
            ctr: CryptoJS.mode.CTR,
            ofb: CryptoJS.mode.OFB
        };

        // 加密按鈕事件
        document.getElementById('ejc_encrypt').addEventListener('click', () => {
            const text = document.getElementById('ejc_text').value;
            const password = document.getElementById('ejc_encryptpw').value;
            if (!text || !password) {
                alert('請輸入明文和密碼');
                return;
            }

            // CryptoJS 加密
            for (const [modeName, mode] of Object.entries(cryptoModes)) {
                const salt = CryptoJS.lib.WordArray.random(16);
                const iv = modeName !== 'ecb' ? CryptoJS.lib.WordArray.random(16) : '';
                const key = CryptoJS.PBKDF2(password, salt, { keySize: 256/32, iterations: 1000 });
                const encrypted = CryptoJS.AES.encrypt(text, key, {
                    iv: iv,
                    mode: mode,
                    padding: CryptoJS.pad.Pkcs7
                });
                const hmac = CryptoJS.HmacSHA256(encrypted.ciphertext, key).toString(CryptoJS.enc.Base64);
                const result = {
                    ct: encrypted.ciphertext.toString(CryptoJS.enc.Base64),
                    salt: salt.toString(CryptoJS.enc.Base64),
                    iv: iv ? iv.toString(CryptoJS.enc.Base64) : '',
                    'hmac-sha256': hmac,
                    config: {
                        mode: modeName.toUpperCase(),
                        padding: 'Pkcs7',
                        iterations: 1000,
                        keySize: 256
                    }
                };
                document.getElementById(`ejc_aes_${modeName}`).textContent = JSON.stringify(result, null, 2);
            }

            // SJCL 加密
            const sjclModes = ['cbc', 'ccm', 'ctr', 'gcm'];
            for (const mode of sjclModes) {
                // 生成隨機鹽
                const salt = sjcl.random.randomWords(4); // 16 bytes
                // 使用 PBKDF2 生成密鑰
                const key = sjcl.misc.pbkdf2(password, salt, 1000, 256);
                // 生成隨機 IV (除了 CTR 模式外,其他模式需要)
                const iv = mode !== 'ctr' ? sjcl.random.randomWords(4) : null;
                
                // 設置加密參數
                const params = {
                    mode: mode,
                    iter: 1000,
                    ks: 256,
                    ts: mode === 'ccm' || mode === 'gcm' ? 64 : undefined // 設置標籤長度為認証模式
                };
                if (iv) params.iv = iv;

                // 加密
                const rp = {};
                const ct = sjcl.mode[mode].encrypt(
                    new sjcl.cipher.aes(key),
                    sjcl.codec.utf8String.toBits(text),
                    iv ? iv : undefined,
                    undefined,
                    mode === 'ccm' || mode === 'gcm' ? 64 : undefined
                );

                // 構建結果對象
                const result = {
                    ct: sjcl.codec.base64.fromBits(ct),
                    salt: sjcl.codec.base64.fromBits(salt),
                    iv: iv ? sjcl.codec.base64.fromBits(iv) : '',
                    config: {
                        mode: mode.toUpperCase(),
                        iterations: 1000,
                        keySize: 256,
                        tagSize: mode === 'ccm' || mode === 'gcm' ? 64 : undefined
                    }
                };

                // 為 CCM 和 GCM 模式添加 HMAC
                if (mode === 'ccm' || mode === 'gcm') {
                    const hmac = sjcl.codec.base64.fromBits(
                        sjcl.misc.hmac(key).mac(ct)
                    );
                    result['hmac-sha256'] = hmac;
                }

                document.getElementById(`sjcl_mode_${mode}`).textContent = JSON.stringify(result, null, 2);
            }
        });

        // 點擊 pre 填充加密結果
        document.querySelectorAll('pre').forEach(pre => {
            pre.addEventListener('click', () => {
                document.getElementById('ejc_encrypttext').value = pre.textContent;
            });
        });

        // 解密按鈕事件
        document.getElementById('ejc_decrypt').addEventListener('click', () => {
            const encryptedText = document.getElementById('ejc_encrypttext').value;
            const password = document.getElementById('ejc_decryptpw').value;
            const resultArea = document.getElementById('ejc_decryptresult');

            if (!encryptedText || !password) {
                resultArea.value = '請輸入加密結果和密碼';
                return;
            }

            try {
                const data = JSON.parse(encryptedText);

                // 檢查是否為 SJCL 格式(舊格式)
                if ('v' in data && 'cipher' in data && data.cipher === 'aes') {
                    try {
                        const decrypted = sjcl.decrypt(password, JSON.stringify(data));
                        resultArea.value = decrypted;
                    } catch (e) {
                        resultArea.value = 'SJCL 解密失敗:' + e.message;
                    }
                    return;
                }

                // 檢查是否為新 SJCL 格式
                if (data.config && sjclModes.includes(data.config.mode.toLowerCase())) {
                    const mode = data.config.mode.toLowerCase();
                    const key = sjcl.misc.pbkdf2(
                        password,
                        sjcl.codec.base64.toBits(data.salt),
                        data.config.iterations,
                        data.config.keySize
                    );
                    const ct = sjcl.codec.base64.toBits(data.ct);
                    const iv = data.iv ? sjcl.codec.base64.toBits(data.iv) : undefined;

                    // 驗證 HMAC(如果存在)
                    if (data['hmac-sha256']) {
                        const computedHmac = sjcl.codec.base64.fromBits(
                            sjcl.misc.hmac(key).mac(ct)
                        );
                        if (computedHmac !== data['hmac-sha256']) {
                            resultArea.value = 'SJCL 解密失敗:HMAC 驗證失敗';
                            return;
                        }
                    }

                    const decrypted = sjcl.mode[mode].decrypt(
                        new sjcl.cipher.aes(key),
                        ct,
                        iv,
                        undefined,
                        data.config.tagSize
                    );
                    resultArea.value = sjcl.codec.utf8String.fromBits(decrypted);
                    return;
                }

                // 假設為 CryptoJS 格式
                const { ct, salt, iv, config } = data;
                const key = CryptoJS.PBKDF2(password, CryptoJS.enc.Base64.parse(salt), {
                    keySize: config.keySize/32,
                    iterations: config.iterations
                });
                const decrypted = CryptoJS.AES.decrypt(
                    { ciphertext: CryptoJS.enc.Base64.parse(ct) },
                    key,
                    {
                        iv: iv ? CryptoJS.enc.Base64.parse(iv) : undefined,
                        mode: cryptoModes[config.mode.toLowerCase()],
                        padding: CryptoJS.pad.Pkcs7
                    }
                );
                const decryptedText = decrypted.toString(CryptoJS.enc.Utf8);
                if (!decryptedText) {
                    resultArea.value = 'CryptoJS 解密失敗:密碼錯誤或數據損壞';
                } else {
                    resultArea.value = decryptedText;
                }
            } catch (e) {
                resultArea.value = '解密失敗:無效的 JSON 格式或數據';
            }
        });
    </script>
</body>
</html>
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 3645
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 152 次
被圈友点赞: 173 次
联系:

Re: 正在研究js-md5

帖子 ejsoon »

gemini, 重寫SJCL:

代码: 全选

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>加密與解密</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
    <script src="https://bitwiseshiftleft.github.io/sjcl/sjcl.js"></script>
    <style>
        body {
            font-family: Arial, sans-serif;
            padding: 20px;
            max-width: 600px;
            margin: 0 auto;
            background-color: #f4f4f4;
        }
        h2, h4 {
            color: #333;
        }
        textarea, input, button {
            display: block;
            width: 100%;
            margin-bottom: 10px;
            padding: 10px;
            font-size: 16px;
        }
        textarea {
            height: 100px;
            resize: vertical;
        }
        input[type="password"] {
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        button {
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        button:hover {
            background-color: #0056b3;
        }
        pre {
            background-color: #fff;
            border: 1px solid #ddd;
            padding: 10px;
            white-space: pre-wrap;
            word-wrap: break-word;
            cursor: pointer;
            margin-bottom: 10px;
            border-radius: 4px;
        }
        pre:hover {
            background-color: #f0f0f0;
        }
        @media (max-width: 600px) {
            body {
                padding: 10px;
            }
            textarea, input, button {
                font-size: 14px;
            }
        }
    </style>
</head>
<body>
    <h2>加密</h2>
    <h4>輸入明文與密碼</h4>
    <textarea id="ejc_text" placeholder="輸入明文"></textarea>
    <input id="ejc_encryptpw" type="password" placeholder="輸入加密密碼">
    <button type="button" id="ejc_encrypt">加密</button>

    <h4>CryptoJS AES 加密結果</h4>
    <pre id="ejc_aes_ecb">ECB 模式結果</pre>
    <pre id="ejc_aes_cbc">CBC 模式結果</pre>
    <pre id="ejc_aes_cfb">CFB 模式結果</pre>
    <pre id="ejc_aes_ctr">CTR 模式結果</pre>
    <pre id="ejc_aes_ofb">OFB 模式結果</pre>

    <h4>SJCL AES 加密結果 (CCM, GCM, OCB2)</h4>
    <pre id="sjcl_mode_ccm">CCM 模式結果</pre>
    <pre id="sjcl_mode_gcm">GCM 模式結果</pre>
    <pre id="sjcl_mode_ocb2">OCB2 模式結果</pre>

    <h2>解密</h2>
    <textarea id="ejc_encrypttext" placeholder="輸入加密結果(JSON格式)"></textarea>
    <input id="ejc_decryptpw" type="password" placeholder="輸入解密密碼">
    <button type="button" id="ejc_decrypt">解密</button>
    <textarea id="ejc_decryptresult" placeholder="解密結果" readonly></textarea>

    <script>
        // CryptoJS 模式映射
        const cryptoModes = {
            ecb: CryptoJS.mode.ECB,
            cbc: CryptoJS.mode.CBC,
            cfb: CryptoJS.mode.CFB,
            ctr: CryptoJS.mode.CTR,
            ofb: CryptoJS.mode.OFB
        };

        // 加密按鈕事件
        document.getElementById('ejc_encrypt').addEventListener('click', () => {
            const text = document.getElementById('ejc_text').value;
            const password = document.getElementById('ejc_encryptpw').value;
            if (!text || !password) {
                alert('請輸入明文和密碼');
                return;
            }

            // CryptoJS 加密 (保持不變)
            for (const [modeName, mode] of Object.entries(cryptoModes)) {
                const salt = CryptoJS.lib.WordArray.random(16);
                const iv = modeName !== 'ecb' ? CryptoJS.lib.WordArray.random(16) : '';
                const key = CryptoJS.PBKDF2(password, salt, { keySize: 256/32, iterations: 1000 });
                const encrypted = CryptoJS.AES.encrypt(text, key, {
                    iv: iv,
                    mode: mode,
                    padding: CryptoJS.pad.Pkcs7
                });
                // 注意:這裡的 HMAC 計算是針對 ciphertext 的,並非 AEAD 模式的內建 Tag
                const hmac = CryptoJS.HmacSHA256(encrypted.ciphertext, key).toString(CryptoJS.enc.Base64);
                const result = {
                    ct: encrypted.ciphertext.toString(CryptoJS.enc.Base64),
                    salt: salt.toString(CryptoJS.enc.Base64),
                    iv: iv ? iv.toString(CryptoJS.enc.Base64) : '',
                    'hmac-sha256': hmac, // 這裡添加 HMAC
                    config: {
                        mode: modeName.toUpperCase(),
                        padding: 'Pkcs7',
                        iterations: 1000,
                        keySize: 256
                    }
                };
                document.getElementById(`ejc_aes_${modeName}`).textContent = JSON.stringify(result, null, 2);
            }

            // ---
            // SJCL 加密 (更新為 CCM, GCM, OCB2)
            const sjclModes = ['ccm', 'gcm', 'ocb2'];
            for (const mode of sjclModes) {
                try {
                    // 生成隨機鹽
                    const salt = sjcl.random.randomWords(4); // 16 bytes
                    // 使用 PBKDF2 生成密鑰
                    const key = sjcl.misc.pbkdf2(password, salt, 1000, 256);
                    // 生成隨機 IV (Nonce)
                    // SJCL AEAD 模式的 IV 長度通常為 128 位 (16 bytes, 即 4 words)
                    const iv = sjcl.random.randomWords(4);
                    
                    // 設置加密參數
                    const tagSize = 128; // 對於 AEAD 模式,tagSize 通常是 128 位 (16 bytes),也可以是 64 位 (8 bytes) 或 96 位 (12 bytes)
                                        // SJCL 預設為 64 位,但 128 位更安全
                    const params = {
                        mode: mode,
                        iter: 1000,
                        ks: 256,
                        ts: tagSize // 設置標籤長度為認證模式
                    };

                    // 加密
                    // sjcl.mode[mode].encrypt 方法的參數順序:
                    // (cipher, plaintext, iv, adata, tlen)
                    // cipher: sjcl.cipher.aes(key)
                    // plaintext: sjcl.codec.utf8String.toBits(text)
                    // iv: iv (Nonce)
                    // adata: 額外認證數據 (通常可選,這裡留空)
                    // tlen: 標籤長度 (tagSize)
                    const ct = sjcl.mode[mode].encrypt(
                        new sjcl.cipher.aes(key),
                        sjcl.codec.utf8String.toBits(text),
                        iv,
                        undefined, // adata (Associated Data)
                        tagSize // tagSize
                    );

                    // 構建結果對象
                    // 對於 AEAD 模式,密文 `ct` 已經包含了加密的數據和認證標籤 (tag)
                    // 所以不需要額外計算 HMAC
                    const result = {
                        ct: sjcl.codec.base64.fromBits(ct),
                        salt: sjcl.codec.base64.fromBits(salt),
                        iv: sjcl.codec.base64.fromBits(iv), // IV 是必須的
                        config: {
                            mode: mode.toUpperCase(),
                            iterations: 1000,
                            keySize: 256,
                            tagSize: tagSize // 記錄使用的標籤長度
                        }
                    };

                    document.getElementById(`sjcl_mode_${mode}`).textContent = JSON.stringify(result, null, 2);
                } catch (e) {
                    console.error(`SJCL ${mode.toUpperCase()} 加密失敗:`, e);
                    document.getElementById(`sjcl_mode_${mode}`).textContent = `SJCL ${mode.toUpperCase()} 加密失敗: ${e.message}`;
                }
            }
        });

        // 點擊 pre 填充加密結果
        document.querySelectorAll('pre').forEach(pre => {
            pre.addEventListener('click', () => {
                document.getElementById('ejc_encrypttext').value = pre.textContent;
            });
        });

        // 解密按鈕事件
        document.getElementById('ejc_decrypt').addEventListener('click', () => {
            const encryptedText = document.getElementById('ejc_encrypttext').value;
            const password = document.getElementById('ejc_decryptpw').value;
            const resultArea = document.getElementById('ejc_decryptresult');

            if (!encryptedText || !password) {
                resultArea.value = '請輸入加密結果和密碼';
                return;
            }

            try {
                const data = JSON.parse(encryptedText);

                // 檢查是否為 SJCL 舊格式 (sjcl.decrypt 統一處理)
                if ('v' in data && 'cipher' in data && data.cipher === 'aes') {
                    try {
                        const decrypted = sjcl.decrypt(password, JSON.stringify(data));
                        resultArea.value = decrypted;
                    } catch (e) {
                        resultArea.value = 'SJCL 舊格式解密失敗:' + e.message;
                    }
                    return;
                }

                // 檢查是否為新 SJCL 格式 (CCM, GCM, OCB2)
                const sjclModes = ['ccm', 'gcm', 'ocb2'];
                if (data.config && sjclModes.includes(data.config.mode.toLowerCase())) {
                    const mode = data.config.mode.toLowerCase();
                    const key = sjcl.misc.pbkdf2(
                        password,
                        sjcl.codec.base64.toBits(data.salt),
                        data.config.iterations,
                        data.config.keySize
                    );
                    const ct = sjcl.codec.base64.toBits(data.ct);
                    const iv = data.iv ? sjcl.codec.base64.toBits(data.iv) : undefined;
                    const tagSize = data.config.tagSize; // 從配置中獲取標籤長度

                    // 對於 AEAD 模式 (CCM, GCM, OCB2),解密操作會自動驗證 Tag
                    // 如果 Tag 不匹配,會拋出錯誤,無需手動驗證 HMAC
                    try {
                        const decrypted = sjcl.mode[mode].decrypt(
                            new sjcl.cipher.aes(key),
                            ct, // 密文 (包含 Tag)
                            iv, // IV
                            undefined, // adata (Associated Data)
                            tagSize // tagSize
                        );
                        resultArea.value = sjcl.codec.utf8String.fromBits(decrypted);
                    } catch (e) {
                        // 如果解密失敗,通常是因為密碼錯誤、數據損壞或 Tag 驗證失敗
                        resultArea.value = `SJCL ${mode.toUpperCase()} 解密失敗:` + e.message;
                    }
                    return;
                }

                // 假設為 CryptoJS 格式 (保持不變)
                const { ct, salt, iv, config } = data;
                const key = CryptoJS.PBKDF2(password, CryptoJS.enc.Base64.parse(salt), {
                    keySize: config.keySize/32,
                    iterations: config.iterations
                });

                // 如果有 HMAC,先驗證 HMAC
                if (data['hmac-sha256']) {
                    const computedHmac = CryptoJS.HmacSHA256(CryptoJS.enc.Base64.parse(ct), key).toString(CryptoJS.enc.Base64);
                    if (computedHmac !== data['hmac-sha256']) {
                        resultArea.value = 'CryptoJS 解密失敗:HMAC 驗證失敗';
                        return;
                    }
                }

                const decrypted = CryptoJS.AES.decrypt(
                    { ciphertext: CryptoJS.enc.Base64.parse(ct) },
                    key,
                    {
                        iv: iv ? CryptoJS.enc.Base64.parse(iv) : undefined,
                        mode: cryptoModes[config.mode.toLowerCase()],
                        padding: CryptoJS.pad.Pkcs7
                    }
                );
                const decryptedText = decrypted.toString(CryptoJS.enc.Utf8);
                if (!decryptedText) {
                    resultArea.value = 'CryptoJS 解密失敗:密碼錯誤或數據損壞';
                } else {
                    resultArea.value = decryptedText;
                }
            } catch (e) {
                resultArea.value = '解密失敗:無效的 JSON 格式或數據' + e.message;
            }
        });
    </script>
</body>
</html>
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 3645
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 152 次
被圈友点赞: 173 次
联系:

Re: 正在研究js-md5

帖子 ejsoon »

gemini,模式h4:

代码: 全选

<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>加密與解密</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
    <script src="https://bitwiseshiftleft.github.io/sjcl/sjcl.js"></script>
    <style>
        body {
            font-family: Arial, sans-serif;
            padding: 20px;
            max-width: 600px;
            margin: 0 auto;
            background-color: #f4f4f4;
        }
        h2, h3, h4 { /* 這裡也更新了 h3 */
            color: #333;
        }
        textarea, input, button {
            display: block;
            width: 100%;
            margin-bottom: 10px;
            padding: 10px;
            font-size: 16px;
        }
        textarea {
            height: 100px;
            resize: vertical;
        }
        input[type="password"] {
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        button {
            background-color: #007bff;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        button:hover {
            background-color: #0056b3;
        }
        pre {
            background-color: #fff;
            border: 1px solid #ddd;
            padding: 10px;
            white-space: pre-wrap;
            word-wrap: break-word;
            cursor: pointer;
            margin-bottom: 10px;
            border-radius: 4px;
        }
        pre:hover {
            background-color: #f0f0f0;
        }
        @media (max-width: 600px) {
            body {
                padding: 10px;
            }
            textarea, input, button {
                font-size: 14px;
            }
        }
    </style>
</head>
<body>
    <h2>加密</h2>
    <h3>輸入明文與密碼</h3>
    <textarea id="ejc_text" placeholder="輸入明文"></textarea>
    <input id="ejc_encryptpw" type="password" placeholder="輸入加密密碼">
    <button type="button" id="ejc_encrypt">加密</button>

    <h3>CryptoJS AES 加密結果</h3>
    <h4>ECB 模式</h4>
    <pre id="ejc_aes_ecb">ECB 模式結果</pre>
    <h4>CBC 模式</h4>
    <pre id="ejc_aes_cbc">CBC 模式結果</pre>
    <h4>CFB 模式</h4>
    <pre id="ejc_aes_cfb">CFB 模式結果</pre>
    <h4>CTR 模式</h4>
    <pre id="ejc_aes_ctr">CTR 模式結果</pre>
    <h4>OFB 模式</h4>
    <pre id="ejc_aes_ofb">OFB 模式結果</pre>

    <h3>SJCL AES 加密結果 (CCM, GCM, OCB2)</h3>
    <h4>CCM 模式</h4>
    <pre id="sjcl_mode_ccm">CCM 模式結果</pre>
    <h4>GCM 模式</h4>
    <pre id="sjcl_mode_gcm">GCM 模式結果</pre>
    <h4>OCB2 模式</h4>
    <pre id="sjcl_mode_ocb2">OCB2 模式結果</pre>

    <h2>解密</h2>
    <textarea id="ejc_encrypttext" placeholder="輸入加密結果(JSON格式)"></textarea>
    <input id="ejc_decryptpw" type="password" placeholder="輸入解密密碼">
    <button type="button" id="ejc_decrypt">解密</button>
    <textarea id="ejc_decryptresult" placeholder="解密結果" readonly></textarea>

    <script>
        // CryptoJS 模式映射
        const cryptoModes = {
            ecb: CryptoJS.mode.ECB,
            cbc: CryptoJS.mode.CBC,
            cfb: CryptoJS.mode.CFB,
            ctr: CryptoJS.mode.CTR,
            ofb: CryptoJS.mode.OFB
        };

        // 加密按鈕事件
        document.getElementById('ejc_encrypt').addEventListener('click', () => {
            const text = document.getElementById('ejc_text').value;
            const password = document.getElementById('ejc_encryptpw').value;
            if (!text || !password) {
                alert('請輸入明文和密碼');
                return;
            }

            // CryptoJS 加密
            for (const [modeName, mode] of Object.entries(cryptoModes)) {
                const salt = CryptoJS.lib.WordArray.random(16);
                const iv = modeName !== 'ecb' ? CryptoJS.lib.WordArray.random(16) : '';
                const key = CryptoJS.PBKDF2(password, salt, { keySize: 256/32, iterations: 1000 });
                const encrypted = CryptoJS.AES.encrypt(text, key, {
                    iv: iv,
                    mode: mode,
                    padding: CryptoJS.pad.Pkcs7
                });
                const hmac = CryptoJS.HmacSHA256(encrypted.ciphertext, key).toString(CryptoJS.enc.Base64);
                const result = {
                    ct: encrypted.ciphertext.toString(CryptoJS.enc.Base64),
                    salt: salt.toString(CryptoJS.enc.Base64),
                    iv: iv ? iv.toString(CryptoJS.enc.Base64) : '',
                    'hmac-sha256': hmac,
                    config: {
                        mode: modeName.toUpperCase(),
                        padding: 'Pkcs7',
                        iterations: 1000,
                        keySize: 256
                    }
                };
                document.getElementById(`ejc_aes_${modeName}`).textContent = JSON.stringify(result, null, 2);
            }

            // SJCL 加密 (更新為 CCM, GCM, OCB2)
            const sjclModes = ['ccm', 'gcm', 'ocb2'];
            for (const mode of sjclModes) {
                try {
                    const salt = sjcl.random.randomWords(4); // 16 bytes
                    const key = sjcl.misc.pbkdf2(password, salt, 1000, 256);
                    const iv = sjcl.random.randomWords(4);
                    
                    const tagSize = 128; 
                    const params = {
                        mode: mode,
                        iter: 1000,
                        ks: 256,
                        ts: tagSize
                    };

                    const ct = sjcl.mode[mode].encrypt(
                        new sjcl.cipher.aes(key),
                        sjcl.codec.utf8String.toBits(text),
                        iv,
                        undefined, 
                        tagSize
                    );

                    const result = {
                        ct: sjcl.codec.base64.fromBits(ct),
                        salt: sjcl.codec.base64.fromBits(salt),
                        iv: sjcl.codec.base64.fromBits(iv),
                        config: {
                            mode: mode.toUpperCase(),
                            iterations: 1000,
                            keySize: 256,
                            tagSize: tagSize
                        }
                    };

                    document.getElementById(`sjcl_mode_${mode}`).textContent = JSON.stringify(result, null, 2);
                } catch (e) {
                    console.error(`SJCL ${mode.toUpperCase()} 加密失敗:`, e);
                    document.getElementById(`sjcl_mode_${mode}`).textContent = `SJCL ${mode.toUpperCase()} 加密失敗: ${e.message}`;
                }
            }
        });

        // 點擊 pre 填充加密結果
        document.querySelectorAll('pre').forEach(pre => {
            pre.addEventListener('click', () => {
                document.getElementById('ejc_encrypttext').value = pre.textContent;
            });
        });

        // 解密按鈕事件
        document.getElementById('ejc_decrypt').addEventListener('click', () => {
            const encryptedText = document.getElementById('ejc_encrypttext').value;
            const password = document.getElementById('ejc_decryptpw').value;
            const resultArea = document.getElementById('ejc_decryptresult');

            if (!encryptedText || !password) {
                resultArea.value = '請輸入加密結果和密碼';
                return;
            }

            try {
                const data = JSON.parse(encryptedText);

                // 檢查是否為 SJCL 舊格式 (sjcl.decrypt 統一處理)
                if ('v' in data && 'cipher' in data && data.cipher === 'aes') {
                    try {
                        const decrypted = sjcl.decrypt(password, JSON.stringify(data));
                        resultArea.value = decrypted;
                    } catch (e) {
                        resultArea.value = 'SJCL 舊格式解密失敗:' + e.message;
                    }
                    return;
                }

                // 檢查是否為新 SJCL 格式 (CCM, GCM, OCB2)
                const sjclModes = ['ccm', 'gcm', 'ocb2'];
                if (data.config && sjclModes.includes(data.config.mode.toLowerCase())) {
                    const mode = data.config.mode.toLowerCase();
                    const key = sjcl.misc.pbkdf2(
                        password,
                        sjcl.codec.base64.toBits(data.salt),
                        data.config.iterations,
                        data.config.keySize
                    );
                    const ct = sjcl.codec.base64.toBits(data.ct);
                    const iv = data.iv ? sjcl.codec.base64.toBits(data.iv) : undefined;
                    const tagSize = data.config.tagSize;

                    try {
                        const decrypted = sjcl.mode[mode].decrypt(
                            new sjcl.cipher.aes(key),
                            ct,
                            iv,
                            undefined,
                            tagSize
                        );
                        resultArea.value = sjcl.codec.utf8String.fromBits(decrypted);
                    } catch (e) {
                        resultArea.value = `SJCL ${mode.toUpperCase()} 解密失敗:` + e.message;
                    }
                    return;
                }

                // 假設為 CryptoJS 格式
                const { ct, salt, iv, config } = data;
                const key = CryptoJS.PBKDF2(password, CryptoJS.enc.Base64.parse(salt), {
                    keySize: config.keySize/32,
                    iterations: config.iterations
                });

                if (data['hmac-sha256']) {
                    const computedHmac = CryptoJS.HmacSHA256(CryptoJS.enc.Base64.parse(ct), key).toString(CryptoJS.enc.Base64);
                    if (computedHmac !== data['hmac-sha256']) {
                        resultArea.value = 'CryptoJS 解密失敗:HMAC 驗證失敗';
                        return;
                    }
                }

                const decrypted = CryptoJS.AES.decrypt(
                    { ciphertext: CryptoJS.enc.Base64.parse(ct) },
                    key,
                    {
                        iv: iv ? CryptoJS.enc.Base64.parse(iv) : undefined,
                        mode: cryptoModes[config.mode.toLowerCase()],
                        padding: CryptoJS.pad.Pkcs7
                    }
                );
                const decryptedText = decrypted.toString(CryptoJS.enc.Utf8);
                if (!decryptedText) {
                    resultArea.value = 'CryptoJS 解密失敗:密碼錯誤或數據損壞';
                } else {
                    resultArea.value = decryptedText;
                }
            } catch (e) {
                resultArea.value = '解密失敗:無效的 JSON 格式或數據' + e.message;
            }
        });
    </script>
</body>
</html>
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 3645
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 152 次
被圈友点赞: 173 次
联系:

Re: 正在研究js-md5

帖子 ejsoon »

gemini介紹二者:

代码: 全选

---
## Crypto.js 簡介

**Crypto.js** 是一個廣受歡迎的 JavaScript 加密函式庫,它提供了多種標準的加密演算法,讓開發者能夠輕鬆地在網頁應用程式中實現資料加密、解密、雜湊和訊息驗證碼(HMAC)等功能。

Crypto.js 的主要特色包括:

* **演算法豐富:** 支援 AES、DES、TripleDES、Rabbit、RC4 等對稱加密演算法;MD5、SHA-1、SHA-256、SHA-512 等雜湊演算法;以及 HMAC 訊息驗證碼。
* **使用方便:** API 設計直觀,易於理解和使用,即使是不熟悉加密原理的開發者也能快速上手。
* **跨平台:** 由於是純 JavaScript 實作,因此可以在任何支援 JavaScript 的環境中運行,包括瀏覽器和 Node.js。
* **開放原始碼:** 允許社群審查和貢獻,有助於提高其安全性和可靠性。

Crypto.js 常用於需要前端加密的場景,例如:

* **用戶端資料加密:** 在資料傳輸到伺服器之前,先在瀏覽器端對敏感資料進行加密。
* **密碼雜湊儲存:** 將用戶密碼進行雜湊處理後再儲存,提高安全性。
* **訊息完整性驗證:** 使用 HMAC 確保訊息在傳輸過程中未被篡改。

---

## SJCL (Stanford JavaScript Crypto Library) 簡介

**SJCL (Stanford JavaScript Crypto Library)** 是由史丹佛大學開發的一個強大的 JavaScript 加密函式庫,其設計目標是提供安全、高性能且易於使用的加密功能。SJCL 尤其強調安全性,並且在設計上考慮了許多潛在的攻擊向量。

SJCL 的主要特色包括:

* **高度安全性:** SJCL 在設計之初就將安全性放在首位,包含了防範時間攻擊(timing attacks)、旁路攻擊(side-channel attacks)等潛在威脅的措施。
* **嚴謹的工程:** 由密碼學專家設計和開發,遵循嚴格的密碼學實踐。
* **支援的演算法:** 支援 AES、CCM (Counter with CBC-MAC) 模式、SHA-256 等核心加密演算法。其中,CCM 模式提供了身份驗證加密,確保資料的機密性、完整性和真實性。
* **模組化設計:** 允許開發者根據需求選擇性地引入所需的模組,減少程式碼體積。
* **良好的文件:** 提供了詳盡的文件和範例,方便開發者學習和使用。

SJCL 通常適用於對安全性要求極高的 Web 應用程式,例如:

* **高敏感度資料加密:** 用於處理需要最高級別保護的敏感資料。
* **安全通訊協定實作:** 作為構建安全通訊協議的底層加密組件。
* **區塊鏈應用:** 在某些區塊鏈相關的前端應用中,可能需要其提供的安全加密功能。

總體而言,Crypto.js 和 SJCL 各有側重。**Crypto.js** 以其豐富的演算法和易用性見長,適合大多數前端加密需求;而 **SJCL** 則以其極致的安全性和嚴謹的設計而聞名,更適合對安全性有最高要求的應用場景。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 3645
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 152 次
被圈友点赞: 173 次
联系:

Re: 正在研究js-md5

帖子 ejsoon »

grok介紹二者:

代码: 全选

### Crypto.js 簡介
Crypto.js 是一個廣泛使用的 JavaScript 加密庫,提供多種加密算法,適用於網頁應用程式和 Node.js 環境。它旨在為開發者提供簡單易用的工具,用於資料加密、解密和雜湊等操作。主要功能包括:

- **支援多種算法**:包括 AES、DES、Triple DES、RC4、SHA-1、SHA-256、MD5、HMAC 等。
- **靈活的模組化設計**:可以按需載入特定模組,減少程式碼體積。
- **跨平台**:適用於瀏覽器和 Node.js 環境,無需額外依賴。
- **用途**:常用於客戶端資料加密、密碼雜湊、簽名驗證等場景。
- **開源**:基於 MIT 許可證,擁有活躍的社群支持。

Crypto.js 適合需要快速實現加密功能的開發者,但由於其維護頻率較低且部分算法(如 MD5 和 SHA-1)在現代安全標準下被認為較弱,建議謹慎選擇適用的算法。

---

### SJCL 簡介
Stanford JavaScript Crypto Library (SJCL) 是由史丹佛大學開發的一個輕量級 JavaScript 加密庫,專為高效、安全的加密操作設計,特別適合網頁應用程式。它的特點包括:

- **高效與輕量**:程式碼精簡,執行速度快,適合資源受限的環境。
- **支援現代加密算法**:包括 AES(CBC 和 CCM 模式)、SHA-256、HMAC、PBKDF2 等,並支援椭圓曲線加密(ECC)。
- **安全設計**:專注於現代加密標準,提供隨機數生成器和安全的密鑰派生功能。
- **易用性**:提供簡潔的 API,方便開發者進行加密、解密和金鑰管理。
- **開源**:基於 BSD 許可證,適合學術和商業用途。

SJCL 特別適合需要高安全性且輕量級加密方案的應用場景,例如瀏覽器端的資料保護或安全的客戶端-伺服器通訊。相較於 Crypto.js,SJCL 更注重現代加密標準,且程式碼更為精簡。

---

### 總結
- **Crypto.js**:功能豐富,支援多種傳統和新式算法,適合快速開發,但部分算法安全性較低。
- **SJCL**:輕量、高效,專注現代加密標準,適合高安全性需求的應用。

若需選擇,建議根據專案的安全性要求和性能需求進行評估:Crypto.js 適合快速原型開發,SJCL 則更適合注重安全性和效率的場景。
https://ejsoon.vip/
弈趣極光:享受思維樂趣
头像
ejsoon
圈圈精英
圈圈精英
帖子: 3645
注册时间: 2022年 11月 18日 17:36
为圈友点赞: 152 次
被圈友点赞: 173 次
联系:

Re: 正在研究js-md5

帖子 ejsoon »

web crypto api
grok

代码: 全选

<!DOCTYPE html>
<html lang="zh-TW">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Web Crypto 加密與解密</title>
  <style>
    body {
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      max-width: 800px;
      margin: 0 auto;
      padding: 20px;
      background-color: #f5f5f5;
    }
    h2 {
      color: #333;
      text-align: center;
    }
    h4 {
      margin: 10px 0 5px;
      color: #555;
    }
    textarea, input {
      width: 100%;
      padding: 10px;
      margin-bottom: 10px;
      border: 1px solid #ccc;
      border-radius: 5px;
      font-size: 16px;
      box-sizing: border-box;
    }
    button {
      width: 100%;
      padding: 12px;
      background-color: #007bff;
      color: white;
      border: none;
      border-radius: 5px;
      font-size: 16px;
      cursor: pointer;
      margin-bottom: 20px;
    }
    button:hover {
      background-color: #0056b3;
    }
    pre {
      background-color: #2d2d2d;
      color: #f8f8f2;
      padding: 10px;
      border-radius: 5px;
      white-space: pre-wrap;
      word-wrap: break-word;
      cursor: pointer;
      font-size: 14px;
      margin-bottom: 10px;
    }
    pre:hover {
      background-color: #3a3a3a;
    }
    @media (max-width: 600px) {
      body {
        padding: 10px;
      }
      textarea, input, button, pre {
        font-size: 14px;
      }
    }
  </style>
</head>
<body>
  <h2>加密</h2>
  <h4>明文</h4>
  <textarea id="ejc_text" rows="4" placeholder="輸入要加密的文字"></textarea>
  <h4>密碼</h4>
  <input id="ejc_encryptpw" type="password" placeholder="輸入加密密碼">
  <button type="button" id="ejc_encrypt">加密</button>
  <h4>AES-CBC 加密結果</h4>
  <pre id="ejc_aes_cbc"></pre>
  <h4>AES-CTR 加密結果</h4>
  <pre id="ejc_aes_ctr"></pre>
  <h4>AES-GCM 加密結果</h4>
  <pre id="ejc_aes_gcm"></pre>

  <h2>解密</h2>
  <h4>加密結果</h4>
  <textarea id="ejc_encrypttext" rows="4" placeholder="輸入或點擊上方加密結果"></textarea>
  <h4>密碼</h4>
  <input id="ejc_decryptpw" type="password" placeholder="輸入解密密碼">
  <button type="button" id="ejc_decrypt">解密</button>
  <h4>解密結果</h4>
  <textarea id="ejc_decryptresult" rows="4" readonly></textarea>

  <script>
    // 將 ArrayBuffer 轉為 Base64
    function arrayBufferToBase64(buffer) {
      return btoa(String.fromCharCode(...new Uint8Array(buffer)));
    }

    // 將 Base64 轉為 ArrayBuffer
    function base64ToArrayBuffer(base64) {
      const binary = atob(base64);
      const len = binary.length;
      const bytes = new Uint8Array(len);
      for (let i = 0; i < len; i++) {
        bytes[i] = binary.charCodeAt(i);
      }
      return bytes.buffer;
    }

    // 使用 PBKDF2 生成密鑰
    async function deriveKey(password, salt, algorithm) {
      const keyMaterial = await crypto.subtle.importKey(
        "raw",
        new TextEncoder().encode(password),
        { name: "PBKDF2" },
        false,
        ["deriveBits", "deriveKey"]
      );
      return crypto.subtle.deriveKey(
        {
          name: "PBKDF2",
          salt: salt,
          iterations: 100000,
          hash: "SHA-256"
        },
        keyMaterial,
        { name: algorithm, length: 256 },
        true,
        ["encrypt", "decrypt"]
      );
    }

    // 加密函數
    async function encrypt(plaintext, password, algorithm, ivLength) {
      const salt = crypto.getRandomValues(new Uint8Array(12));
      const iv = crypto.getRandomValues(new Uint8Array(ivLength));
      const key = await deriveKey(password, salt, algorithm);
      const encodedText = new TextEncoder().encode(plaintext);

      let encrypted;
      if (algorithm === "AES-GCM") {
        encrypted = await crypto.subtle.encrypt(
          { name: algorithm, iv: iv, tagLength: 128 },
          key,
          encodedText
        );
      } else {
        encrypted = await crypto.subtle.encrypt(
          { name: algorithm, iv: iv },
          key,
          encodedText
        );
      }

      return {
        iv: arrayBufferToBase64(iv),
        mode: algorithm.toLowerCase(),
        salt: arrayBufferToBase64(salt),
        ct: arrayBufferToBase64(encrypted)
      };
    }

    // 解密函數
    async function decrypt(encryptedData, password) {
      try {
        const iv = base64ToArrayBuffer(encryptedData.iv);
        const salt = base64ToArrayBuffer(encryptedData.salt);
        const ct = base64ToArrayBuffer(encryptedData.ct);
        const algorithm = encryptedData.mode.toUpperCase();
        const key = await deriveKey(password, salt, algorithm);

        let decrypted;
        if (algorithm === "AES-GCM") {
          decrypted = await crypto.subtle.decrypt(
            { name: algorithm, iv: iv, tagLength: 128 },
            key,
            ct
          );
        } else {
          decrypted = await crypto.subtle.decrypt(
            { name: algorithm, iv: iv },
            key,
            ct
          );
        }
        return new TextDecoder().decode(decrypted);
      } catch (e) {
        return "解密失敗:密碼錯誤或數據無效";
      }
    }

    // 加密按鈕事件
    document.getElementById("ejc_encrypt").addEventListener("click", async () => {
      const text = document.getElementById("ejc_text").value;
      const password = document.getElementById("ejc_encryptpw").value;
      if (!text || !password) {
        alert("請輸入明文和密碼");
        return;
      }

      const cbcResult = await encrypt(text, password, "AES-CBC", 16);
      const ctrResult = await encrypt(text, password, "AES-CTR", 16);
      const gcmResult = await encrypt(text, password, "AES-GCM", 12);

      document.getElementById("ejc_aes_cbc").textContent = JSON.stringify(cbcResult, null, 2);
      document.getElementById("ejc_aes_ctr").textContent = JSON.stringify(ctrResult, null, 2);
      document.getElementById("ejc_aes_gcm").textContent = JSON.stringify(gcmResult, null, 2);
    });

    // pre 點擊事件
    document.querySelectorAll("pre").forEach(pre => {
      pre.addEventListener("click", () => {
        document.getElementById("ejc_encrypttext").value = pre.textContent;
      });
    });

    // 解密按鈕事件
    document.getElementById("ejc_decrypt").addEventListener("click", async () => {
      const encryptedText = document.getElementById("ejc_encrypttext").value;
      const password = document.getElementById("ejc_decryptpw").value;
      if (!encryptedText || !password) {
        alert("請輸入加密結果和密碼");
        return;
      }

      try {
        const encryptedData = JSON.parse(encryptedText);
        const result = await decrypt(encryptedData, password);
        document.getElementById("ejc_decryptresult").value = result;
      } catch (e) {
        document.getElementById("ejc_decryptresult").value = "解密失敗:無效的 JSON 格式";
      }
    });
  </script>
</body>
</html>
https://ejsoon.vip/
弈趣極光:享受思維樂趣
回复

在线用户

正浏览此版面之用户: 没有注册用户 和 9 访客