PHP文件加解密求助:解密后返回空数据问题
解密返回空数据的问题修复方案
核心问题分析
你的代码存在几个关键错误,导致解密失败返回空:
- AES密钥逻辑错误:直接用RSA公钥作为AES密钥,AES-256需要固定32字节的密钥,RSA公钥是长文本,不符合要求;且RSA的正确用法是加密AES密钥,而非直接替代AES密钥。
- IV未持久化:加密时生成的随机IV没有和加密数据一起存储,解密时重新生成新的IV,导致无法匹配解密。
- 密钥不匹配:加密用RSA公钥当AES密钥,解密却用RSA私钥,两者完全不一致。
- 变量覆盖:encrypt.php中
$publicKeyPath被私钥路径覆盖,属于代码冗余错误。 - SQL注入风险:直接拼接用户输入到SQL语句,存在安全隐患。
修复步骤与代码修正
1. 修正加密逻辑(encrypt.php)
- 生成随机32字节的AES密钥,用RSA公钥加密这个AES密钥,和加密后的文件数据、IV一起存入数据库。
- 保存IV和加密后的AES密钥,确保解密时能获取到正确的参数。
- 修复变量覆盖问题,处理SQL注入。
修正后的encrypt.php:
<?php include "koneksi.php"; function generateKeyPairFromFileName() { $config = array( "default_md" => "sha512", "private_key_bits" => 2048, // 512位RSA不安全,建议用2048位 "private_key_type" => OPENSSL_KEYTYPE_RSA, ); $keypair = openssl_pkey_new($config); if (!$keypair) { die("Failed to generate key pair: " . openssl_error_string()); } openssl_pkey_export($keypair, $privateKey, null, $config); $publicKeyDetails = openssl_pkey_get_details($keypair); $publicKey = $publicKeyDetails['key']; return array( 'publicKey' => $publicKey, 'privateKey' => $privateKey ); } function savePublicKeyToLocal($publicKey, $fileName) { $publicKeyPath = $fileName . '_public_key.pem'; file_put_contents($publicKeyPath, $publicKey); return $publicKeyPath; } function savePrivateKeyToLocal($privateKey, $fileName) { $privateKeyPath = $fileName . '_private_key.pem'; file_put_contents($privateKeyPath, $privateKey); return $privateKeyPath; } function encryptFile($data, $aesKey) { $iv = openssl_random_pseudo_bytes(16); // AES-CBC需要16字节IV $encryptedData = openssl_encrypt($data, 'aes-256-cbc', $aesKey, OPENSSL_RAW_DATA, $iv); return array( 'iv' => base64_encode($iv), 'encryptedData' => base64_encode($encryptedData) ); } if(isset($_POST['generate'])){ $uploadedFileName = $_FILES['file']['name']; $filetmp = $_FILES['file']['tmp_name']; $fileContent = file_get_contents($filetmp); // 生成RSA密钥对 $keyPair = generateKeyPairFromFileName(); savePublicKeyToLocal($keyPair['publicKey'], $uploadedFileName); savePrivateKeyToLocal($keyPair['privateKey'], $uploadedFileName); // 生成随机AES-256密钥 $aesKey = openssl_random_pseudo_bytes(32); // 用RSA公钥加密AES密钥 openssl_public_encrypt($aesKey, $encryptedAesKey, $keyPair['publicKey']); $encryptedAesKeyBase64 = base64_encode($encryptedAesKey); // 加密文件内容 $encryptedFileData = encryptFile($fileContent, $aesKey); // 预处理SQL,防止注入 $stmt = $conn->prepare("INSERT INTO upload (nama_file, filedata, iv, encrypted_aes_key) VALUES (?, ?, ?, ?)"); $stmt->bind_param("ssss", $uploadedFileName, $encryptedFileData['encryptedData'], $encryptedFileData['iv'], $encryptedAesKeyBase64); if ($stmt->execute()) { echo "File berhasil diunggah dan dienkripsi."; } else { echo "Gagal mengunggah dan menyimpan file ke database: " . $stmt->error; } $stmt->close(); } ?> <form action="" method="post" enctype="multipart/form-data"> Pilih file untuk diunggah (PDF, DOCX, XLSX, TXT, maks 15 MB): <input type="file" name="file"> <button name="generate" type="submit">Generate</button> </form>
2. 修正解密逻辑(decrypt.php)
- 从数据库获取加密后的文件数据、IV、加密后的AES密钥。
- 用RSA私钥解密AES密钥。
- 使用正确的IV和AES密钥解密文件内容。
- 修复SQL注入问题。
修正后的decrypt.php:
<?php error_reporting(E_ALL); ini_set('display_errors', 1); include "koneksi.php"; function decryptFile($encryptedDataBase64, $aesKey, $ivBase64) { $encryptedData = base64_decode($encryptedDataBase64); $iv = base64_decode($ivBase64); $decryptedData = openssl_decrypt($encryptedData, 'aes-256-cbc', $aesKey, OPENSSL_RAW_DATA, $iv); return $decryptedData; } if(isset($_GET['nama_file'])){ $fileName = $_GET['nama_file']; // 读取RSA私钥 $privateKeyPath = $fileName . '_private_key.pem'; if (!file_exists($privateKeyPath)) { die("File kunci privat tidak ditemukan: " . $privateKeyPath); } $privateKey = file_get_contents($privateKeyPath); // 预处理SQL查询,防止注入 $stmt = $conn->prepare("SELECT filedata, iv, encrypted_aes_key FROM upload WHERE nama_file = ?"); $stmt->bind_param("s", $fileName); $stmt->execute(); $result = $stmt->get_result(); if ($result && $result->num_rows > 0) { $row = $result->fetch_assoc(); // 用RSA私钥解密AES密钥 $encryptedAesKey = base64_decode($row['encrypted_aes_key']); openssl_private_decrypt($encryptedAesKey, $aesKey, $privateKey); // 解密文件内容 $decryptedData = decryptFile($row['filedata'], $aesKey, $row['iv']); // 输出解密内容(如果是二进制文件,建议设置正确的Header后输出) // 示例:如果是PDF,可添加 header('Content-Type: application/pdf'); echo $decryptedData; } else { echo "File tidak ditemukan!"; } $stmt->close(); } ?>
3. 数据库表结构调整
需要在upload表中新增两个字段:
ALTER TABLE upload ADD COLUMN iv VARCHAR(255) NOT NULL; ALTER TABLE upload ADD COLUMN encrypted_aes_key TEXT NOT NULL;
额外注意事项
- RSA密钥长度建议用2048位或以上,512位属于不安全级别。
- 对于二进制文件(如docx、xlsx、pdf),直接echo输出可能显示乱码,建议设置对应的Content-Type并让浏览器下载,示例:
// 在输出decryptedData前添加: $fileExt = pathinfo($fileName, PATHINFO_EXTENSION); switch($fileExt) { case 'pdf': $mime = 'application/pdf'; break; case 'docx': $mime = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; break; case 'xlsx': $mime = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'; break; case 'txt': $mime = 'text/plain'; break; default: $mime = 'application/octet-stream'; } header("Content-Type: $mime"); header("Content-Disposition: attachment; filename=\"$fileName\""); echo $decryptedData; exit;
内容的提问来源于stack exchange,提问作者Arip Budiman
相关产品推荐
相关产品推荐

