Jak podpisać żądanie usługi Amazon w .NET z SOAP i bez GPW

Amazon Product Advertising API (dawniej Amazon Associates Web Service lub Amazon AWS) wprowadził nową regułę, która obowiązuje do 15 sierpnia 2009 r. Wszystkie żądania usług internetowych do nich muszą być podpisane. Dostarczyli przykładowy kod na swojej stronie, pokazujący, jak to zrobić w C #, używając zarówno REST, jak i SOAP. Implementacja, której używam, to SOAP. Możesz znaleźć przykładowy kodtutaj, Nie włączam go, ponieważ jest spora kwota.

Problem, jaki mam, to przykładowy kod wykorzystujący WSE 3, a nasz obecny kod nie korzysta z GPW. Czy ktoś wie, jak zaimplementować tę aktualizację, używając tylko wygenerowanego automatycznie kodu z WSDL? Chciałbym teraz nie przestawiać się na rzeczy z WSE 3, jeśli nie muszę, ponieważ ta aktualizacja jest bardziej szybką poprawką, aby nas powstrzymać, dopóki nie będziemy mogli w pełni wdrożyć tego w obecnej wersji dev (sierpień 3. zaczynają odrzucać 1 na 5 żądań w środowisku na żywo, jeśli nie są podpisane, co jest złą wiadomością dla naszej aplikacji).

Oto fragment głównej części, która faktycznie podpisuje żądanie 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;
    }
}

I to jest wywoływane w ten sposób podczas wykonywania rzeczywistego wywołania usługi internetowej

// 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());

questionAnswers(4)

yourAnswerToTheQuestion