14

I'm stuck with the crypto api because I know little about cryptography. I have this

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

Signature:
rNbdUP-p4pEGfwQSwR6VPvAVZ-sZu-ptgw8SofYYmNTlfUB9iUbb593eCAuT5jsqDTC

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

I want to verify the signature, how do I do that?

(I'm using node v0.10.18)

dododedodonl
  • 4,585
  • 6
  • 30
  • 43
  • 2
    Node.js crypto `verify` function expects the key to be in PEM format. The tricky part here is converting the RSA XML key to PEM. I can't find anything written in javascript to do that. You might have to port something like https://github.com/phpseclib/phpseclib/blob/master/phpseclib/Crypt/RSA.php#L866-922 to javascript. – Trevor Dixon Sep 16 '13 at 20:43
  • 1
    Is the "Original data:" an XML document with an xml-signature? If so you will need more than something to verify digital signatures. I did find a reference to [xml-crypto](http://architects.dzone.com/articles/check-out-digital-signature) which may help. – pd40 Sep 16 '13 at 23:05
  • 1
    Original data is not an XML document with the signature inside its structure (like the link you provided). – dododedodonl Sep 17 '13 at 08:38
  • Do you get those three bits separately or do they come together exactly as shown in the code block above? – Thom Seddon Oct 18 '13 at 06:52
  • The three data pieces come separately via an incoming connection. – dododedodonl Oct 31 '13 at 14:30
  • 1
    The first looks like a pubkey but what is the signature and how do you get it. Please post the original XML or a link to it. It is important to know what is that. – user568109 Nov 20 '13 at 15:08

1 Answers1

14

I'm not really a node.js dev, so this is super hacky.. Here's a function that should output a RSA public key PEM from Base64 modulus and exponent. I'm going based on Trevor's comment about node.js verify expecting a PEM.

This function composes a ASN.1 DER structure in hex, then hex-decodes it, then base64-encodes it, and then sandwiches it between -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY-----. After all, that's all a PEM is.

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
}

Binary manipulation with hex strings? Yuck.. but hey it's a hack.

Tracker1
  • 19,103
  • 12
  • 80
  • 106
tsechin
  • 687
  • 6
  • 7
  • Not working for me. Not clear what is wrong of course, just that the verify still fails. – Aaron Dec 12 '13 at 23:38
  • Slight modification to get that code working properly. ```pem = '-----BEGIN PUBLIC KEY-----\n'; 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 Dec 13 '13 at 16:18
  • @Aaron I edited the answer.. used a regex instead of the loop. – Tracker1 Feb 13 '14 at 01:14
  • 3
    Created an npm module for this... https://github.com/tracker1/node-rsa-pem-from-mod-exp `npm install rsa-pem-from-mod-exp` – Tracker1 Feb 13 '14 at 21:18
  • Unrelated question, but any way to get the private key out of the xml? – caspian311 Apr 15 '14 at 19:36
  • @caspian311 I know it's been a while, but you would be best to use an XML to Object module to read the XML, then use that as input to the module I created... Although it's a bit heavy, if you have to do a bit of html and/or xml manipulation of not too large segments, you might want to look into the `cherio` module, which is like jquery in node without a DOM. – Tracker1 Apr 20 '15 at 03:24
  • 1
    @Tracker1 We wound up patching an existing library that helped us out (https://github.com/quartzjer/ursa#ursacreateprivatekeyfromcomponentsmodulus-exponent-p-q-dp-dq-inverseq-d). The issue was that your module only used the exponent and modulus found in the public key but not the other components used in the private key. – caspian311 Apr 22 '15 at 01:06
  • Thank you guys. Have been trying to figure this out since yesterday. Worked like a charm – Faisal Jun 20 '19 at 09:44