🚧 improve image encryption and decryption

This commit is contained in:
2025-03-22 13:05:50 +08:00
parent 3a03224f8c
commit 781a71a27c
39 changed files with 1274 additions and 977 deletions

View File

@@ -0,0 +1,148 @@
/**
* hybrid_encrypt_js.js
* 前端混合解密实现与Go后端的hybrid_encrypt.go配合使用
*/
/**
* 使用RSA私钥和AES-GCM解密数据
* @param {ArrayBuffer} ciphertext - 加密的数据
* @param {CryptoKey} privateKey - RSA私钥
* @returns {Promise<ArrayBuffer>} - 解密后的数据
*/
async function hybridDecrypt(ciphertext, privateKey) {
// 将ArrayBuffer转换为Uint8Array以便处理
const ciphertextArray = new Uint8Array(ciphertext);
// 获取RSA密钥大小字节
const keySize = getPrivateKeySize(privateKey);
// 检查密文长度是否足够
if (ciphertextArray.length < keySize + 12) {
throw new Error('密文太短');
}
// 分解密文各部分
const encryptedKey = ciphertextArray.slice(0, keySize);
const nonce = ciphertextArray.slice(keySize, keySize + 12);
const data = ciphertextArray.slice(keySize + 12);
// 使用RSA私钥解密AES密钥
const aesKeyBuffer = await window.crypto.subtle.decrypt(
{
name: 'RSA-OAEP',
hash: { name: 'SHA-256' },
},
privateKey,
encryptedKey
);
// 使用AES密钥和GCM模式解密数据
const aesKey = await window.crypto.subtle.importKey(
'raw',
aesKeyBuffer,
{ name: 'AES-GCM', length: 256 },
false,
['decrypt']
);
const plaintext = await window.crypto.subtle.decrypt(
{
name: 'AES-GCM',
iv: nonce,
},
aesKey,
data
);
return plaintext;
}
/**
* 从PEM格式导入RSA私钥
* @param {string} pemString - PEM格式的私钥字符串
* @returns {Promise<CryptoKey>} - 导入的RSA私钥
*/
async function importPrivateKeyFromPEM(pemString) {
// 移除PEM头尾和换行符
const pemContents = pemString
.replace('-----BEGIN RSA PRIVATE KEY-----', '')
.replace('-----END RSA PRIVATE KEY-----', '')
.replace(/\s+/g, '');
// Base64解码
const binaryDer = window.atob(pemContents);
const derArray = new Uint8Array(binaryDer.length);
for (let i = 0; i < binaryDer.length; i++) {
derArray[i] = binaryDer.charCodeAt(i);
}
// 导入私钥
const privateKey = await window.crypto.subtle.importKey(
'pkcs8',
derArray.buffer,
{
name: 'RSA-OAEP',
hash: { name: 'SHA-256' },
},
false,
['decrypt']
);
return privateKey;
}
/**
* 获取RSA私钥的大小字节
* @param {CryptoKey} privateKey - RSA私钥
* @returns {number} - 密钥大小(字节)
*/
function getPrivateKeySize(privateKey) {
// 在实际应用中可能需要从privateKey对象中提取密钥大小
// 这里简化处理假设使用2048位RSA密钥256字节
return 256; // 2048位 / 8 = 256字节
}
/**
* 解密Base64编码的混合加密图片数据
* @param {string} base64Data - Base64编码的加密图片数据
* @param {string} privateKeyPEM - PEM格式的RSA私钥
* @returns {Promise<string>} - 解密后的图片Base64字符串
*/
async function decryptImage(base64Data, privateKeyPEM) {
try {
// 解码Base64
const binaryString = window.atob(base64Data);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
// 导入私钥
const privateKey = await importPrivateKeyFromPEM(privateKeyPEM);
// 解密数据
const decryptedData = await hybridDecrypt(bytes.buffer, privateKey);
// 将解密后的图片数据转换为Base64
const decryptedArray = new Uint8Array(decryptedData);
let binary = '';
for (let i = 0; i < decryptedArray.length; i++) {
binary += String.fromCharCode(decryptedArray[i]);
}
// 返回带有MIME类型的Base64图片数据
// 注意这里假设是JPEG图片实际应用中可能需要根据图片类型动态设置
return 'data:image/jpeg;base64,' + window.btoa(binary);
} catch (error) {
console.error('图片解密失败:', error);
throw error;
}
}
// 导出函数
export {
hybridDecrypt,
importPrivateKeyFromPEM,
decryptImage
};