【问题标题】:troubles generating signature for alibaba cloud为阿里巴巴云生成签名的麻烦
【发布时间】:2018-07-18 03:30:01
【问题描述】:

阅读HTTP API docs。我的请求因签名错误而失败。从错误消息中,我可以看到我要签名的字符串是正确的,但看起来我无法生成正确的 HMAC-SHA1(说真的为什么还要使用 SHA1??)。

所以我决定尝试在同一个文档中复制样本的签名。

[47] pry(main)> to_sign = "GET&%2F&AccessKeyId%3Dtestid&Action%3DDescribeRegions&Format%3DXML&SignatureMethod%3DHMAC-SHA1&SignatureNonce%3D3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf&SignatureVersion%3D1.0&Timestamp%3D2016-02-23T12%253A46%253A24Z&Version%3D2014-05-26"

[48] pry(main)> Base64.encode64 OpenSSL::HMAC.digest("sha1", "testsecret", to_sign)
=> "MLAxpXej4jJ7TL0smgWpOgynR7s=\n"

[49] pry(main)> Base64.encode64 OpenSSL::HMAC.digest("sha1", "testsecret&", to_sign)
=> "VyBL52idtt+oImX0NZC+2ngk15Q=\n"

[50] pry(main)> Base64.encode64 OpenSSL::HMAC.hexdigest("sha1", "testsecret&", to_sign)
=> "NTcyMDRiZTc2ODlkYjZkZmE4MjI2NWY0MzU5MGJlZGE3ODI0ZDc5NA==\n"

[51] pry(main)> Base64.encode64 OpenSSL::HMAC.hexdigest("sha1", "testsecret", to_sign)
=> "MzBiMDMxYTU3N2EzZTIzMjdiNGNiZDJjOWEwNWE5M2EwY2E3NDdiYg==\n"

[52] pry(main)> OpenSSL::HMAC.hexdigest("sha1", "testsecret&", to_sign)
=> "57204be7689db6dfa82265f43590beda7824d794"

[53] pry(main)> OpenSSL::HMAC.hexdigest("sha1", "testsecret", to_sign)
=> "30b031a577a3e2327b4cbd2c9a05a93a0ca747bb"

显然,这些都不匹配CT9X0VtwR86fNWSnsc6v8YGOjuE= 的示例签名。知道这里缺少什么吗?

更新:从 Golang 客户端工具中获取 tcpdump 我看到它执行了 POST 请求,例如:

POST /?AccessKeyId=**********&Action=DescribeRegions&Format=JSON&RegionId=cn-qingdao&Signature=aHZVpIMb0%2BFKdoWSIVaFJ7bd2LA%3D&SignatureMethod=HMAC-SHA1&SignatureNonce=c29a0e28964c470a8997aebca4848b57&SignatureType=&SignatureVersion=1.0&Timestamp=2018-07-16T19%3A46%3A33Z&Version=2014-05-26 HTTP/1.1

    Host: ecs.aliyuncs.com
    User-Agent: Aliyun-CLI-V3.0.3
    Content-Length: 0
    Content-Type: application/x-www-form-urlencoded
    x-sdk-client: golang/1.0.0
    x-sdk-core-version: 0.0.1
    x-sdk-invoke-type: common
    Accept-Encoding: gzip

当我从上述请求中获取参数并生成签名时,它确实匹配。所以我尝试了所有的树:GETPOST 带有 URL 参数,POST 带有正文中的参数。每次我收到签名错误。如果我使用与 golang 工具完全相同的参数重做请求,我会收到 nonce already used 错误(如预期的那样)。

【问题讨论】:

    标签: alibaba-cloud


    【解决方案1】:
    • 看来aliyun ruby sdk(非官方,仅供参考)有效。您可能需要查看how it's implemented
    • 检查其 string_to_sign 的外观。我跑了一下,似乎与您提供的略有不同。参数与& 连接而不是%26GET&%2F&AccessKeyId%3Dtestid&Action%3DDescribeRegions&Format%3DXML&SignatureMethod%3DHMAC-SHA1&SignatureNonce%3D3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf&SignatureVersion%3D1.0&Timestamp%3D2016-02-23T12%253A46%253A24Z&Version%3D2014-05-26
    需要“红宝石” 需要“阿里云” $DEBUG = 真 选项 = { :access_key_id => "k", :access_key_secret => "s", :service => :ecs } service = Aliyun::Service.new 选项 将 service.DescribeRegions({})

    【讨论】:

    • @JohnHanley 为什么它不安全?你能描述一下具体的问题吗?
    • 但是只有客户端和服务器可以看到,中间没有人可以看到。您是否担心客户端、服务器或两者之间的任何内容? stackoverflow.com/questions/2629222/…
    • @JohnHanley,“查找互联网”不是一个有效的论点。虽然 GET 在查看确切的请求要求方面有点难看,但我看不到任何明显的利用方式(除非使用非 tls 连接,在这种情况下事情已经没有希望了)。请列出您看到的具体问题。
    • ruby 库创建的签名与我的第一个示例 [48] 匹配。有趣的是,运行库时,我得到了相同的签名不完整错误。另一方面,官方的 Go cli 工具确实可以使用我提供的密钥。缺少使用 HTTP 并捕获 Go cli 工具的流量我没有想法:/ @JohnHanley,GET 只是一个词。您甚至可以使用 GET 调用来获取请求正文,即使任何可敬的规范都没有强制要求。我已经阅读了有关 GET 调用的问题,但如果事情是根据 ali 文档实现的,我看不到任何款待。
    • 现在这真是令人沮丧。使用 ruby​​ 库我做了很多尝试。一个成功了。其余的都以相同的不完整签名消息失败。虽然 Go cli 工具的工作原理很吸引人。
    【解决方案2】:

    终于搞定了。就我而言,主要问题是我一直在对签名参数进行双百分比编码,因此结果无效。对我帮助最大的是运行aliyun cli 实用程序并捕获流量,然后运行具有完全相同参数的查询来比较确切的查询字符串。

    但让我为我列出一些要点:

    1. 生成 hmac-sha1 sig 后,不要对其进行百分比编码,只需将其添加到使用普通格式 www 编码的查询中
    2. HTTP 查询中的参数顺序不重要;签名字符串中的参数顺序 很重要
    3. 我发现以下所有类型的请求都可以工作:GET、带有 URL 查询参数的 POST、带有 www 编码的请求正文中的参数的 POST;我在每个文档中都使用 GET,但我看到 aliyun 在查询中使用 POST 与查询参数和有序参数
    4. 生成HMAC-SHA1时必须在密钥末​​尾添加&字符
    5. 以二进制形式生成 HMAC-SHA1,然后编码为 Base64(无十六进制值)
    6. 某些参数可能不区分大小写,例如Format 既可用作 json 也可用作 JSON
    7. 我看到 aliyun、@wanghq 和 John 使用 UUID 4 作为 SignatureNonce,但我推迟到纯随机(根据文档),因为它似乎只是重放攻击保护。所以加密安全的随机数必须是不必要的。
    8. +*~ 的特殊编码规则似乎只适用于用于签名的字符串,实际上并不适用于在 HTTP 查询中以这种方式编码数据。

    我决定不使用@wanghq 的包装器,因为它对我也不起作用disables certificate validation,但也许它会被修复。只是我认为一旦确定了签名,查询就足够简单了,而且额外的间接层是不值得的。对他的回答 +1,因为这有助于让我的签名正确。

    这是发出简单请求的示例 ruby​​ 代码:

    require 'base64'
    require 'cgi'
    require 'openssl'
    require 'time'
    require 'rest-client'
    
    # perform a request against Alibaba Cloud API
    # @see https://www.alibabacloud.com/help/doc-detail/25489.htm
    def request(action:, params: {})
      api_url = "https://ecs.aliyuncs.com/"
    
      # method = "POST"
      method = "GET"
      process_params!(http: method, action: action, params: params)
      RestClient::Request.new(method: method, url: api_url, headers: {params: params})
      # RestClient::Request.new(method: method, url: api_url, payload: params)
      # RestClient::Request.new(method: method, url: api_url, payload: params.map{|k,v| "#{k}=#{CGI.escape(v)}"}.join("&"))
    end
    
    # generates the required common params for a request and adds them to params
    # @return undefined
    # @see https://www.alibabacloud.com/help/doc-detail/25490.htm
    def process_params!(http:, action:, params:)
      params.merge!({
        "Action" => action,
        "AccessKeyId" => config[:auth][:key_id],
        "Format" => "JSON",
        "Version" => "2014-05-26",
        "Timestamp" => Time.now.utc.iso8601
      })
      sign!(http: http, action: action, params: params)
    end
    
    # generate request signature and adds to params
    # @return undefined
    # @see https://www.alibabacloud.com/help/doc-detail/25492.htm
    def sign!(http:, action:, params:)
      params.delete "Signature"
      params["SignatureMethod"] = "HMAC-SHA1"
      params["SignatureVersion"] = "1.0"
      params["SignatureNonce"] = "#{rand(1_000_000_000_000)}"
      # params["SignatureNonce"] = SecureRandom.uuid.gsub("-", "")
    
      canonicalized_query_string = params.sort.map { |key, value|
        "#{key}=#{percent_encode value}"
      }.join("&")
    
      string_to_sign = %{#{http}&#{percent_encode("/")}&#{percent_encode(canonicalized_query_string)}}
    
      params["Signature"] = hmac_sha1(string_to_sign)
    end
    
    # @param data [String]
    # @return [String]
    def hmac_sha1(data, secret: config[:auth][:key_secret])
      Base64.encode64(OpenSSL::HMAC.digest('sha1', "#{secret}&", data)).strip
    end
    
    # encode strings per Alibaba cloud rules for signing
    # @return [String] encoded string
    # @see https://www.alibabacloud.com/help/doc-detail/25492.htm
    def percent_encode(str)
      CGI.escape(str).gsub(?+, "%20").gsub(?*, "%2A").gsub("%7E", ?~)
    end
    
    ## example call
    request(action: "DescribeRegions")
    

    代码可以稍微简化,但决定让它非常接近文档说明。

    附:不知道为什么约翰删除了他的答案,但在上面留下了一个指向他的网页的链接,供任何寻找示例代码的 python 人使用

    【讨论】:

      猜你喜欢
      • 2012-05-05
      • 2021-02-27
      • 2021-03-01
      • 1970-01-01
      • 2020-06-28
      • 2019-09-21
      • 2021-12-18
      • 2018-05-02
      • 2020-06-14
      相关资源
      最近更新 更多