class Aws::Signature::V4::Signature

Attributes

headers[RW]

Your code goes here…

signature[RW]

Your code goes here…

Public Class Methods

new(region, key_id, secret_key, algorithm: 'AWS4-HMAC-SHA256', content_type: 'application/x-amz-json-1.0', content_type_sign_header: 'application/x-amz-json-1.0,application/x-www-form-urlencoded', t: nil) click to toggle source

@param [String] region AWS region @param [String] key_id AWS user key id @param [String] secret_key AWS user secret key

# File lib/aws/signature/v4.rb, line 20
def initialize (region, key_id, secret_key, algorithm: 'AWS4-HMAC-SHA256', content_type: 'application/x-amz-json-1.0', content_type_sign_header: 'application/x-amz-json-1.0,application/x-www-form-urlencoded', t: nil)
  # calculate date and time
  @t = t || Time.now.utc
  @date = @t.strftime('%Y%m%d')
  @time = @t.strftime('%Y%m%dT%H%M%SZ')
  # mandatory fields
  @region = region
  @key_id = key_id
  @secret_key = secret_key
  # optional fields
  @algorithm = algorithm
  # content type used as signing header for generating token
  @content_type_sign_header = content_type_sign_header
  # final content type sent with service request
  @content_type = content_type
end

Public Instance Methods

generate_signature(service_name, amz_target, http_method, payload, host, uri) click to toggle source

Public - generates AWS authentication signature key v4 to use with low level API calls (not using AWS SDKs) This also sets ‘Authorization’ header in the current instance See docs.aws.amazon.com/general/latest/gr/signing_aws_api_requests.html for reference @param [String] service_name AWS service name e.g. dynamodb @param [String] amz_target AWS service target operation @param [String] http_method HTTP method e.g. POST @param [String] payload HTTP body content @param [String] host HTTP host url @param [String] uri HTTP path after host @return [String] signature string that can be used in Authentication header to allow access to AWS services using APIs

# File lib/aws/signature/v4.rb, line 47
def generate_signature (service_name, amz_target, http_method, payload, host, uri)
  # remove protocol (http://, https://) from host
  host = host.sub(/^https?\:\/\/(www.)?/,'')
  # construct headers
  # ordering of header values is important, so we reconstruct rather than add new fields
  @headers = {
      'Content-Type': @content_type_sign_header,
      'Host': host,
      'X-Amz-Date': @time,
      'X-Amz-Target': amz_target
  }
  # calculate credential scope
  credential_scope = "#{@date}/#{@region}/#{service_name}/aws4_request"

  hashed_payload = Digest::hexencode(Digest::SHA256.digest(payload.to_json)).downcase

  # CanonicalHeaders
  canonical_headers = @headers.keys.map {|k,v| k.downcase }.join(';')
  canonical_headers_entry = @headers.map {|k,v| k.to_s.downcase + ':' + v.to_s }.join("\n")

  # Task 1: Create a Canonical Request For Signature Version 4
  # http://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
  canonical_request =
      "#{http_method}\n"\
      "#{uri}\n"\
      "\n"\
      "#{canonical_headers_entry}\n"\
      "\n"\
      "#{canonical_headers}\n"\
      "#{hashed_payload}"

  hashed_canonical_request = Digest::hexencode(Digest::SHA256.digest(canonical_request)).downcase

  # Task 2: Create a String to Sign for Signature Version 4
  # http://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html
  string_to_sign =
      "#{@algorithm}\n"\
      "#{@time}\n"\
      "#{credential_scope}\n"\
      "#{hashed_canonical_request}"

  # Task 3: Calculate the AWS Signature Version 4
  # http://docs.aws.amazon.com/general/latest/gr/sigv4-calculate-signature.html
  signingKey = get_signature_key (service_name)

  # Task 4: Add the Signing Information to the Request
  # http://docs.aws.amazon.com/general/latest/gr/sigv4-add-signature-to-request.html
  @signature = Digest::hexencode(OpenSSL::HMAC.digest( OpenSSL::Digest.new('SHA256'),signingKey, string_to_sign))
  # generate authorization header and update it in headers
  generate_authorization_header(service_name, amz_target, http_method, payload, host, uri)
  @signature
end
get_signature_key(service_name) click to toggle source
# File lib/aws/signature/v4.rb, line 100
def get_signature_key (service_name)
  kDate    = OpenSSL::HMAC.digest('sha256', "AWS4" + @secret_key, @date)
  kRegion  = OpenSSL::HMAC.digest('sha256', kDate, @region)
  kService = OpenSSL::HMAC.digest('sha256', kRegion, service_name)
  kSigning = OpenSSL::HMAC.digest('sha256', kService, "aws4_request")
  kSigning
end

Private Instance Methods

generate_authorization_header(service_name, amz_target, http_method, payload, host, uri) click to toggle source

Public - returns authorization header value using signature key v4

# File lib/aws/signature/v4.rb, line 111
def generate_authorization_header(service_name, amz_target, http_method, payload, host, uri)
  # remove protocol (http://, https://) from host
  host = host.sub(/^https?\:\/\/(www.)?/,'')
  # update headers host and amz_target
  # ordering of header values is important, so we reconstruct rather than add new fields
  @headers = {
      'Content-Type': @content_type_sign_header,
      'Host': host,
      'X-Amz-Date': @time,
      'X-Amz-Target': amz_target
  }
  # generate signature token
  generate_signature(service_name, amz_target, http_method, payload, host, uri) unless @signature
  # calculate signed headers
  signed_headers = @headers.keys.map {|k,v| k.downcase }.join(';')
  # calculate credential scope
  credential_scope = "#{@date}/#{@region}/#{service_name}/aws4_request"
  # generate AWS authorization header value
  authorization_header = "#{@algorithm} Credential=#{@key_id}/#{credential_scope}, SignedHeaders=#{signed_headers}, Signature=#{@signature}"
  @headers[:'Content-Type'] = @content_type
  @headers['Authorization'] = authorization_header
  authorization_header
end