使用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
相关产品推荐
相关产品推荐

