Overview
If you have a Business account for increased volumes of
traffic, you have to sign the request URL using your private key, rather than
using an API key which was required for V2.
This article details what is required and has an example of
how to implement signing using Python.
The thing which tripped me up was this:
“Note: Modified Base64 for URLs
replaces the
+
and /
characters of standard Base64
with -
and _
respectively, so that these
Base64 signatures no longer need to be URL-encoded.”
Our key had ‘_’ characters in which needed swapping to ‘/’ in
order to convert to Base64 bytes and many of the generated hashes had ‘+’ and
‘/’ characters which needed converting to ‘-‘ and ‘_’.
String Formats
The following constants are the string formats I used for
the full URL and the portion of the URL for signing:
private const string SEARCH_URL = "http://maps.googleapis.com/maps/api/geocode/json?latlng={0},{1}&sensor=false&client={2}&signature={3}";
private const string SIGN_URL = "/maps/api/geocode/json?latlng={0},{1}&sensor=false&client={2}";
CreateSignature Method
This method creates a signature for the URL with the private
key:
private string CreateSignature(string url, string privateKey)
{
// Replace file-safe characters
byte[] key = Convert.FromBase64String(privateKey.Replace('-', '+').Replace('_', '/'));
byte[] buffer = Encoding.UTF8.GetBytes(url);
byte[] hash = null;
// Initialize the keyed hash object.
using (HMACSHA1 hmac = new HMACSHA1(key))
{
// Create hash
hash = hmac.ComputeHash(buffer);
}
// Replace unsafe characters
return Convert.ToBase64String(hash).Replace('+', '-').Replace('/', '_');
}
Implementation
It can be implemented in the following example which returns
an address for a lat/long position (Position and Address are basic POCO
objects):
private string _privateKey = "My_Private_Key=";
private string _clientId = "My-ClientId";
public async Task<Address> GetAddress(Position position)
{
string url = string.Format(SIGN_URL, position.Latitude,
position.Longitude, _clientId);
string sig = this.CreateSignature(url, this._privateKey);
string search = string.Format(SEARCH_URL, position.Latitude,
position.Longitude, _clientId, sig);
string data = null;
try
{
var req = WebRequest.CreateHttp(search);
var res = await req.GetResponseAsync();
using (Stream s = res.GetResponseStream())
using (StreamReader str = new StreamReader(s))
{
data = await str.ReadToEndAsync();
}
}
catch (WebException wex)
{
throw new Exception("Web server request failed", wex);
}
if (data != null)
{
var address = this.ParseAddressJson(data);
return address;
}
return null;
}
I’ve not included the JSON parsing routine, but that bit’s
fairly easy to implement.
If something is wrong with the signature you will get a 403
code.