Как подписать запрос веб-службы Amazon в .NET с SOAP и без WSE
API рекламы продуктов Amazon (ранее Amazon Associates Web Service или Amazon AWS) внедрил новое правило, согласно которому к 15 августа 2009 года все запросы веб-служб к ним должны быть подписаны. Они предоставили пример кода на своем сайте, показывающий, как сделать это в C #, используя REST и SOAP. Реализация, которую я использую, - SOAP. Вы можете найти образец кодаВотЯ не включаю его, потому что есть изрядное количество.
Проблема, с которой я сталкиваюсь, состоит в том, что их пример кода использует WSE 3, а наш текущий код не использует WSE. Кто-нибудь знает, как реализовать это обновление, просто используя автоматически сгенерированный код из WSDL? Мне бы не хотелось переключаться на WSE 3 прямо сейчас, если бы я не нуждался в этом, так как это обновление - скорее быстрый патч, который держит нас, пока мы не сможем полностью реализовать это в текущем версия для разработчиков (3 августа они начинают сбрасывать 1 из 5 запросов в реальной среде, если они не подписаны, что является плохой новостью для нашего приложения).
Вот фрагмент основной части, которая выполняет фактическое подписание запроса 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;
}
}
И это вызывается вот так при совершении реального вызова веб-службы
// 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());