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

使用Qt QCA::SecureMessage验证签名消息的密钥问题及解决方案咨询

Qt QCA::SecureMessage 签名验证问题及解决方案

问题描述

编写Qt应用通过QCA::SecureMessage读取并验证非分离签名消息时,已确认消息和公钥文件读取正常,但验证始终失败,错误提示公钥未找到。手动通过gpg --import导入公钥后验证成功,但部署时无法操作命令行。相关代码及错误信息如下:

验证代码

bool verifyMessage(const QString& messagePath, const QString& pubKeyPath){
    // ... Checking that files exist and can be opened
    QFile messageFile(messagePath);
    messageFile.open(QIODevice::ReadOnly);
    
    // Initialize QCA and store manager
    QCA::Initializer init;
    QCA::KeyStoreManager::start();
    QCA::KeyStoreManager keyStoreManager(this);
    keyStoreManager.waitForBusyFinished();

    // qDebug() << keyStoreManager.keyStores(); // this yields 2 existing stores, ("qca-softstore", "qca-default-systemstore"), both valid and readOnly

    QCA::KeyStore pgpStore(QStringLiteral("qca-default-systemstore"), &keyStoreManager);
    QCA::ConvertResult convResult;
    QCA::PGPKey publicPGPKey = QCA::PGPKey::fromFile(pubKeyPath, &convResult);
    if (convResult != QCA::ConvertGood || publicPGPKey.isNull()) {
        qCWarning(DroneActivationLog) << "Failed to load public key";
        return false;
    }
    // pgpStore.writeEntry(publicPGPKey); // This fails because the store is readonly
    QByteArray signedData = messageFile.readAll(); // messageFile contains the whole signed message, signature is not detached
    
    // Initializing pgp and message
    QCA::OpenPGP pgp;
    QCA::SecureMessageKey key;
    QCA::SecureMessage message(&pgp);
    key.setPGPPublicKey(publicPGPKey);

    message.setRecipient(key); // I suspect this is wrong
    message.setFormat(QCA::SecureMessage::Format::Ascii);
    message.startVerify();
    message.update(signedData);
    message.end();
    message.waitForFinished();

    if (message.verifySuccess()) {
        qDebug() << "VERIFICATION SUCCESS" << message.read();
        return true;
    }
    qDebug() << "VERIFICATION ERROR\n" << message.diagnosticText();
    return false;
}

错误信息

VERIFICATION ERROR
"GPGProc: Pipe setup completeGPGProc: Running: [/usr/bin/gpg --no-tty --pinentry-mode loopback --enable-special-filenames --status-fd 16 --command-fd 13 --armor --decrypt]GPGProc: Process started{PLAINTEXT 75 1728306273 }{PLAINTEXT_LENGTH 11}{NEWSIG}{ERRSIG F092BE69AD20348E 1 10 00 1728306273 9 -}{NO_PUBKEY F092BE69AD20348E}GPGProc: Process finished: 2GPGProc: DoneGPG Process Finished: exitStatus=2stderr: [gpg: Signature made Mon 07 Oct 2024 01:04:33 PM UTC
gpg:                using RSA key F092BE69AD20348E
gpg: Can't check signature: No public key
]GpgAction error: ErrorUnknown
wasSigned: verifyResult: VerifyNoKey"

核心疑问:

  • 能否编程导入公钥到现有只读密钥库?
  • 是否需要创建自定义密钥库,如何添加到KeyStoreManager?
  • 仅用发送方公钥验证的方案是否合理,有无更简便方式?

解决方案

1. 直接使用加载的公钥验证(最优方案)

问题核心错误是调用了setRecipient(key)——这个方法用于指定加密消息的接收者,而签名验证需要指定签名者的公钥,只需调用setSignerKey即可,完全不需要依赖密钥库。修改后的代码如下:

bool verifyMessage(const QString& messagePath, const QString& pubKeyPath){
    // 检查文件存在性与可读取性
    if (!QFile::exists(messagePath) || !QFile::exists(pubKeyPath)) {
        qCWarning(DroneActivationLog) << "Required file not found";
        return false;
    }

    QFile messageFile(messagePath);
    if (!messageFile.open(QIODevice::ReadOnly)) {
        qCWarning(DroneActivationLog) << "Failed to open message file";
        return false;
    }

    QCA::Initializer init;
    QCA::ConvertResult convResult;
    QCA::PGPKey publicPGPKey = QCA::PGPKey::fromFile(pubKeyPath, &convResult);
    if (convResult != QCA::ConvertGood || publicPGPKey.isNull()) {
        qCWarning(DroneActivationLog) << "Failed to load public key";
        return false;
    }

    QByteArray signedData = messageFile.readAll();
    messageFile.close();

    QCA::OpenPGP pgp;
    QCA::SecureMessage message(&pgp);
    QCA::SecureMessageKey signerKey;
    signerKey.setPGPPublicKey(publicPGPKey);

    // 关键:设置签名者公钥而非接收者
    message.setSignerKey(signerKey);
    message.setFormat(QCA::SecureMessage::Format::Ascii);
    
    if (!message.startVerify()) {
        qCWarning(DroneActivationLog) << "Failed to initiate verification";
        return false;
    }
    message.update(signedData);
    message.end();
    message.waitForFinished();

    if (message.verifySuccess()) {
        qDebug() << "VERIFICATION SUCCESS" << message.read();
        return true;
    }
    qDebug() << "VERIFICATION ERROR\n" << message.diagnosticText();
    return false;
}

该方案无需操作密钥库,绕开了系统密钥库只读的限制,代码更简洁,也不依赖系统密钥库状态。

2. 自定义可写密钥库(可选)

如果确实需要将公钥持久化存储,可以创建自定义的可写密钥库:

  • 系统默认的qca-default-systemstore和qca-softstore通常为只读,无法写入;
  • 可以创建基于文件的自定义密钥库,代码示例:
QCA::Initializer init;
QCA::KeyStoreManager::start();
QCA::KeyStoreManager keyStoreManager(this);
keyStoreManager.waitForBusyFinished();

// 创建自定义可写密钥库(存储到本地文件)
QCA::KeyStore customStore = QCA::KeyStore::create(
    QStringLiteral("qca-softstore"), 
    QStringLiteral("my_app_keystore"), 
    this
);

if (customStore.isValid() && !customStore.isReadOnly()) {
    QCA::ConvertResult convResult;
    QCA::PGPKey publicPGPKey = QCA::PGPKey::fromFile(pubKeyPath, &convResult);
    if (convResult == QCA::ConvertGood && !publicPGPKey.isNull()) {
        customStore.writeEntry(publicPGPKey);
    }

    // 后续验证时,可从自定义密钥库获取密钥
    QList<QCA::KeyStoreEntry> entries = customStore.entryList(QCA::KeyStoreEntry::PublicKey);
    // 查找目标公钥并使用
}

注意:需要确保应用对密钥库存储路径有写入权限,部署时需妥善管理密钥库文件。

3. 方案合理性与简化总结

  • 仅使用发送方公钥验证签名是合理且标准的PGP签名验证流程,符合非对称加密的安全逻辑;
  • 最简便的方式就是直接使用加载的公钥验证(方案1),无需涉及密钥库管理,避免了系统依赖问题。

内容的提问来源于stack exchange,提问作者Michele Bolognini

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 15:00:55