繁体   English   中英

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

[英]Verify PKCS#7 (PEM) signature / unpack data in node.js

我从第三方系统获得了PKCS#7加密包。 该软件包未经过压缩,未加密,PEM编码,使用X.509证书签名。 我还有来自提供商的PEM证书文件。

里面的数据是XML

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

  • 提取数据
  • 验证签名

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

好的,终于明白了。

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

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

第三, PKCS#7格式指定了几种包称为签名数据的包类型。 这些格式由ASN1对象开头的OBJECT IDENTIFIER值(包装序列的第一个元素)区分 - 您可以转到http://lapo.it/asn1js/并粘贴完整解析结构的包文本

接下来,我们需要解析包(Base64 - > ASN1 - >一些对象表示)。 不幸的是,没有npm包。 我找到了一个很好的项目伪造 ,没有发布到npm注册表(虽然npm兼容)。 它解析了PEM格式,但生成的树是一个非常不愉快的东西。 基于他们的加密数据和Enveloped Data实现,我在自己的fork中创建了Signed Data的部分实现。 UPD:我的拉取请求后来合并到了锻造项目。

现在终于解析了整个事情。 那时我发现了一个很棒的(可能是整个网络上唯一的)有关签名PKCS#7验证的解释性文章: http//qistoph.blogspot.com/2012/01/manual-verify-pkcs7-signed-data- with.html

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

数据签名过程分为两步:

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

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

由于PKCS#7细节,这组属性存储在特定于上下文的构造类型(class = 0x80,type = 0)内,但应该作为普通SET(class = 0,type = 17)进行签名和验证。

正如Chris提到的那样( https://stackoverflow.com/a/16154756/108533 ),这只会验证包中的属性是否有效。 我们还应该根据摘要属性验证实际数据哈希。

所以最后这里是一个进行验证的代码( 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 = Buffer.from(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");
}

你的答案是朝着正确方向迈出的一大步。 但是,您错过了验证的重要部分!

您应该根据签名属性中包含的摘要验证数据的哈希值。 否则,某人可能会用恶意数据替换内容。 尝试使用您的代码验证以下“包”(并查看内容): http//pastebin.com/kaZ2XQQc

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

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

基于这个答案的灵感,我已经使用node-signpdfnode- forge实现了一个用于签名和验证pdf文件的示例

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM