0

I am trying to create a sha256 signature using a RSA Private Key but I am getting a 401 "Could not authenticate in-request, auth signature : Signature verification failed: affil-product, version: 2.0.0, env: prod

I think the issue is to do whit how it get my .pem file. I have read the Microsoft documentation and the provided Walmart example. I am following this guide. I created a non password protected key pair and uploaded the public key to Walmart. I then added my consumer ID and key version to appsettings.json {"Settings": {"consumerID": "e2ca6a2f-56f2-4465-88b3-273573b1e0c9","keyVer": "4"}}.

I am then getting this data in program.cs via the following code.

     IConfiguration config = new ConfigurationBuilder()
        .AddJsonFile("appsettings.json")
        .AddEnvironmentVariables()
        .Build();
// Get values from the config given their key and their target type.
Settings settings = config.GetRequiredSection("Settings").Get<Settings>();

I then instantiate Walmart affiliate object allowing us to use the methods needed to access and read Walmart api

     WalMartAfilAPI wallMartAfilAPI = new WalMartAfilAPI();

From there I Create a RSACryptoServiceProvider object and import the .pem and export the parameter.

RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider();
var rsaPem = File.ReadAllText("D:\\Users\\Adam\\source\\repos\\DealsBot\\DealsBot\\DealsBot\\wallmartAfill\\WM_IO_private_key.pem");
//now we instantiate the RSA object
var rsa = RSA.Create();
//replace the private key with our .pem
rsa.ImportFromPem(rsaPem);
//Export the key information to an RSAParameters object.
// You must pass true to export the private key for signing.
// However, you do not need to export the private key
// for verification.          
RSAParameters Key = rsa.ExportParameters(true);

From here I get the time stamp and call methods from the Walmart Affiliate object.

//Get current im in unix epoch milliseconds
    TimeSpan t = DateTime.UtcNow - new DateTime(1970, 1, 1);
    var time = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds().ToString();
    Console.WriteLine(time);


    byte[] conicData = wallMartAfilAPI.Canonicalize(settings.KeyVer, settings.ConsumerID, time);
    byte[] signedData = wallMartAfilAPI.HashAndSignBytes(conicData, Key);

 

    if (wallMartAfilAPI.VerifySignedHash(conicData, signedData, Key))
    {
    Console.WriteLine("The data was verified");
  ;
    Console.WriteLine(Convert.ToBase64String(signedData));


    }
    else
    {

Here is the WalMartAfilAPI class

namespace DealsBot.wallmartAfill
{
    public class WalMartAfilAPI
    {

        public byte[] Canonicalize(string version, string consumerId, string timestamp)
        {
            ASCIIEncoding ByteConverter = new ASCIIEncoding();
            // Follow after the java code, which just orders the keys/values.
            StringBuilder keyBuilder = new StringBuilder();
            StringBuilder valueBuilder = new StringBuilder();
            SortedDictionary<string, string> dictionary = new SortedDictionary<string, string>() { { "WM_CONSUMER.ID", consumerId }, { "WM_CONSUMER.INTIMESTAMP", timestamp }, { "WM_SEC.KEY_VERSION", version } };

            foreach (string key in dictionary.Keys)
            {
                keyBuilder.Append($"{key.Trim()};");
                valueBuilder.AppendLine($"{dictionary[key].Trim()}");
            }
            string[] conHeader =  { keyBuilder.ToString(), valueBuilder.ToString() };
            byte[] originalData = ByteConverter.GetBytes(conHeader[1]);

            return originalData;
        }


        public byte[] HashAndSignBytes(byte[] DataToSign, RSAParameters Key)
        {
            try
            {
                // Create a new instance of RSACryptoServiceProvider using the
                // key from RSAParameters.
                RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider();

                RSAalg.ImportParameters(Key);

                // Hash and sign the data. Pass a new instance of SHA256
                // to specify the hashing algorithm.
                return RSAalg.SignData(DataToSign, SHA256.Create());
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(e.Message);

                return null;
            }
        }

        public bool VerifySignedHash(byte[] DataToVerify, byte[] SignedData, RSAParameters Key)
        {
            try
            {
                // Create a new instance of RSACryptoServiceProvider using the
                // key from RSAParameters.
                RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider();

                RSAalg.ImportParameters(Key);

                // Verify the data using the signature.  Pass a new instance of SHA256
                // to specify the hashing algorithm.
                return RSAalg.VerifyData(DataToVerify, SHA256.Create(), SignedData);
            }
            catch (CryptographicException e)
            {
                Console.WriteLine(e.Message);

                return false;
            }
        }

       
    }
}
  • I'm not familiar with the Walmart API - but reading through your code, it appears you are using the timestamp in milliseconds. The default implementation is usually in Seconds. Possibly that is causing an issue? – Nick Goloborodko Aug 14 '22 at 21:19

1 Answers1

0

As of today, auth signature code is available in Java (https://www.walmart.io/docs/affiliate/onboarding-guide) The idea we provided sample code to help the customers to implement the logic at customer end by referring it You can implement the logic in(C# .NET, Python, PHP or JS) in such a way that whenever your system invoking Walmart APIs, generate the signature on the fly and pass as input parameter This is how all of customers implemented and consuming our APIs Pls refer the below documentation for complete. https://walmart.io/docs/affiliate/quick-start-guide https://www.walmart.io/docs/affiliate/onboarding-guide Regards, Firdos IO Support