Using a signed Google Maps API geocoding request from a .NET command-line application

So, I am writing an application for caching geocoding data when importing records. It works fine for me when I use an unsigned request, but I can’t understand what happened when I try to use my clientid firm and signature. I always get the forbidden 403.

Here is my url constructor:

private const string _googleUri = "http://maps.googleapis.com/maps/api/geocode/xml?address="; private const string _googleClientId = "XXXXXXXX"; private const string _googleSignature = "XXXXXXXXXXXXXXXXXXXXXXXX"; //RESOLVED private static String GetGeocodeUri(string address) { ASCIIEncoding encoding = new ASCIIEncoding(); string url = String.Format("{0}{1}&client={2}&sensor=false" , _googleUri , HttpUtility.UrlEncode(address) , _googleClientId); // converting key to bytes will throw an exception, need to replace '-' and '_' characters first. string usablePrivateKey = _googleSignature.Replace("-", "+").Replace("_", "/"); byte[] privateKeyBytes = Convert.FromBase64String(usablePrivateKey); Uri uri = new Uri(url); byte[] encodedPathAndQueryBytes = encoding.GetBytes( uri.LocalPath + uri.Query ); // compute the hash HMACSHA1 algorithm = new HMACSHA1(privateKeyBytes); byte[] hash = algorithm.ComputeHash(encodedPathAndQueryBytes); // convert the bytes to string and make url-safe by replacing '+' and '/' characters string signature = Convert.ToBase64String(hash).Replace("+", "-").Replace("/", "_"); // Add the signature to the existing URI. return uri.Scheme + "://" + uri.Host + uri.LocalPath + uri.Query + "&signature=" + signature; } 

Here is the program:

 public static AddressClass GetResponseAddress(string address) { AddressClass GoogleAddress = new AddressClass(); XmlDocument doc = new XmlDocument(); String myUri = GetGeocodeUri(address); try { doc.Load(myUri); XmlNode root = doc.DocumentElement; if (root.SelectSingleNode("/GeocodeResponse/status").InnerText == "OK") { GoogleAddress.Latitude = Double.Parse(root.SelectSingleNode("/GeocodeResponse/result/geometry/location/lat").InnerText); GoogleAddress.Longitude = Double.Parse(root.SelectSingleNode("/GeocodeResponse/result/geometry/location/lat").InnerText); } } catch (Exception ex) { Console.WriteLine("Exception <" + ex.Message + ">"); } return GoogleAddress; } 

Now, my initial reaction to it does not work, is that Google should skip the domain of the abstract, because it must be registered. So I tried this with HttpWebRequest and installed the referent in my domain, but still not a cube.

 //Not needed, Just an alternate method public static AddressClass GetResponseAddress(string address) { AddressClass GoogleAddress = new AddressClass(); WebClient client = new WebClient(); XmlDocument doc = new XmlDocument(); Uri myUri = new Uri(GetGeocodeUri(address)); HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(myUri); myRequest.Referer = "http://www.myDomain.com/"; //I've even tried pretending to be Chrome //myRequest.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.75 Safari/535.7"; try { doc.Load(myRequest.GetResponse().GetResponseStream()); XmlNode root = doc.DocumentElement; if (root.SelectSingleNode("/GeocodeResponse/status").InnerText == "OK") { GoogleAddress.Latitude = Double.Parse(root.SelectSingleNode("/GeocodeResponse/result/geometry/location/lat").InnerText); GoogleAddress.Longitude = Double.Parse(root.SelectSingleNode("/GeocodeResponse/result/geometry/location/lat").InnerText); } } catch (Exception ex) { Console.WriteLine("Exception <" + ex.Message + ">"); } return GoogleAddress; } 

Any help would be greatly appreciated.

+6
source share
4 answers

URL encoding is sometimes required (see below), but not enough. Your problem is that you are not signing your requests.

The value in your _googleSignature constant _googleSignature not a signature, but your secret cryptographic key, which is bad. Your private cryptographic key should never be part of any request on its own.

Instead, you need to use it to generate a new signature for each unique request. See the Maps API documentation for business authentication , it also gives an example for Signing a URL in Java :)

When signing requests to the Google Maps API web services using your Maps API for Business client ID and your private cryptographic key, the Referer header and source IP address are completely irrelevant;)

URL coding is only required in the address parameter, as part of Creating a valid URL . You should never encode a URL with your signature, as it is already safe for URLs using modified Base64 for URLs.

+4
source
 const String gmeClientID = "gme-myClientId"; const String key = "myGoogleKey"; var urlRequest = String.Format("/maps/api/geocode/json?latlng={0},{1}&sensor=false&client={2}",Latitude,Longitude,gmeClientID); HMACSHA1 myhmacsha1 = new HMACSHA1(); myhmacsha1.Key = Convert.FromBase64String(key); var hash = myhmacsha1.ComputeHash(Encoding.ASCII.GetBytes(urlRequest)); var url = String.Format("http://maps.googleapis.com{0}&signature={1}", urlRequest, Convert.ToBase64String(hash).Replace("+", "-").Replace("/", "_")); var request = (HttpWebRequest)HttpWebRequest.Create(url); 
+5
source

You probably need to encode the URL parameters correctly before substituting them into the query string. You can use HttpUtility.UrlEncode if you want to import the System.Web assembly (and not use the .NET client profile), or you can include or borrow code from the Microsoft Web Protection Library for this.

 address = HttpUtility.UrlEncode(address); // better than Replace(" ", "+"); return String.Format("{0}{1}&client={2}&sensor=false&signature={3}", _googleUri, address, HttpUtility.UrlEncode(_googleClientId), HttpUtility.UrlEncode(_googleSignature)); 
0
source

I think that they will check whether the Ip request matches the request of the domain for which the signature was registered.

Can you send a request from your web server?

0
source

Source: https://habr.com/ru/post/906886/


All Articles