丁同亚的博客
夺朱非正色

crypto-js不支持 AES_GCM 加密 ,听说可以用 crypto 支持?当时是自己写的....

crypto API 要求页面在安全上下文(Secure Context)中运行。localhost 和 127.0.0.1 被视为安全上下文,但通过普通 IP 地址(如 192.168.x.x)访问时,浏览器可能认为不安全。

解决方法:
如果必须使用 IP 地址,可以尝试将 IP 地址添加到浏览器的安全上下文中(不推荐,仅限开发环境)。
在生产环境中,强烈建议使用 HTTPS 来确保安全上下文。


async function deriveKeyFromMaterial(keyMaterial, lengthBits) {
    // 使用 SHA-256 哈希算法来生成足够长度的密钥材料
    //   const hashedKey = await crypto.subtle.digest(
    //     "SHA-256",
    //     new TextEncoder().encode(keyMaterial)
    //   );
    //   console.log(hashedKey, "hashedKey");
    let hashedKey = stringToBytes(keyMaterial);
    hashedKey = adjustKeyLength(hashedKey, 16);
    // 导入哈希后的密钥材料作为AES密钥
    return await crypto.subtle.importKey(
        "raw",
        // lengthBits === 256 ? hashedKey : hashedKey.slice(0, lengthBits / 8),
        hashedKey,
        { name: "AES-GCM" },
        false,
        ["encrypt", "decrypt"]
    );
}
function stringToBytes(str) {
    const encoder = new TextEncoder(); // 默认使用 UTF-8 编码
    return encoder.encode(str);
}
function adjustKeyLength(keyBytes, requiredLength) {
    if (keyBytes.length === requiredLength) {
        return keyBytes;
    }

    const adjustedKey = new Uint8Array(requiredLength);
    if (keyBytes.length < requiredLength) {
        // 填充
        adjustedKey.set(keyBytes);
        for (let i = keyBytes.length; i < requiredLength; i++) {
            adjustedKey[i] = 0; // 用 0 填充
        }
    } else {
        // 截断
        for (let i = 0; i < requiredLength; i++) {
            adjustedKey[i] = keyBytes[i];
        }
    }
    return adjustedKey;
}
async function encryptText(text, keyMaterial, keyLengthBits) {
    const key = await deriveKeyFromMaterial(keyMaterial, keyLengthBits);
    // 生成IV
    const iv = crypto.getRandomValues(new Uint8Array(12)); // GCM模式推荐使用12字节IV

    // 加密数据
    const encoder = new TextEncoder();
    const encryptedData = await crypto.subtle.encrypt(
        {
            name: "AES-GCM",
            iv: iv,
        },
        key,
        encoder.encode(text)
    );

    // 将IV和加密后的数据拼接起来
    const combined = new Uint8Array(iv.length + encryptedData.byteLength);
    combined.set(iv, 0);
    combined.set(new Uint8Array(encryptedData), iv.length);

    // 将结果编码为Base64字符串
    return btoa(String.fromCharCode.apply(null, combined));
}
async function decryptText(encryptedBase64, keyMaterial, keyLengthBits) {
    // 将Base64字符串转换为Uint8Array
    const binaryString = atob(encryptedBase64);
    const combined = new Uint8Array(binaryString.length);
    for (let i = 0; i < binaryString.length; i++) {
        combined[i] = binaryString.charCodeAt(i);
    }

    // 提取IV
    const iv = combined.slice(0, 12);

    // 提取加密数据
    const encryptedBytes = combined.slice(12);

    // 导入密钥材料(需要确保这个密钥与Java端使用的是一样的)
    const key = await deriveKeyFromMaterial(keyMaterial, keyLengthBits);

    try {
        // 解密数据
        const decryptedData = await crypto.subtle.decrypt(
            {
                name: "AES-GCM",
                iv: iv,
            },
            key,
            encryptedBytes
        );

        // 将解密后的数据转换为字符串
        const decoder = new TextDecoder();
        return decoder.decode(decryptedData);
    } catch (error) {
        console.error("Decryption failed:", error);
        throw error;
    }
}

const keyMaterial = "_aes_secret_key_"; // 确保这与Java端的密钥一致
const textToEncrypt = "admsdsdin";

encryptText(textToEncrypt, keyMaterial).then((encrypted) => {
    console.log("Encrypted:", encrypted);
    decryptText(encrypted, keyMaterial).then((decrypted) => {
        console.log("Decrypted:", decrypted);
    });
});
posted on 2025-02-05 09:20  丁同亚的博客  阅读(132)  评论(0)    收藏  举报