亚马逊产品广告 API(前身为 Amazon Associates Web Service 或 Amazon AWS)实施了一项新规则,即在 2009 年 8 月 15 日之前,所有向其发送的 Web 服务请求都必须签名。他们在他们的网站上提供了示例代码,展示了如何使用 REST 和 SOAP 在 C# 中执行此操作。我正在使用的实现是 SOAP。你可以在这里找到示例代码,我不包括它,因为有相当数量。
我遇到的问题是他们的示例代码使用 WSE 3,而我们当前的代码不使用 WSE。有谁知道如何仅使用 WSDL 中自动生成的代码来实现此更新?如果我不需要的话,我现在不想切换到 WSE 3 的东西,因为这个更新更像是一个快速的补丁来阻止我们,直到我们可以在当前的开发版本中完全实现它(八月第三,他们开始在实时环境中丢弃五分之一的请求,如果他们没有签名,这对我们的应用程序来说是个坏消息)。
下面是对 SOAP 请求进行实际签名的主要部分的片段。
class ClientOutputFilter : SoapFilter
{
// to store the AWS Access Key ID and corresponding Secret Key.
String akid;
String secret;
// Constructor
public ClientOutputFilter(String awsAccessKeyId, String awsSecretKey)
{
this.akid = awsAccessKeyId;
this.secret = awsSecretKey;
}
// Here's the core logic:
// 1. Concatenate operation name and timestamp to get StringToSign.
// 2. Compute HMAC on StringToSign with Secret Key to get Signature.
// 3. Add AWSAccessKeyId, Timestamp and Signature elements to the header.
public override SoapFilterResult ProcessMessage(SoapEnvelope envelope)
{
var body = envelope.Body;
var firstNode = body.ChildNodes.Item(0);
String operation = firstNode.Name;
DateTime currentTime = DateTime.UtcNow;
String timestamp = currentTime.ToString("yyyy-MM-ddTHH:mm:ssZ");
String toSign = operation + timestamp;
byte[] toSignBytes = Encoding.UTF8.GetBytes(toSign);
byte[] secretBytes = Encoding.UTF8.GetBytes(secret);
HMAC signer = new HMACSHA256(secretBytes); // important! has to be HMAC-SHA-256, SHA-1 will not work.
byte[] sigBytes = signer.ComputeHash(toSignBytes);
String signature = Convert.ToBase64String(sigBytes); // important! has to be Base64 encoded
var header = envelope.Header;
XmlDocument doc = header.OwnerDocument;
// create the elements - Namespace and Prefix are critical!
XmlElement akidElement = doc.CreateElement(
AmazonHmacAssertion.AWS_PFX,
"AWSAccessKeyId",
AmazonHmacAssertion.AWS_NS);
akidElement.AppendChild(doc.CreateTextNode(akid));
XmlElement tsElement = doc.CreateElement(
AmazonHmacAssertion.AWS_PFX,
"Timestamp",
AmazonHmacAssertion.AWS_NS);
tsElement.AppendChild(doc.CreateTextNode(timestamp));
XmlElement sigElement = doc.CreateElement(
AmazonHmacAssertion.AWS_PFX,
"Signature",
AmazonHmacAssertion.AWS_NS);
sigElement.AppendChild(doc.CreateTextNode(signature));
header.AppendChild(akidElement);
header.AppendChild(tsElement);
header.AppendChild(sigElement);
// we're done
return SoapFilterResult.Continue;
}
}
在进行实际的 Web 服务调用时会这样调用
// create an instance of the serivce
var api = new AWSECommerceService();
// apply the security policy, which will add the require security elements to the
// outgoing SOAP header
var amazonHmacAssertion = new AmazonHmacAssertion(MY_AWS_ID, MY_AWS_SECRET);
api.SetPolicy(amazonHmacAssertion.Policy());