我正在尝试使用 JavaScript 验证 PKCS#7 签名。我知道 Web Cryptography API 不支持 PKCS#7,但我只是想验证原始 RSA 签名,而不是完整的 PKCS#7 结构。
我能够解析 PKCS#7、验证证书链、提取签名数据并使用forge验证签名。我不明白为什么无法使用 Web Cryptography API 验证此签名。
考虑这个例子:
var publicKey_pem = "\
-----BEGIN PUBLIC KEY-----\n\
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyKy7Fm7M5eKVVsOWvpDY\n\
7OrJ4WJY8kRupJFY2TmsfeOxcZTztx7igt3PRhUtk/P9hNbRge1Hh2lQbnlEozn3\n\
i335jkGHoHbqGhBim6o4PUikByxEY46NEzxa0p4MdmnmcWh8oqhNH6k0T1ss1eAM\n\
kCJZNwXqGM64+VuJ58k+H0f1NYFDmmnZVkXNeiRcS7T8MtJEDv0Kni06Brl9KMQa\n\
xEUx1DLEwmTtW3tV/EA6erIYwpI/yWLdLxT/LxYdtAo9sx55sXXSgFKzTJkLwlhE\n\
vrgVywIbOsmG0tcRw2NSP4R3XFpETxcwVRjkhbFpcDMjtjilEXZBrB8gaeI4gvX+\n\
TwIDAQAB\n\
-----END PUBLIC KEY-----"
.replace(/^\s+/gm, "");
var signature_b64 = "\
nuzPQx94kofXoc3TZlcBH+bAFG6b73cq9OXvGeE/mQ4qRDeWPKWZNC0HfkKtSyng\n\
kTBWRDw7GeIvOQTY9OXtHunnrn3epPO+HzTmDpCwvv0oNVxoTPlnuBuLzP1mpuIT\n\
RIgiOJ/xTEqzpjwoCG/HxySb5n4KNu3ii4XB+c914x6V/YU3wDCt60+p71QW3tz0\n\
lvQPlG3CoMouSYi7sGhAdJMPJA1J5B24FAdqCrOB3xXTuX++HqH0fe6eR5cuzDJN\n\
xkkjV+GDciyVPSrQb42gf9gl7qtOLvhrwor7efin+FhlWvL5plLn53Ao04scghTf\n\
hZZLxrEkliWG5E3iGarvXA=="
.replace(/(\n|\x20)/g, "");
var data_b64 = "\
MYGxMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE1\n\
MTExNjAwNTMxMFowLwYJKoZIhvcNAQkEMSIEIMcfOGYfrzbx+5cHx8CvoxB6M+25\n\
Jd+QuGubzoJJjts7MEYGCyqGSIb3DQEJEAITMTcwNTAzMDEwLzALBglghkgBZQME\n\
AgEEIJ3unWyG0PypTJy/MC9YWBUFjDUAG9AdNs1Byq3tpp23"
.replace(/(\n|\x20)/g, "");
// using forge 0.7.x
var pki = forge.pki,
util = forge.util,
md = forge.md,
raw = util.binary.raw;
var publicKey = pki.publicKeyFromPem(publicKey_pem);
var signature = new util.ByteBuffer(atob(signature_b64));
var data = new util.ByteBuffer(atob(data_b64));
var hash = md.createMessageDigest("sha256")
.update(data).digest();
var verified = publicKey.verify(hash, signature,
"RSASSA-PKCS1-V1_5");
console.info("Verification using forge: " + verified);
var modulus_b64 = "\
yKy7Fm7M5eKVVsOWvpDY7OrJ4WJY8kRupJFY2TmsfeOxcZTztx7igt3PRhUtk/P9\n\
hNbRge1Hh2lQbnlEozn3i335jkGHoHbqGhBim6o4PUikByxEY46NEzxa0p4Mdmnm\n\
cWh8oqhNH6k0T1ss1eAMkCJZNwXqGM64+VuJ58k+H0f1NYFDmmnZVkXNeiRcS7T8\n\
MtJEDv0Kni06Brl9KMQaxEUx1DLEwmTtW3tV/EA6erIYwpI/yWLdLxT/LxYdtAo9\n\
sx55sXXSgFKzTJkLwlhEvrgVywIbOsmG0tcRw2NSP4R3XFpETxcwVRjkhbFpcDMj\n\
tjilEXZBrB8gaeI4gvX+Tw=="
.replace(/(\n|\x20|=)/g, "")
.replace(/\//g, "_")
.replace(/\+/g, "-");
var key = {
kty: "RSA",
alg: "RS256",
e: "AQAB",
n: modulus_b64
};
var algo = {
name: "RSASSA-PKCS1-v1_5",
hash: {name: "SHA-256"}
};
var use = ["verify"];
var crypto = window.crypto.subtle;
crypto.importKey("jwk", key, algo, false, use).then(function(publicKey) {
var sig = raw.decode(signature.copy().getBytes());
var dat = raw.decode(data.copy().getBytes());
return crypto.verify(algo, publicKey, sig, dat);
}).then(function(res) {
console.info("Verification using Web Crypto: " + res);
}, function(error) {
console.error(error);
});
使用 forge 验证成功,但使用 Web Cryptography API 验证失败。我不明白这是怎么回事,因为他们使用的是相同的算法规范。
我能够使用 OpenSSL 验证 Web Cryptography API 生成的签名。使用 OpenSSL 检查这两个签名有一点不同:
# verify the external signature using OpenSSL
openssl rsautl -in sig.bin -verify -inkey pub.pem -pubin -asn1parse
0:d=0 hl=2 l= 47 cons: SEQUENCE
2:d=1 hl=2 l= 11 cons: SEQUENCE
4:d=2 hl=2 l= 9 prim: OBJECT :sha256
15:d=1 hl=2 l= 32 prim: OCTET STRING
0000 - 83 8f e4 de ba 8b b7 24-7a db 6d 43 12 c8 57 f5 .......$z.mC..W.
0010 - 9c 80 1d 53 1b bf 7e 66-a3 d8 d6 fa ee 2a 4e 8a ...S..~f.....*N.
# verify the web crypto signature using OpenSSL
openssl rsautl -in sig.bin -verify -inkey pub.pem -pubin -asn1parse
0:d=0 hl=2 l= 49 cons: SEQUENCE
2:d=1 hl=2 l= 13 cons: SEQUENCE
4:d=2 hl=2 l= 9 prim: OBJECT :sha256
15:d=2 hl=2 l= 0 prim: NULL
17:d=1 hl=2 l= 32 prim: OCTET STRING
0000 - a5 91 a6 d4 0b f4 20 40-4a 01 17 33 cf b7 b1 90 ...... @J..3....
0010 - d6 2c 65 bf 0b cd a3 2b-57 b2 77 d9 ad 9f 14 6e .,e....+W.w....n
这可能是问题的原因吗?如果是这样,是否可以将外部签名转换为 Web Cryptography API 使用的编码?