From 1bf86ce4dc758677299a43841e379ef436246d57 Mon Sep 17 00:00:00 2001 From: Frederick Cheung Date: Sun, 2 Nov 2014 17:05:41 +0000 Subject: [PATCH] [AWS|Signature] V4 parameter signature needs to add X-Amz-Credential etc. before signing --- lib/fog/aws/signaturev4.rb | 56 +++++++++++++++++++++------------- tests/aws/signaturev4_tests.rb | 13 +++++++- 2 files changed, 47 insertions(+), 22 deletions(-) diff --git a/lib/fog/aws/signaturev4.rb b/lib/fog/aws/signaturev4.rb index 528b962b9..e6c745fea 100644 --- a/lib/fog/aws/signaturev4.rb +++ b/lib/fog/aws/signaturev4.rb @@ -4,6 +4,7 @@ require 'fog/aws/core' module Fog module AWS class SignatureV4 + ALGORITHM = 'AWS4-HMAC-SHA256' def initialize(aws_access_key_id, secret_key, region,service) @region = region @service = service @@ -11,50 +12,70 @@ module Fog @hmac = Fog::HMAC.new('sha256', 'AWS4' + secret_key) end - def signature_parameters(params, date) - signature_components(params, date) + def signature_parameters(params, date, body_sha = nil) + params = params.dup.merge(:query => params[:query].merge( + 'X-Amz-Algorithm' => ALGORITHM, + 'X-Amz-Credential' => "#{@aws_access_key_id}/#{credential_scope(date)}", + 'X-Amz-SignedHeaders' => signed_headers(params[:headers]) + )) + signature_components(params, date, body_sha) end - def signature_header(params, date) - components = signature_components(params, date) - "#{components['X-Amz-Algorithm']} Credential=#{components['X-Amz-Credential']}, SignedHeaders=#{components['X-Amz-SignedHeaders']}, Signature=#{components['X-Amz-Signature']}" + def signature_header(params, date, body_sha = nil) + components_to_header(signature_components(params, date, body_sha)) end def sign(params, date) #legacy method name signature_header(params, date) end - protected - def signature_components(params, date) + def components_to_header components + "#{components['X-Amz-Algorithm']} Credential=#{components['X-Amz-Credential']}, SignedHeaders=#{components['X-Amz-SignedHeaders']}, Signature=#{components['X-Amz-Signature']}" + end + + def signature_components(params, date, body_sha) canonical_request = <<-DATA #{params[:method].to_s.upcase} #{params[:path]} #{canonical_query_string(params[:query])} #{canonical_headers(params[:headers])} #{signed_headers(params[:headers])} -#{Digest::SHA256.hexdigest(params[:body] || '')} +#{body_sha || Digest::SHA256.hexdigest(params[:body] || '')} DATA canonical_request.chop! - credential_scope = "#{date.utc.strftime('%Y%m%d')}/#{@region}/#{@service}/aws4_request" + string_to_sign = <<-DATA -AWS4-HMAC-SHA256 +#{ALGORITHM} #{date.to_iso8601_basic} -#{credential_scope} +#{credential_scope(date)} #{Digest::SHA256.hexdigest(canonical_request)} DATA string_to_sign.chop! - + signature = derived_hmac(date).sign(string_to_sign) { - 'X-Amz-Algorithm' => 'AWS4-HMAC-SHA256', - 'X-Amz-Credential' => "#{@aws_access_key_id}/#{credential_scope}", + 'X-Amz-Algorithm' => ALGORITHM, + 'X-Amz-Credential' => "#{@aws_access_key_id}/#{credential_scope(date)}", 'X-Amz-SignedHeaders' => signed_headers(params[:headers]), 'X-Amz-Signature' => signature.unpack('H*').first } end + def derived_hmac(date) + kDate = @hmac.sign(date.utc.strftime('%Y%m%d')) + kRegion = Fog::HMAC.new('sha256', kDate).sign(@region) + kService = Fog::HMAC.new('sha256', kRegion).sign(@service) + kSigning = Fog::HMAC.new('sha256', kService).sign('aws4_request') + Fog::HMAC.new('sha256', kSigning) + end + + + def credential_scope(date) + "#{date.utc.strftime('%Y%m%d')}/#{@region}/#{@service}/aws4_request" + end + protected def canonical_query_string(query) @@ -79,13 +100,6 @@ DATA headers.keys.map {|key| key.to_s}.sort.map {|key| key.downcase}.join(';') end - def derived_hmac(date) - kDate = @hmac.sign(date.utc.strftime('%Y%m%d')) - kRegion = Fog::HMAC.new('sha256', kDate).sign(@region) - kService = Fog::HMAC.new('sha256', kRegion).sign(@service) - kSigning = Fog::HMAC.new('sha256', kService).sign('aws4_request') - Fog::HMAC.new('sha256', kSigning) - end end end end diff --git a/tests/aws/signaturev4_tests.rb b/tests/aws/signaturev4_tests.rb index 194f6f142..29068a26d 100644 --- a/tests/aws/signaturev4_tests.rb +++ b/tests/aws/signaturev4_tests.rb @@ -44,7 +44,18 @@ Shindo.tests('AWS | signaturev4', ['aws']) do 'X-Amz-Algorithm' => 'AWS4-HMAC-SHA256', 'X-Amz-Credential' => 'AKIDEXAMPLE/20110909/us-east-1/host/aws4_request', 'X-Amz-SignedHeaders' => 'date;host', - 'X-Amz-Signature' => '0dc122f3b28b831ab48ba65cb47300de53fbe91b577fe113edac383730254a3b' + 'X-Amz-Signature' => 'a6c6304682c74bcaebeeab2fdfb8041bbb39c6976300791a283057bccf333fb2' + } + end + end + + tests("inject body sha") do + returns(@signer.signature_parameters({:query => {'a' => 'foo', 'b' => 'foo'}, :headers => {'Host' => 'host.foo.com', 'Date' => 'Mon, 09 Sep 2011 23:36:00 GMT'}, :method => :get, :path => '/'}, @now, 'STREAMING-AWS4-HMAC-SHA256-PAYLOAD')) do + { + 'X-Amz-Algorithm' => 'AWS4-HMAC-SHA256', + 'X-Amz-Credential' => 'AKIDEXAMPLE/20110909/us-east-1/host/aws4_request', + 'X-Amz-SignedHeaders' => 'date;host', + 'X-Amz-Signature' => '22c32bb0d0b859b94839de4e9360bca1806e73d853f5f97ae0d849f0bdf42fb0' } end end