14

The Google App Script function computeDigest returns a byte array of the signature. How can I get the string representation of the digest?

I have already tried the bin2String() function.

function sign(){     
var signature = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, "thisisteststring")
Logger.log(bin2String(signature));
}


function bin2String(array) {
  var result = "";
  for (var i = 0; i < array.length; i++) {
    result += String.fromCharCode(parseInt(array[i], 2));
  }
  return result;
}

but it puts "" in the Logs

Saqib Ali
  • 3,953
  • 10
  • 55
  • 100

6 Answers6

27

If we put Logger.log(signature); right after the call to computeDigest(), we get:

[8, 30, -43, 124, -101, 114, -37, 10, 78, -13, -102, 51, 65, -24, -83, 81]

As represented in javascript, the digest includes both positive and negative integers, so we can't simply treat them as ascii characters. The MD5 algorithm, however, should provide us with 8-bit values, in the range 0x00 to 0xFF (255). Those negative values, then, are just a misinterpretation of the high-order bit; taking it to be a sign bit. To correct, we need to add 256 to any negative value.

How to convert decimal to hex in JavaScript? gives us this for retrieving hex characters:

hexString = yourNumber.toString(16);

Putting that together, here's your sign() function, which is also available as a gist:

function sign(message){     
  message = message || "thisisteststring";
  var signature = Utilities.computeDigest(
                       Utilities.DigestAlgorithm.MD5,
                       message,
                       Utilities.Charset.US_ASCII);
  Logger.log(signature);
  var signatureStr = '';
    for (i = 0; i < signature.length; i++) {
      var byte = signature[i];
      if (byte < 0)
        byte += 256;
      var byteStr = byte.toString(16);
      // Ensure we have 2 chars in our byte, pad with 0
      if (byteStr.length == 1) byteStr = '0'+byteStr;
      signatureStr += byteStr;
    }   
  Logger.log(signatureStr);
  return signatureStr;
}

And here's what the logs contain:

[13-04-25 21:46:55:787 EDT] [8, 30, -43, 124, -101, 114, -37, 10, 78, -13, -102, 51, 65, -24, -83, 81]
[13-04-25 21:46:55:788 EDT] 081ed57c9b72db0a4ef39a3341e8ad51

Let's see what we get from this on-line MD5 Hash Generator:

081ed57c9b72db0a4ef39a3341e8ad51

I tried it with a few other strings, and they consistently matched the result from the on-line generator.

Community
  • 1
  • 1
Mogsdad
  • 44,709
  • 21
  • 151
  • 275
17

Just in case this is helpful to anyone else, I've put together a more succinct version of Mogsdad's solution:

function md5(str) {
  return Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, str).reduce(function(str,chr){
    chr = (chr < 0 ? chr + 256 : chr).toString(16);
    return str + (chr.length==1?'0':'') + chr;
  },'');
}
mikegreiling
  • 1,160
  • 12
  • 21
8

One-liner:

Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, "teststring")
  .map(function(b) {return ("0" + (b < 0 && b + 256 || b).toString(16)).substr(-2)})
  .join("")
Nommyde
  • 91
  • 1
  • 5
  • 1
    Can you explain a bit more about how this works? In the interest of educating future readers. – Dan Lowe Dec 05 '15 at 23:22
  • 1) "map" takes each byte in array returned by "computeDigest" and performs some transformation on it. 2) "(b < 0 && b + 256 || b)" - && takes precedence over || so it actually looks like this: ( (b < 0 && b + 256) || b ). It says "If b < 0, give b+256, otherwise give b". 3) ".toString(16)" Convert the corrected "b" value to hex string 4) "0" + hex string (in case b is less than 16, e.g. if b is 1 then: "0"+"1" -> "01". In case b is 16 or more we get "010". But we just want two chars so 5) "substr(-2)" take last two chars 6) join together – CarelZA Jul 26 '17 at 20:36
  • 2
    In fact you don't need conditionals: inputArray.map(function(b) {return ("0"+((b+256)%256).toString(16)).slice(-2)}).join(""); – CarelZA Jul 27 '17 at 06:11
7

Did somebody say succinct? (/fulldecent arrives to party with the drinking hat, including straws, after everyone else already passed out)

Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, "thisisteststring")
  .map(function(chr){return (chr+256).toString(16).slice(-2)})
  .join('')
William Entriken
  • 37,208
  • 23
  • 149
  • 195
3

Here is an easy way to transform a Byte[] into a String.

Found this in the documentation provided by Google here : https://developers.google.com/apps-script/reference/utilities/utilities#base64Decode(String)

Utilities.newBlob(myByteArray).getDataAsString();

Better late than never. (And since this topic still comes first when searching this topic in Goole, this might help some folks).

Maxime T
  • 848
  • 1
  • 9
  • 17
-1

From this post:

function string2Bin(str) {
  var result = [];
  for (var i = 0; i < str.length; i++) {
    result.push(str.charCodeAt(i).toString(2));
  }
  return result;
}
Community
  • 1
  • 1
Woot4Moo
  • 23,987
  • 16
  • 94
  • 151
  • well actually it is bin2string. But I have already tried that. and it doesn't work. Try it for yourself. – Saqib Ali Apr 25 '13 at 14:09