为什么分离签名的验证会导致 OpenPGP.js 的 "Message digest did not match" 错误?

Why does verification of a detached signature result in a "Message digest did not match" error for OpenPGP.js?

我可以毫无问题地通过 gpg 对测试文件进行签名和验证,但是通过 OpenGPG.js 验证签名失败并出现错误“消息摘要不匹配”。这是为什么?

$ gpg --armor --quiet --batch --yes --detach-sig index.html
$ gpg --verify index.html.asc index.html
gpg: Signature made Wed 25 Nov 2020 08:26:34 PM PST
gpg:                using RSA key C361FDC3F93B9E8F8BD7E08D5F873051B2D6C347
gpg: Good signature from <key>
$ node sandbox.js 
{ signatures:
   [ { keyid: [Keyid],
       verified: [Promise],
       signature: [Signature],
       valid: false,
       error:
        Error: Message digest did not match
            at Signature.verify (/home/caleb/src/islands/node_modules/openpgp/dist/openpgp.js:41176:11)
            at process._tickCallback (internal/process/next_tick.js:68:7)
            at Function.Module.runMain (internal/modules/cjs/loader.js:834:11)
            at startup (internal/bootstrap/node.js:283:19)
            at bootstrapNodeJSCore (internal/bootstrap/node.js:623:3) } ],
  data: 'Test!\n' }

sandbox.js(基于the OpenPGP.js example):

openpgp = require('openpgp');
fs = require('fs');

async function sandbox() {
    path = './'

    let msg_data = fs.readFileSync(path + "index.html", 'utf8');
    let sig_data = fs.readFileSync(path + "index.html.asc", 'utf8');
    let pubkey_data = fs.readFileSync(path + "pubkey.asc", 'utf8');

    let msg = await openpgp.cleartext.fromText(msg_data);
    let sig = await openpgp.signature.readArmored(sig_data);
    let pubkey = await openpgp.key.readArmored(pubkey_data);

    openpgp.verify({
        message: msg,
        signature: sig,
        publicKeys: pubkey.keys
    }).then(function(verified) {
        console.log(verified);
    });
}

sandbox();

index.html:

Test!

index.html.sig:

-----BEGIN PGP SIGNATURE-----

iQIzBAABCgAdFiEEw2H9w/k7no+L1+CNX4cwUbLWw0cFAl+/LnoACgkQX4cwUbLW
w0cfcBAAlYx+81hVyOmBKF8BVHUyZ7c7xNEDEy8fe2EvEQlz4KL+O5PdWFWdlopl
FrdHenxQktfTZPs+bjWTu52OwIGomAQP7Vu4zvkScQw2M4xdBwuJTsB9/hZNJPLX
+9lWFVUffYDFPHziNRZCK1vlYpi/fJO1KrmK3ggSJCUylkjl+QtZbSE1EAuxNF4S
3NsxzH1YcyuZP8dvuMFqfHNQVfDcny5AB67fmHdf/aMnq6B05d437qqfVgCjIAKp
11naQ4g8hNSVIwacCWO/sjsa5kSvg2oKI76GTQtBFwybLh9KJTByOUbkbNOJooWM
+kpbfWSIQ7Dtl6Q4dDF9C2QJH0Af4YgKjzgT3hWU57FEw/3i7DTseZ3r2YtDcBkv
GNaFmRG0mBkRy5pyAliKC91LPWH/FznRQLSA4S2XNce1AxNJKWZpxlbtf79ns6RQ
TagE2gRt/vq+UjdN+FL6uyEdhVroWOv2vFTY5bsdD9wy8HyBSyXr0wfSMze7Ci1e
t1s098eM9MFwtx2oNOcWbKYxHy4lnDotnGkZ1WFOQMWHTKlVichL1eFNQcx08Mhw
82hoOnS49kNHrzgVV8jP7y+0Nfjcg9vqUV1kR2KkxDO7xoxtwev0k7Ol/mDOA3xi
ckPKkU60NlzMHsQe+DzW2wCMKC751ds50huLXMo1txoQ5yo6m6o=
=7Z6F
-----END PGP SIGNATURE-----

pubkey.asc:

-----BEGIN PGP PUBLIC KEY BLOCK-----

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
tYSsa0zo3W8EwXhkoia5hOf/YhubcMkhqXU=
=Hgou
-----END PGP PUBLIC KEY BLOCK-----

npm list 显示 openpgp@4.10.8.

Ingo Klöcker 回答了这个问题on the gnupg users mailing list。总而言之,gpg 的默认操作模式使用非 签名时的文本模式,它显示 OpenPGP.js 默认为文本模式(至少在这里使用的方式)。将 --textmode 添加到 gpg 命令行允许 OpenPGP.js 验证签名,从而解决了问题。

如果不需要文本模式,也可以从文件中读取二进制数据:

...
    let msg_data = fs.readFileSync(path + "index.html", null);
    let sig_data = fs.readFileSync(path + "index.html.sig", null);
    let pubkey_data = fs.readFileSync(path + "pubkey.asc", 'utf8');

    let msg = await openpgp.message.fromBinary(msg_data);
    let sig = await openpgp.signature.read(sig_data);
    let pubkey = await openpgp.key.readArmored(pubkey_data);

    openpgp.verify({
        message: msg,
        signature: sig,
        publicKeys: pubkey.keys
    }).then(function(verified) {
        console.log(verified);
    });
...