Skip to content

Instantly share code, notes, and snippets.

@thewheat
Last active February 21, 2024 14:16
Show Gist options
  • Star 67 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save thewheat/7342c76ade46e7322c3e to your computer and use it in GitHub Desktop.
Save thewheat/7342c76ade46e7322c3e to your computer and use it in GitHub Desktop.

Intercom user_hash

Remember that your secret key should never be exposed to the public

  • So Javascript code below should only be used for testing unless modified and used to run on a server

Javascript

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/hmac-sha256.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/enc-base64-min.js"></script>
<script>
  var hash = CryptoJS.HmacSHA256("Message", "secret");
  var hashInHex = CryptoJS.enc.Hex.stringify(hash);
  document.write(hashInHex); // aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597
</script>

Node

const crypto = require('crypto');
const hmac = crypto.createHmac('sha256', 'secret');
hmac.update('Message');
console.log(hmac.digest('hex')); // aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597

PHP

<?php
echo hash_hmac('sha256', 'Message', 'secret'); // thanks @AshleyPinner https://gist.github.com/thewheat/7342c76ade46e7322c3e#gistcomment-1820677
?>

Java

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class Test {
  public static void main(String[] args) {
  try {
      String secret = "secret";
      String message = "Message";

      Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
      SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
      sha256_HMAC.init(secret_key);

      byte[] hash = (sha256_HMAC.doFinal(message.getBytes()));
      StringBuffer result = new StringBuffer();
      for (byte b : hash) {
        result.append(String.format("%02x", b)); // thanks sachins! https://gist.github.com/thewheat/7342c76ade46e7322c3e#gistcomment-1863031 
      }
      System.out.println(result.toString()); // aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597
    }
    catch (Exception e){
      System.out.println("Error");
    }
  }
}

Groovy

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;

def hmac_sha256(String secretKey, String data) {
 try {
    Mac mac = Mac.getInstance("HmacSHA256")
    SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), "HmacSHA256")
    mac.init(secretKeySpec)
    byte[] digest = mac.doFinal(data.getBytes())
    return digest
   } catch (InvalidKeyException e) {
    throw new RuntimeException("Invalid key exception while converting to HMac SHA256")
  }
}

def hash = hmac_sha256("secret", "Message")
StringBuffer result = new StringBuffer();
for (byte b : hash) {
result.append(String.format("%02x", b)); // thanks sachins! https://gist.github.com/thewheat/7342c76ade46e7322c3e#gistcomment-1863031
}
print(result.toString()); // aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597

C#

using System.Security.Cryptography;

namespace Test
{
	public class MyHmac
	{
		public static void Main(string[] args){
			var hmac = new MyHmac ();
			System.Console.WriteLine(hmac.CreateToken ("Message", "secret")); // AA747C502A898200F9E4FA21BAC68136F886A0E27AEC70BA06DAF2E2A5CB5597
		}
		private string CreateToken(string message, string secret)
		{
			secret = secret ?? "";
			var encoding = new System.Text.ASCIIEncoding();
			byte[] keyByte = encoding.GetBytes(secret);
			byte[] messageBytes = encoding.GetBytes(message);
			using (var hmacsha256 = new HMACSHA256(keyByte))
			{
				byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);

				var sb = new System.Text.StringBuilder();
				for (var i = 0; i <= hashmessage.Length - 1; i++)
				{
					sb.Append(hashmessage[i].ToString("x2")); // Thanks! @ermirbeqiraj https://gist.github.com/thewheat/7342c76ade46e7322c3e#gistcomment-3180664
				}
				return sb.ToString();
			}
		}
	}
}

Objective C

#import <Foundation/Foundation.h>
#import <CommonCrypto/CommonHMAC.h>

NSData *hmacForKeyAndData(NSString *key, NSString *data)
{
    const char *cKey  = [key cStringUsingEncoding:NSASCIIStringEncoding];
    const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];
    unsigned char cHMAC[CC_SHA256_DIGEST_LENGTH];
    CCHmac(kCCHmacAlgSHA256, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
    return [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
}

// http://stackoverflow.com/a/9084784
NSString *hexadecimalString(NSData *data){
    /* Returns hexadecimal string of NSData. Empty string if data is empty.   */

    const unsigned char *dataBuffer = (const unsigned char *)[data bytes];

    if (!dataBuffer)
        return [NSString string];

    NSUInteger          dataLength  = [data length];
    NSMutableString     *hexString  = [NSMutableString stringWithCapacity:(dataLength * 2)];

    for (int i = 0; i < dataLength; ++i)
        [hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]];

    return [NSString stringWithString:hexString];
}
int main (int argc, const char * argv[])
{
    @autoreleasepool {
        NSLog(@"%@", hexadecimalString(hmacForKeyAndData(@"secret", @"Message")));
    }
    return 0;
}

Go

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
)

func ComputeHmac256(message string, secret string) string {
    key := []byte(secret)
    h := hmac.New(sha256.New, key)
    h.Write([]byte(message))
    return hex.EncodeToString(h.Sum(nil))
}

func main() {
    fmt.Println(ComputeHmac256("Message", "secret")) // aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597
}

Ruby

require 'openssl'

OpenSSL::HMAC.hexdigest('sha256', "secret", "Message") # aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597

Python (2.x)

import hashlib
import hmac

KEY = "secret"
MESSAGE = "Message"
result = hmac.new(KEY, MESSAGE, hashlib.sha256).hexdigest()
print result # aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597

Python (3.x)

import hashlib
import hmac

KEY = "secret"
KEY_BYTES=KEY.encode('ascii')
MESSAGE = "Message"
MESSAGE_BYTES=MESSAGE.encode('ascii')
result = hmac.new(KEY_BYTES, MESSAGE_BYTES, hashlib.sha256).hexdigest()

print (result) # aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597

Perl

use Digest::SHA qw(hmac_sha256_hex);
$digest = hmac_sha256_hex("Message", "secret");
print $digest; # aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597

Swift

// Add
// #import <CommonCrypto/CommonHMAC.h>
// to the bridging Objective-C bridging header.

import Foundation

enum CryptoAlgorithm {
    case MD5, SHA1, SHA224, SHA256, SHA384, SHA512

    var HMACAlgorithm: CCHmacAlgorithm {
        var result: Int = 0
        switch self {
        case .MD5:      result = kCCHmacAlgMD5
        case .SHA1:     result = kCCHmacAlgSHA1
        case .SHA224:   result = kCCHmacAlgSHA224
        case .SHA256:   result = kCCHmacAlgSHA256
        case .SHA384:   result = kCCHmacAlgSHA384
        case .SHA512:   result = kCCHmacAlgSHA512
        }
        return CCHmacAlgorithm(result)
    }

    var digestLength: Int {
        var result: Int32 = 0
        switch self {
        case .MD5:      result = CC_MD5_DIGEST_LENGTH
        case .SHA1:     result = CC_SHA1_DIGEST_LENGTH
        case .SHA224:   result = CC_SHA224_DIGEST_LENGTH
        case .SHA256:   result = CC_SHA256_DIGEST_LENGTH
        case .SHA384:   result = CC_SHA384_DIGEST_LENGTH
        case .SHA512:   result = CC_SHA512_DIGEST_LENGTH
        }
        return Int(result)
    }
}

extension String {

    func hmac(algorithm: CryptoAlgorithm, key: String) -> String {
        let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
        let strLen = Int(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
        let digestLen = algorithm.digestLength
        let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
        let keyStr = key.cStringUsingEncoding(NSUTF8StringEncoding)
        let keyLen = Int(key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))

        CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)

        let digest = stringFromResult(result, length: digestLen)

        result.dealloc(digestLen)

        return digest
    }

    private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
        var hash = NSMutableString()
        for i in 0..<length {
            hash.appendFormat("%02x", result[i])
        }
        return String(hash)
    }

}


print("Message".hmac(CryptoAlgorithm.SHA256, key: "secret")) //aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597
@beastawakens
Copy link

beastawakens commented May 5, 2016

Bash

echo -n "your_user_data" | openssl dgst -sha256 -hmac "your API secret"

@mike-north
Copy link

New CDN tags for javascript assets. Google ones don't work anymore

<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/hmac-sha256.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/components/enc-base64-min.js"></script>

@mike-north
Copy link

Elixir

hmac_digest = :sha256
|> :crypto.hmac(intercom_secure_key, user.intercom_id)
|> Base.encode16
|> String.downcase

@gawin
Copy link

gawin commented Jun 6, 2016

Lua

local crypto = require("crypto")
local hmac = require("crypto.hmac")

local result = hmac.digest("sha256", "Intercom.user_id", "Intercom.secret_key")

Note that you need to have LuaCrypto and OpenSSL installed. Install via LuaRocks:
luarocks install luacrypto

@botekchristophe
Copy link

botekchristophe commented Jun 21, 2016

Scala

import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

def encryptSHA256(message: String, secret: String): String = {
    val sha256_HMAC = Mac.getInstance("HmacSHA256")
    val secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256")
    sha256_HMAC.init(secret_key)
    sha256_HMAC.doFinal(message.getBytes())
      .map("%02X" format _)
      .mkString
  }

@AshleyPinner
Copy link

AshleyPinner commented Jul 7, 2016

FYI, the PHP example can be done more simply as:

<?php
echo hash_hmac('sha256', 'Message', 'secret');
?>

If you don't specify the final argument, it defaults to false and outputs as lowercase hexits, as per the docs

@hawkz
Copy link

hawkz commented Aug 12, 2016

Django

Create a template tag:

import hashlib as h 
import hmac as h2

@register.filter(name='hmac')
def hmac(userid, key): 
    """Return the encrypted key"""
    return h2.new(str(key), str(userid), digestmod=h.sha256).hexdigest()

In your template do something like:

{% load your_template_tag_here %}

<script> 
{% if not user.is_authenticated %} 
    window.intercomSettings = { 
      app_id: "XXXXXXXX" 
    }; 
{% else %} 
    window.intercomSettings = { 
      app_id: "XXXXXXXX", 
      name: "{{ request.user.get_full_name }}", // Full name 
      user_id: "{{ request.user.id }}", // User ID 
      created_at: {{ request.user.date_joined|date:"U" }}, // Signup date as a Unix timestamp 
      user_hash: "{{ request.user.id|hmac:'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' }}" 
    }; 
{% endif %} 
</script> 

@sachins
Copy link

sachins commented Aug 31, 2016

The Java example works for Android, but make sure to lowercase the result. So result.append(String.format("%02X", b)); should be result.append(String.format("%02x", b));

@atskimura
Copy link

Apex

String userId = 'userid';
String intercomSecret = 'intercom_secret';
Blob userHashBlob = crypto.generateMac('HmacSHA256', Blob.valueOf(userId), Blob.valueOf(intercomSecret));
String userHash = EncodingUtil.convertToHex(userHashBlob);

@scottsouthard
Copy link

scottsouthard commented Feb 26, 2017

Liquid

{% assign my_secret_string = "user_id" | hmac_sha256: "secret_key" %}
My encoded string is: {{ my_secret_string }}

@hasmMarques
Copy link

hasmMarques commented Sep 28, 2017

Hi,
Here string CreateToken(string message, string secret)
what means the 'secret' input parameter? it's the apikey?
Cheers,
Hasm

@whitecaps
Copy link

whitecaps commented Nov 6, 2017

@hasmMarques The secret argument in this case refers to the "Identity Verification Secret" found in your "App Settings" > "Identity verification" page.

@thevalyreangroup
Copy link

Update for Swift 4 and also returns the string in lower case for API satisfaction.

import Foundation

enum CryptoAlgorithm {
    case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
    
    var HMACAlgorithm: CCHmacAlgorithm {
        var result: Int = 0
        switch self {
        case .MD5:      result = kCCHmacAlgMD5
        case .SHA1:     result = kCCHmacAlgSHA1
        case .SHA224:   result = kCCHmacAlgSHA224
        case .SHA256:   result = kCCHmacAlgSHA256
        case .SHA384:   result = kCCHmacAlgSHA384
        case .SHA512:   result = kCCHmacAlgSHA512
        }
        return CCHmacAlgorithm(result)
    }
    
    var digestLength: Int {
        var result: Int32 = 0
        switch self {
        case .MD5:      result = CC_MD5_DIGEST_LENGTH
        case .SHA1:     result = CC_SHA1_DIGEST_LENGTH
        case .SHA224:   result = CC_SHA224_DIGEST_LENGTH
        case .SHA256:   result = CC_SHA256_DIGEST_LENGTH
        case .SHA384:   result = CC_SHA384_DIGEST_LENGTH
        case .SHA512:   result = CC_SHA512_DIGEST_LENGTH
        }
        return Int(result)
    }
}

extension String {
    
    func hmac(algorithm: CryptoAlgorithm, key: String) -> String {
        let str = self.cString(using: String.Encoding.utf8)
        let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8))
        let digestLen = algorithm.digestLength
        let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
        let keyStr = key.cString(using: String.Encoding.utf8)
        let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8))
        
        CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)
        
        let digest = stringFromResult(result: result, length: digestLen)
        
        result.deallocate(capacity: digestLen)
        
        return digest
    }
    
    private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
        let hash = NSMutableString()
        for i in 0..<length {
            hash.appendFormat("%02x", result[i])
        }
        return String(hash).lowercased()
    }
}

@wmonk
Copy link

wmonk commented Mar 11, 2019

Rust

Translated from our existing python code. The key here was stringifying the uuid before signing it. As Uuid::as_bytes gives a different output to the string version.

use ring::{hmac, digest};
use hex;
use uuid;

let id = uuid::Uuid::new_v4();
let signing_key = hmac::SigningKey::new(
    &digest::SHA256,
    &INTERCOM_KEY.as_bytes()
);
let signed_user_id = hmac::sign(
    &signing_key,
    id.to_string().as_bytes()
);
let intercom_hash = hex::encode(
    signed_user_id.as_ref()
);

@perchrh
Copy link

perchrh commented Nov 26, 2019

Kotlin

The secret parameter is the key received from Intercom, and message is the userId to be hashed.

import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec

object IntercomIdentifyHasher {

    fun createHash(secret: String, message: String): String {
        val algorithm = Mac.getInstance("HmacSHA256")
        val secretKey = SecretKeySpec(secret.toByteArray(), "HmacSHA256")
        algorithm.init(secretKey)

        val hash = algorithm.doFinal(message.toByteArray())
        val result = StringBuffer()
        for (b in hash) {
            result.append(String.format("%02x", b))
        }
        return result.toString()
    }
}

@ermirbeqiraj
Copy link

C# snipped needs .ToString("x2") instead of .ToString("X2")

@thewheat
Copy link
Author

Thanks @ermirbeqiraj I've updated it 👍

@raaowx
Copy link

raaowx commented Jun 17, 2020

Update for Swift 5

Update for Swift 4 and also returns the string in lower case for API satisfaction.

import Foundation
import CommonCrypto.CommonHMAC // !! ADDED

enum CryptoAlgorithm {
    case MD5, SHA1, SHA224, SHA256, SHA384, SHA512
    
    var HMACAlgorithm: CCHmacAlgorithm {
        var result: Int = 0
        switch self {
        case .MD5:      result = kCCHmacAlgMD5
        case .SHA1:     result = kCCHmacAlgSHA1
        case .SHA224:   result = kCCHmacAlgSHA224
        case .SHA256:   result = kCCHmacAlgSHA256
        case .SHA384:   result = kCCHmacAlgSHA384
        case .SHA512:   result = kCCHmacAlgSHA512
        }
        return CCHmacAlgorithm(result)
    }
    
    var digestLength: Int {
        var result: Int32 = 0
        switch self {
        case .MD5:      result = CC_MD5_DIGEST_LENGTH
        case .SHA1:     result = CC_SHA1_DIGEST_LENGTH
        case .SHA224:   result = CC_SHA224_DIGEST_LENGTH
        case .SHA256:   result = CC_SHA256_DIGEST_LENGTH
        case .SHA384:   result = CC_SHA384_DIGEST_LENGTH
        case .SHA512:   result = CC_SHA512_DIGEST_LENGTH
        }
        return Int(result)
    }
}

extension String {
    
    func hmac(algorithm: CryptoAlgorithm, key: String) -> String {
        let str = self.cString(using: String.Encoding.utf8)
        let strLen = Int(self.lengthOfBytes(using: String.Encoding.utf8))
        let digestLen = algorithm.digestLength
        let result = UnsafeMutablePointer<CUnsignedChar>.allocate(capacity: digestLen)
        let keyStr = key.cString(using: String.Encoding.utf8)
        let keyLen = Int(key.lengthOfBytes(using: String.Encoding.utf8))
        
        CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)
        
        let digest = stringFromResult(result: result, length: digestLen)
        
        result.deallocate() // !! UPDATED: Error: deallocate(capacity:)' is unavailable: Swift currently only supports freeing entire heap blocks, use deallocate() instead
        
        return digest
    }
    
    private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
        let hash = NSMutableString()
        for i in 0..<length {
            hash.appendFormat("%02x", result[i])
        }
        return String(hash).lowercased()
    }
}

@jchavarri
Copy link

OCaml

Using ahrefs/ocaml-sodium:

let hex_of_bytes b =
  let s = Buffer.create (Bytes.length b * 2) in
  Bytes.iter (fun c -> Printf.bprintf s "%02x" (Char.code c)) b;
  Buffer.contents s

let hmac_sha256 ~key payload =
  let open Sodium.Auth.Hmac_sha256.Bytes in
  let state = init (Bytes.of_string key) in
  update state (Bytes.of_string payload);
  hex_of_bytes (of_auth (Sodium.Auth.Hmac_sha256.final state))

@luizvnegrini
Copy link

luizvnegrini commented Sep 29, 2021

Dart

Using cryto (Flutter package):

final key = utf8.encode(__YOUR_SECRET_HERE__);
final bytes = utf8.encode(__DATA__);

final hmacSha256 = Hmac(sha256, key);
final digest = hmacSha256.convert(bytes);

@jamesfernandes
Copy link

jamesfernandes commented Feb 27, 2023

C#

Simpler version

var encoding = new System.Text.ASCIIEncoding();

byte[] keyBytes = encoding.GetBytes("secret"); // secret key
byte[] userIdBytes = encoding.GetBytes("Message"); // user id or email

using var hmacsha256 = new System.Security.Cryptography.HMACSHA256(keyBytes);
byte[] hash = hmacsha256.ComputeHash(userIdBytes);

var token = BitConverter.ToString(hash).Replace("-", string.Empty).ToLowerInvariant();

Console.WriteLine(token); // aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597

Note: this will run exactly as-is in Program.cs of a bare-bones .NET 7 console project.

@simplenotezy
Copy link

simplenotezy commented May 22, 2023

Dart

flutter pub add crypto

import 'dart:convert';
import 'dart:io';

import 'package:crypto/crypto.dart';

String generateIntercomIdentityVerification(String userIdOrEmail) {
  // Create an HMAC instance with the SHA-256 algorithm and the secret key
  final hmac = Hmac(
    sha256,
    utf8.encode('YOUR_SECRET'),
  );

  // Generate the HMAC digest of the data
  final digest = hmac.convert(utf8.encode(userIdOrEmail));

  // Convert the digest to a string representation
  return digest.toString();
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment