You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 04:32:34