I am looking to implement HMACSHA256 encoding in Excel VBA.
Several examples/answers make use of System.Text.UTF8Encoding & System.Security.Cryptography.HMACSHA256 however I'm using Windows 10/Office 365 which results in an automation error due to the .NET 4.6 framework (per this stackoverflow post).
There does appear to be an existing VB6 implementation, however it makes use of various Windows libraries which I'd rather avoid as my code may need to run on Office for Mac. I'm also interested in implementing this from first principles as a learning experience.
Per the comment on this page I decide to write my own HMAC wrapper on top of an existing SHA256 class module.
Using the spec and pseudocode in the comment I came up with the below code which I added to the SHA256 class module.
Public Function HMAC_SHA256(MessageString As String, KeyString As String) As String
Dim iKeyPad As String, oKeyPad As String, i As Long
'ensure key is same size as BLOCKSIZE (64)
If Len(KeyString) > BLOCKSIZE Then KeyString = SHA256(KeyString) 'hash to reduce to 64 bytes
If Len(KeyString) < BLOCKSIZE Then KeyString = KeyString & _
String(BLOCKSIZE - Len(KeyString), &H0) 'add 0x00 padding
'create iKeyPad & oKeyPad by Xor'ing iPad/oPad with KeyString
For i = 1 To Len(KeyString)
'iPad = the byte 0x36 repeated BLOCKSIZE times
iKeyPad = iKeyPad & ChrW$(AscB(Mid$(KeyString, i, 1)) Xor &H36)
'oPad = the byte 0x5C repeated BLOCKSIZE times
oKeyPad = oKeyPad & ChrW$(AscB(Mid$(KeyString, i, 1)) Xor &H5C)
Next i
'generate hash
HMAC_SHA256 = SHA256(oKeyPad & SHA256(iKeyPad & MessageString))
End Function
SHA256 is the main function defined in the class module
When I call the function it returns a correctly formatted but incorrect HMACSHA256 digest.
Sub foo()
Dim SourceString As String, KeyVal As String
Dim foo As CSHA256
SourceString = "what do ya want for nothing?"
KeyVal = "Jefe"
Set foo = New CSHA256
Debug.Print "HMAC_SHA256: " & foo.HMAC_SHA256(SourceString, KeyVal)
Debug.Print "SHA256: " & foo.SHA256(SourceString)
End Sub
digest output:
HMAC_SHA256: 1fa93aea23d7b353ef468e5ac460b2840c694842e7ac2018d58a7bf5b790b6e3
SHA256: b381e7fec653fc3ab9b178272366b8ac87fed8d31cb25ed1d0e1f3318644c89c
the SHA256 digest is correct, I've verified it against the test vectors in the spec and with online generators. however the HMAC256 digest is incorrect, it should be...
5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
There is not much happening in the function so I can't figure out where this is going wrong. The string/byte conversions seem to be the likely candidate; I have experimented with various options for doing the conversion...
Sub StringByteExperiments()
Dim KeyString As String, i As Long
Dim ByteArr_UTF8() As Byte, ByteArr_UTF16() As Byte
Dim ByteArr_UTF32() As Byte, ByteArr_ASC(3) As Byte
KeyString = "Jefe"
'=== String to Byte Array ===
ByteArr_UTF8 = StrConv(KeyString, vbFromUnicode)
'=> {74,101,102,101}
ByteArr_UTF16 = KeyString
'=> {74,0,101,0,102,0,101,0}
ByteArr_UTF32 = StrConv(KeyString, vbUnicode)
'=> {74,0,0,0,101,0,0,0,102,0,0,0,101,0,0,0}
For i = 1 To Len(KeyString)
ByteArr_ASC(i - 1) = AscB(Mid(KeyString, i, 1))
Next i
'=> {74,101,102,101}
'=== Byte Array to String ===
KeyString = StrConv(ByteArr_UTF8, vbUnicode)
'=> "Jefe"
KeyString = ByteArr_UT16
'=> doesn't compile
KeyString = Join(ByteArr_UTF16)
'=> err #5, invalid procedure call or argument
KeyString = StrConv(ByteArr_UTF16, vbFromUnicode)
'=> "??" can't parse
KeyString = StrConv(ByteArr_UTF16, vbUnicode)
'=> "J e f e "
KeyString = StrConv(ByteArr_UTF32, vbFromUnicode)
'=> "Jefe"
KeyString = StrConv(ByteArr_UTF32, vbUnicode)
'=> "J e f e "
KeyString = vbNullString
For i = LBound(ByteArr_ASC) To UBound(ByteArr_ASC)
KeyString = KeyString & Chr(ByteArr_ASC(i))
Next i
'=> "Jefe"
End Sub
I tried using the various different options with the function and all resulted in an incorrect digest. As I understand it HMAC_SHA256 works with UTF8 byte arrays, and that appears to be what my current function is doing so am at a loss as what the error could be.
Does anyone have any ideas where I might be going wrong? Or know of any resource/method where I could determine the correct UTF8 byte array for a known key value and also the correct post-Xor-operation byte array & string value for iPad & oPad. This would allow me to compare with my function at various steps along the way to help track down the problem.
Thanks
~a
Bookmarks