使用 Qt 的 QCA::SecureMessage 验证 PGP 签名消息

问题描述 投票:0回答:1

我想编写一个 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 的应用程序可以在只知道发送者的公钥的情况下读取和验证它,还是有更简单的方法?

c++ qt cryptography
1个回答
0
投票

正如 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);
}

这里的缺点是现在签名是分离的,即它存在于单独的文件中,但至少可以验证消息。

© www.soinside.com 2019 - 2024. All rights reserved.