2012-01-25 5 views
6

Estoy escribiendo una aplicación para guardar en caché los datos de geocodificación mientras importo los registros. Funciono bien cuando utilizo una solicitud sin firmar, sin embargo, parece que no puedo entender qué sucede cuando trato de usar el cliente y la firma de mi empresa. Siempre obtengo un 403 Forbidden.Uso de una solicitud de geocodificación de la API de Google Maps firmada desde una aplicación de línea de comandos .NET

Aquí es mi 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; 

    } 

aquí está el programa:

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; 
    } 

Ahora, mi reacción inicial a que no funcione es que Google debe faltar el dominio árbitro, ya que deben ser registrados . Así que lo intenté con HttpWebRequest y configuré el referer en mi dominio, pero aún no había dado.

//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; 
    } 

Cualquier ayuda sería muy apreciada.

+1

Estoy casi seguro de que esto va más allá de los términos de servicio para la API en cuestión. La razón por la que tiene un problema es porque no está diseñado para usarse en una aplicación tradicional solo como una aplicación web. No olvide que no hay un límite de transacción a menos que pague a Google. –

+0

Google permite el almacenamiento en caché antes de generar informes para grandes conjuntos de datos, siempre que se realice correctamente .--- (b) Sin captura previa, almacenamiento en caché o almacenamiento de contenido.No debe precapturar, almacenar en caché o almacenar ningún Contenido, excepto que puede almacenar: (i) cantidades limitadas de Contenido con el fin de mejorar el rendimiento de su Implementación de la API de Maps si lo hace de forma temporal, segura y en un manera que no permita el uso del Contenido fuera del Servicio; y (ii) cualquier identificador de contenido o clave que la Documentación de las API de Maps le permita específicamente almacenar. – copjon

+0

@Ramhound Además, sí hay un límite de consultas de 2.500/día sin pagar y 100.000 si paga. – copjon

Respuesta

4

La codificación URL a veces es necesaria (ver a continuación) pero no lo suficiente. Su problema es que, de hecho, no está firmando sus solicitudes.

El valor en su constante _googleSignature no es una firma, pero su clave criptográfica privada, que es incorrecta. Su clave criptográfica privada nunca debe ser parte de ninguna solicitud en sí misma.

En su lugar, debe usarlo para generar una nueva firma para cada solicitud única. Por favor, consulte la documentación Maps API for Business Authentication, también incluye un ejemplo para Signing a URL in Java :)

Al firmar peticiones a la API de servicios web de Google Maps con el API de Maps para ID de cliente de negocios y su clave criptográfica privada, la dirección IP de cabecera Referer y la fuente son totalmente irrelevantes;)

La codificación URL solo es necesaria en el parámetro address, como parte de Building a Valid URL. Nunca debe codificar con URL su firma, ya que es URL segura utilizando el Base64 modificado para las URL.

+0

Gracias, no he tenido la oportunidad de implementarlo todavía, pero pensé que me estaba perdiendo algo como esto. – copjon

+2

¡Simplemente lo introduje en mi código y funciona como un amuleto! Gracias. Publicando el código final anterior para cualquier persona que pueda necesitarlo para el futuro. – copjon

0

Probablemente necesite codificar URL correctamente los parámetros antes de sustituirlos por la cadena de consulta. Puede usar HttpUtility.UrlEncode si está dispuesto a importar el ensamblado System.Web (y no utiliza un perfil de cliente .NET) o puede incluir o pedir prestado el código de Microsoft's Web Protection Library para hacerlo.

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

Gracias por el consejo ... pero todavía no funciona. – copjon

+0

Actualiza tu pregunta con tu intento actual. –

+0

@Ramhound Claro, se actualizó. – copjon

0

Creo que verificará si la solicitud de Ip corresponde con el dominio para el que se registró la firma.

¿Puede intentar enviar la solicitud desde su servidor web?

+0

Estaba pensando que solo esperaba que funcionara independientemente de la máquina en la que esté. – copjon

5
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); 
+1

Esto es realmente un gran ejemplo de implementación, ahora que he solucionado un problema donde la clave no se decodificaba desde la codificación URLSafe de google. Esto es solo un problema si la clave provista contiene _ o -, que google cambia a/o +. Esto hace que ya no sean válidos base64, por lo que debe deshacer eso antes de pasarlo a 'FromBase64String'. – jep

Cuestiones relacionadas