amzn / selling-partner-api-models

This repository contains OpenAPI models for developers to use when developing software to call Selling Partner APIs.
Apache License 2.0
609 stars 732 forks source link

Upload encrypt feed data, got SignatureDoesNotMatch error #979

Closed NoraYan closed 3 years ago

NoraYan commented 3 years ago

Here is error message <?xml version="1.0" encoding="UTF-8"?>

SignatureDoesNotMatchThe request signature we calculated does not match the signature you provided. Check your key and signing method.AKIA5U6MO6RALVZCBKC6AWS4-HMAC-SHA256 20210120T060045Z 20210120/us-east-1/s3/aws4_request 7278ced04580c0915c905a6dcc925f569fabb1597ca8c8588a4109caa38760453a22cd1860df0e2f82841d0c6774028a5bacd36d23372456d34eb9f464c642db41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 31 30 31 32 30 54 30 36 30 30 34 35 5a 0a 32 30 32 31 30 31 32 30 2f 75 73 2d 65 61 73 74 2d 31 2f 73 33 2f 61 77 73 34 5f 72 65 71 75 65 73 74 0a 37 32 37 38 63 65 64 30 34 35 38 30 63 30 39 31 35 63 39 30 35 61 36 64 63 63 39 32 35 66 35 36 39 66 61 62 62 31 35 39 37 63 61 38 63 38 35 38 38 61 34 31 30 39 63 61 61 33 38 37 36 30 34 35POST //NinetyDays/amzn1.tortuga.3.991d8245-bd5c-4024-be65-9a64aad0a74f.T3NLOFO4J28AMU X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIA5U6MO6RALVZCBKC6%2F20210120%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20210120T060045Z&X-Amz-Expires=300&X-Amz-SignedHeaders=content-type%3Bhost content-type:text/xml; charset=utf-8 host:tortuga-prod-na.s3-external-1.amazonaws.com content-type;host UNSIGNED-PAYLOAD50 4f 53 54 0a 2f 2f 4e 69 6e 65 74 79 44 61 79 73 2f 61 6d 7a 6e 31 2e 74 6f 72 74 75 67 61 2e 33 2e 39 39 31 64 38 32 34 35 2d 62 64 35 63 2d 34 30 32 34 2d 62 65 36 35 2d 39 61 36 34 61 61 64 30 61 37 34 66 2e 54 33 4e 4c 4f 46 4f 34 4a 32 38 41 4d 55 0a 58 2d 41 6d 7a 2d 41 6c 67 6f 72 69 74 68 6d 3d 41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 26 58 2d 41 6d 7a 2d 43 72 65 64 65 6e 74 69 61 6c 3d 41 4b 49 41 35 55 36 4d 4f 36 52 41 4c 56 5a 43 42 4b 43 36 25 32 46 32 30 32 31 30 31 32 30 25 32 46 75 73 2d 65 61 73 74 2d 31 25 32 46 73 33 25 32 46 61 77 73 34 5f 72 65 71 75 65 73 74 26 58 2d 41 6d 7a 2d 44 61 74 65 3d 32 30 32 31 30 31 32 30 54 30 36 30 30 34 35 5a 26 58 2d 41 6d 7a 2d 45 78 70 69 72 65 73 3d 33 30 30 26 58 2d 41 6d 7a 2d 53 69 67 6e 65 64 48 65 61 64 65 72 73 3d 63 6f 6e 74 65 6e 74 2d 74 79 70 65 25 33 42 68 6f 73 74 0a 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3a 74 65 78 74 2f 78 6d 6c 3b 20 63 68 61 72 73 65 74 3d 75 74 66 2d 38 0a 68 6f 73 74 3a 74 6f 72 74 75 67 61 2d 70 72 6f 64 2d 6e 61 2e 73 33 2d 65 78 74 65 72 6e 61 6c 2d 31 2e 61 6d 61 7a 6f 6e 61 77 73 2e 63 6f 6d 0a 0a 63 6f 6e 74 65 6e 74 2d 74 79 70 65 3b 68 6f 73 74 0a 55 4e 53 49 47 4e 45 44 2d 50 41 59 4c 4f 41 44AD9722310E2BEF3D6aFIJkz/8eBw9kvpyDvNOukh+6FupfsbUUN7uK1NimjLewGsBS1oC9o2lLSJi1MtxiRDwplBmO4=

Here is my code in c#:

var key = Convert.FromBase64String(response.payload.encryptionDetails.key);
var iv = Convert.FromBase64String(response.payload.encryptionDetails.initializationVector);

string feedData = File.ReadAllText(filePath);
var fileData = EncryptStringToBytes_Aes(feedData, key, iv);

var result = UploadFile(fileData, response.payload.url);

private byte[] EncryptStringToBytes_Aes(string plainText, byte[] key, byte[] initializationVector)
{
            // Check arguments.
            if (plainText == null || plainText.Length <= 0)
                throw new ArgumentNullException("plainText");
            if (key == null || key.Length <= 0)
                throw new ArgumentNullException("Key");
            if (initializationVector == null || initializationVector.Length <= 0)
                throw new ArgumentNullException("initializationVector");
            byte[] encrypted;

            // Create an Aes object
            // with the specified key and IV.
            using (Aes aesAlg = Aes.Create())
            {
                aesAlg.Key = key;
                aesAlg.IV = initializationVector;

                // Create an encryptor to perform the stream transform.
                ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);

                // Create the streams used for encryption.
                using (MemoryStream msEncrypt = new MemoryStream())
                {
                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                    {
                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt, Encoding.UTF8))
                        {
                            //Write all data to the stream.
                            swEncrypt.Write(plainText);
                        }
                        encrypted = msEncrypt.ToArray();
                    }
                }
            }

            // Return the encrypted bytes from the memory stream.
            return encrypted;
 }
private bool UploadFile(byte[] bytes, string url)
{
            var contentType = "text/xml; charset=utf-8";

            RestClient restClient = new RestClient(url);
            IRestRequest restRequest = new RestRequest(Method.PUT);
            restRequest.AddParameter(contentType, bytes, ParameterType.RequestBody);
            var response = restClient.Execute(restRequest);
            if (!response.IsSuccessful)
            {
                return false;
            }
            return true;
}

Can anyone please help me out? Thanks !

StefanosPs commented 3 years ago

I think when you try to encrypt and upload the feed data( https://github.com/amzn/selling-partner-api-docs/blob/main/guides/use-case-guides/feeds-api-use-case-guide-2020-09-04.md#step-2-encrypt-and-upload-the-feed-data ), you don't have to provide any signature to the PUT request (https://docs.aws.amazon.com/AmazonS3/latest/dev/PresignedUrlUploadObject.html). Maybe I can help you further if you give me the hole request

NoraYan commented 3 years ago

@StefanosPs Thanks for replying. I have solved the issue. If put contentType as json string directly in the step 1 request, it won't work. I need to Serialize Object to json, then it works. Here is the correct code. request.AddParameter("application/json", JsonConvert.SerializeObject(new { contentType= "text/xml; charset=utf-8" }), ParameterType.RequestBody);

Now I am facing another issue, after creating a feed, the feed processing status keeps showing IN_QUEUE after 8 hours. Does it mean it stuck or I did any thing wrong? Please help. Thanks.

cdragon1116 commented 3 years ago

@StefanosPs @NoraYan

May I ask more about upload xml ? I'm struggling on the 3rd step: createFeed, here's my steps:

Step 1 - Successfully create FeedDocuments by feeds/2020-09-04/documents

# 200 response
POST 'feeds/2020-09-04/documents'

# body    
{ contentType: 'text/xml; charset=UTF-8' }

Step 2 - upload feed data

# 200 response

PUT https://tortuga-prod-eu.s3-eu-west-1.amazonaws.com/%2FNinetyDays/amzn1.tortuga.3.7ef12484-983f-4dd8-beb8-478c43a2809f.T2BTYHA9X9HH2B?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20210125T151942Z&X-Amz-SignedHeaders=content-type%3Bhost&X-Amz-Expires=300&X-Amz-Credential=AKIAX2ZVOZFBFZP5MT6J%2F20210125%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Signature=d9fb135db4981f6740e68eb0614c7e5c4b3383d6685c5e470eda1c9844cbf410

# headers
{ "Content-Type": 'text/xml; charset=UTF-8' }

# body   
 '<?xml version="1.0" encoding="utf-8"?> <AmazonEnvelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="amzn-envelope.xsd"> <Header> <DocumentVersion>1.01</DocumentVersion> <MerchantIdentifier>MySellerID</MerchantIdentifier> </Header> <MessageType>Price</MessageType> <Message> <MessageID>1</MessageID> <Price> <SKU>IF-KBWK-DDLE</SKU> <StandardPrice currency="USD">24.00</StandardPrice> </Price> </Message> </AmazonEnvelope>'

Step 3 - createFeed (Failed)

POST 'feeds/2020-09-04/feeds'

# body   
{
  feedType: 'POST_PRODUCT_PRICING_DATA',
  marketplaceIds: ["A2EUQ1WTGCTBG2", "ATVPDKIKX0DER"],
  inputFeedDocumentId: 'amzn1.tortuga.3.7ef12484-983f-4dd8-beb8-478c43a2809f.T2BTYHA9X9HH2B',
}

# 400 response
{"errors"=>[{"code"=>"InvalidInput", "message"=>"Invalid request parameters", "details"=>""}]}

I get stuck in step 3, keep getting 400 response but couldn't figure out why.

Am I doing wrong on any steps? I signed the xml body with AES-256-CBC in Ruby, since there's no example code, not sure I did wrongly on this step? (But strangely I got 200 response when I upload xml data)

cipher = OpenSSL::Cipher.new('AES-256-CBC')
cipher.encrypt  # set cipher to be encryption mode
cipher.key = Base64.decode64(encryption_details['key'])
cipher.iv  = Base64.decode64(encryption_details['initializationVector'])
encrypted_data = cipher.update(body) + cipher.final

Appreciate any help or lint!

StefanosPs commented 3 years ago

@cdragon1116 I have the same problem. Please let me know if you know the solution amzn/selling-partner-api-models#898

Jis-collab commented 3 years ago

I am trying to update the inventory (Available Quantity ) using Feed Api..I can see that my Available Quantity is updated on the webpage (Status Changed Date not ) and feed state is IN_QUEUE for a long time. How long will it take for the feed state to change to DONE ?

NoraYan commented 3 years ago

@Jis-collab usually for small file update, it will take within an hour, large file will take within 8 hours. If more than that, it usually means stuck. It took a minute for my inventory update. I had once the feed status stuck in IN_QUEUE for more than 12 hours. Then I checked that because I had another update was stuck in IN_PROCESS for a long time and return FATAL at the end. Then I found out my xml had wrong value. I deleted all the feeds in the queue, correct the xml, then update again, it was successful.

NoraYan commented 3 years ago

@cdragon1116 I didn't use multiple marketplace ids in the request, can you try one to see if it works? I use ATVPDKIKX0DER

esoler91 commented 3 years ago

@NoraYan Hi. Did you get it to work? Does the file encryption work well for you? All the steps work for me but the answer is: The uploaded document has an illegal block size.

jason1004 commented 3 years ago

@StefanosPs Thanks for replying. I have solved the issue. If put contentType as json string directly in the step 1 request, it won't work. I need to Serialize Object to json, then it works. Here is the correct code. request.AddParameter("application/json", JsonConvert.SerializeObject(new { contentType= "text/xml; charset=utf-8" }), ParameterType.RequestBody);

Now I am facing another issue, after creating a feed, the feed processing status keeps showing IN_QUEUE after 8 hours. Does it mean it stuck or I did any thing wrong? Please help. Thanks.

Can you provide the complete UploadFile method code?

StefanosPs commented 3 years ago

@jason1004 I think this is the work flow that you need is https://github.com/amzn/selling-partner-api-docs/blob/main/guides/en-US/use-case-guides/feeds-api-use-case-guide/feeds-api-use-case-guide-2020-09-04.md

About the XML content you can read the document of the link above https://images-na.ssl-images-amazon.com/images/G/02/rainier/help/XML_Documentation_Intl.pdf

jason1004 commented 3 years ago

If the feeds api uploads an excel file, how to encrypt the data?

Using the EncryptStringToBytes_Aes method above, what you get is the result of doing the following: image

tanaililing commented 3 years ago

@StefanosPs Thanks for replying. I have solved the issue. If put contentType as json string directly in the step 1 request, it won't work. I need to Serialize Object to json, then it works. Here is the correct code. request.AddParameter("application/json", JsonConvert.SerializeObject(new { contentType= "text/xml; charset=utf-8" }), ParameterType.RequestBody);

Now I am facing another issue, after creating a feed, the feed processing status keeps showing IN_QUEUE after 8 hours. Does it mean it stuck or I did any thing wrong? Please help. Thanks.

hi,Can you provide me with the complete code? I didn't solve the problem, thank you,We look forward to your reply

shahwarkhalid commented 2 years ago

@tanaililing @jason1004 this piece of code worked for me with Ruby to upload file contents to a pre-signed s3 url.

faraday_connection = Faraday::Connection.new(@url) @response = faraday_connection.send(:put, nil, @data.to_json, { "Content-Type": "text/tab-separated-values; charset=UTF-8" })

shahwarkhalid commented 2 years ago

So the steps which I have concluded so far for uploading the feed data are:

It returns success response for this step: https://github.com/amzn/selling-partner-api-docs/blob/main/guides/en-US/use-case-guides/feeds-api-use-case-guide/feeds-api-use-case-guide-2020-09-04.md#step-2-encrypt-and-upload-the-feed-data