Hash of a cell text in Google Spreadsheet
Asked Answered
A

9

69

How can I compute a MD5 or SHA1 hash of text in a specific cell and set it to another cell in Google Spreadsheet?

Is there a formula like =ComputeMD5(A1) or =ComputeSHA1(A1)?

Or is it possible to write custom formula for this? How?

Aerogram answered 3/11, 2011 at 11:54 Comment(0)
A
127

Go to Extensions > Apps Script, then paste the following code:

function MD5 (input) {
  var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, input);
  var txtHash = '';
  for (i = 0; i < rawHash.length; i++) {
    var hashVal = rawHash[i];
    if (hashVal < 0) {
      hashVal += 256;
    }
    if (hashVal.toString(16).length == 1) {
      txtHash += '0';
    }
    txtHash += hashVal.toString(16);
  }
  return txtHash;
}

Save the script after that and then use the MD5() function in your spreadsheet while referencing a cell.

This script is based on Utilities.computeDigest() function.

Asphodel answered 8/8, 2012 at 15:36 Comment(5)
Nice answer. You might want to add Utilities.sleep(100) somewhere inside the function, to prevent it from crashing when using the autofill function (otherwise you will run into a limit on how often a function can be called)Avoidance
I can't seem to get this to match what md5sum(1) does; for example frew in the cli gives c241183cbf6766bd86061a60d6c8fe1b with no newline, but cfb5d06a43aad502d0f6219143ba0e34 from the function.Adele
This is really helpful. Seems like the line hashVal += 256 should read hashVal += 128 though. Utlities.computeDigest() returns a byte array. The bytes are represented as integers ranging from -128 to 127. Adding 128 will convert them to numbers between 0 and 256.Trichroism
I'd add Charset UTF8 to the digest computation. Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, input, Utilities.Charset.UTF_8);Til
update: Extensions > Apps ScriptOnwards
M
22

Thanks to gabhubert for the code.

This is the SHA1 version of that code (very simple change)

function GetSHA1(input) {
  var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.SHA_1, input);
  var txtHash = '';
  for (j = 0; j <rawHash.length; j++) {
    var hashVal = rawHash[j];
    if (hashVal < 0)
      hashVal += 256; 
    if (hashVal.toString(16).length == 1)
     txtHash += "0";
    txtHash += hashVal.toString(16);
    }
  return txtHash;
}
Mikaelamikal answered 7/10, 2013 at 10:18 Comment(2)
This failed for me, until i added var j to the loop. Maybe I made a mistake, maybe the google is stricter now than when this was written.Faydra
My final valid solution: jsfiddle.net/alexander_js_developer/gmx2f08oIdeology
A
6

Ok, got it,

Need to create custom function as explained in http://code.google.com/googleapps/appsscript/articles/custom_function.html

And then use the apis as explained in http://code.google.com/googleapps/appsscript/service_utilities.html

I need to handtype the complete function name so that I can see the result in the cell.

Following is the sample of the code that gave base 64 encoded hash of the text

function getBase64EncodedMD5(text)
{ 
  return Utilities.base64Encode( Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, text));
}
Aerogram answered 4/11, 2011 at 14:8 Comment(1)
Base64 is not a hash in any meaningful sense of the term. It's a numbering system conversion.Gamboge
T
3

The difference between this solution and the others is:

  1. It fixes an issue some of the above solution have with offsetting the output of Utilities.computeDigest (it offsets by 128 instead of 256)

  2. It fixes an issue that causes some other solutions to produce the same hash for different inputs by calling JSON.stringify() on input before passing it to Utilities.computeDigest()

function MD5(input) {
  var result = "";
  var byteArray = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, JSON.stringify(input));
  for (i=0; i < byteArray.length; i++) {
    result += (byteArray[i] + 128).toString(16) + "-";
  }
  result = result.substring(result, result.length - 1); // remove trailing dash
  return result;
}
Trichroism answered 7/11, 2018 at 17:59 Comment(0)
H
2

to get hashes for a range of cells, add this next to gabhubert's function:

function RangeGetMD5Hash(input) {
  if (input.map) {            // Test whether input is an array.
    return input.map(GetMD5Hash); // Recurse over array if so.
  } else {
    return GetMD5Hash(input)
  }
}

and use it in cell this way:

=RangeGetMD5Hash(A5:X25)

It returns range of same dimensions as source one, values will spread down and right from cell with formulae.

It's universal single-value-function to range-func conversion method (ref), and it's way faster than separate formuleas for each cell; in this form, it also works for single cell, so maybe it's worth to rewrite source function this way.

Hacker answered 31/12, 2014 at 8:4 Comment(0)
L
2

Based on @gabhubert but using array operations to get the hexadecimal representation

function sha(str){
    return Utilities
      .computeDigest(Utilities.DigestAlgorithm.SHA_1, str) // string to digested array of integers
      .map(function(val) {return val<0? val+256 : val}) // correct the offset
      .map(function(val) {return ("00" + val.toString(16)).slice(-2)}) // add padding and enconde
      .join(''); // join in a single string
}
Leonard answered 25/10, 2017 at 12:51 Comment(1)
Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, "thisisteststring") .map(function(chr){return (chr+256).toString(16).slice(-2)}) .join('')Paigepaik
F
1

Using @gabhubert answer, you could do this, if you want to get the results from a whole row. From the script editor.

function GetMD5Hash(value) {
  var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, value);
  var txtHash = '';
    for (j = 0; j <rawHash.length; j++) {
   var hashVal = rawHash[j];
    if (hashVal < 0)
      hashVal += 256; 
    if (hashVal.toString(16).length == 1)
      txtHash += "0";
    txtHash += hashVal.toString(16);
  }
    return txtHash;
}

function straightToText() {
  var ss = SpreadsheetApp.getActiveSpreadsheet().getSheets();
  var r = 1;
  var n_rows = 9999;
  var n_cols = 1;
  var column = 1;
  var sheet = ss[0].getRange(r, column, n_rows, ncols).getValues(); // get first sheet, a1:a9999
  var results = [];
  for (var i = 0; i < sheet.length; i++) {
    var hashmd5= GetMD5Hash(sheet[i][0]);
    results.push(hashmd5);
  }
  var dest_col = 3;
  for (var j = 0; j < results.length; j++) {
    var row = j+1;
    ss[0].getRange(row, dest_col).setValue(results[j]);  // write output to c1:c9999 as text
  }  
}

And then, from the Run menu, just run the function straightToText() so you can get your result, and elude the too many calls to a function error.

Frangipani answered 25/3, 2014 at 11:4 Comment(1)
foretasteing you to look at my answer for doing thisHacker
M
1

I was looking for an option that would provide a shorter result. What do you think about this? It only returns 4 characters. The unfortunate part is that it uses i's and o's which can be confused for L's and 0's respectively; with the right font and in caps it wouldn't matter much.

function getShortMD5Hash(input) {
  var rawHash = Utilities.computeDigest(Utilities.DigestAlgorithm.MD5, input);
  var txtHash = '';
    for (j = 0; j < 16; j += 8) { 
    hashVal = (rawHash[j] + rawHash[j+1] + rawHash[j+2] + rawHash[j+3]) ^ (rawHash[j+4] + rawHash[j+5] + rawHash[j+6] + rawHash[j+7])
    if (hashVal < 0)
      hashVal += 1024;
    if (hashVal.toString(36).length == 1)
      txtHash += "0";
    txtHash += hashVal.toString(36);
  }
    return txtHash.toUpperCase();
  }
Myke answered 12/11, 2015 at 6:25 Comment(1)
I don't understand the utility of a 4 byte hash.Grandsire
L
1

I needed to get a hash across a range of cells, so I run it like this:

function RangeSHA256(input)
{
    return Array.isArray(input) ?
        input.map(row => row.map(cell => SHA256(cell))) :
        SHA256(input);
}
Lactoprotein answered 22/9, 2020 at 6:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.