ruby - AWS authentication V4 signature failure; where am I going wrong in generating the signature? -
i generating form using ruby code below (passing csv file credentials downloads aws console argument). if submit file using form, the request signature calculated not match signature provided. check key , signing method.
. have copied signing code http://docs.aws.amazon.com/general/latest/gr/signature-v4-examples.html#signature-v4-examples-ruby. i've looked @ amazon mws - request signature calculated not match signature provided , s3 "signature doesn't match" client side post jquery-file-upload, these don't seem apply in case. going wrong?
#!/usr/bin/env ruby require 'nokogiri' require 'csv' require 'ostruct' require 'base64' require 'json' require 'openssl' header = nil data = nil csv.foreach(argv[0]) |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 |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 amazon s3") } } } end puts builder.to_html
ok, turns out policy signature should not base64 encoded output hex digest. struggling same problem, worked in end:
#!/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']) |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 '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 |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
Comments
Post a Comment