cancel
Showing results for 
Search instead for 
Did you mean: 

API - AWS S3

Dan.Lister
Level 4
Hi Guys, 

I'm trying to setup a Web API for AWS S3, but i'm struggling to get a response. 

I've got it working using Postman, but I'm getting the error SignatureDoesNotMatch when using BP.

I have downloaded and tweaked the AWS:Rekognition API to try and get this to work, but I don't think my Authorization Header is correct. I am using the code stage provided with the API to generation the header, with a couple of tweaks, but I'm not sure i'm doing it correctly.

13235.png


Has anyone done this before or can anyone point me in the right direction?

Thanks

------------------------------
Dan Lister
Developer
Europe/London
------------------------------
5 REPLIES 5

Hi,

What is the response code and message that you get from AWS?

------------------------------
Shashank Kumar
DX Integrations Partner Consultant
Blue Prism
Singapore
+6581326707
------------------------------

Hi,

Thanks for responding.

Response Code is :-

HTTP Status Code: 403
HTTP Response Content: SignatureDoesNotMatch
The request signature we calculated does not match the signature you provided. Check your key and signing method.

Thanks


------------------------------
Dan Lister
Developer
Arvato
Europe/London
------------------------------

Hi Dan,

It seems the signature passed from the vbo isn't matching with the POSTMAN request. Would you be able to share the screenshot of POSTMAN and the web api file?

------------------------------
Shashank Kumar
DX Integrations Partner Consultant
Blue Prism
Singapore
+6581326707
------------------------------

Hi, 

I'm afraid I can't attach the Web API file, but here is the Postman Screenshot:

13224.png



Thanks

------------------------------
Dan Lister
Developer
Arvato
Europe/London
------------------------------

Hi, 

I've just realised that I hadn't edited the Global Code on that object and it looks that this is set up for a POST request. I've tried tweaking again, but my knowledge just isn't good enough to understand and ensure I'm doing it correctly.

I've been trying to follow this guide, but having no luck. https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html

Can anyone help?

Thanks

Global Code
// SHA256 hash of an empty request body
public string EMPTY_BODY_SHA256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";

public string SCHEME = "AWS4";
public string ALGORITHM = "HMAC-SHA256";
public string TERMINATOR = "aws4_request";

// format strings for the date/time and date stamps required during signing
public string ISO8601BasicFormat = "yyyyMMddTHHmmssZ";
public string DateStringFormat = "yyyyMMdd";

// some common x-amz-* parameters
string X_Amz_Algorithm = "X-Amz-Algorithm";
string X_Amz_Credential = "X-Amz-Credential";
string X_Amz_SignedHeaders = "X-Amz-SignedHeaders";
string X_Amz_Date = "X-Amz-Date";
string X_Amz_Signature = "X-Amz-Signature";
string X_Amz_Expires = "X-Amz-Expires";
string X_Amz_Content_SHA256 = "X-Amz-Content-SHA256";
string X_Amz_Decoded_Content_Length = "X-Amz-Decoded-Content-Length";
string X_Amz_Meta_UUID = "X-Amz-Meta-UUID";

// the name of the keyed hash algorithm used in signing
string HMACSHA256 = "HMACSHA256";

Regex CompressWhitespaceRegex = new Regex("\\s+");

string strAction = "POST";
Uri uri;

HashAlgorithm CanonicalRequestHashAlgorithm = HashAlgorithm.Create("SHA-256");

public string ComputeSignature(IDictionary<string, string> headers,
                            string dateTimeStamp,   
			    string queryParameters,
                            string bodyHash,
                            string awsAccessKey,
                            string awsSecretKey,
			    string awsUri,
			    string awsRegion,
			    string awsService)
{
    
	// first get the date and time for the subsequent request, and convert to ISO 8601 format
    // for use in signature generation
    var requestDateTime = DateTime.UtcNow;

    // update the headers with required 'x-amz-date' and 'host' values
    headers.Add(X_Amz_Date, dateTimeStamp);

    uri = new Uri(awsUri);
	var hostHeader = uri.Host;
    if (!uri.IsDefaultPort)
        hostHeader += ":" + uri.Port;
    headers.Add("Host", hostHeader);

    // canonicalize the headers; we need the set of header names as well as the
    // names and values to go into the signature process
    var canonicalizedHeaderNames = CanonicalizeHeaderNames(headers);
    var canonicalizedHeaders = CanonicalizeHeaders(headers);

    // canonicalize the various components of the request
    var canonicalRequest = CanonicalizeRequest(uri,
                                               strAction,
                                               null,
                                               canonicalizedHeaderNames,
                                               canonicalizedHeaders,
                                               bodyHash);

    // generate a hash of the canonical request, to go into signature computation
    var canonicalRequestHashBytes
        = CanonicalRequestHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(canonicalRequest));

    // construct the string to be signed
    var stringToSign = new StringBuilder();

    var dateStamp = requestDateTime.ToString(DateStringFormat, CultureInfo.InvariantCulture);
    var scope = string.Format("{0}/{1}/{2}/{3}",
                              dateStamp,
                              awsRegion,
                              awsService,
                              TERMINATOR);

    stringToSign.AppendFormat("{0}-{1}\n{2}\n{3}\n", SCHEME, ALGORITHM, dateTimeStamp, scope);
    stringToSign.Append(ToHexString(canonicalRequestHashBytes, true));

    // compute the signing key
    var kha = KeyedHashAlgorithm.Create(HMACSHA256);
    kha.Key = DeriveSigningKey(HMACSHA256, awsSecretKey, awsRegion, dateStamp, awsService);

    // compute the AWS4 signature and return it
    var signature = kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign.ToString()));
    var signatureString = ToHexString(signature, true);
    //Console.WriteLine("\nSignature:\n{0}", signatureString);

    var authString = new StringBuilder();
    authString.AppendFormat("{0}-{1} ", SCHEME, ALGORITHM);
    authString.AppendFormat("Credential={0}/{1}, ", awsAccessKey, scope);
    authString.AppendFormat("SignedHeaders={0}, ", canonicalizedHeaderNames);
    authString.AppendFormat("Signature={0}", signatureString);

    var authorization = authString.ToString();

    return authorization;
}
public string CanonicalizeRequest(Uri endpointUri,
                             string httpMethod,
                             string queryParameters,
                             string canonicalizedHeaderNames,
                             string canonicalizedHeaders,
                             string bodyHash)
{
    var canonicalRequest = new StringBuilder();

    canonicalRequest.AppendFormat("{0}\n", httpMethod);
    canonicalRequest.AppendFormat("{0}\n", CanonicalResourcePath(endpointUri));
    canonicalRequest.AppendFormat("{0}\n", queryParameters);

    canonicalRequest.AppendFormat("{0}\n", canonicalizedHeaders);
    canonicalRequest.AppendFormat("{0}\n", canonicalizedHeaderNames);

    canonicalRequest.Append(bodyHash);

    return canonicalRequest.ToString();
}
public string CanonicalResourcePath(Uri endpointUri)
{
    if (string.IsNullOrEmpty(endpointUri.AbsolutePath))
        return "/";

    // encode the path per RFC3986
    return UrlEncode(endpointUri.AbsolutePath, true);
}
public byte[] DeriveSigningKey(string algorithm, string awsSecretAccessKey, string awsRegion, string date, string awsService)
{
    string ksecretPrefix = SCHEME;
    char[] ksecret = null;

    ksecret = (ksecretPrefix + awsSecretAccessKey).ToCharArray();

    byte[] hashDate = ComputeKeyedHash(algorithm, Encoding.UTF8.GetBytes(ksecret), Encoding.UTF8.GetBytes(date));
    byte[] hashRegion = ComputeKeyedHash(algorithm, hashDate, Encoding.UTF8.GetBytes(awsRegion));
    byte[] hashService = ComputeKeyedHash(algorithm, hashRegion, Encoding.UTF8.GetBytes(awsService));
    return ComputeKeyedHash(algorithm, hashService, Encoding.UTF8.GetBytes(TERMINATOR));
}
public byte[] ComputeKeyedHash(string algorithm, byte[] key, byte[] data)
{
    var kha = KeyedHashAlgorithm.Create(algorithm);
    kha.Key = key;
    return kha.ComputeHash(data);
}
public string CanonicalizeHeaderNames(IDictionary<string, string> headers)
{
    var headersToSign = new List<string>(headers.Keys);
    headersToSign.Sort(StringComparer.OrdinalIgnoreCase);

    var sb = new StringBuilder();
    foreach (var header in headersToSign)
    {
        if (sb.Length > 0)
            sb.Append(";");
        sb.Append(header.ToLower());
    }
    return sb.ToString();
}
public string CanonicalizeHeaders(IDictionary<string, string> headers)
{
    if (headers == null || headers.Count == 0)
        return string.Empty;

    // step1: sort the headers into lower-case format; we create a new
    // map to ensure we can do a subsequent key lookup using a lower-case
    // key regardless of how 'headers' was created.
    var sortedHeaderMap = new SortedDictionary<string, string>();
    foreach (var header in headers.Keys)
    {
        sortedHeaderMap.Add(header.ToLower(), headers[header]);
    }

    // step2: form the canonical header:value entries in sorted order. 
    // Multiple white spaces in the values should be compressed to a single 
    // space.
    var sb = new StringBuilder();
    foreach (var header in sortedHeaderMap.Keys)
    {
        var headerValue = CompressWhitespaceRegex.Replace(sortedHeaderMap[header], " ");
        sb.AppendFormat("{0}:{1}\n", header, headerValue.Trim());
    }

    return sb.ToString();
}
public string UrlEncode(string data, bool isPath = false)
{
    // The Set of accepted and valid Url characters per RFC3986. Characters outside of this set will be encoded.
    const string validUrlCharacters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";

    var encoded = new StringBuilder(data.Length * 2);
    string unreservedChars = String.Concat(validUrlCharacters, (isPath ? "/:" : ""));

    foreach (char symbol in System.Text.Encoding.UTF8.GetBytes(data))
    {
        if (unreservedChars.IndexOf(symbol) != -1)
            encoded.Append(symbol);
        else
            encoded.Append("%").Append(String.Format("{0:X2}", (int)symbol));
    }

    return encoded.ToString();
}
public string ToHexString(byte[] data, bool lowercase)
{
    var sb = new StringBuilder();
    for (var i = 0; i < data.Length; i++)
    {
        sb.Append(data[i].ToString(lowercase ? "x2" : "X2"));
    }
    return sb.ToString();
}

AuthSignature Code Stage

string strBody = RequestBody;

// precompute hash of the body content            
var contentHash = CanonicalRequestHashAlgorithm.ComputeHash(Encoding.UTF8.GetBytes(strBody));
var contentHashString = ToHexString(contentHash, true);

var requestDateTime = DateTime.UtcNow;
var dateTimeStamp = requestDateTime.ToString(ISO8601BasicFormat, CultureInfo.InvariantCulture);
strAuthDate = dateTimeStamp;

var headers = new Dictionary<string, string>
            {
                {"content-type", "application/x-amz-json-1.1"},
                {"x-amz-target", AWSTarget}
            };

strAuthSignature = ComputeSignature(headers,
       			      dateTimeStamp,
                              "",   // no query parameters
                              contentHashString,
                              AccessID,
                              SecretKey,
                              AWSEndpoint,
			      AWSRegion,
                              AWSService);


------------------------------
Dan Lister
Developer
Arvato
Europe/London
------------------------------