admin管理员组

文章数量:1355658

I'm trying to authenticate a message sent from TrialPay using Node.js and Express. TrialPay signs requests with an HMAC-MD5 hash, and provides these instructions on validating.

This is my code:

app.post('/trialpay', function(req, res) {

    var key = "[MY MERCHANT KEY]";
    var hash = req.header("TrialPay-HMAC-MD5");
    var data = req.body.toString();

    var crypted = require("crypto").createHmac("md5", key)
        .update(data)
        .digest("hex");

    if (hash == crypted) {
        res.writeHead(200, {"Content-Type": "plain/text"});
        res.end("Success!");
    } else {
        throw new Error("Invalid TrialPay Hash");
    }  
});

This is, obviously, not working (hash doesn't match).

Disclaimer: I'm extremely new to Node.js, and have little Javascript experience, to begin with.

UPDATE

I did not realize that the link was protected.

TrialPay uses your Notification-Key (set in your account information) as the secret key to sign the HMAC. For GET requests the query string that follows the question mark (in the URL) is signed. For POST requests the entire POST body is signed.

Here is an example of how TrialPay instructs you to validate in Google App Engine (Python):

class MyHandler(webapp.RequestHandler):
  def post(self):
  key = '[YOUR MERCHANT KEY]'
  tphash = self.request.headers['TrialPay-HMAC-MD5'] 
  if hmacmd5(key,self.request.body) != tphash:
    logging.info('invalid trialpay hash')
    return 

UPDATE 2

The req.body prints out as:

{ 
  oid: 'sample-order-id',
  sid: 'customer-sid',
  order_date: '04/24/2012',
  timestamp: '04/24/2012 16:28:46',
  first_name: 'customer-firstname',
  last_name: 'customer-lastname',
  email: '[email protected]',
  revenue: '10.00',
  zip_code: '94041',
  country: 'US' 
}

I'm trying to authenticate a message sent from TrialPay using Node.js and Express. TrialPay signs requests with an HMAC-MD5 hash, and provides these instructions on validating.

This is my code:

app.post('/trialpay', function(req, res) {

    var key = "[MY MERCHANT KEY]";
    var hash = req.header("TrialPay-HMAC-MD5");
    var data = req.body.toString();

    var crypted = require("crypto").createHmac("md5", key)
        .update(data)
        .digest("hex");

    if (hash == crypted) {
        res.writeHead(200, {"Content-Type": "plain/text"});
        res.end("Success!");
    } else {
        throw new Error("Invalid TrialPay Hash");
    }  
});

This is, obviously, not working (hash doesn't match).

Disclaimer: I'm extremely new to Node.js, and have little Javascript experience, to begin with.

UPDATE

I did not realize that the link was protected.

TrialPay uses your Notification-Key (set in your account information) as the secret key to sign the HMAC. For GET requests the query string that follows the question mark (in the URL) is signed. For POST requests the entire POST body is signed.

Here is an example of how TrialPay instructs you to validate in Google App Engine (Python):

class MyHandler(webapp.RequestHandler):
  def post(self):
  key = '[YOUR MERCHANT KEY]'
  tphash = self.request.headers['TrialPay-HMAC-MD5'] 
  if hmacmd5(key,self.request.body) != tphash:
    logging.info('invalid trialpay hash')
    return 

UPDATE 2

The req.body prints out as:

{ 
  oid: 'sample-order-id',
  sid: 'customer-sid',
  order_date: '04/24/2012',
  timestamp: '04/24/2012 16:28:46',
  first_name: 'customer-firstname',
  last_name: 'customer-lastname',
  email: '[email protected]',
  revenue: '10.00',
  zip_code: '94041',
  country: 'US' 
}
Share Improve this question edited Apr 25, 2012 at 15:19 Paul Burke asked Apr 24, 2012 at 19:52 Paul BurkePaul Burke 25.6k9 gold badges67 silver badges62 bronze badges 10
  • 1 Your link doesn't work -- it won't let me log in. That said, you should stay clear of throwing inside your request handlers (basically, anywhere in node). Accept the third parameter (the callback, next in express lingo) and pass the error to next instead. Or even better, handle the error here, where you can, and res.send(403, 'Invalid TrialPay Hash'). Also, you don't mention what kind of data you are posting -- it would be interesting to see what req.body and especially req.body.toString() contains, if you could console.log that. – Linus Thiel Commented Apr 24, 2012 at 20:03
  • Thanks for the ment and advice @LinusGThiel. When I tried console.log(req.body.toString()); it only prints out "[object Object]". Please excuse my ignorance, here. – Paul Burke Commented Apr 24, 2012 at 20:13
  • That's what I suspected! What does console.log(req.body) give you? – Linus Thiel Commented Apr 24, 2012 at 20:21
  • You are filling in [MY MERCHANT KEY] with the correct data, right? – ControlAltDel Commented Apr 24, 2012 at 20:31
  • Okay, console.log(req.body) gives me back the list of JSON parameters. So, how do I pass this to crypto.update()? It needs a String or Buffer. I tried both (perhaps incorrectly) and neither worked. – Paul Burke Commented Apr 24, 2012 at 20:32
 |  Show 5 more ments

2 Answers 2

Reset to default 6

This should do the trick:

var crypto = require('crypto');

function calculateSignature(key) {
    return function(req, res, next) {
        var hash = req.header("TrialPay-HMAC-MD5"),
            hmac = crypto.createHmac("md5", key);

        req.on("data", function(data) {
            hmac.update(data);
        });

        req.on("end", function() {
            var crypted = hmac.digest("hex");

            if(crypto.timingSafeEqual(
              Buffer.from(crypted),
              Buffer.from(hash.padEnd(crypted.length))
            )) {
                // Valid request
                return res.send("Success!", { "Content-Type": "text/plain" });
            } else {
                // Invalid request
                return res.send("Invalid TrialPay hash", { "Content-Type": "text/plain" }, 403);
            }
        });

        req.on("error", function(err) {
            return next(err);
        });
    }
}

app.post("/trialpay", calculateSignature("[MY MERCHANT KEY]"));

For Parse Cloud Code:(I have tested) The point is express.bodyParser will parse the url encoded string which is used to hash.

var parseExpressRawBody = require('parse-express-raw-body');
var queryString = require('querystring');
app.post('/trialpay',parseExpressRawBody(),function(req, res) {
var hmac, calculatedSignature,payloadStr=req.body.toString();
hmac = crypto.createHmac('md5', TrialPayMerchentKey);
hmac.update(payloadStr);
calculatedSignature = hmac.digest('hex');

if (req.headers['trialpay-hmac-md5'] === calculatedSignature) {
   ~~~~~~

本文标签: