I want to use selling-partner-api-docs for .Net . I found one reference from below url but that is use Java example coding : 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
But i want to use .Net coding can any one suggest of .Net coding of below java coding part
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.nio.charset.StandardCharsets;
import com.amazon.spapi.documents.UploadHelper;
import com.amazon.spapi.documents.UploadSpecification;
import com.amazon.spapi.documents.exception.CryptoException;
import com.amazon.spapi.documents.exception.HttpResponseException;
import com.amazon.spapi.documents.impl.AESCryptoStreamFactory;
/* We want to maintain encryption at rest, so do not write unencrypted data to disk. This is bad:
InputStream source = new FileInputStream(new File("/path/to/myFeed.xml"));
Instead, if your data can fit in memory, you can create an InputStream from a String (see encryptAndUpload_fromString()).
Otherwise, you can pipe data into an InputStream using Piped streams (see encryptAndUpload_fromPipedInputStream()).
*/
public class UploadExample {
private final UploadHelper uploadHelper = new UploadHelper.Builder().build();
// key, initializationVector, and url are returned by the createFeedDocument operation.
public void encryptAndUpload_fromString(String key, String initializationVector, String url) {
AESCryptoStreamFactory aesCryptoStreamFactory =
new AESCryptoStreamFactory.Builder(key, initializationVector)
.build();
// This contentType must be the same value that was provided to createFeedDocument.
String contentType = String.format("text/plain; charset=%s", StandardCharsets.UTF_8);
// The character set must be the same one that is specified in contentType.
try
(InputStream source = new ByteArrayInputStream("my feed data".getBytes(StandardCharsets.UTF_8))) {
UploadSpecification uploadSpec =
new UploadSpecification.Builder(contentType, aesCryptoStreamFactory, source, url)
.build();
uploadHelper.upload(uploadSpec);
}
catch (CryptoException | HttpResponseException | IOException e) {
// Handle exception.
}
}
// key, initializationVector, and url are returned from createFeedDocument.
public void encryptAndUpload_fromPipedInputStream(String key, String initializationVector, String url) {
AESCryptoStreamFactory aesCryptoStreamFactory =
new AESCryptoStreamFactory.Builder(key, initializationVector)
.build();
// This contentType must be the same value that was provided to createFeedDocument.
String contentType = String.format("text/plain; charset=%s", StandardCharsets.UTF_8);
try
(PipedInputStream source = new PipedInputStream()) {
new Thread(
new Runnable() {
public void run() {
try
(PipedOutputStream feedContents = new PipedOutputStream(source)) {
// The character set must be the same one that is specified in contentType.
feedContents.write("my feed data\n".getBytes(StandardCharsets.UTF_8));
feedContents.write("more feed data".getBytes(StandardCharsets.UTF_8));
}
catch (IOException e) {
// Handle exception.
}
}
}).start();
UploadSpecification uploadSpec =
new UploadSpecification.Builder(contentType, aesCryptoStreamFactory, source, url)
.build();
uploadHelper.upload(uploadSpec);
}
catch (CryptoException | HttpResponseException | IOException e) {
}
}
}
Edit -----------------------------------------------------
This is what I have tried and this is what I have got.
STEP 1
REQUEST URL: https://sellingpartnerapi-na.amazon.com/feeds/2020-09-04/documents
REQUEST BODY:{"contentType":"text/plain;charset=utf-8"}
Request Headers = {Host: sellingpartnerapi-na.amazon.com
x-amz-date: 20210203T120516Z
Authorization: AWS4-HMAC-SHA256 Credential=XXXXXXXX/20210203/us-east-1/execute-api/aws4_request, SignedHeaders=host;x-amz-date, Signature=XXXX
The Credential and Signature are created on the partner portal.
RESPONSE STEP 1
{"payload":
{"encryptionDetails":{"standard":"AES","initializationVector":"TTAVo5bUDNfuk7KPzgm+ow==",
"key":"GrpKm3UIvxiM5xUTlzaCC9xJFORMX41chAKUk0G6Cbg="},
"feedDocumentId":"amzn1.tortuga.3.9968967c-048c-4e8b-a6c1-ffd764f005d4.T508PJ0OCPKJ3",
"url":"https://tortuga-prod-na.s3-external-1.amazonaws.com/%2FNinetyDays/amzn1.tortuga.3.9968967c-048c-4e8b-a6c1-ffd764f005d4.T508PJ0OCPKJ3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20210203T114111Z&X-Amz-SignedHeaders=content-type%3Bhost&X-Amz-Expires=300&X-Amz-Credential=AKIA5U6MO6RANYPNEUPL%2F20210203%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=1fd8b69523c06d76664c22c4093be5e8adc187436f7119aa9d4b51302cc8ae84"}}
STEP 2:
In step 2 I am using the URL coming from the first Step Response but it is not getting me result.
REQUEST URL:
https://tortuga-prod-na.s3-external-1.amazonaws.com/%2FNinetyDays/amzn1.tortuga.3.9968967c-048c-4e8b-a6c1-ffd764f005d4.T508PJ0OCPKJ3?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20210203T114111Z&X-Amz-SignedHeaders=content-type%3Bhost&X-Amz-Expires=300&X-Amz-Credential=AKIA5U6MO6RANYPNEUPL%2F20210203%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Signature=1fd8b69523c06d76664c22c4093be5e8adc187436f7119aa9d4b51302cc8ae84
See the Signature and the Credential here are coming different than one we have got from the response of Step 1
RESPONSE FROM STEP 2
<?xml version="1.0" encoding="UTF-8"?>
-<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
<AWSAccessKeyId>AKIA5U6MO6RANYPNEUPL</AWSAccessKeyId>
<StringToSign>AWS4-HMAC-SHA256 20210203T114111Z 20210203/us-east-1/s3/aws4_request 057d93b83f8254c64b8ffccdfb885b79e5d96c0d2045c27732fc42ae722e335e</StringToSign>
<SignatureProvided>1fd8b69523c06d76664c22c4093be5e8adc187436f7119aa9d4b51302cc8ae84</SignatureProvided>
<StringToSignBytes>41 57 53 34 2d 48 4d 41 43 2d 53 48 41 32 35 36 0a 32 30 32 31 30 32 30 33 54 31 31 34 31 31 31 5a 0a 32 30 32 31 30 32 30 33 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 30 35 37 64 39 33 62 38 33 66 38 32 35 34 63 36 34 62 38 66 66 63 63 64 66 62 38 38 35 62 37 39 65 35 64 39 36 63 30 64 32 30 34 35 63 32 37 37 33 32 66 63 34 32 61 65 37 32 32 65 33 33 35 65</StringToSignBytes>
<CanonicalRequest>PUT //NinetyDays/amzn1.tortuga.3.9968967c-048c-4e8b-a6c1-ffd764f005d4.T508PJ0OCPKJ3 X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIA5U6MO6RANYPNEUPL%2F20210203%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20210203T114111Z&X-Amz-Expires=300&X-Amz-SignedHeaders=content-type%3Bhost content-type:text/plain; charset=utf-8 host:tortuga-prod-na.s3-external-1.amazonaws.com content-type;host UNSIGNED-PAYLOAD</CanonicalRequest>
<CanonicalRequestBytes>50 55 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 36 38 39 36 37 63 2d 30 34 38 63 2d 34 65 38 62 2d 61 36 63 31 2d 66 66 64 37 36 34 66 30 30 35 64 34 2e 54 35 30 38 50 4a 30 4f 43 50 4b 4a 33 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 4e 59 50 4e 45 55 50 4c 25 32 46 32 30 32 31 30 32 30 33 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 32 30 33 54 31 31 34 31 31 31 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 70 6c 61 69 6e 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 44</CanonicalRequestBytes>
<RequestId>48A2CCE3EFA66E89</RequestId>
<HostId>hiZxZwoTgGG4PBvGLchnKV94AA57zzGqnHh5BbTCIAt1ubD47O+8uQMClkDDBoJBgiXgVb57TRE=</HostId>
</Error>
Hopefully this helps some c sharpers out there for Step #2 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
Assuming you have received an OK response for Step #1
The response for Step #1 will look something like this:
{
"payload":
{
"feedDocumentId":"amzn1.tortuga.3.920614b0-fc4c-4393-b0d9-fff175300000.T29XK4YL08B2VM",
"url":"https://tortuga-prod-na.s3.amazonaws.com/%2FNinetyDays/amzn1.tortuga.3.920614b0-fc4c-4393-b0d9-fff175300000.T29XK4YL08B2VM?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20200919T035824Z&X-Amz-SignedHeaders=<headers>&X-Amz-Expires=300&X-Amz-Credential=<credential>&X-Amz-Signature=<signature>",
"encryptionDetails":
{
"standard":"AES",
"initializationVector":"kF3bZt0FSv6JQEimfEJD8g==",
"key":"5EZo/P06OGF0UAy8QuOnMIaQbkAvYBru6EGsFvK8wJ2="
}
}
Convert EncryptionDetails.Key and EncryptionDetails.InitializationVector to bytes and read your flat file (feed) into a string variable...
var key = Convert.FromBase64String(createFeedDocumentResponse.Payload.EncryptionDetails.Key);
var iv = Convert.FromBase64String(createFeedDocumentResponse.Payload.EncryptionDetails.InitializationVector);
string feedData = File.ReadAllText(#"C:\temp\AmazonFlatFileTest.txt");
Encrypt the feed using AES w/ the key and iv variables above...
Here's an encrypt and decrypt function I took from Microsoft and altered slightly (credit to https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.aescryptoserviceprovider?view=net-5.0)...
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;
}
And the decrypt function... You will need this in Step #5 decrypt and process the results.
EDIT: It's noticed for larger inputs, Amazon will use GZIP compression with their response (Payload.CompressionAlgorithm). Included 'compressionAlgorithm' parameter to the DecryptStringFromBytes_Aes method.
private string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] initializationVector, string compressionAlgorithm)
{
// Validate Compression Algorithm
var isGzip = string.Equals(compressionAlgorithm, "GZIP", StringComparison.OrdinalIgnoreCase);
var compressionAlgorithmValid = compressionAlgorithm == null || isGzip;
if (!compressionAlgorithmValid)
{
throw new InvalidOperationException($"Unexpected CompressionAlgorithm encounted. compressionAlgorithm = {compressionAlgorithm}");
}
// Check arguments.
if (cipherText == null || cipherText.Length <= 0)
throw new ArgumentNullException("cipherText");
if (Key == null || Key.Length <= 0)
throw new ArgumentNullException("Key");
if (initializationVector == null || initializationVector.Length <= 0)
throw new ArgumentNullException("IV");
// Declare the string used to hold
// the decrypted text.
string plaintext = null;
// Create an Aes object
// with the specified key and IV.
using (Aes aesAlg = Aes.Create())
{
aesAlg.Key = Key;
aesAlg.IV = initializationVector;
// Create a decryptor to perform the stream transform.
ICryptoTransform decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
// Create the streams used for decryption.
using (MemoryStream msDecrypt = new MemoryStream(cipherText))
{
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
{
if (isGzip)
{
using (var decompressedFileStream = new MemoryStream())
{
using (GZipStream decompressionStream = new GZipStream(csDecrypt, CompressionMode.Decompress))
{
decompressionStream.CopyTo(decompressedFileStream);
decompressedFileStream.Position = 0;
using (var writer = new StreamReader(decompressedFileStream))
{
plaintext = writer.ReadToEnd();
}
}
}
}
else
{
using (StreamReader srDecrypt = new StreamReader(csDecrypt, Encoding.UTF8))
{
// Read the decrypted bytes from the decrypting stream
// and place them in a string.
plaintext = srDecrypt.ReadToEnd();
}
}
}
}
}
return plaintext;
}
With the returned result from EncryptStringToBytes_Aes, you can now upload to S3 url (provided to you in Payload from Step #1). Here is an example using the library Rest Sharp (which some Amazon employee seems to like judging by the reference in Amazon's C# client example).
NB: The Content Type must match the content type in your CreateFeedDocument request (Step #1).
Here is a function you can use to upload to S3...
private async Task UploadFile(byte[] bytes, string url)
{
var contentType = "text/plain; charset=utf-8"; // this should be the same as what was used in Step #1 (in the CreateFeedDocument API request)
RestClient restClient = new RestClient(url);
IRestRequest restRequest = new RestRequest(Method.PUT);
restRequest.AddParameter(contentType, bytes, ParameterType.RequestBody);
var response = await restClient.ExecuteAsync(restRequest);
if (!response.IsSuccessful)
{
// your error logic
}
// success. Move to Step #3
}
Happy days...
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 8 years ago.
Improve this question
Can anyone tell me how can I read this grid into arrays like a[i][j] ? I searched on google but I can't seem to find anything useful.Thank you very much for helping!
static void Main(string[] args)
{
String grid = "08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08" +
"49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00" +
"81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65" +
"52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91" +
"22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80" +
"24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50" +
"32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70" +
"67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21" +
"24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72" +
"21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95" +
"78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92" +
"16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57" +
"86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58" +
"19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40" +
"04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66" +
"88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69" +
"04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36" +
"20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16" +
"20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54" +
"01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48";
int[] a = new int[20];
for(int i=0;i<20;i++)
for (int j = 1; j < 20; j++)
{
}
}
As suggested in the comments you can simply separate your numbers and use split string. for example:
private static void Main(string[] args)
{
String grid = "08,02,22,97,38,15,00,40,00,75,04,05,07,78,52,12,50,77,91,08," +
"49,49,99,40,17,81,18,57,60,87,17,40,98,43,69,48,04,56,62,00," +
"81,49,31,73,55,79,14,29,93,71,40,67,53,88,30,03,49,13,36,65," +
"52,70,95,23,04,60,11,42,69,24,68,56,01,32,56,71,37,02,36,91," +
"22,31,16,71,51,67,63,89,41,92,36,54,22,40,40,28,66,33,13,80," +
"24,47,32,60,99,03,45,02,44,75,33,53,78,36,84,20,35,17,12,50," +
"32,98,81,28,64,23,67,10,26,38,40,67,59,54,70,66,18,38,64,70," +
"67,26,20,68,02,62,12,20,95,63,94,39,63,08,40,91,66,49,94,21," +
"24,55,58,05,66,73,99,26,97,17,78,78,96,83,14,88,34,89,63,72," +
"21,36,23,09,75,00,76,44,20,45,35,14,00,61,33,97,34,31,33,95," +
"78,17,53,28,22,75,31,67,15,94,03,80,04,62,16,14,09,53,56,92," +
"16,39,05,42,96,35,31,47,55,58,88,24,00,17,54,24,36,29,85,57," +
"86,56,00,48,35,71,89,07,05,44,44,37,44,60,21,58,51,54,17,58," +
"19,80,81,68,05,94,47,69,28,73,92,13,86,52,17,77,04,89,55,40," +
"04,52,08,83,97,35,99,16,07,97,57,32,16,26,26,79,33,27,98,66," +
"88,36,68,87,57,62,20,72,03,46,33,67,46,55,12,32,63,93,53,69," +
"04,42,16,73,38,25,39,11,24,94,72,18,08,46,29,32,40,62,76,36," +
"20,69,36,41,72,30,23,88,34,62,99,69,82,67,59,85,74,04,36,16," +
"20,73,35,29,78,31,90,01,74,31,49,71,48,86,81,16,23,57,05,54," +
"01,70,54,71,83,51,54,69,16,92,33,48,61,43,52,01,89,19,67,48";
var splitstring = grid.Split(',');
var a = new int[20,20];
const int rowCount = 19; //counts 0 as 1
var rowIndex = 0;
var colIndex = 0;
foreach (var s in splitstring)
{
if (rowIndex > rowCount)
{
rowIndex = 0;
colIndex++;
}
a[colIndex, rowIndex] = Int32.Parse(s);
rowIndex++;
}
}
Note the Int32.Parse(s) will throw an exception if the parse fails. You can instead use an Int32.TryParse and use the out value for your result. Depends on what you want to do.
Consider add a space to the end of each "line" such as the following:
String grid = "08 02 .. 91 08 " +
"01 70 .. 67 48 ";
// ^-- add space here
This will allow the string to be converted trivially into a 1D array of strings with string.Split.
string grid = "08 02 .. 91 08"; // every number is space-separated now
string[] gridArray = grid.Split(" "); // -> ["08", "02", .. "91", "08"]
(Even without ensuring the extra spaces, a 1D array can be achieved using a Regular Expression split: var gridArray = Regex.Split(grid, "(?:\s|(?<=\d{2})(?=\d{2}))"), but I recommend "normalizing" the input string literals if possible.)
And each index in the resulting 1D array can be accessed as so, where columns represents the number of columns of the super-imposed matrix, or the "width" of each row.
int columns = 20;
int gridIndex = j * columns + i; // where j is a *row* and i is a *column*
// for a column-major matrix
string numStr = gridArray[gridIndex]; // and value at the [column,row]
Then it's just a matter of converting numStr to an integer and assigning it to the appropriate array index.
If every number is separated by a space such that ever number NN is in form "NN " it also takes up 3 characters. In this case the intermediate Split can be skipped, using the same idea of indexing into the source as a 1D sequence.
int gridNumOffset = (j * columns + i) * 3;
string numStr = grid.Substring(gridNumOffset, 2);
(Finding the substring offset even when there is no space at the end-of-line can be done using a little bit more math, which is a good exercise and the formula just becomes (j * columns + i) * 3 + f(i), where f(i) applies the appropriate offset.)
Another more mundane approach, assuming that the original string cannot be modified to include an end-of-line space/character, is to read in each line as N characters, deal with it, and move on. The concepts from above can be applied:
int rowWidth = (columns * 3) - 1; // -1 assuming no line-end space
for(int j = 0; j < rows; j++) { // j is *rows*, for column-major matrix
string rowStr = str.Substring(j * rowWidth, rowWidth);
string[] row = rowStr.Split(" "); // 1D array just for this row
for (int i = 0; i < columns; i++) {
string numStr = row[i];
// Convert and put value into the correct matrix position
}
}