Reputation: 11
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.
Upvotes: 1
Views: 617
Reputation: 11
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.
Upvotes: 1
Reputation: 1090
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.
Upvotes: 1