gpt4 book ai didi

javascript - 如何使用 CryptoJS 使用 AES 算法加密文件?

转载 作者:行者123 更新时间:2023-12-05 04:24:14 24 4
gpt4 key购买 nike

我有使用 RC4 算法加密文件的代码。强烈建议我使用更可靠的算法:AES。从 CryptoJS 文档中,我了解到它的工作方式与 RC4 相同。即第一个参数为要加密的字符串,第二个参数为密码字符串。

但是简单地用 AES 替换 RC4 方法没有帮助,我不知道去哪里寻找必要的信息。

谢谢!

这是我的工作(用于 RC4)代码:

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js" integrity="sha512-E8QSvWZ0eCLGk4km3hxSsNmGWbLtSCSUcewDQPQWZF6pEU8GlT8a5fF32wOl1i8ftdMhssTrF/OhyGWwonTcXA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<div>
<h1>encrypt/decrypt file</h1>
<ol>
<li>Set password</li>
<li>Pick a file</li>
<li>Download decrypted/encrypted file</li>
</ol>
<div>
<input type="text" id="pass" placeholder="pass">
<button id="encrypt">encrypt file</button>
<button id="decrypt">decrypt file</button>
<button id="test">test</button>
</div>
</div>

<script>
// support
const download = (data, filename, type) => {
const file = new Blob([data], {
type: type
});
const a = document.createElement('a');
const url = URL.createObjectURL(file);

a.href = url;
a.download = filename;
document.body.appendChild(a);

a.click();

setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
};
const pickAFile = (getText = true) => {
return new Promise((resolve, reject) => {
const input = document.createElement('input');
input.type = 'file';
input.onchange = (e) => {
const file = e.target.files[0];
const reader = new FileReader();
if (!getText) {
resolve(file);
} else {
reader.onload = (e) => resolve(e.target.result);
reader.onerror = (e) => reject(e);
reader.readAsText(file);
}
};
input.click();
});
};
const convertWordArrayToUint8Array = (wordArray) => {
const arrayOfWords = wordArray.hasOwnProperty('words') ? wordArray.words : [];

const length = wordArray.hasOwnProperty('sigBytes') ?
wordArray.sigBytes :
arrayOfWords.length * 4;

const uInt8Array = new Uint8Array(length);
let index = 0;
let word;
let i;

for (i = 0; i < length; i++) {
word = arrayOfWords[i];
uInt8Array[index++] = word >> 24;
uInt8Array[index++] = (word >> 16) & 0xff;
uInt8Array[index++] = (word >> 8) & 0xff;
uInt8Array[index++] = word & 0xff;
}
return uInt8Array;
};
// /support

function app() {
const passNode = document.querySelector('input#pass');
const encryptNode = document.querySelector('#encrypt');
const decryptNode = document.querySelector('#decrypt');

encryptNode.addEventListener('click', () => {
if (!passNode.value) return alert('Password input is empty! Aborting.');
const pass = CryptoJS.SHA3(passNode.value);
pickAFile(false).then((file) => {
const reader = new FileReader();

reader.onload = (e) => {
const wordArray = CryptoJS.lib.WordArray.create(e.target.result);
const encrypted = CryptoJS.RC4.encrypt(wordArray, pass).toString();
download(encrypted, `encrypted-${file.name}`, file.type);
};

reader.readAsArrayBuffer(file);
});
});

decryptNode.addEventListener('click', () => {
if (!passNode.value) return alert('Password input is empty! Aborting.');
const pass = CryptoJS.SHA3(passNode.value);
pickAFile(false).then((file) => {
const reader = new FileReader();

reader.onload = (e) => {
try {
const decrypted = CryptoJS.RC4.decrypt(e.target.result, pass);
const typedArray = convertWordArrayToUint8Array(decrypted);
download(typedArray, `decrypted-${file.name}`, file.type);
} catch (error) {
console.log('wrong password!');
}
};

reader.readAsText(file);
});
});
}

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', app);
} else {
app();
}
</script>

最佳答案

从 RC4 转换为 AES 时,请考虑以下事项:

  • AES 定义的 key 大小为 16 (AES-128)、24 (AES-192) 和 32 字节 (AES-256)。 key 越大,越安全(尽管如今所有变体都被认为是安全的)。下面使用 32 字节的 key 。

    您应用的 SHA3 CryptoJS 实现(实际上是 Keccak,请参阅 Hashing/SHA-3)默认输出大小为 64 字节,因此不能直接用作 AES key 。因此,为简单起见,我应用输出大小为 32 字节的 SHA-256。您也可以使用 SHA-3 并且只取例如前 32 个字节。

    但是,通过摘要推导 key 是一个漏洞。更安全的是使用像 Argon2 或 PBKDF2 这样的 key 派生。后者受 CryptoJS 支持,因此我建议切换到 PBKDF2。因为我在这里专注于将 RC4 转换为 AES,所以我会把更改留给你。

  • AES 在 CBC 模式(CryptoJS 默认使用;默认填充是 PKCS#7 btw)中需要一个随机 IV(其长度等于 block 大小,因此 AES 为 16 个字节)。出于安全原因,这不能是静态的,但必须为每次加密随机生成(使用 CSPRNG )。

    IV 不是 secret 的,需要解密。因此通常与密文拼接:IV|ciphertext。

    解密前根据已知的IV长度将IV和密文分开。

考虑到上述几点,从 RC4 到 AES 的可能转换是(另请参阅代码中的注释以了解更改的解释):

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js" integrity="sha512-E8QSvWZ0eCLGk4km3hxSsNmGWbLtSCSUcewDQPQWZF6pEU8GlT8a5fF32wOl1i8ftdMhssTrF/OhyGWwonTcXA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

<div>
<h1>encrypt/decrypt file</h1>
<ol>
<li>Set password</li>
<li>Pick a file</li>
<li>Download decrypted/encrypted file</li>
</ol>
<div>
<input type="text" id="pass" placeholder="pass">
<button id="encrypt">encrypt file</button>
<button id="decrypt">decrypt file</button>
<button id="test">test</button>
</div>
</div>

<script>
// support
const download = (data, filename, type) => {
const file = new Blob([data], {
type: type
});
const a = document.createElement('a');
const url = URL.createObjectURL(file);

a.href = url;
a.download = filename;
document.body.appendChild(a);

a.click();

setTimeout(function() {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
};
const pickAFile = (getText = true) => {
return new Promise((resolve, reject) => {
const input = document.createElement('input');
input.type = 'file';
input.onchange = (e) => {
const file = e.target.files[0];
const reader = new FileReader();
if (!getText) {
resolve(file);
} else {
reader.onload = (e) => resolve(e.target.result);
reader.onerror = (e) => reject(e);
reader.readAsText(file);
}
};
input.click();
});
};
const convertWordArrayToUint8Array = (wordArray) => {
const arrayOfWords = wordArray.hasOwnProperty('words') ? wordArray.words : [];

const length = wordArray.hasOwnProperty('sigBytes') ?
wordArray.sigBytes :
arrayOfWords.length * 4;

const uInt8Array = new Uint8Array(length);
let index = 0;
let word;
let i;

for (i = 0; i < length; i++) {
word = arrayOfWords[i];
uInt8Array[index++] = word >> 24;
uInt8Array[index++] = (word >> 16) & 0xff;
uInt8Array[index++] = (word >> 8) & 0xff;
uInt8Array[index++] = word & 0xff;
}
return uInt8Array;
};
// /support

function app() {
const passNode = document.querySelector('input#pass');
const encryptNode = document.querySelector('#encrypt');
const decryptNode = document.querySelector('#decrypt');

encryptNode.addEventListener('click', () => {
if (!passNode.value) return alert('Password input is empty! Aborting.');
const key = CryptoJS.SHA256(passNode.value); // Fix 1: Derive 32 bytes key
pickAFile(false).then((file) => {
const reader = new FileReader();

reader.onload = (e) => {
const iv = CryptoJS.lib.WordArray.random(16); // Fix 2: Create random 16 bytes IV
const wordArray = CryptoJS.lib.WordArray.create(e.target.result);
const encrypted = CryptoJS.AES.encrypt(wordArray, key, {iv: iv}); // Fix 3: Encrypt with AES using the above key and IV
const ivCiphertext = iv.clone().concat(encrypted.ciphertext).toString(CryptoJS.enc.Base64); // Fix 4: Concatenate IV and ciphertext
download(ivCiphertext, `encrypted-${file.name}`, file.type);
};

reader.readAsArrayBuffer(file);
});
});

decryptNode.addEventListener('click', () => {
if (!passNode.value) return alert('Password input is empty! Aborting.');
const key = CryptoJS.SHA256(passNode.value); // Fix 5: Derive 32 bytes key
pickAFile(false).then((file) => {
const reader = new FileReader();

reader.onload = (e) => {
try {
const ivCiphertext = CryptoJS.enc.Base64.parse(e.target.result); // Fix 6: Separate IV and ciphertext
const iv = CryptoJS.lib.WordArray.create(ivCiphertext.words.slice(0, 4));
const ciphertext = CryptoJS.lib.WordArray.create(ivCiphertext.words.slice(4));
const decrypted = CryptoJS.AES.decrypt({ciphertext: ciphertext}, key, {iv: iv}); // Fix 7: Decrypt
const typedArray = convertWordArrayToUint8Array(decrypted);
download(typedArray, `decrypted-${file.name}`, file.type);
} catch (error) {
console.log('wrong password!');
}
};

reader.readAsText(file);
});
});
}

if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', app);
} else {
app();
}
</script>

请注意,代码仍然可以优化(这也适用于 RC4 变体):在加密期间,数据(即连接的 IV 和密文)是 Base64编码。 Base64 是一种二进制到文本的编码,可将数据大小增加约 33%。当要将任意二进制数据表示为文本时使用它。
但是,由于这里的数据是以文件的形式存储的,所以这样的转换并不是真正必要的(当然,也有可能是帖子中看不出来的原因)。相反,可以存储原始数据(即非 Base64 编码数据),这将相应地减小文件大小。

关于javascript - 如何使用 CryptoJS 使用 AES 算法加密文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73551878/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com