使用来自 .NET 命令行应用程序的签名 Google Maps API 地理编码请求

发布于 2024-12-28 23:22:15 字数 4068 浏览 4 评论 0原文

因此,我正在编写一个应用程序来在导入记录时缓存地理编码数据。当我使用未签名的请求时,它工作正常,但是当我尝试使用我公司的 clientid 和签名时,我似乎无法弄清楚出了什么问题。我总是收到 403 Forbidden。

这是我的 URL 构建器:

    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;

    } 

这是程序:

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

现在,我对它不起作用的最初反应是 Google 一定缺少引用域,因为它们必须注册。所以我尝试使用 HttpWebRequest 并将引用者设置为我的域,但仍然没有骰子。

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

任何帮助将不胜感激。

So I'm writing an application to cache geocoding data as I import records. I've got it working fine when I use an unsigned request, however I can't seem to figure out what's wrong when I try to use my company's clientid and signature. I always get a 403 Forbidden.

Here's my URL builder:

    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's 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 not working was that Google must be missing the referer domain because they must be registered. So I tried it with HttpWebRequest and set the referer to my domain, but still no dice.

//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 much appreciated.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

风启觞 2025-01-04 23:22:15
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);
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);
迷路的信 2025-01-04 23:22:15

URL 编码有时是必要的(见下文),但还不够。您的问题是您实际上并没有签署您的请求。

_googleSignature 常量中的值不是签名,而是您的私有加密密钥,这是不好的。您的私有加密密钥永远不应该成为任何请求的一部分。

相反,您需要使用它为每个独特的请求生成新的签名。请参阅Maps API for Business Authentication文档,其中还包括用 Java 签署网址的示例 :)

签署对Google Maps API Web 服务与您的 Maps API for Business 客户端 ID 和您的私有加密密钥、Referer 标头和源 IP 地址完全无关;)

仅在 address 参数上需要 URL 编码,如下所示构建有效网址的一部分。您永远不应该对您的签名进行 URL 编码,因为通过对 URL 使用修改后的 Base64,它已经是 URL 安全的。

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

The value in your _googleSignature constant is not a signature, but your private cryptographic key, which is bad. Your private cryptographic key should never, ever be part of any request by itself.

Instead, you need to use it to generate a new signature for every unique request. Please see the Maps API for Business Authentication documentation, it also includes an example for Signing a URL in Java :)

When signing requests to the Google Maps API Web Services with your Maps API for Business client id and your private cryptographic key, the Referer header and source IP address are totally irrelevant ;)

URL-encoding is only necessary on the address parameter, as part of Building a Valid URL. You should never URL-encode your signature as it's already URL-safe by using the modified Base64 for URLs.

再浓的妆也掩不了殇 2025-01-04 23:22:15

在将参数替换到查询字符串之前,您可能需要对参数进行正确的 URL 编码。如果您愿意导入系统,可以使用 HttpUtility.UrlEncode .Web 程序集(并且不使用客户端 .NET 配置文件),或者您可以包含或借用 Microsoft 的 Web 保护库 来执行此操作。

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

You probably need to properly URL-encode the parameters before you substitute them into the query string. You can use HttpUtility.UrlEncode if you're willing to import the System.Web assembly (and not use a client .NET profile) or you can include or borrow code from Microsoft's Web Protection Library to do 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));
恬淡成诗 2025-01-04 23:22:15

我认为他们会检查请求来自的 Ip 是否与签名注册的域匹配。

您可以尝试从您的网络服务器发送请求吗?

I think they will check if the Ip the request comes from matches the domain the signature was registered for.

Can you try to send the request from your webserver?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文