我想编写一个 Qt 应用程序,读取签名消息并通过
QCA::SecureMessage
验证其签名。使用 QCA 的 examples 页面并从公开的 repository 中读取他们的单元测试,我想出了以下代码:
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"
我的解释是公钥没有被实际使用,需要导入。 事实上,如果我手动从 shell 导入密钥,执行
gpg --import pubKey.asc
在执行我的应用程序之前,它就可以工作,因为密钥是在系统的只读密钥库中找到的。但是,当我发布此代码时,我将无法访问执行机器上的命令行。
我可以通过编程方式将公钥导入到现有的 KeyStore 中吗?或者我应该创建自己的密钥库,因为现有的两个密钥库都是只读的?在这种情况下,我如何将它添加到 KeyStoreManager 中,从它的文档中似乎没有这样的方法? 更一般地说:这是将签名文件发送到设备的明智方法,我的基于 Qt 的应用程序可以在只知道发送者的公钥的情况下读取和验证它,还是有更简单的方法?
正如 crhis_se 在评论中指出的那样,QCA 显然不支持这个特定的用例。 为了解决验证签名文件的问题,我最终采用了不同的解决方案,使用
QCA::PublicKey
读取 RSA 公钥并验证消息:
bool verifyMessage(const QString& pubKeyPath, const QString& messagePath, const QString& signaturePath){
// ... Checking that files exist
QFile messageFile(messagePath);
messageFile.open(QIODevice::ReadOnly);
QFile signatureFile(signaturePath);
signatureFile.open(QIODevice::ReadOnly);
// Initialize QCA
QCA::Initializer init;
QCA::ConvertResult convResult;
QCA::PublicKey publicKey = QCA::RSAPublicKey::fromPEMFile(pubKeyPath, &convResult);
if (convResult != QCA::ConvertGood) {
qCWarning(DroneActivationLog) << "Could not read public key";
return;
}
QCA::MemoryRegion fileRegion(messageFile.readAll()); // Depending on the format, trimmed() might be necessary to remove the EOF character.
return publicKey.verifyMessage(fileRegion, signatureFile.readAll(), QCA::EMSA3_SHA256);
}
这里的缺点是现在签名是分离的,即它存在于单独的文件中,但至少可以验证消息。