fix: 优化客户端错误提示和校验诊断信息

This commit is contained in:
2026-07-21 03:02:42 +00:00
parent 1c8ec37d2b
commit d9e32c6cea
14 changed files with 1739 additions and 627 deletions
+79 -26
View File
@@ -1,6 +1,7 @@
#include "PolicyHelper.h"
#include "ConfigHelper.h"
#include <QApplication>
#include <QCoreApplication>
#include <QDateTime>
#include <QDir>
#include <QFile>
@@ -29,15 +30,26 @@ static QString resolvePolicyPath(const QString& baseDir, const QString& relative
bool PolicyHelper::loadPolicy(const QString& relativePath)
{
QFile file(resolvePolicyPath(m_baseDir, relativePath, false));
if (!file.open(QIODevice::ReadOnly)) { m_error = "Cannot open policy file"; return false; }
QJsonParseError error;
const QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
if (error.error != QJsonParseError::NoError || !doc.isObject()) {
m_error = "Invalid policy JSON: " + error.errorString(); return false;
}
return loadPolicyObject(doc.object());
}
const QString path = resolvePolicyPath(m_baseDir, relativePath, false);
QFile file(path);
if (!file.open(QIODevice::ReadOnly)) {
m_error = QCoreApplication::translate(
"PolicyHelper",
"Cannot open signed version policy file: %1. Error: %2.")
.arg(path, file.errorString());
return false;
}
QJsonParseError error;
const QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
if (error.error != QJsonParseError::NoError || !doc.isObject()) {
m_error = QCoreApplication::translate(
"PolicyHelper",
"Signed version policy is not valid JSON. File: %1. JSON error: %2.")
.arg(path, error.errorString());
return false;
}
return loadPolicyObject(doc.object());
}
bool PolicyHelper::loadPolicyObject(const QJsonObject& policy, const QString& signedText)
{
@@ -51,7 +63,10 @@ bool PolicyHelper::savePolicy(const QString& relativePath) const
QString writeError;
const QString path = resolvePolicyPath(m_baseDir, relativePath, true);
if (!ConfigHelper::writeFileWithElevationIfNeeded(path, bytes, &writeError)) {
m_error = "Cannot save policy file: " + writeError;
m_error = QCoreApplication::translate(
"PolicyHelper",
"Cannot save signed version policy file: %1. Error: %2.")
.arg(path, writeError);
return false;
}
m_error.clear();
@@ -84,35 +99,73 @@ QByteArray PolicyHelper::canonicalPolicyBytes(const QJsonObject& policy) const
bool PolicyHelper::verifySignature(const QByteArray& payload, const QString& signatureBase64) const
{
#ifndef HAVE_OPENSSL
Q_UNUSED(payload); Q_UNUSED(signatureBase64); m_error = "OpenSSL unavailable"; return false;
#else
QString keyPath = m_baseDir + "/config/manifest_public_key.pem";
QFile keyFile(keyPath);
if (!keyFile.open(QIODevice::ReadOnly)) { m_error = "Cannot open policy public key"; return false; }
#ifndef HAVE_OPENSSL
Q_UNUSED(payload);
Q_UNUSED(signatureBase64);
m_error = QCoreApplication::translate(
"PolicyHelper",
"OpenSSL is unavailable, so the signed version policy cannot be verified.");
return false;
#else
QString keyPath = m_baseDir + "/config/manifest_public_key.pem";
QFile keyFile(keyPath);
if (!keyFile.open(QIODevice::ReadOnly)) {
m_error = QCoreApplication::translate(
"PolicyHelper",
"Cannot open version policy public key: %1. Error: %2.")
.arg(keyPath, keyFile.errorString());
return false;
}
const QByteArray keyData = keyFile.readAll();
BIO* bio = BIO_new_mem_buf(keyData.constData(), keyData.size());
EVP_PKEY* key = bio ? PEM_read_bio_PUBKEY(bio, nullptr, nullptr, nullptr) : nullptr;
if (bio) BIO_free(bio);
if (!key) { m_error = "Invalid policy public key"; return false; }
if (!key) {
m_error = QCoreApplication::translate(
"PolicyHelper",
"Version policy public key is invalid: %1.")
.arg(keyPath);
return false;
}
EVP_MD_CTX* ctx = EVP_MD_CTX_new();
const QByteArray signature = QByteArray::fromBase64(signatureBase64.toUtf8());
bool ok = ctx && EVP_DigestVerifyInit(ctx, nullptr, EVP_sha256(), nullptr, key) == 1
&& EVP_DigestVerifyUpdate(ctx, payload.constData(), payload.size()) == 1
&& EVP_DigestVerifyFinal(ctx, reinterpret_cast<const unsigned char*>(signature.constData()), signature.size()) == 1;
if (ctx) EVP_MD_CTX_free(ctx); EVP_PKEY_free(key);
if (!ok) m_error = "Invalid RSA policy signature";
return ok;
#endif
}
if (!ok) {
m_error = QCoreApplication::translate(
"PolicyHelper",
"Version policy RSA signature is invalid. The policy file may have been changed, or the public key does not match the server private key.");
}
return ok;
#endif
}
bool PolicyHelper::isValid() const
{
if (!m_loaded) return false;
const QStringList required{"app_id","channel","current_version","policy_seq","allow_run",
"force_update","allow_rollback","offline_allowed","valid_until","signature_alg","key_id","signature"};
for (const QString& key : required) if (!m_policy.contains(key)) { m_error = "Missing policy field: " + key; return false; }
if (m_policy.value("signature_alg").toString() != "RSA-2048-SHA256") return false;
if (!m_loaded) {
m_error = QCoreApplication::translate("PolicyHelper", "Version policy has not been loaded.");
return false;
}
const QStringList required{"app_id","channel","current_version","policy_seq","allow_run",
"force_update","allow_rollback","offline_allowed","valid_until","signature_alg","key_id","signature"};
for (const QString& key : required) {
if (!m_policy.contains(key)) {
m_error = QCoreApplication::translate(
"PolicyHelper",
"Version policy is missing required field: %1.")
.arg(key);
return false;
}
}
if (m_policy.value("signature_alg").toString() != "RSA-2048-SHA256") {
m_error = QCoreApplication::translate(
"PolicyHelper",
"Version policy signature algorithm is unsupported: %1.")
.arg(m_policy.value("signature_alg").toString());
return false;
}
const QByteArray payload = m_signedText.isEmpty() ? canonicalPolicyBytes(m_policy) : m_signedText.toUtf8();
return verifySignature(payload, m_policy.value("signature").toString());
}