AWS身份验证V4签名失败;我在哪里生成签名?

时间:2015-06-25 09:50:50

标签: ruby amazon-web-services amazon-s3

我正在使用下面的ruby代码生成表单(传递CSV文件,其中包含从AWS控制台下载的凭据作为参数)。如果我使用此表单提交文件,则会获得The request signature we calculated does not match the signature you provided. Check your key and signing method.。我已从http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-ruby复制了签名代码。我查看了Amazon MWS - request signature calculated does not match the signature provideds3 "signature doesn't match" client side post jquery-file-upload,但这些似乎并不适用于我的情况。我哪里错了?

#!/usr/bin/env ruby

require 'nokogiri'
require 'csv'
require 'ostruct'
require 'base64'
require 'json'
require 'openssl'

header = nil
data = nil
CSV.foreach(ARGV[0]) do |row|
  if header.nil?
    header = row.collect{|c| c.strip.gsub(/\s/, '') }
  else
    data = row
  end
end
creds = OpenStruct.new(Hash[*(header.zip(data).flatten)])

bucket = 'zotplus'
region = 'eu-central-1'
service = 's3'
dateStamp = Time.now.strftime('%Y%m%d')

policy = {
  'expiration' => '2029-01-01T00:00:00Z',
  'conditions' => [
    {'bucket' => bucket},
    ['starts-with', '$key', 'uploads/'],
    {'acl' => 'private'},
    {'success_action_redirect' => 'http://zotplus.github.io/submitted.html'},
    ['starts-with', '$Content-Type', 'multipart/form-data'],
    ['content-length-range', 0, 1048576],
    {'x-amz-date' => "#{dateStamp}T000000Z"},
    {'x-amz-credential' => "#{creds.AccessKeyId}/#{dateStamp}/#{region}/#{service}/aws4_request"}
  ]
}

form = {}
%w{acl success_action_redirect bucket x-amz-date x-amz-credential}.each{|eq|
  form[eq] = policy['conditions'].detect{|c| c.is_a?(Hash) && c[eq] }[eq]
}
form['key'] = policy['conditions'].detect{|c| c.is_a?(Array) && c[0,2] = ['starts-with', '$key']}[2] + '${filename}'

policy_string = Base64.encode64(policy.to_json).gsub("\n","")

kDate    = OpenSSL::HMAC.digest('sha256', "AWS4" + creds.SecretAccessKey, dateStamp)
kRegion  = OpenSSL::HMAC.digest('sha256', kDate, region)
kService = OpenSSL::HMAC.digest('sha256', kRegion, service)
kSigning = OpenSSL::HMAC.digest('sha256', kService, 'aws4_request')

signature = Base64.encode64(OpenSSL::HMAC.digest('sha256', kSigning, policy_string)).gsub("\n","")

form['policy'] = policy_string
form['x-amz-signature'] = signature
form['x-amz-algorithm'] = 'AWS4-HMAC-SHA256'

builder = Nokogiri::HTML::Builder.new do |doc|
  doc.html {
    doc.head {
      doc.title {
        doc.text 'submit file'
      }
      doc.meta('http-equiv' => "Content-Type", content: "text/html; charset=UTF-8")
    }

    doc.body {
      doc.form(action: "http://zotplus-964ec2b7-379e-49a4-9c8a-edcb20db343f.s3.amazonaws.com/", method: "post", enctype: "multipart/form-data") {
        form.each_pair{|k, v|
          doc.input(type: "hidden", name: k, value: v)
        }

        doc.text 'File: '
        doc.input(type: "file", name: "file")
        doc.input(type: "submit", name: "submit", value: "Upload to Amazon S3")
      }
    }
  }
end
puts builder.to_html

2 个答案:

答案 0 :(得分:1)

好的,事实证明政策签名不应该是base64编码,而只是输出为十六进制摘要。对于那些在同样问题上苦苦挣扎的人来说,这最终是有效的:

#!/usr/bin/env ruby

require 'json'
require 'base64'
require 'openssl'
require 'csv'
require 'ostruct'
require 'nokogiri'

algorithm = 'AWS4-HMAC-SHA256'
service = 's3'
requestType = 'aws4_request'
successStatus = '201'
bucket = 'zotplus-964ec2b7-379e-49a4-9c8a-edcb20db343f'
region = 'eu-central-1'
acl = 'private'

header = nil
data = nil
CSV.foreach(ENV['ZOTPLUSAWSCREDENTIALS']) do |row|
  if header.nil?
    header = row.collect{|c| c.strip.gsub(/\s/, '') }
  else
    data = row
  end
end
creds = OpenStruct.new(Hash[*(header.zip(data).flatten)])

date = Time.now.strftime('%Y%m%dT%H%M%SZ')
shortDate = date.sub(/T.*/, '')
credentials = [ creds.AccessKeyId, shortDate, region, service, requestType ].join('/')

policy = Base64.encode64({
  'expiration' => (Time.now + (60*60*24*365*30)).strftime('%Y-%m-%dT%H:%M:%SZ'), # 30 years from now
  'conditions' => [
    {'bucket' => bucket},
    {'acl' => acl},
    ['starts-with', '$key', ''],
    ['starts-with', '$Content-Type', ''],
    {'success_action_status' => successStatus},
    {'x-amz-credential' => credentials},
    {'x-amz-algorithm' => algorithm},
    {'x-amz-date' => date},
    ['content-length-range', 0, 1048576],
  ]
}.to_json).gsub("\n","")

signingKey = ['AWS4' + creds.SecretAccessKey, shortDate, region, service, requestType].inject{|key, data| OpenSSL::HMAC.digest('sha256', key, data) } 

form = OpenStruct.new({
  action: "http://#{bucket}.#{service}-#{region}.amazonaws.com",
  fields: [ # order matters!
    {key: '${filename}'},
    {'Content-Type': 'text/plain'},
    {acl: acl},
    {success_action_status: successStatus},
    {policy: policy},
    {'x-amz-algorithm': algorithm},
    {'x-amz-credential': credentials},
    {'x-amz-date': date},
    {'x-amz-signature': OpenSSL::HMAC.hexdigest('sha256', signingKey, policy)}
  ]
})

################################################

builder = Nokogiri::HTML::Builder.new do |doc|
  doc.html {
    doc.head {
      doc.meta(charset: 'utf-8')
      doc.title { doc.text 'Upload' }
    }
    doc.body {
      doc.form(action: form.action, method: 'POST', enctype: "multipart/form-data") {
        form.fields.each{|field|
          field.each_pair{|name, value|
            doc.input(type: 'hidden', name: name, value: value)
          }
        }
        doc.input(type: 'file', name: 'file')
        doc.input(type: 'submit', value: 'Save')
      }
    }
  }
end
puts builder.to_html

答案 1 :(得分:0)

是的,它有效:D 我试过这种方式

def index
access_key = 'YOUR_ACCESS_KEY'
secret_key = 'YOUR_SECRET_KEY'
time = Time.now.utc
date_stamp = time.strftime("%Y%m%d")
region_name = 'ap-south-1'
key_date    = hmac_digest('sha256', "AWS4" + secret_key, date_stamp)
key_region  = hmac_digest('sha256', key_date, region_name)
key_service = hmac_digest('sha256', key_region, 's3')
key_signing = hmac_digest('sha256', key_service, "aws4_request")
algorithm = 'AWS4-HMAC-SHA256'
amzdate = time.strftime('%Y%m%dT%H%M%SZ')
credential_scope = access_key + '/' + date_stamp + '/ap-south-1/s3/aws4_request'
policy = generate_policy(credential_scope, algorithm, amzdate)
signature = OpenSSL::HMAC.hexdigest('sha256', key_signing, policy)
render json: { policy: policy, signature: signature, key: access_key, date: amzdate, credentials: credential_scope, algorithm: algorithm }
end
def generate_policy(credential_scope, algorithm, amzdate)
Base64.encode64({
  'expiration' => (Time.now + (60 * 60 * 24 * 365 * 30)).strftime('%Y-%m-%dT%H:%M:%SZ'), # 30 years from now
  'conditions' => [
    { 'bucket' => 'adcreation-m' },
    { 'acl' => 'public-read' },
    ['starts-with', '$key', ''],
    ['starts-with', '$Content-Type', ''],
    { 'success_action_status' => '201' },
    { 'x-amz-credential' => credential_scope },
    { 'x-amz-algorithm' => algorithm },
    { 'x-amz-date' => amzdate },
    ['content-length-range', 0, 256000000]
  ]
}.to_json).delete("\n")
end

def hmac_digest(digest, key, data)
 OpenSSL::HMAC.digest(digest, key, data)
end
相关问题