2014-02-26 104 views
3

对于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) 

回答

3

在这里你获取来自输入字符串的键值:

SharedSecretKey = asc.Getbytes_4(sSharedSecretKey) 

但在这里,你如果获得从Base64编码输入字符串解码:

decodedKey = base64.urlsafe_b64decode(privateKey) 

NET的Getbytes不会Base64编码解码,因此投入有很大的不同。

如果解码SharedSecretKey为一个字节数组,你会得到正确的输出:

IFxkS7B/ePtZrvU8sGmiaipTHio= 

虽然注意到由于urlsafe_b64encode不同的Base64重新编码语义。

(如果你在.NET通过Converter解码则需要从关键失去尾随填充=