HS256, a class for calculating HMAC-SHA-256 from a Key and Data in VB6.
note:
HS1 (HMAC-SHA-1) also included now. Scroll down to the latest versions of both!
Description
The HS256 class uses Windows Crypto API calls wherever possible to calculate HMACs (Hash-based Message Authentication Codes) based on SHA 256 hashes. The class also provides formatting functions between binary data and Strings (Base64, Hex, and Hex variations) as well as converting Strings to and from UTF8 encoding.
All of these things are useful in creating message authentication strings for many purposes. These HMACs are needed in message authentication for the SOAP and REST APIs provided by many cloud computing services. They are also sometimes used to sign other types of messages (e.g. email).
Feature list
HMAC-SHA-256 calculation without the use of any external cryptography components, relies on the Windows Crypto API.
Translation of binary data to and from Base64 strings.
Translation of binary data to and from Hex strings (several variations such as Hex with address and ASCII).
Translation of standard "Unicode" (UTF16-LE) String data to and from UTF8 encoding.
Screenshot
Screenshot of the (attached) demo shown below.
Author name
Bob Riemersma
System requirements
Requires Windows XP or later. May require XP SP1 or SP2 (Crypto API documentation is not explicit on this point). Some minor format variations are emulated under Windows XP instead being performed via Crypto API calls, one minor sub-variation is only available in Windows 2003 R2, Vista, or later.
No special processor, disk, or memory requirements. Requires Visual Basic 6.0 development system, preferably Service Pack 6b or later, Standard Edition or better.
License info
This software is released into the Public Domain. It may be used for any purpose as is or in derivative works. No warranty of fitness or merchantability, and no support is offered. This source code is available AS IS.
Usage
Add the class module to your VB6 project. See comments at the head of the source regarding use of the methods and properties provided.
The demonstration project
The HS256 class is posted here as part of a demo program.
This program demonstrates the 7 SHA-256 Test Case vector sets found in RFC 4231:
Identifiers and Test Vectors for HMAC-SHA-224, HMAC-SHA-256,
HMAC-SHA-384, and HMAC-SHA-512
Last edited by dilettante; Apr 16th, 2011 at 09:00 PM.
This is a pair of classes meant to be used as an adjunct to the HMAC-SHA-256 class when working with Web Services, primarily REST APIs like those of Azure or AWS.
Caveats, license terms, etc. are the same as spelled out for HS256 above.
These classes are identical in their methods, and format VB6 Date values to/from HTTP (RFC 1123) and ISO 8601 timestamp strings with time zone conversion.
WinHttpTime uses entrypoints in winhttp.dll
WinInetTime uses entrypoints in wininet.dll
A normal program would never use both classes. The idea was to avoid having to load both DLLs (oleaut32.dll and wininet.dll) for a program using WinHttpRequest. However I believe the VB6 runtime always loads wininet.dll and probably both for the process anyway, which probably makes WinHttpTime entirely redundant (i.e. always use WinInetTime).
The classes are declared so that they produce a default instance (PredeclaredId = True). Programs using these classes don't have to create an instance, there will be a global instance named the same as the class.
This is not true of the HS256 class, since you might well need several instances, all initialized to different keys.
The attachment is a trivial demo containing and showing the operation of both classes.
Note: To clarify, these two classes are two ways of doing the same things.
Last edited by dilettante; Apr 14th, 2012 at 01:43 PM.
Reason: added note
Also... due to popular demand an HMAC-SHA-1 implementation created from the most recent HMAC-SHA-256 edition.
The demo incorporates test vectors from RFC 2202:
Test Cases for HMAC-MD5 and HMAC-SHA-1
Please verify these further if you have other known test cases. We'd like to have these cleaned up so people can rely on them. Looking good so far though!
These two Classes are separate since a program normally would only need one or the other, which is also why many auxiliary functions are duplicated between them.
Last edited by dilettante; Apr 16th, 2011 at 09:02 PM.
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
I just read this post and I'm hoping you can help me out. I am a VB6 programmer, but until recently I knew nothing about XML and currently still know nothing about HMAC SHA-256 encryption.
The company I'm working for needs to me to develop an Amazon app to download our order information and Amazon requires a "Signature" parameter to be inserted in their XML queries. This needs to be (forgive me if I state this awkwardly as I barely know what I'm talking about) the result of some parameters encrypted using HMAC SHA-256 and then converted to HMAC Base 64.
From the looks of your sample program, I can see that this may be able to give me the information I need. I've downloaded it and I can run it and see the results, but really what I need is a simple VB6 function to convert a key and data string to SHA-256 and perhaps I just haven't had enough sleep, but I can't figure out where that is in your program.
Could you give a tired, slightly dim programmer a nudge in the right direction? Thanks!
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
I hope this helps.
Going through the AWS SimpleDB documentation for REST Authentication I find the docs are good but not great. They leave out small tidbits and swing between specific and vague on important things like character encoding, what precisely they mean by "newline" and other small things. HS256 itself took some work, but figuring out the AWS "canonicalization" process is a small nightmare. They outline the steps but leaving you hanging here and there on specifics.
They even give an example but don't show what the final Signature should be, making verification of my example here difficult.
In any case, the example attached here is way stripped down but with copious comments. In those comments I have made notes about a few points you may need to get clarification on. Try the AWS developer support forums for answers.
Keep in mind that different Amazon Services have different requirements. Grab all of the docs they offer (old and new) for the Service you need to use.
Last edited by dilettante; Sep 8th, 2011 at 10:05 PM.
Reason: reposted attachment - typos in code comments, doh!
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
That is awesome! Thank you! I'm still not quite there because I can't figure out what string Amazon wants to add to the key (which is presumably my AWS Access Key ID) to arrive at the signature string.
They've provided me with a link to tool called the MWS Scratchpad, and from there, I can see that the signature changes all the time, so I think the timestamp must be included in the string to process somehow? I've sent them a support email request and it's been a couple of days and they haven't answered - they are pretty slow with this generally.
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
Yes, a fresh Timestamp is probably one of the headers you need for the Marketplace Web Service (or whatever it is) much like the generalized AWS offerings. That and your Access Key ID header go into building the "string to sign." I'm sure their different services each have their own requirements for additional headers.
Some services may not use the "headers" of the "string to sign" as actual request headers, but instead send them as a parameter string (part of the URL). They may want a User-Agent header though (that does not seem to be part of the signature).
Perhaps the Scratchpad will help you get it all unraveled.
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
So, just in case I can't get any useful information out of Amazon, is there some way I can decode the signature that their MWS Scratchpad app gives me - it was created with (I think) my AWS Access Key ID and some mystery string. The Scratchpad gives me a value for "SHA 256 HMAC" (some big long incoherent string) and "Base64 HMAC" which is the value used for the Signature parameter.
Is there some way to take my AWS Access Key ID and one of the values they give me to decode the original string so I can look at it in text format? If I had that, I think I might be able to finish my app pretty quickly.
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
No, it doesn't go backwards. These are one-way hash operations which is sort of the point. At Amazon's end they validate by taking your ID you sent, looking up the "secret key" in their database, and parts from the request contents and then doing the same calculation as you. Then they compare the signature they get with the one you sent.
So everything is critical from character encoding to order of operations, what gets included in the "string to sign" and what gets left out. It almost feels like 90% of the security of this process is how confusing it is to duplicate even based on their documentation. The "good news" is Azure and other services are just as bad about this.
I looked at MWS Scratchpad and the main benefit it might offer you is a way to validate your results without actually having to "hit" the service. You could compare the signature output by your code against the signature created by Scratchpad and tweak your code until you get the same thing.
The details for MWS are described starting at Page 11 of this document:
You might even want to print out the pages about this topic and get out three colors of pens and 5 colors of highlighting markers and go at it studying hard.
Last edited by dilettante; Sep 10th, 2011 at 08:12 AM.
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
My primary use of this class is in an Access db with vba. Your class works for me through most of my environments, however..
I have two windows 7 machines and an xp machine. On one of the windows 7 machines the class fails during initialization on this line as a class within the Access db:
Vb Code:
ElseIf CryptAcquireContext(hAdvProvider, _
0&, _
StrPtr(strProvider), _
PROV_RSA_AES, _
CRYPT_VERIFYCONTEXT) = 0 Then
Err.Raise vbObjectError Or &HC368&, _
"HS256.Class_Initialize", _
"Failed to obtain CryptoAPI RSA AES context, system error " _
& CStr(Err.LastDllError)
End If
However, your vb6 demo works fine on the same machine.
The class also works within the Access db on the other windows 7 machine and on the xp machine.
So it seems it can create a base provider but not an advanced provider (from within the Access Db):
Vb Code:
If CryptAcquireContext(hBaseProvider, _
0&, _
StrPtr(MS_DEFAULT_PROVIDER), _
PROV_RSA_FULL, _
CRYPT_VERIFYCONTEXT) = 0 Then
Err.Raise vbObjectError Or &HC366&, _
"HS256.Class_Initialize", _
"Failed to obtain CryptoAPI Base context, system error " _
& CStr(Err.LastDllError)
ElseIf CryptAcquireContext(hAdvProvider, _
0&, _
StrPtr(strProvider), _
PROV_RSA_AES, _
CRYPT_VERIFYCONTEXT) = 0 Then
Err.Raise vbObjectError Or &HC368&, _
"HS256.Class_Initialize", _
"Failed to obtain CryptoAPI RSA AES context, system error " _
& CStr(Err.LastDllError)
End If
Any guesses as to what the cause of this would be? Maybe a missing win7 service pack?
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
Well this error is NTE_KEYSET_NOT_DEF (&H80090019) "The requested provider does not exist."
Very strange.
Are you somehow using Access in a non-interactive session?
Something to try, adding a flag bit. Make these changes:
Near the top
Code:
Private Const PROV_RSA_FULL As Long = 1
Private Const PROV_RSA_AES As Long = 24
Private Const CRYPT_VERIFYCONTEXT As Long = &HF0000000
Private Const CRYPT_MACHINE_KEYSET As Long = 32 <-- new Const
Private Const MS_DEFAULT_PROVIDER As String = _
"Microsoft Base Cryptographic Provider v1.0"
Private Const MS_ENH_RSA_AES_PROV As String = _
"Microsoft Enhanced RSA and AES Cryptographic Provider"
Private Const MS_ENH_RSA_AES_PROV_XP As String = _
"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"
Inside Class_Initialize
Code:
If CryptAcquireContext(hBaseProvider, _
0&, _
StrPtr(MS_DEFAULT_PROVIDER), _
PROV_RSA_FULL, _
CRYPT_VERIFYCONTEXT Or CRYPT_MACHINE_KEYSET) = 0 Then <-- add flag
Err.Raise vbObjectError Or &HC366&, _
"HS256.Class_Initialize", _
"Failed to obtain CryptoAPI Base context, system error " _
& CStr(Err.LastDllError)
ElseIf CryptAcquireContext(hAdvProvider, _
0&, _
StrPtr(strProvider), _
PROV_RSA_AES, _
CRYPT_VERIFYCONTEXT Or CRYPT_MACHINE_KEYSET) = 0 Then <-- add flag
Err.Raise vbObjectError Or &HC368&, _
"HS256.Class_Initialize", _
"Failed to obtain CryptoAPI RSA AES context, system error " _
& CStr(Err.LastDllError)
End If
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
Um... &H20 is the same as 32.
I've tested this code on a 64-bit Windows machine and it works fine there, and as you pointed out my demo program runs properly on your problem system as well.
Turning UAC off is almost always a poor idea. You seem to be grasping at straws but so am I at this point. Without more information I don't think I can help.
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
Good luck with Amazon, that stuff is a bear as most Web Services are when you have to roll your own code.
I am attaching a demo Excel Workbook. It contains basically the same thing as in the VB6 demo. The Userform is shown via a Command Button I put on Sheet1 of the Workbook.
This doesn't try to do anything fancy, it simply shows how to use the Class from Excel. To get it there you just import the VB6 Class from within the VBA Editor IDE.
The Class didn't require any changes, though of course the Form-to-Userform logic required a few.
I hope this helps a little.
Last edited by dilettante; Jan 10th, 2012 at 02:02 AM.
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
Thank you . after several hours to understand how to include your class to vba project, i have attached my aim,thank you ,the hs256 class is very very very useful...
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
I am trying to integrate your HS256 class into Access vba for integration into Amazon MWS and coming up with some problems.
I have successfully created a class HS256 using the HS256.cls in v2.1 above.
I have:
Dim hs256 As clsHS256
Dim bytekey() As Byte
hs256 = New clsHS256
bytekey = hs256.ToUTF8("MY-SECRET-KEY-HERE")
hs256.InitHmac (bytekey) ' <----- Error on this line
Erase bytekey
I get an error "Type mismatch: array or user defined type expected".
I get the same if I replace the line with
hs256.InitHmac (bytekey(0))
Any help any of you might be able to offer would be gratefully received. Alternatively does anyone have a working example of MWS integration into Access vba?
Getting MWS to integrate into Access is much harder than I expected and way harder than it should be!!!
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
Glad with this thread dilettante, especially:
I hope this helps.
Going through the AWS SimpleDB documentation for REST Authentication I find the docs are good but not great. They leave out small tidbits and swing between specific and vague on important things like character encoding, what precisely they mean by "newline" and other small things. HS256 itself took some work, but figuring out the AWS "canonicalization" process is a small nightmare. They outline the steps but leaving you hanging here and there on specifics.
They even give an example but don't show what the final Signature should be, making verification of my example here difficult.
In any case, the example attached here is way stripped down but with copious comments. In those comments I have made notes about a few points you may need to get clarification on. Try the AWS developer support forums for answers.
Keep in mind that different Amazon Services have different requirements. Grab all of the docs they offer (old and new) for the Service you need to use.
It's very nice,thank you.
Because general principle of OAuth is same (using HMAC-SHA1 or RSA-SHA1), what if made into OAuth Class (clsOAuth.cls) for generate a signature (it will be very useful), so with the signature generated by OAuth class (clsOAuth.cls) we can easily access:
Amazon API
Twitter API
Google API
Facebook API
etc ... API with OAuth.
Because it's very hard to find in Google, OAuth Class created with VB6. I've tried to modify the code and make clsOAuth.cls but it always fails and displays the error message:
HTTP/1.1 401 Unauthorized or Invalid Signature and token.
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
I'm having a (reasonably obscure?) problem with this class. I have it integrated into Access to support integration into Amazon MWS and it is providing the signatures correctly on my development system (Access 2007 on Windows XP running under Parallels on my Mac). When I put the code onto a production environment running the runtime version of Access (32 bit) on windows 2008 server 64 bit a different (incorrect) signature is calculated and so the calls fail.
Are there any diagnostics you could suggest I run to try to track down the problem?
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
I tested the compiled VB6 examples and tested them on Server 2008 R2 64 bit. They work there just as expected. Server 2008 is a slightly different OS (Vista Server basically) but it shouldn't be all that different. The demos work as expected on both 32 and 64 bit Vista.
I'd tend to blame your issues on a divergence between VB6 and VBA in later Office versions, but if it works as expected under Access 2007 32-bit running under XP we can probably ignore that possibility.
So if it isn't at the OS and API level, and it isn't about differences between VB6 and later versions of VBA... we aren't left with many obvious things to suspect.
I'm not sure where to start trying to diagnose this except to log intermediate values at every step of the process, then compare the logged results from both of your run time environments.
The Encode() method may be helpful here for logging the Byte array values in hex & ascii. I'd log things to text files and then open each log in Notepad and tile them side-by-side for comparison.
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
Thanks, I will do that.
Before I start doing this please could you verify that the following routine (which I use to call your class) is calling it in the right way. In particular the last line.
Code:
Public Function getSignature(stringtosign As String)
Dim bytekey() As Byte
Dim hs256 As clsHS256
Set hs256 = New clsHS256
bytekey = hs256.ToUTF8(SecretKey)
hs256.InitHmac bytekey
Erase bytekey
Dim bytecqs() As Byte
bytecqs = hs256.ToUTF8(stringtosign)
'Create the Signature hash:
Dim byteSignature() As Byte
byteSignature = hs256.HmacSha256(bytecqs)
Erase bytecqs
'Convert the Signature to Base64:
getSignature = hs256.Encode(byteSignature, edfBase64, efNoFolding)
Erase byteSignature
getSignature = Left$(getSignature, Len(getSignature) - 2)
End Function
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
To verify things I stuck your vb RFC 4231 test routines into vba and ran on both XP with Access 2007 and on windows server 2008 64 bit with the access runtime environment. The results were identical and correct so your code works perfectly. I must be doing something wrong in the code in my earlier post.
Phill
My vba version of your test routines is below for completeness.
Code:
Option Compare Database
Option Explicit
'HMAC-SHA-256 Test vectors from RFC 4231:
'
' Identifiers and Test Vectors for HMAC-SHA-224, HMAC-SHA-256,
' HMAC-SHA-384, and HMAC-SHA-512
Private Const TEST_KEYS As String = _
"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b" & "0b0b0b0b|" _
& "4a656665|" _
& "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & "aaaaaaaa|" _
& "0102030405060708090a0b0c0d0e0f10" & "111213141516171819|" _
& "0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c" & "0c0c0c0c|" _
& "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" _
& "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" _
& "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" _
& "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" _
& "aaaaaa|" _
& "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" _
& "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" _
& "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" _
& "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" & "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" _
& "aaaaaa"
Private Const TEST_DATA As String = _
"4869205468657265|" _
& "7768617420646f2079612077616e7420" & "666f72206e6f7468696e673f|" _
& "dddddddddddddddddddddddddddddddd" & "dddddddddddddddddddddddddddddddd" _
& "dddddddddddddddddddddddddddddddd" & "dddd|" _
& "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" & "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" _
& "cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd" & "cdcd|" _
& "546573742057697468205472756e6361" & "74696f6e|" _
& "54657374205573696e67204c61726765" & "72205468616e20426c6f636b2d53697a" _
& "65204b6579202d2048617368204b6579" & "204669727374|" _
& "54686973206973206120746573742075" & "73696e672061206c6172676572207468" _
& "616e20626c6f636b2d73697a65206b65" & "7920616e642061206c61726765722074" _
& "68616e20626c6f636b2d73697a652064" & "6174612e20546865206b6579206e6565" _
& "647320746f2062652068617368656420" & "6265666f7265206265696e6720757365" _
& "642062792074686520484d414320616c" & "676f726974686d2e"
Private Const TEST_EXPECTED As String = _
"b0344c61d8db38535ca8afceaf0bf12b" & "881dc200c9833da726e9376c2e32cff7|" _
& "5bdcc146bf60754e6a042426089575c7" & "5a003f089d2739839dec58b964ec3843|" _
& "773ea91e36800e46854db8ebd09181a7" & "2959098b3ef8c122d9635514ced565fe|" _
& "82558a389a443c0ea4cc819899f2083a" & "85f0faa3e578f8077a2e3ff46729665b|" _
& "a3b6167473100ee06e0c796c2955552b|" _
& "60e431591ee0b67f0d8a26aacbf5b77f" & "8e0bc6213728c5140546040f0ee37f54|" _
& "9b09ffa71b942fcb27635fbcd5b0e944" & "bfdc63644f0713938a7f51535c3a35e2"
Private Const TEST_DESCRIPTIONS As String = _
"Case 1|" _
& "Case 2 - key shorter than the length of the HMAC output|" _
& "Case 3 - combined length of key and data > 64 bytes|" _
& "Case 4 - combined length of key and data > 64 bytes|" _
& "Case 5 - truncation of output to 128 bits|" _
& "Case 6 - key larger than 128 bytes|" _
& "Case 7 - key and data that is larger than 128 bytes"
Private strKeys() As String
Private strData() As String
Private strExpected() As String
Private strDescriptions() As String
Private HS256 As clsHS256
Private Function Fold(ByVal S As String) As String
With HS256
S = .Encode(.Decode(S), edfHexAsciiAddr, efCrLf)
End With
S = vbTab & Replace$(S, vbNewLine, vbNewLine & vbTab)
Fold = Left$(S, Len(S) - 1)
End Function
Public Sub testSHAvectors()
Dim intCase As Integer
Dim bytSig() As Byte
strKeys = Split(TEST_KEYS, "|")
strData = Split(TEST_DATA, "|")
strExpected = Split(TEST_EXPECTED, "|")
strDescriptions = Split(TEST_DESCRIPTIONS, "|")
Set HS256 = New clsHS256
For intCase = 0 To 6 '1 (one) less than RFC 4231 case numbers.
logtofile (strDescriptions(intCase) & " —" & vbNewLine)
logtofile ("Key:" & vbNewLine & Fold(strKeys(intCase)))
logtofile ("Data:" & vbNewLine & Fold(strData(intCase)))
With HS256
.InitHmac .Decode(strKeys(intCase))
bytSig = .HmacSha256(.Decode(strData(intCase)))
If intCase = 4 Then ReDim Preserve bytSig(15) 'Truncate to 128 bits ("Case 5").
logtofile ("HmacSha256 result:" & vbNewLine _
& Fold(.Encode(bytSig)))
End With
logtofile ("Expected (RFC) result:" & vbNewLine & Fold(strExpected(intCase)))
Next
End Sub
Public Sub logtofile(text As String)
Dim bd As String
Dim fn As String
Dim FNum As Integer
FNum = FreeFile
bd = BaseDir()
fn = bd & "/SIGNATURETESTS.txt"
If fIsFileDIR(fn) Then
Open fn For Append Access Write As #FNum
Else
Open fn For Output Access Write As #FNum
End If
Print #FNum, text; vbNewLine
Close #FNum
End Sub
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
OK. Have resolved it now.
I don't understand why, but on Windows XP vba the encode method is returning the 2 extra characters CRLF at the end of the string. This is not the case for the exact same code on my copy of windows server 2008 SP2.
I now test to see if the code is running under windows server 2008 or not, deleting the last 2 extra characters only if not and all works fine.
In case anyone else has this pain in the future, code is below. Thanks for your advice earlier.
Code:
Private Type OSVERSIONINFO
dwOSVersionInfoSize As Long
dwMajorVersion As Long
dwMinorVersion As Long
dwBuildNumber As Long
dwPlatformId As Long
szCSDVersion As String * 128
End Type
Private Declare Function GetVersionEx Lib "kernel32" _
Alias "GetVersionExA" (lpVersionInformation As _
OSVERSIONINFO) As Long
' returns true if running on windows server2008
Public Function Is2008() As Boolean
Dim oOSInfo As OSVERSIONINFO
oOSInfo.dwOSVersionInfoSize = Len(oOSInfo)
GetVersionEx oOSInfo
If oOSInfo.dwMajorVersion = 6 Then
Is2008 = True
Else
Is2008 = False
End If
End Function
Public Function getSignature(stringtosign As String) As String
Dim bytekey() As Byte
Dim signaturecalculated As String
Set HS256 = New clsHS256
bytekey = HS256.ToUTF8(SecretKey)
HS256.InitHmac bytekey
Erase bytekey
Dim bytecqs() As Byte
bytecqs = HS256.ToUTF8(stringtosign)
'Create the Signature hash:
Dim byteSignature() As Byte
byteSignature = HS256.HmacSha256(bytecqs)
Erase bytecqs
'Convert the Signature to Base64:
signaturecalculated = HS256.Encode(byteSignature, edfBase64, efNoFolding)
Erase byteSignature
If Not Is2008 Then
getSignature = Left$(signaturecalculated, Len(signaturecalculated) - 2)
Else
getSignature = signaturecalculated
End If
End Function
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
Ok, I think that helps explain what was going wrong. The problem was in encoding the result to Base64 with no "folding."
There are a number of things that differ between OS versions. XP uses an earlier name for the Enhanced RSA and AES Cryptographic Provider (yet at least late Server 2003 does not, could this be service packs or the R2 release?) and both XP and Server 2003 have some formatting limitations in their CryptBinaryToString() flags. The latter is why you saw a trailing CRLF when you encoded to Base64.
Your fix will work for you, but it isn't really a general fix since Encode() can work on longer Byte arrays where you'd get CRLFs within the Base64 string as well as at the end.
What I've done is modify the OS version sniffing logic and added a second version Boolean. Along with the new blnIsWin5_1 I've also added the kludge-code needed to accomodate these older OSs. I did the same for my HS1 class as well to head off problems for anyone using that code.
Everything tests out good here, including a separate test program to specifically look for the problem you had found.
The new HS256 package also includes the Excel demo, with the same update (a separate .ZIP file within the main .ZIP file). This .XLS is the large item that makes the HS256 package so big compared to the HS1 package.
I believe these updates address the issue, but feedback is always welcome.
Last edited by dilettante; Jan 22nd, 2013 at 10:31 PM.
Control Panel -> System and Security -> System returns the following information:
Windows Edition: Windows 7 Ultimate, Service Pack 1
64-bit Operating System
I don't understand why the API would return incorrect information. Do you have any suggestions for me?
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
Nevermind.
I was running this code in the IDE, and I had configured VB6 to run in Windows XP (Service Pack 3) compatibility mode. If anyone else runs in to this issue, check your compatibility settings prior to pulling your hair out.
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
Wow, I had no idea anyone does this. It can break lots of code because of the "version lie" appcompat shim.
As an alternative I suppose the code could try for the current crypto context name and only try the legacy XP name upon failure. But that still leaves other issues not as easily handled indirectly.
Glad you found it though. I can see where it might get frustrating to track down.
One thing to note is that the sample URL there mistakenly shows braces (i.e. {}) around the value "clientID" and if you use those you will not get the proper results. This just appears to be a flaw in the posted sample URL.
The demo program uses multiline TextBoxes for I/O, though the values are not on multiple lines (the long text just wraps).
Last edited by dilettante; Sep 23rd, 2013 at 11:20 AM.
Re: [VB6] HMAC-SHA-256, HMAC-SHA-1 Using Crypto API
I have added another rendition of the Google Maps API for Business "signer" logic.
Testing code and such have been removed, redundant intermediate variables have been removed, and more formalized URL parsing is used. The result is probably more directly usable in real applications.
Here we have a class GoogleMapsSigner with a single method Sign(). It is dependent on the HS1 class.
To use these in VB6 just add both class modules to your Project. They should also import into Office VBA easily enough as well (Access, Excel) but have not been tested in 64-bit Office applications which might require tweaking due to the changes in integer types there.
The attachment has both of the necessary class modules as well as a test Project similar to the one above.