VBA中的HMAC-SHA1 URL加密产生不正确的输出
对于Google Maps Business API查询,我需要使用HMAC-SHA1对每个查询进行数字签名。我们使用VBA宏的Excel文件发送查询并解析输出,所以我想在VBA中创建签名。我发现这个问题+回答:Base64 HMAC SHA1 String in VBA
但是,当查询发送到Google API时,此代码的字符串签名无效。
Google提供了一些示例脚本。我测试了Python sample script与我用于测试上述VBA代码的相同输入,并且Python代码确实返回了有效签名。所以看起来,提供的VBA代码并没有构建出适当的HMAC-SHA1签名,但我找不到问题(我没有加密的经验,只有基本的VBA知识)。
我创建了用于测试的HMAC-SHA1密钥:1412SxPev45oMMRQSXazwQp789yM=
当 “abc
” 运行作为字符串输入我得到以下回报:
VBA中的HMAC-SHA1 URL加密产生不正确的输出
VBA代码:Fsu0z3i6Ma5HCrP3eXucrdssJLc=
的Python代码:IFxkS7B_ePtZrvU8sGmiaipTHio=
没有人有任何想法如何计算正确的HMAC-SHA1在VBA相当于Python的输出?
编辑2014年3月4日:
每亚历克斯K.的建议,我就确定SharedSecretKey解码为Base64,使用代码http://thydzik.com。我在下面的VBA代码中添加了函数DecodeBase64
。
由于此输出是正确的,但尚未URL安全的(所以不等同于Python的输出),我用VBA Replace()
功能与_
这些解决方案与-
和/
更换+
一起产生正确的输出,这被Google服务器接受。
使用VBA脚本:
Public Function Base64_HMACSHA1(ByVal sTextToHash As String, ByVal sSharedSecretKey As String)
Dim asc As Object, enc As Object
Dim TextToHash() As Byte
Dim SharedSecretKey() As Byte
Set asc = CreateObject("System.Text.UTF8Encoding")
Set enc = CreateObject("System.Security.Cryptography.HMACSHA1")
TextToHash = asc.Getbytes_4(sTextToHash)
SharedSecretKey = asc.Getbytes_4(sSharedSecretKey)
enc.Key = SharedSecretKey
Dim bytes() As Byte
bytes = enc.ComputeHash_2((TextToHash))
Base64_HMACSHA1 = EncodeBase64(bytes)
Set asc = Nothing
Set enc = Nothing
End Function
Private Function EncodeBase64(ByRef arrData() As Byte) As String
Dim objXML As MSXML2.DOMDocument
Dim objNode As MSXML2.IXMLDOMElement
Set objXML = New MSXML2.DOMDocument
' byte array to base64
Set objNode = objXML.createElement("b64")
objNode.DataType = "bin.base64"
objNode.nodeTypedValue = arrData
EncodeBase64 = objNode.Text
Set objNode = Nothing
Set objXML = Nothing
End Function
添加的代码为Base64解码:
Private Function decodeBase64(ByVal strData As String) As Byte()
Dim objXML As MSXML2.DOMDocument
Dim objNode As MSXML2.IXMLDOMElement
Set objXML = New MSXML2.DOMDocument
Set objNode = objXML.createElement("b64")
objNode.DataType = "bin.base64"
objNode.Text = strData
decodeBase64 = objNode.nodeTypedValue
Set objNode = Nothing
Set objXML = Nothing
End Function
使用Python脚本:
#!/usr/bin/python
# coding: utf8
import sys
import hashlib
import urllib
import hmac
import base64
import urlparse
print("")
print("URL Signer 1.0")
print("")
# Convert the URL string to a URL, which we can parse
# using the urlparse() function into path and query
# Note that this URL should already be URL-encoded
url = urlparse.urlparse("YOUR_URL_TO_SIGN")
privateKey = "YOUR_PRIVATE_KEY"
# We only need to sign the path+query part of the string
urlToSign = url.path + "?" + url.query
# Decode the private key into its binary format
decodedKey = base64.urlsafe_b64decode(privateKey)
# Create a signature using the private key and the URL-encoded
# string using HMAC SHA1. This signature will be binary.
signature = hmac.new(decodedKey, urlToSign, hashlib.sha1)
# Encode the binary signature into base64 for use within a URL
encodedSignature = base64.urlsafe_b64encode(signature.digest())
originalUrl = url.scheme + "://" + url.netloc + url.path + "?" + url.query
print("Full URL: " + originalUrl + "&signature=" + encodedSignature)
在这里你获取来自输入字符串的键值:
SharedSecretKey = asc.Getbytes_4(sSharedSecretKey)
但在这里,你如果获得从Base64编码输入字符串解码:
decodedKey = base64.urlsafe_b64decode(privateKey)
NET的Getbytes
不会Base64编码解码,因此投入有很大的不同。
如果解码SharedSecretKey
为一个字节数组,你会得到正确的输出:
IFxkS7B/ePtZrvU8sGmiaipTHio=
虽然注意到由于urlsafe_b64encode
不同的Base64重新编码语义。
(如果你在.NET通过Converter
解码则需要从关键失去尾随填充=
)