4

I have a signature method that is meant to be used in Node.js but I'd like to implement it client-side with crypto-js. It should work in latest Chrome versions.

I have tried to follow some answers like this one: Decode a Base64 String using CryptoJS

But I either get errors such as "Error: Malformed UTF-8 data", or a different result than the expected hmacDigest.

I am not sure how I could find an alternative to the "binary" digest although I found this question: How to get digest representation of CryptoJS.HmacSHA256 in JS

The method is supposed to answer the following:

"Message signature using HMAC-SHA512 of (URI path + SHA256(nonce + POST data)) and base64 decoded secret API key"

This is the Nodejs version (with crypto):

const crypto = require('crypto')

function sign(path, params, secret) {
  const message = querystring.stringify(params)
  const secretBase64 = Buffer.from(secret, 'base64')
  const hash = crypto.createHash('sha256')
  const hmac = crypto.createHmac('sha512', secretBase64)

  const hashDigest = hash.update(params.nonce + message).digest('binary')
  const hmacDigest = hmac.update(path + hashDigest, 'binary').digest('base64')

  return hmacDigest
}

note: querystring is just an helper module that can also run in browsers: https://nodejs.org/api/querystring.html

This is my attempt (wip) at implementing with crypto-js:

import cryptojs from 'crypto-js')

function sign (path, params, secret) {
  const message = querystring.stringify(params)
  const secretParsed = cryptojs.enc.Base64.parse(secret)
  const secretDecoded = cryptojs.enc.Utf8.stringify(secretParsed) // -> this throws an error as "Error: Malformed UTF-8 data"

  const hash = cryptojs.SHA256(params.nonce + message).toString(cryptojs.enc.hex)
  const hmac = cryptojs.HmacSHA512(path + hash, secretDecoded).toString(cryptojs.enc.hex)
  return hmac
}
Kev
  • 5,049
  • 5
  • 32
  • 53
  • You're going to have to give us more info. Browser does not have `Buffer` or `querystring`, so where are you getting those? What is the *exact* error (with relevant code) you're getting? In what version of what browser? – Jared Smith Feb 15 '18 at 19:20
  • @JaredSmith Yes browser does not have Buffer. That's why I have asked this question. There are other few differences, like the methods available on crypto vs crypto-js and I am trying to see how I could achieve the same signature method on the client side. I have updated the question with some details. (should work in chrome, and querystring is a helper that can be used client side). – Kev Feb 15 '18 at 19:38
  • there are a million browser implementations of Buffer on npm, and I'm pretty sure browserify has on built in as well. Are you having a *specific* issue (i.e. one that I can reproduce right now, in my browser)? If not I'm voting to close... – Jared Smith Feb 15 '18 at 20:04
  • Yes, for example, crypto-js does not have a digest('binary') equivalent, so how can I go around that? (I have linked a question about that, but I couldn't make it work properly, the hmacDigest result was not the same. I am adding my attempt at solving it) – Kev Feb 15 '18 at 20:24
  • I have the same issue. I think Buffer can be safely replaced by `btoa` but I also have no idea how to generate binary from cryptoJS – bluppfisk Sep 26 '18 at 18:00

1 Answers1

0

Try this ! I think this is what you looking for !

const crypto = require("crypto")

const sign = (path, params, secret) => {
    const message = querystring.stringify(params)
    const secret = Buffer.from(secret, 'base64')
    let sign = params.nonce + message
    let hash = crypto.createHmac('sha256', secret)
                     .update(sign)
                     .digest("base64").toString()

    let encoded = encodeURIComponent(hash)
    return encoded
}
Pushpendra Kumar
  • 1,721
  • 1
  • 15
  • 21
  • I have updated my question. Have a look at my attempt to solve it, hopefully this makes clearer what I am trying to achieve. – Kev Feb 15 '18 at 20:45