#include "ConfigEncryption.h" #include #include #include #include #include "VrLog.h" #ifdef _WIN32 #include #include #include // 取消 Windows API 宏定义,避免与我们的函数名冲突 #ifdef EncryptFile #undef EncryptFile #endif #ifdef DecryptFile #undef DecryptFile #endif #else // Linux/Unix 平台头文件 #include #include #include #endif const char* ConfigEncryption::MAGIC_HEADER = "VRENC1.0"; QByteArray ConfigEncryption::GenerateKey(const QString& password, const QByteArray& salt) { // 使用 PBKDF2 简化版本:多轮 SHA-256 哈希 QByteArray key = password.toUtf8() + salt; // 进行多轮哈希增强安全性 for (int i = 0; i < 10000; i++) { key = QCryptographicHash::hash(key, QCryptographicHash::Sha256); } return key; } QByteArray ConfigEncryption::EncryptConfig(const QByteArray& plainData, const QString& password) { if (plainData.isEmpty() || password.isEmpty()) { LOG_ERROR("加密失败:数据或密码为空\n"); return QByteArray(); } try { // 生成随机盐值 QByteArray salt; salt.resize(SALT_SIZE); for (int i = 0; i < SALT_SIZE; i++) { salt[i] = static_cast(QRandomGenerator::global()->bounded(256)); } // 生成加密密钥 QByteArray key = GenerateKey(password, salt); // 加密数据(使用流密码方式:异或操作) QByteArray encryptedData; encryptedData.reserve(plainData.size()); for (int i = 0; i < plainData.size(); i++) { // 使用密钥循环异或 encryptedData.append(plainData[i] ^ key[i % key.size()]); } // 构建最终格式:魔术头 + 盐值 + 加密数据 QByteArray result; result.append(MAGIC_HEADER, 8); // 8字节魔术头 result.append(salt); // 16字节盐值 result.append(encryptedData); // 加密数据 // 添加校验和(最后4字节) QByteArray checksum = QCryptographicHash::hash(result, QCryptographicHash::Md5); result.append(checksum.left(4)); LOG_INFO("配置数据加密成功,原始大小: %d, 加密后大小: %d\n", plainData.size(), result.size()); return result; } catch (const std::exception& e) { LOG_ERROR("加密过程发生异常: %s\n", e.what()); return QByteArray(); } } QByteArray ConfigEncryption::DecryptConfig(const QByteArray& encryptedData, const QString& password) { if (encryptedData.isEmpty() || password.isEmpty()) { LOG_ERROR("解密失败:数据或密码为空\n"); return QByteArray(); } try { // 检查最小长度:魔术头(8) + 盐值(16) + 校验和(4) = 28字节 if (encryptedData.size() < 28) { LOG_ERROR("解密失败:数据长度不足\n"); return QByteArray(); } // 验证魔术头 QByteArray header = encryptedData.left(8); if (header != QByteArray(MAGIC_HEADER, 8)) { LOG_ERROR("解密失败:文件格式错误(魔术头不匹配)\n"); return QByteArray(); } // 验证校验和 QByteArray dataWithoutChecksum = encryptedData.left(encryptedData.size() - 4); QByteArray storedChecksum = encryptedData.right(4); QByteArray calculatedChecksum = QCryptographicHash::hash(dataWithoutChecksum, QCryptographicHash::Md5).left(4); if (storedChecksum != calculatedChecksum) { LOG_ERROR("解密失败:数据完整性校验失败\n"); return QByteArray(); } // 提取盐值 QByteArray salt = encryptedData.mid(8, SALT_SIZE); // 提取加密数据 QByteArray encData = encryptedData.mid(8 + SALT_SIZE, encryptedData.size() - 8 - SALT_SIZE - 4); // 生成解密密钥 QByteArray key = GenerateKey(password, salt); // 解密数据(使用相同的异或操作) QByteArray plainData; plainData.reserve(encData.size()); for (int i = 0; i < encData.size(); i++) { plainData.append(encData[i] ^ key[i % key.size()]); } LOG_INFO("配置数据解密成功,解密后大小: %d\n", plainData.size()); return plainData; } catch (const std::exception& e) { LOG_ERROR("解密过程发生异常: %s\n", e.what()); return QByteArray(); } } bool ConfigEncryption::EncryptFile(const QString& filePath, const QString& password) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { LOG_ERROR("无法打开文件进行加密: %s\n", filePath.toStdString().c_str()); return false; } QByteArray plainData = file.readAll(); file.close(); if (plainData.isEmpty()) { LOG_ERROR("文件为空,无法加密: %s\n", filePath.toStdString().c_str()); return false; } // 加密数据 QByteArray encryptedData = ConfigEncryption::EncryptConfig(plainData, password); if (encryptedData.isEmpty()) { LOG_ERROR("加密数据失败: %s\n", filePath.toStdString().c_str()); return false; } // 写入加密数据 if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { LOG_ERROR("无法打开文件进行写入: %s\n", filePath.toStdString().c_str()); return false; } qint64 written = file.write(encryptedData); file.close(); if (written != encryptedData.size()) { LOG_ERROR("写入加密数据失败: %s\n", filePath.toStdString().c_str()); return false; } LOG_INFO("文件加密成功: %s\n", filePath.toStdString().c_str()); return true; } bool ConfigEncryption::DecryptFile(const QString& filePath, const QString& password) { QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { LOG_ERROR("无法打开文件进行解密: %s\n", filePath.toStdString().c_str()); return false; } QByteArray encryptedData = file.readAll(); file.close(); if (encryptedData.isEmpty()) { LOG_ERROR("文件为空,无法解密: %s\n", filePath.toStdString().c_str()); return false; } // 解密数据 QByteArray plainData = ConfigEncryption::DecryptConfig(encryptedData, password); if (plainData.isEmpty()) { LOG_ERROR("解密数据失败: %s\n", filePath.toStdString().c_str()); return false; } // 写入解密数据 if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { LOG_ERROR("无法打开文件进行写入: %s\n", filePath.toStdString().c_str()); return false; } qint64 written = file.write(plainData); file.close(); if (written != plainData.size()) { LOG_ERROR("写入解密数据失败: %s\n", filePath.toStdString().c_str()); return false; } LOG_INFO("文件解密成功: %s\n", filePath.toStdString().c_str()); return true; } bool ConfigEncryption::VerifyPassword(const QByteArray& encryptedData, const QString& password) { // 尝试解密,如果成功则密码正确 QByteArray decrypted = DecryptConfig(encryptedData, password); return !decrypted.isEmpty(); } #ifdef _WIN32 bool ConfigEncryption::SetDirectoryPermissions(const QString& directoryPath) { LOG_INFO("设置目录权限: %s\n", directoryPath.toStdString().c_str()); // 转换为宽字符 std::wstring wPath = directoryPath.toStdWString(); // 构建安全描述符字符串(SDDL) // D: - DACL // (D;;GA;;;WD) - 拒绝所有用户的通用访问 // (A;;GA;;;BA) - 允许管理员组的通用访问 // (A;;GA;;;SY) - 允许系统的通用访问 LPCWSTR sddl = L"D:(D;;GA;;;WD)(A;;GA;;;BA)(A;;GA;;;SY)"; PSECURITY_DESCRIPTOR pSD = NULL; if (!ConvertStringSecurityDescriptorToSecurityDescriptorW( sddl, SDDL_REVISION_1, &pSD, NULL)) { DWORD error = GetLastError(); LOG_ERROR("创建安全描述符失败,错误代码: %lu\n", error); return false; } // 设置目录的安全描述符 DWORD result = SetNamedSecurityInfoW( const_cast(wPath.c_str()), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, NULL, NULL); if (result != ERROR_SUCCESS) { LOG_ERROR("设置目录权限失败,错误代码: %lu\n", result); LocalFree(pSD); return false; } // 应用 DACL PACL pDacl = NULL; BOOL bDaclPresent = FALSE; BOOL bDaclDefaulted = FALSE; if (!GetSecurityDescriptorDacl(pSD, &bDaclPresent, &pDacl, &bDaclDefaulted)) { LOG_ERROR("获取 DACL 失败\n"); LocalFree(pSD); return false; } if (bDaclPresent) { result = SetNamedSecurityInfoW( const_cast(wPath.c_str()), SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION, NULL, NULL, pDacl, NULL); if (result != ERROR_SUCCESS) { LOG_ERROR("应用 DACL 失败,错误代码: %lu\n", result); LocalFree(pSD); return false; } } LocalFree(pSD); LOG_INFO("目录权限设置成功\n"); return true; } #else bool ConfigEncryption::SetDirectoryPermissions(const QString& directoryPath) { LOG_INFO("设置目录权限(Linux): %s\n", directoryPath.toStdString().c_str()); // 转换路径为标准 C 字符串 std::string path = directoryPath.toStdString(); // 设置目录权限为 700 (仅所有者可读写执行) // S_IRUSR | S_IWUSR | S_IXUSR = 0700 if (chmod(path.c_str(), S_IRUSR | S_IWUSR | S_IXUSR) != 0) { LOG_ERROR("设置目录权限失败: %s (errno: %d)\n", directoryPath.toStdString().c_str(), errno); return false; } LOG_INFO("目录权限设置成功(权限: 700)\n"); return true; } #endif