Delphi使用IPWorks调用Amazon MWS接口签名不匹配问题求助
Delphi调用Amazon MWS API SignatureDoesNotMatch错误修复方案
使用环境
- Delphi 10.2.3 Tokyo
- IPWorks SSL、IPWorks Encrypt组件
核心错误点
1. 参数拼接错误
你使用&作为参数分隔符,这是HTML转义后的符号,实际URL/表单参数应该直接用&,导致参数解析错误,签名计算的输入串和服务端不一致。
2. 参数未按要求排序
AWS签名规则要求所有请求参数必须按照键的ASCII码升序排序后再拼接,你当前的参数顺序是自定义的,不符合规则。
3. 换行符转义错误
Delphi中字符串内的\n不会被解析为实际换行符,需要用#10表示LF换行,导致签名输入串的结构和服务端要求不符。
4. 重复Base64编码
你已经将TipcHash组件的EncodeHash属性设为True,调用ComputeHash后得到的HashValue已经是Base64编码后的结果,后续额外做了一次Base64编码属于重复操作,直接导致签名值错误。
5. 请求参数放置位置错误
POST请求的表单参数应该放在请求体中,不应该拼接在URL的query部分,不符合MWS API的请求规范。
6. URL编码规则不匹配
Delphi自带的TNetEncoding.URL.Encode默认会将空格转为+,而AWS要求空格必须转为%20,且需要符合RFC 3986编码规则,编码不一致也会导致签名错误。
修复后代码示例
object HTTPS: TipwHTTP FollowRedirects = frAlways SSLCertStore = 'MY' OnTransfer = HTTPSTransfer end object HashMaker: TipcHash Algorithm = haHMACSHA256 EncodeHash = True end // 符合AWS要求的URL编码方法,空格转%20,遵循RFC3986 function AwsUrlEncode(const S: string): string; var I: Integer; C: Char; begin Result := ''; for I := 1 to Length(S) do begin C := S[I]; if C in ['A'..'Z', 'a'..'z', '0'..'9', '-', '_', '~', '.'] then Result := Result + C else if C = ' ' then Result := Result + '%20' else Result := Result + '%' + IntToHex(Ord(C), 2); end; end; function GetISO8601DateTimeStr(const ADateTime: TDateTime): String; var d: String; l: Integer; begin d := DateToISO8601(TTimeZone.Local.ToUniversalTime(ADateTime), True); l := Length(d); d := Copy(d, 1, l - 5) + 'Z'; // 移除毫秒部分 Result := d; end; const DOMAIN_NAME = 'https://mws.amazonservices.com/Orders/2013-09-01'; var sl: TStringList; param, signature, signInput: String; I: Integer; begin sl := TStringList.Create; try // 先添加未编码的参数,键值分开,方便后续排序和编码 sl.Values['AWSAccessKeyId'] := edtAwsAccessKey.Text; sl.Values['Action'] := 'ListOrders'; sl.Values['CreatedAfter'] := GetISO8601DateTimeStr(dtpOrdersCreatedFrom.Date); sl.Values['MWSAuthToken'] := edtMWSAuthToken.Text; // 注意这里原代码写错了,把MWSAuthToken的值填成了MarketplaceID sl.Values['MarketplaceId.Id.1'] := edtMarketplaceID.Text; sl.Values['SellerId'] := edtSellerID.Text; sl.Values['SignatureMethod'] := 'HmacSHA256'; sl.Values['SignatureVersion'] := '2'; sl.Values['Timestamp'] := GetISO8601DateTimeStr(Now); sl.Values['Version'] := '2013-09-01'; // 参数按ASCII升序排序 sl.Sorted := True; // 编码后拼接参数串 param := ''; for I := 0 to sl.Count - 1 do begin if I > 0 then param := param + '&'; param := param + AwsUrlEncode(sl.Names[I]) + '=' + AwsUrlEncode(sl.ValueFromIndex[I]); end; // 构造签名输入串,用#10表示换行 signInput := 'POST'#10 + 'mws.amazonservices.com'#10 + '/Orders/2013-09-01'#10 + param; // 计算签名,不要重复编码 HashMaker.Key := edtSecretKey.Text; HashMaker.Algorithm := haHMACSHA256; HashMaker.InputMessage := signInput; HashMaker.ComputeHash; signature := HashMaker.HashValue; // 签名只需要做一次URL编码 signature := AwsUrlEncode(signature); // 把签名加到参数里 param := param + '&Signature=' + signature; SetStatus('最终参数: ' + param); finally sl.Free; end; // 发送POST请求,参数放在请求体 with HTTPS do begin ContentType := 'application/x-www-form-urlencoded; charset=UTF-8'; Accept := 'text/xml'; Timeout := 30000; PostData := param; Post(DOMAIN_NAME); end; end;
额外注意点
- 请确认AWS Secret Access Key没有前后空格,复制时不要带多余字符。
- 本地时间和服务器时间差不能超过15分钟,否则会直接触发签名无效错误。
内容的提问来源于stack exchange,提问作者Steve F
相关产品推荐
相关产品推荐

