使用RSA私钥生成Walmart Affiliate SHA256签名遇401认证失败
问题:RSA私钥生成SHA256签名时Walmart API返回401验证失败
尝试用RSA私钥生成SHA256签名调用Walmart联盟API时,收到401错误:
Could not authenticate in-request, auth signature : Signature verification failed: affil-product, version: 2.0.0, env: prod
怀疑问题出在.pem私钥文件的处理上。已按Walmart指南创建无密码密钥对并上传公钥,配置了appsettings.json:
{"Settings": {"consumerID": "e2ca6a2f-56f2-4465-88b3-273573b1e0c9","keyVer": "4"}}
现有代码流程
读取配置(Program.cs)
IConfiguration config = new ConfigurationBuilder() .AddJsonFile("appsettings.json") .AddEnvironmentVariables() .Build(); // 从配置中获取参数 Settings settings = config.GetRequiredSection("Settings").Get<Settings>();
实例化API对象
WalMartAfilAPI wallMartAfilAPI = new WalMartAfilAPI();
导入私钥并导出参数
RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider(); var rsaPem = File.ReadAllText("D:\\Users\\Adam\\source\\repos\\DealsBot\\DealsBot\\DealsBot\\wallmartAfill\\WM_IO_private_key.pem"); var rsa = RSA.Create(); rsa.ImportFromPem(rsaPem); // 导出私钥参数用于签名 RSAParameters Key = rsa.ExportParameters(true);
生成时间戳并调用签名方法
// 生成Unix毫秒级时间戳 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 { // 此处代码未完整展示 }
WalMartAfilAPI类代码
namespace DealsBot.wallmartAfill { public class WalMartAfilAPI { public byte[] Canonicalize(string version, string consumerId, string timestamp) { ASCIIEncoding ByteConverter = new ASCIIEncoding(); 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 { RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider(); RSAalg.ImportParameters(Key); 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 { RSACryptoServiceProvider RSAalg = new RSACryptoServiceProvider(); RSAalg.ImportParameters(Key); return RSAalg.VerifyData(DataToVerify, SHA256.Create(), SignedData); } catch (CryptographicException e) { Console.WriteLine(e.Message); return false; } } } }
问题排查与修复方案
1. 规范化(Canonicalize)逻辑错误
Walmart签名规范要求同时包含排序后的键和值拼接内容,当前代码仅使用了值部分,忽略了键部分,导致签名原始数据不匹配。
修改Canonicalize方法:
public byte[] Canonicalize(string version, string consumerId, string timestamp) { ASCIIEncoding byteConverter = new ASCIIEncoding(); SortedDictionary<string, string> dictionary = new SortedDictionary<string, string>() { { "WM_CONSUMER.ID", consumerId }, { "WM_CONSUMER.INTIMESTAMP", timestamp }, { "WM_SEC.KEY_VERSION", version } }; StringBuilder canonicalBuilder = new StringBuilder(); // 先拼接排序后的键,用分号分隔 foreach (var key in dictionary.Keys) { canonicalBuilder.Append($"{key.Trim()};"); } canonicalBuilder.AppendLine(); // 再拼接对应的值,每个值单独一行 foreach (var key in dictionary.Keys) { canonicalBuilder.AppendLine(dictionary[key].Trim()); } // 移除末尾多余换行,确保格式严格匹配 string canonicalString = canonicalBuilder.ToString().TrimEnd(); return byteConverter.GetBytes(canonicalString); }
2. RSA签名算法未明确指定
Walmart要求使用SHA256withRSA(PKCS#1 v1.5)签名,当前代码未明确指定签名填充方式,可能导致签名格式不兼容。
修改签名和验证方法:
public byte[] HashAndSignBytes(byte[] DataToSign, RSAParameters Key) { try { using (RSA rsa = RSA.Create()) { rsa.ImportParameters(Key); return rsa.SignData(DataToSign, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); } } catch (CryptographicException e) { Console.WriteLine(e.Message); return null; } } public bool VerifySignedHash(byte[] DataToVerify, byte[] SignedData, RSAParameters Key) { try { using (RSA rsa = RSA.Create()) { rsa.ImportParameters(Key); return rsa.VerifyData(DataToVerify, SignedData, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); } } catch (CryptographicException e) { Console.WriteLine(e.Message); return false; } }
3. 私钥处理冗余优化
移除冗余的RSACryptoServiceProvider实例,简化私钥导入逻辑:
var rsaPem = File.ReadAllText("D:\\Users\\Adam\\source\\repos\\DealsBot\\DealsBot\\DealsBot\\wallmartAfill\\WM_IO_private_key.pem"); using (var rsa = RSA.Create()) { rsa.ImportFromPem(rsaPem); RSAParameters Key = rsa.ExportParameters(true); // 后续签名逻辑放在using块内,避免资源泄漏 }
4. 时间戳一致性检查
确保生成签名的时间戳与请求API时携带的时间戳完全一致,且时间戳在Walmart服务器允许的5分钟误差范围内,建议签名生成后立即发起请求。
内容的提问来源于stack exchange,提问作者Geoffery Blackard
相关产品推荐
相关产品推荐

