Reputation: 39
I am getting Invalid Request with below error message when trying to retrieve secret value using AWS secrets manager REST API GetSecretValue
I followed the steps given here (https://docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html#sig-v4-examples-post) to sign the request using sigv4.
My code looks like below:
def sign(self, key, msg):
return hmac.new(key, msg.encode('utf-8'), hashlib.sha256).digest()
def get_signature_key(self, key, dateStamp, regionName, serviceName):
kDate = self.sign(('AWS4' + key).encode('utf-8'), dateStamp)
kRegion = self.sign(kDate, regionName)
kService = self.sign(kRegion, serviceName)
kSigning = self.sign(kService, 'aws4_request')
return kSigning
def get_request_url(self, region, access_key, secret_key, token, secret_name):
method = 'POST'
service = 'secretsmanager'
host = 'secretsmanager.' + region + '.amazonaws.com'
endpoint = 'https://secretsmanager.' + region + '.amazonaws.com'
content_type = 'application/x-amz-json-1.0'
amz_target = 'secretsmanager.GetSecretValue'
request_parameters = '{'
request_parameters += '"SecretId":"%s"' %(secret_name)
request_parameters += '}'
t = datetime.datetime.utcnow()
amz_date = t.strftime('%Y%m%dT%H%M%SZ')
date_stamp = t.strftime('%Y%m%d')
canonical_uri = '/'
canonical_querystring = ''
canonical_headers = 'content-type:' + content_type + '\n' + 'host:' + host + '\n' + 'x-amz-date:' + amz_date + '\n' + 'x-amz-target:' + amz_target + '\n' + 'x-amz-security-token:' + token + '\n'
signed_headers = 'content-type;host;x-amz-date;x-amz-target;x-amz-security-token'
payload_hash = hashlib.sha256(request_parameters.encode('utf-8')).hexdigest().strip()
canonical_request = method + '\n' + canonical_uri + '\n' + canonical_querystring + '\n' + canonical_headers + '\n' + signed_headers + '\n' + payload_hash
algorithm = 'AWS4-HMAC-SHA256'
credential_scope = date_stamp + '/' + region + '/' + service + '/' + 'aws4_request'
string_to_sign = algorithm + '\n' + amz_date + '\n' + credential_scope + '\n' + hashlib.sha256(canonical_request.encode('utf-8')).hexdigest()
signing_key = self.get_signature_key(secret_key, date_stamp, region, service)
signature = hmac.new(signing_key, (string_to_sign).encode('utf-8'), hashlib.sha256).hexdigest()
authorization_header = algorithm + ' ' + 'Credential=' + access_key + '/' + credential_scope + ', ' + 'SignedHeaders=' + signed_headers + ', ' + 'Signature=' + signature
headers = { 'Content-Type':content_type, 'Host':host, 'X-Amz-Date':amz_date, 'X-Amz-Target':amz_target, 'X-Amz-Content-Sha256':payload_hash, 'X-Amz-Security-Token':token.encode('ascii'), 'Authorization':authorization_header.encode('ascii')}
# ************* SEND THE REQUEST *************
print('\nBEGIN REQUEST++++++++++++++++++++++++++++++++++++')
print('Request URL = ' + endpoint)
r = requests.post(endpoint, headers=headers, data=request_parameters)
print('\nRESPONSE++++++++++++++++++++++++++++++++++++')
print('Response code: %d\n' % r.status_code)
print(r.text)
Upvotes: 0
Views: 2105
Reputation: 11006
Order Matters
Both canonical_headers
and signed_headers
should be sorted. From your link:
Create the canonical headers. Header names must be trimmed and lowercase, and sorted in code point order from low to high.
and
Create the list of signed headers. This lists the headers in the canonical_headers list, delimited with ";" and in alpha order.
This implies that x-amz-security-token
must come before x-amz-target
:
canonical_headers = 'content-type:' + content_type + '\n' + 'host:' + host + '\n' + 'x-amz-date:' + amz_date + '\n' + 'x-amz-security-token:' + token + '\n' + 'x-amz-target:' + amz_target + '\n'
signed_headers = 'content-type;host;x-amz-date;x-amz-security-token;x-amz-target'
Also, I believe you'll need to update the Content-Type
to application/x-amz-json-1.1
.
Tested using this lightly modified version of your code.
Upvotes: 1