AWS4请求使用Ruby签名

时间:2017-07-25 19:03:46

标签: ruby-on-rails ruby amazon-web-services amazon-cloudfront

我有一个代码库,我们在有限的容量中使用AWS CloudFront,并试图通过此处记录的API端点获取分发失效的状态:http://docs.aws.amazon.com/cloudfront/latest/APIReference/API_GetInvalidation.html

此端点要求使用我正在处理的AWS4签名对请求标头进行签名。我根据AWS SDK for Ruby中的签名代码对我的大部分请求签名代码进行了建模:https://github.com/aws/aws-sdk-ruby/blob/master/aws-sdk-core/lib/aws-sdk-core/signers/v4.rb

我的代码如下:

class CFAgent

  RFC8601BASIC = "%Y%m%dT%H%M%SZ"
  CF_AGENT_ACCESS_KEY = "validaccesskey"
  CF_AGENT_SECRET_ACCESS_KEY = "validsecret"

  def self.cf_dist_invalidation_status
    dist_id = "abc123"
    invalidation_id = "xyz"
    url = "https://cloudfront.amazonaws.com/2017-03-25/#{dist_id}/invalidation/#{invalidation_id}"

    headers = {
      "Content-Type" => "application/json; charset=utf8"
    }
    headers = sign("GET", URI.parse(url), headers, "{}", CF_AGENT_ACCESS_KEY, CF_AGENT_SECRET_ACCESS_KEY, "us-east-1")
    puts "signed headers:", headers

    res = SimpleHttp.get(url, headers)
    puts "Status:", res.code
    puts "Body:", res.body
  end

  def self.sign(method, uri, headers, body, access_key, secret_key, region, service_name=nil)
    method = method.upcase
    service = service_name || uri.host.split(".", 2)[0]

    date_header = headers["Date"] || headers["DATE"] || headers["date"]
    date = (date_header ? Time.parse(date_header) : Time.zone.now).utc.strftime(RFC8601BASIC)

    body_digest = hexdigest(body)

    headers['X-Amz-Date'] = date
    headers['Host'] = host(uri)
    headers['X-Amz-Content-Sha256'] ||= body_digest

    headers['Authorization'] = authorization(method, uri, headers, body_digest, date, access_key, secret_key, region, service)
    headers
  end

  private

  def self.host(uri)
    if ((uri.scheme == 'http' && uri.port == 80) || (uri.scheme == 'https' && uri.port = 443))
      uri.host
    else
      "#{uri.host}:#{uri.port}"
    end
  end

  def self.authorization(method, uri, headers, body_digest, date, access_key, secret_key, region, service)
    [
      "AWS4-HMAC-SHA256 Credential=#{credential(access_key, date, region, service)}",
      "SignedHeaders=#{signed_headers(headers)}",
      "Signature=#{signature(method, uri, headers, body_digest, date, access_key, secret_key, region, service)}"
    ].join(', ')
  end

  def self.credential(access_key, date, region, service)
    "#{access_key}/#{credential_string(date, region, service)}"
  end

  def self.signature(method, uri, headers, body_digest, date, access_key, secret_key, region, service)
    k_date = hmac("AWS4" + secret_key, date[0,8])
    k_region = hmac(k_date, region)
    k_service = hmac(k_region, service)
    k_credentials = hmac(k_service, "aws4_request")
    hexhmac(k_credentials, string_to_sign(method, uri, headers, body_digest, date, access_key, secret_key, region, service))
  end

  def self.string_to_sign(method, uri, headers, body_digest, date, access_key, secret_key, region, service)
    [
      'AWS4-HMAC-SHA256',
      date,
      credential_string(date, region, service),
      hexdigest(canonical_request(method, uri, headers, body_digest))
    ].join("\n")
  end

  def self.credential_string(date, region, service)
    [
      date[0,8],
      region,
      service,
      "aws4_request"
    ].join("/")
  end

  def self.canonical_request(method, uri, headers, body_digest)
    [
      method,
      Pathname.new(uri.path).cleanpath.to_s,
      uri.query,
      canonical_headers(headers),
      signed_headers(headers),
      body_digest
    ].join("\n")
  end

  def self.canonical_headers(headers)
    c_headers = []
    headers.each_pair do |k,v|
      k = k.downcase
      c_headers << [k,v]
    end
    c_headers = c_headers.sort_by(&:first)
    c_headers.map{|k,v| "#{k}:#{canonical_header_value(v.to_s)}" }.join("\n")
  end

  def self.canonical_header_value(value)
    value.match(/^".*"$/) ? value : value.gsub(/\s+/, ' ').strip
  end

  def self.signed_headers(headers)
    headers.keys.inject([]) do |signed_headers, header_key|
      header_key = header_key.downcase
      signed_headers << header_key
      signed_headers
    end.sort.join(';')
  end

  def self.hexdigest(value)
    Digest::SHA256.new.update(value).hexdigest
  end

  def self.hmac(key, value)
    OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, value)
  end

  def self.hexhmac(key, value)
    OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), key, value)
  end

end

当我调用CFAgent.cfinvalidation_status时,我收到403错误,其中包含&#34; MissingInvalidationToken&#34;码。 &#34; SimpleHTTP&#34;是一个简单的库,它包装Net / HTTP以发出HTTP / HTTPS请求(请假设这有效)。

我错过了什么?

提前致谢。

0 个答案:

没有答案