在Node.js中将XML转换为PEM

15

我因了解加密知识较少而陷入了加密API的困境。我有以下内容:

XML:
<RSAKeyValue>
    <Modulus>1znidPBIcMcO7K/53tkTSyKqxlG5Mcws8kVtijS4tyEU4W/FEVWYpOtv+Stnb4Vt</Modulus>
    <Exponent>AQAB</Exponent>
</RSAKeyValue>

Signature:
rNbdUP-p4pEGfwQSwR6VPvAVZ-sZu-ptgw8SofYYmNTlfUB9iUbb593eCAuT5jsqDTC

Original data:
<xml>...some big xml...</xml>

我想要验证签名,应该怎么做?

(我正在使用 node v0.10.18)


2
Node.js的加密verify函数期望密钥以PEM格式提供。这里棘手的部分是将RSA XML密钥转换为PEM格式。我找不到任何用JavaScript编写的相关内容。您可能需要将类似于https://github.com/phpseclib/phpseclib/blob/master/phpseclib/Crypt/RSA.php#L866-922的内容移植到JavaScript中。 - Trevor Dixon
1
“Original data:” 是一个带有 XML 签名的 XML 文档吗?如果是,那么您需要更多的东西来验证数字签名。我找到了一个关于 xml-crypto 的参考资料,可能会有所帮助。 - pd40
1
原始数据不是一个带有签名的XML文档(就像您提供的链接中那样)。 - dododedodonl
你是单独获取这三个位还是像上面的代码块一样一起获取的? - Thom Seddon
这三个数据片段通过一个传入连接单独到达。 - dododedodonl
1
第一个看起来像是公钥,但签名是什么以及如何获取它?请发布原始XML或链接。重要的是要知道那是什么。 - user568109
1个回答

14

我不是一个真正的node.js开发人员,所以这个方法非常hacky。。这里有一个函数,它应该可以从Base64模数和指数输出RSA公钥PEM。我基于Trevor的评论,关于node.js verify 期望PEM来操作。

这个函数使用十六进制组成ASN.1 DER结构,然后将其进行十六进制解码,再进行base64编码,并在-----BEGIN PRIVATE KEY----------END PRIVATE KEY-----之间加上它。毕竟,这就是PEM的全部内容。

function rsaPublicKeyPem(modulus_b64, exponent_b64) {

    function prepadSigned(hexStr) {
        msb = hexStr[0]
        if (
            (msb>='8' && msb<='9') || 
            (msb>='a' && msb<='f') || 
            (msb>='A'&&msb<='F')) {
            return '00'+hexStr;
        } else {
            return hexStr;
        }
    }

    function toHex(number) {
        var nstr = number.toString(16)
        if (nstr.length%2==0) return nstr
        return '0'+nstr
    }

    // encode ASN.1 DER length field
    // if <=127, short form
    // if >=128, long form
    function encodeLengthHex(n) {
        if (n<=127) return toHex(n)
        else {
            n_hex = toHex(n)
            length_of_length_byte = 128 + n_hex.length/2 // 0x80+numbytes
            return toHex(length_of_length_byte)+n_hex
        }
    }

    var modulus = new Buffer(modulus_b64,'base64');
    var exponent = new Buffer(exponent_b64, 'base64');

    var modulus_hex = modulus.toString('hex')
    var exponent_hex = exponent.toString('hex')

    modulus_hex = prepadSigned(modulus_hex)
    exponent_hex = prepadSigned(exponent_hex)

    var modlen = modulus_hex.length/2
    var explen = exponent_hex.length/2

    var encoded_modlen = encodeLengthHex(modlen)
    var encoded_explen = encodeLengthHex(explen)
    var encoded_pubkey = '30' + 
        encodeLengthHex(
            modlen + 
            explen + 
            encoded_modlen.length/2 + 
            encoded_explen.length/2 + 2
        ) + 
        '02' + encoded_modlen + modulus_hex +
        '02' + encoded_explen + exponent_hex;

    var seq2 = 
        '30 0d ' +
          '06 09 2a 86 48 86 f7 0d 01 01 01' +
          '05 00 ' +
        '03' + encodeLengthHex(encoded_pubkey.length/2 + 1) +
        '00' + encoded_pubkey;

    seq2 = seq2.replace(/ /g,'');

    var der_hex = '30' + encodeLengthHex(seq2.length/2) + seq2;

    der_hex = der_hex.replace(/ /g, '');

    var der = new Buffer(der_hex, 'hex');
    var der_b64 = der.toString('base64');

    var pem = '-----BEGIN PUBLIC KEY-----\n' 
        + der_b64.match(/.{1,64}/g).join('\n') 
        + '\n-----END PUBLIC KEY-----\n';

    return pem
}

使用十六进制字符串进行二进制操作? 很糟糕...但是嘿,这是一种技巧。


对我来说不起作用。当然不清楚出了什么问题,只是验证仍然失败。 - Aaron
稍作修改即可使该代码正常工作。 for (var i = 0; i < der_b64.length; i += 64) { var len = der_b64.length - i; if (len > 64) { len = 64; } pem += der_b64.substr(i, len) + "\n"; } pem += '-----END PUBLIC KEY-----\n' - Aaron
@Aaron 我编辑了答案...使用正则表达式代替循环。 - Tracker1
3
为此创建了一个npm模块... https://github.com/tracker1/node-rsa-pem-from-mod-exp npm install rsa-pem-from-mod-exp - Tracker1
1
@Tracker1 我们最终修补了一个现有的库来帮助我们(https://github.com/quartzjer/ursa#ursacreateprivatekeyfromcomponentsmodulus-exponent-p-q-dp-dq-inverseq-d)。问题在于,你的模块只使用了公钥中的指数和模数,而没有使用私钥中使用的其他组件。 - caspian311
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接