Generate a Hash from string in Javascript

前端 未结 22 992
不知归路
不知归路 2020-11-22 03:34

I need to convert strings to some form of hash. Is this possible in JavaScript?

I\'m not utilizing a server-side language so I can\'t do it that way.

相关标签:
22条回答
  • 2020-11-22 04:11

    I'm a bit surprised nobody has talked about the new SubtleCrypto API yet.

    To get an hash from a string, you can use the subtle.digest method :

    function getHash(str, algo = "SHA-256") {
      let strBuf = new TextEncoder('utf-8').encode(str);
      return crypto.subtle.digest(algo, strBuf)
        .then(hash => {
          window.hash = hash;
          // here hash is an arrayBuffer, 
          // so we'll connvert it to its hex version
          let result = '';
          const view = new DataView(hash);
          for (let i = 0; i < hash.byteLength; i += 4) {
            result += ('00000000' + view.getUint32(i).toString(16)).slice(-8);
          }
          return result;
        });
    }
    
    getHash('hello world')
      .then(hash => {
        console.log(hash);
      });

    0 讨论(0)
  • 2020-11-22 04:12

    EDIT

    based on my jsperf tests, the accepted answer is actually faster: http://jsperf.com/hashcodelordvlad

    ORIGINAL

    if anyone is interested, here is an improved ( faster ) version, which will fail on older browsers who lack the reduce array function.

    hashCode = function(s){
      return s.split("").reduce(function(a,b){a=((a<<5)-a)+b.charCodeAt(0);return a&a},0);              
    }
    

    one-liner arrow function version :

    hashCode = s => s.split('').reduce((a,b)=>{a=((a<<5)-a)+b.charCodeAt(0);return a&a},0)
    
    0 讨论(0)
  • 2020-11-22 04:12

    This should be a bit more secure hash than some other answers, but in a function, without any preloaded source

    I created basicly a minified simplified version of sha1.
    You take the bytes of the string and group them by 4 to 32bit "words"
    Then we extend every 8 words to 40 words (for bigger impact on the result).
    This goes to the hashing function (the last reduce) where we do some maths with the current state and the input. We always get 4 words out.
    This is almost a one-command/one-line version using map,reduce... instead of loops, but it is still pretty fast

    String.prototype.hash = function(){
        var rot = (word, shift) => word << shift | word >>> (32 - shift);
        return unescape(encodeURIComponent(this.valueOf())).split("").map(char =>
                char.charCodeAt(0)
            ).reduce((done, byte, idx, arr) =>
                idx % 4 == 0 ? [...done, arr.slice(idx, idx + 4)] : done
            , []).reduce((done, group) =>
                [...done, group[0] << 24 | group[1] << 16 | group[2] << 8 | group[3]]
            , []).reduce((done, word, idx, arr) =>
                idx % 8 == 0 ? [...done, arr.slice(idx, idx + 8)] : done
            , []).map(group => {
                while(group.length < 40)
                    group.push(rot(group[group.length - 2] ^ group[group.length - 5] ^ group[group.length - 8], 3));
                return group;
            }).flat().reduce((state, word, idx, arr) => {
                var temp = ((state[0] + rot(state[1], 5) + word + idx + state[3]) & 0xffffffff) ^ state[idx % 2 == 0 ? 4 : 5](state[0], state[1], state[2]);
                state[0] = rot(state[1] ^ state[2], 11);
                state[1] = ~state[2] ^ rot(~state[3], 19);
                state[2] = rot(~state[3], 11);
                state[3] = temp;
                return state;
            }, [0xbd173622, 0x96d8975c, 0x3a6d1a23, 0xe5843775,
                (w1, w2, w3) => (w1 & rot(w2, 5)) | (~rot(w1, 11) & w3),
                (w1, w2, w3) => w1 ^ rot(w2, 5) ^ rot(w3, 11)]
            ).slice(0, 4).map(p =>
                p >>> 0
            ).map(word =>
                ("0000000" + word.toString(16)).slice(-8)
            ).join("");
    };
    

    we also convert the output to hex to get a string instead of word array.
    Usage is simple. for expample "a string".hash() will return "88a09e8f9cc6f8c71c4497fbb36f84cd"

    String.prototype.hash = function(){
    	var rot = (word, shift) => word << shift | word >>> (32 - shift);
    	return unescape(encodeURIComponent(this.valueOf())).split("").map(char =>
    			char.charCodeAt(0)
    		).reduce((done, byte, idx, arr) =>
    			idx % 4 == 0 ? [...done, arr.slice(idx, idx + 4)] : done
    		, []).reduce((done, group) =>
    			[...done, group[0] << 24 | group[1] << 16 | group[2] << 8 | group[3]]
    		, []).reduce((done, word, idx, arr) =>
    			idx % 8 == 0 ? [...done, arr.slice(idx, idx + 8)] : done
    		, []).map(group => {
    			while(group.length < 40)
    				group.push(rot(group[group.length - 2] ^ group[group.length - 5] ^ group[group.length - 8], 3));
    			return group;
    		}).flat().reduce((state, word, idx, arr) => {
    			var temp = ((state[0] + rot(state[1], 5) + word + idx + state[3]) & 0xffffffff) ^ state[idx % 2 == 0 ? 4 : 5](state[0], state[1], state[2]);
    			state[0] = rot(state[1] ^ state[2], 11);
    			state[1] = ~state[2] ^ rot(~state[3], 19);
    			state[2] = rot(~state[3], 11);
    			state[3] = temp;
    			return state;
    		}, [0xbd173622, 0x96d8975c, 0x3a6d1a23, 0xe5843775,
    			(w1, w2, w3) => (w1 & rot(w2, 5)) | (~rot(w1, 11) & w3),
    			(w1, w2, w3) => w1 ^ rot(w2, 5) ^ rot(w3, 11)]
    		).slice(0, 4).map(p =>
    			p >>> 0
    		).map(word =>
    			("0000000" + word.toString(16)).slice(-8)
    		).join("");
    };
    let str = "the string could even by empty";
    console.log(str.hash())//9f9aeca899367572b875b51be0d566b5

    0 讨论(0)
  • 2020-11-22 04:14

    My quick (very long) one liner based on FNV's Multiply+Xor method:

    my_string.split('').map(v=>v.charCodeAt(0)).reduce((a,v)=>a+((a<<7)+(a<<3))^v).toString(16);
    
    0 讨论(0)
  • 2020-11-22 04:14

    Slightly simplified version of @esmiralha's answer.

    I don't override String in this version, since that could result in some undesired behaviour.

    function hashCode(str) {
        var hash = 0;
        for (var i = 0; i < str.length; i++) {
            hash = ~~(((hash << 5) - hash) + str.charCodeAt(i));
        }
        return hash;
    }
    
    0 讨论(0)
  • 2020-11-22 04:15

    SubtleCrypto.digest

    I’m not utilizing a server-side language so I can’t do it that way.

    Are you sure you can’t do it that way?

    Did you forget you’re using Javascript, the language ever-evolving?

    Try SubtleCrypto. It supports SHA-1, SHA-128, SHA-256, and SHA-512 hash functions.


    async function hash(message/*: string */) {
    	const text_encoder = new TextEncoder;
    	const data = text_encoder.encode(message);
    	const message_digest = await window.crypto.subtle.digest("SHA-512", data);
    	return message_digest;
    } // -> ArrayBuffer
    
    function in_hex(data/*: ArrayBuffer */) {
    	const octets = new Uint8Array(data);
    	const hex = [].map.call(octets, octet => octet.toString(16).padStart(2, "0")).join("");
    	return hex;
    } // -> string
    
    (async function demo() {
    	console.log(in_hex(await hash("Thanks for the magic.")));
    })();

    0 讨论(0)
提交回复
热议问题