我正在使用下面的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 provided和s3 "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
答案 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