Walmart.io authentication issue - Could not authenticate in-request, auth signature in C#

594 Views Asked by At

I am trying to implement for C#, here is my code:

                WebClient downloader = new WebClient();
                downloader.Headers["WM_CONSUMER.ID"] = consumerId;
                long intimestamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalMilliseconds;
                downloader.Headers["WM_CONSUMER.INTIMESTAMP"] = intimestamp.ToString();
                downloader.Headers["WM_SEC.KEY_VERSION"] = priviateKeyVersion;
                string data = downloader.Headers["WM_CONSUMER.ID"] + "\n" + downloader.Headers["WM_CONSUMER.INTIMESTAMP"] + "\n" + downloader.Headers["WM_SEC.KEY_VERSION"] + "\n";
                downloader.Headers["WM_SEC.WM_SEC.AUTH_SIGNATURE"] = getWalmartSig(data);

                url = "https://developer.api.walmart.com/api-proxy/service/affil/product/v2/items/" + id;

                string json = downloader.DownloadString(url);

to get signature, I use BouncyCastle

    private string getWalmartSig(string data)
    {
        AsymmetricCipherKeyPair keyPair;
        using (var reader = File.OpenText(@"key.pem"))
        { // file containing RSA PKCS1 private key
            keyPair = (AsymmetricCipherKeyPair)new PemReader(reader).ReadObject();

            RSACryptoServiceProvider key = new RSACryptoServiceProvider();
            RSAParameters rsaParam = DotNetUtilities.ToRSAParameters((RsaKeyParameters)keyPair.Public);
            ISigner signer = SignerUtilities.GetSigner("SHA256WithRSA");
            signer.Init(true, keyPair.Private);
            byte[] msg = Encoding.UTF8.GetBytes(data);
            signer.BlockUpdate(msg, 0, msg.Length);
            return Convert.ToBase64String(signer.GenerateSignature());
        }
    }

keep getting forbidden. Please help.

2

There are 2 best solutions below

4
On

If your private key has a password you have to get the pair using another method.

private static string getWalmartSig(string data)
        {
            try
            {
                AsymmetricCipherKeyPair keyPair;
                using (var reader = File.OpenText(@"key.pem")) 
                { // file containing RSA PKCS1 private key
                    keyPair = DecodePrivateKey(reader.ReadToEnd(), Constants.password);  //modified to include password for reading private key. 
                    RSACryptoServiceProvider key = new RSACryptoServiceProvider();
                    RSAParameters rsaParam = DotNetUtilities.ToRSAParameters((RsaKeyParameters)keyPair.Public);
                    ISigner signer = SignerUtilities.GetSigner("SHA256WITHRSAENCRYPTION"); //CryptoConfig.MapNameToOID("SHA256") //SHA256WithRSA //modified for using different Encryption. 
                    signer.Init(true, keyPair.Private);
                    byte[] msg = Encoding.UTF8.GetBytes(data);
                    signer.BlockUpdate(msg, 0, msg.Length);
                    return Convert.ToBase64String(signer.GenerateSignature());
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                return null;
            }
        }

Refer to Decrypt passphrase protected PEM containing private key for reference.

 private static AsymmetricCipherKeyPair DecodePrivateKey(string encryptedPrivateKey, string password) //from https://stackoverflow.com/questions/44767290/decrypt-passphrase-protected-pem-containing-private-key
    {
        try
        {
            TextReader textReader = new StringReader(encryptedPrivateKey);
            PemReader pemReader = new PemReader(textReader, new PasswordFinder(password));
            var privateKeyObject = (AsymmetricCipherKeyPair)pemReader.ReadObject(); //modified for direct casting. 

            RsaPrivateCrtKeyParameters rsaPrivatekey = (RsaPrivateCrtKeyParameters)privateKeyObject.Private;  //modified to use the private key
            RsaKeyParameters rsaPublicKey = new RsaKeyParameters(false, rsaPrivatekey.Modulus, rsaPrivatekey.PublicExponent);
            AsymmetricCipherKeyPair kp = new AsymmetricCipherKeyPair(rsaPublicKey, rsaPrivatekey);
            return kp;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex);
            return null;
        }
    }

And now the extension class. Refer to the same link for reference

private class PasswordFinder : IPasswordFinder
{
    private string password;

    public PasswordFinder(string password)
    {
        this.password = password;
    }


    public char[] GetPassword()
    {
        return password.ToCharArray();
    }
}

Notice the changes I have made to the methods. That should get your code running.

0
On

Olorunfemi Ajibulu is correct, you have a typo on your AUTH_SIGNATURE header name. This is why you are getting a forbidden. However once you correct that, I can almost guarantee you will be getting a 401 from here on out. API does not seem to be authenticating.