4

Somehow I need to generate a hash in Classic ASP which is equivalent to PHP's following function's output:

$hash = hash_hmac('SHA256', $message, pack('H*', $secret));

where $message = 'stackoverflow'; $secret = '1234567890ABCDEF';. I tried quite a lot approaches online, but none matches the PHP result:

bcb3452cd48c0f9048e64258ca24d0f3399563971d4a5dcdc531a7806b059e36

Method 1: Using dvim_brix_crypto-js-master_VB.asp online (using CrytoJS)

Function mac256(ent, key) 
    Dim encWA
    Set encWA = ConvertUtf8StrToWordArray(ent)
    Dim keyWA
    Set keyWA = ConvertUtf8StrToWordArray(key)
    Dim resWA
    Set resWA = CryptoJS.HmacSHA256(encWA, key)
    Set mac256 = resWA
End Function

Function ConvertUtf8StrToWordArray(data)
    If (typename(data) = "String") Then
        Set ConvertUtf8StrToWordArray = CryptoJS.enc.Utf8.parse(data)
    Elseif (typename(data) = "JScriptTypeInfo") Then
        On error resume next
        'Set ConvertUtf8StrToWordArray = CryptoJS.enc.Utf8.parse(data.toString(CryptoJS.enc.Utf8)) 
        Set ConvertUtf8StrToWordArray = CryptoJS.lib.WordArray.create().concat(data) 'Just assert that data is WordArray
        If Err.number>0 Then
            Set ConvertUtf8StrToWordArray = Nothing
        End if
        On error goto 0
    Else
        Set ConvertUtf8StrToWordArray = Nothing
    End if
End Function

The script can be found here. This method gives:

c8375cf0c0db721ecc9c9b3a034284117d778ee8594285196c41d5020917f78c

Method 2: Pure Classic ASP Approach

Public Function HMAC_SHA256(prmKey, prmData)
    Dim theKey : theKey = prmKey
    Dim Block_Size, O_Pad, I_Pad
    Block_Size = 64
    O_Pad = 92 'HEX: 5c'
    I_Pad = 54 'HEX: 36'

    Dim iter, iter2
    If Len(theKey) < Block_Size Then
        For iter = 1 to Block_Size - Len(theKey)
            theKey = theKey & chr(0)
        Next
    ElseIf Len(theKey) > Block_Size Then
        theKey = SHA256(theKey)
    End If

    Dim o_key_pad : o_key_pad = ""
    Dim i_key_pad : i_key_pad = ""
    For iter = 1 to Block_Size
        o_key_pad = o_key_pad & Chr(Asc(Mid(theKey,iter,1)) xor O_Pad)
        i_key_pad = i_key_pad & Chr(Asc(Mid(theKey,iter,1)) xor I_Pad)
    Next

    HMAC_SHA256 = SHA256(o_key_pad & SHA256(i_key_pad & prmData))
End Function
result = HMAC_SHA256(secret, message)

This method gives:

bc0511316791176484c7d80bc8faaecd8388b75fb97516181ba6b361fd032531

Method 3: Using Amazon AWS's sha256.wsc (using CrytoJS)

Dim sha
Set sha = GetObject( "script:" & Server.MapPath("sha256.wsc") )
sha.hexcase = 0
result = sha.b64_hmac_sha256(secret, message)

The WSC can be found here. This method gives (same result as Method 1):

c8375cf0c0db721ecc9c9b3a034284117d778ee8594285196c41d5020917f78c

I think the problem is the pack() part, which changes the Hex string to binary. Therefore, I found a way to reproduce the pack() function in ASP:

Dim key2, hexarr, binstr
key2 = "12 34 56 78 90 AB CD EF"
hexarr = Split(key2)
ReDim binarr(UBound(hexarr))

For i = 0 To UBound(hexarr)
  binarr(i) = Chr(CInt("&h" & hexarr(i)))
Next

binstr = Join(binarr, "")

where the key2 is the original secret with space added in every 2 characters. By replacing the secret with binstr, the methods now produce:

Method 1: 8ab9e595eab259acb10aa18df7fdf0ecc5ec593f97572d3a4e09f05fdd3aeb8f
Method 2: d23fcafb41d7b581fdae8c2a4a65bc3b19276a4bd367eda9e8e3de43b6a4d355
Method 3: 8ab9e595eab259acb10aa18df7fdf0ecc5ec593f97572d3a4e09f05fdd3aeb8f

None of the above results is identical to PHP's one. What did I miss now?

Raptor
  • 53,206
  • 45
  • 230
  • 366
  • Could it be the encoding? `Chr()` returns ACSII characters whereas `ChrW()` returns Unicode characters. – user692942 Nov 15 '16 at 07:49
  • Yep think it is encoding see this answer to a related answer for a c# implementation - [A: C# equivalent to hash_hmac in PHP](http://stackoverflow.com/a/12804391/692942). The hash expects UTF-8 encoded input data. – user692942 Nov 15 '16 at 07:56
  • @Lankymart but how to achieve the PHP result (i.e. non UTF-8 approach) ? – Raptor Nov 15 '16 at 10:16
  • Hmm...how do you know the PHP method is right? I've just tried from http://www.freeformatter.com/hmac-generator.html and with your data get `c8375cf0c0db721ecc9c9b3a034284117d778ee8594285196c41d5020917f78c`. – user692942 Nov 15 '16 at 10:32
  • It is part of Mastercard Internet Gateway Service (MIGS) checksum, which does not support UTF-8 encoding. – Raptor Nov 15 '16 at 10:41
  • Have you tried `$hash = hash_hmac('SHA256', $message, pack('h*', $secret));` to see if the byte order makes a difference in the PHP call? – user692942 Nov 15 '16 at 10:41
  • The above result is : `e338a6f6b986045efb2310f26e35c8efbf8b7db7267395a3bc0b2b6250f27941`, which doesn't match any of above, again. – Raptor Nov 15 '16 at 10:44
  • 2
    If it doesn't support UTF-8 encoding then your method 1 and method 2 and possibly method 3 approaches are not like for like. You will need to convert any data you pass to ASCII first. To be honest you could go around in circles with this for hours. – user692942 Nov 15 '16 at 10:47
  • Oops. I guess I will try harder , thanks . – Raptor Nov 15 '16 at 11:00

1 Answers1

4

Check out the following example.

The only requirement with this approach is Microsoft .Net Framework 2.0 (preinstalled starting from Windows Server 2003 R2) to use Com Interops.

I tried to be descriptive in the comments but feel free to ask questions about it.

'Returns Byte(), UTF-8 bytes of unicode string
Function Utf8Bytes(text)
    With Server.CreateObject("System.Text.UTF8Encoding")
        Utf8Bytes = .GetBytes_4(text)
    End With
End Function

'Returns String, sequential hexadecimal digits per byte
'data As Byte()
Function BinToHex(data)
    With Server.CreateObject("MSXML2.DomDocument").CreateElement("b64")
        .dataType = "bin.hex"
        .nodeTypedValue = data
        BinToHex = .text
    End With
End Function

'Returns Byte(), a keyed hash generated using SHA256 method
'data As String, key As Byte()
Function HashHmacSha256(data, key)
    With Server.CreateObject("System.Security.Cryptography.HMACSHA256")
        .Key = key
        HashHmacSha256 = .ComputeHash_2(UTF8Bytes(data))
    End With
End Function

'Returns Byte(), of a packed hexadecimal string
'instead of PHP's pack('H*'
Function HexToBin(data)
    With Server.CreateObject("MSXML2.DomDocument").CreateElement("b64")
        .dataType = "bin.hex"
        .text = data
        HexToBin = .nodeTypedValue
    End With
End Function

packed_secret = HexToBin("1234567890ABCDEF")
message = "stackoverflow"
binary_hash = HashHmacSha256(message, packed_secret)
string_hash = BinToHex(binary_hash)

Response.Write string_hash
Kul-Tigin
  • 16,728
  • 1
  • 35
  • 64
  • Thanks, but I'm not using .NET framework. It's an ancient server. – Raptor Nov 15 '16 at 11:11
  • 1
    @Raptor is it not possible to at least have the .NET framework installed? – user692942 Nov 15 '16 at 11:22
  • I always forget that some of the .Net classes are COM interops. – user692942 Nov 15 '16 at 11:23
  • I'm not the server admin, thus I can't install any .NET frameworks. I got only the FTP account... – Raptor Nov 16 '16 at 01:51
  • @raptor in which case request it from your hosting provider. – user692942 Nov 17 '16 at 08:49
  • @Raptor Did you at least try to run it? Off topic, you're dealing with MasterCard. HMAC w/ SHA256 would be one of your minor problems if the server is realy ancient. Be prepared for secure connection problems related with old SSL/TLS implementations existing in older Windows OSes. – Kul-Tigin Nov 17 '16 at 21:11
  • 1
    Thanks guys. I obtained help from bank and resolved my problem. They provided official ASP functions for implementation. – Raptor Nov 21 '16 at 02:09