Files

148 lines
4.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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
};