validationnode.js中的PKCS#7(PEM)签名/解包数据

我从第三方系统获得PKCS#7encryption包。 该软件包没有压缩,没有encryption,PEM编码,使用X.509证书签名。 我也有提供者的PEM证书文件。

里面的数据是XML

我需要在Node.JS中执行以下操作:

  • 提取数据
  • validation签名

样本包(没有敏感信息,数据是指我们的qa系统) http://pastebin.com/7ay7F99e

你的回答是朝着正确方向迈出的一大步。 但是你错过了validation的一个重要部分!

您应该根据签名属性中包含的摘要validation数据的散列。 否则,有人可能用恶意数据replace内容。 试着用你的代码validation下面的'包'(看看内容): http : //pastebin.com/kaZ2XQQc

我并不是一个NodeJS开发者(这实际上是我第一次尝试:p),但是这里有一个build议来帮助你开始。

var fs = require('fs'); var crypto = require('crypto'); var pkcs7 = require('./js/pkcs7'); // forge from my own fork var asn1 = require('./js/asn1'); var folder = ''; var pkg = fs.readFileSync(folder + 'package').toString(); var cert = fs.readFileSync(folder + 'cert.pem').toString(); try { var msg = pkcs7.messageFromPem(pkg); var attrs = msg.rawCapture.authenticatedAttributes; // got the list of auth attrs var set = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, attrs); // packed them inside of the SET object var buf = new Buffer(asn1.toDer(set).data, 'binary'); // DO NOT forget 'binary', otherwise it tries to interpret bytes as UTF-8 chars var sig = msg.rawCapture.signature; var shasum = crypto.createHash('sha1'); // better be based on msg.rawCapture.digestAlgorithms shasum.update(msg.rawCapture.content.value[0].value[0].value); for(var n in attrs) { var attrib = attrs[n].value; var attrib_type = attrib[0].value; var attrib_value = attrib[1].value[0].value; if(attrib_type == "\x2a\x86\x48\x86\xf7\x0d\x01\x09\x04") { // better would be to use the OID (1.2.840.113549.1.9.4) if(shasum.digest('binary') == attrib_value) { console.log('hash matches'); var v = crypto.createVerify('RSA-SHA1'); v.update(buf); console.log(v.verify(cert, sig)); // -> should type true } else { console.log('hash mismatch'); } } } } catch (_e) { console.dir(_e); } 

好的,终于明白了。

首先,PKCS消息是使用ASN1进行二进制编码的复杂结构。

其次,可以使用Base64编码将它们序列化为二进制文件( DER编码 )或文本PEM文件。

第三, PKCS#7格式指定了我称之为签名数据的几种软件包types。 这些格式通过ASN1对象(包装序列的第一个元素)开头的OBJECT IDENTIFIER值进行区分 – 您可以访问http://lapo.it/asn1js/并粘贴完整parsing结构的包文本 。

接下来,我们需要parsing包(Base64 – > ASN1 – >一些对象表示)。 不幸的是,这里没有npm包。 我发现一个很好的项目伪造 ,不是发布到npmregistry(虽然npm兼容)。 它parsing了PEM格式,但是生成的树是一个相当不愉快的事情。 基于他们的encryption数据和封装数据实现,我在自己的分支中创build了签名数据的部分实现。 UPD:我的拉取请求后来被合并到伪造项目。

现在我们终于解决了整个事情。 在那个时候,我发现了一个很好的(也许是整个networking上唯一)关于签名的PKCS#7validation的解释性文章: http : //qistoph.blogspot.com/2012/01/manual-verify-pkcs7-signed-data- with.html

我能够从文件中提取并成功解码签名,但是里面的哈希与数据的哈希不同。 上帝保佑克里斯谁解释实际发生的事情。

数据签名过程分为两步:

  1. 原始内容的哈希计算
  2. 构造了一组“授权属性”,包括:数据types,签名时间和数据散列

然后使用签名者的私钥对步骤2中的集合进行签名。

由于PKCS#7的具体情况,这组属性存储在上下文特定的构造types(class = 0x80,type = 0)中,但应作为普通SET(class = 0,type = 17)进行签名和validation。

正如克里斯提到的( https://stackoverflow.com/a/16154756/108533 )这只validation包中的属性是有效的。 我们还应该根据摘要属性validation实际的数据散列。

所以最后这里是一个执行validation的代码( cert.pem是提供程序发给我的证书文件, package是通过HTTP POST从他们获得的PEM编码的消息):

 var fs = require('fs'); var crypto = require('crypto'); var forge = require('forge'); var pkcs7 = forge.pkcs7; var asn1 = forge.asn1; var oids = forge.pki.oids; var folder = '/a/path/to/files/'; var pkg = fs.readFileSync(folder + 'package').toString(); var cert = fs.readFileSync(folder + 'cert.pem').toString(); var res = true; try { var msg = pkcs7.messageFromPem(pkg); var attrs = msg.rawCapture.authenticatedAttributes; var set = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.SET, true, attrs); var buf = new Buffer(asn1.toDer(set).data, 'binary'); var sig = msg.rawCapture.signature; var v = crypto.createVerify('RSA-SHA1'); v.update(buf); if (!v.verify(cert, sig)) { console.log('Wrong authorized attributes!'); res = false; } var h = crypto.createHash('SHA1'); var data = msg.rawCapture.content.value[0].value[0].value; h.update(data); var attrDigest = null; for (var i = 0, l = attrs.length; i < l; ++i) { if (asn1.derToOid(attrs[i].value[0].value) === oids.messageDigest) { attrDigest = attrs[i].value[1].value[0].value; } } var dataDigest = h.digest(); if (dataDigest !== attrDigest) { console.log('Wrong content digest'); res = false; } } catch (_e) { console.dir(_e); res = false; } if (res) { console.log("It's OK"); }