Como assinar uma solicitação de serviço da Web da Amazon no .NET com SOAP e sem o WSE

A API de publicidade de produtos da Amazon (anteriormente Amazon Associates Web Service ou Amazon AWS) implementou uma nova regra que, até 15 de agosto de 2009, todas as solicitações de serviços da Web a elas devem ser assinadas. Eles forneceram código de amostra em seu site mostrando como fazer isso em C # usando REST e SOAP. A implementação que estou usando é SOAP. Você pode encontrar o código de amostraAqui, Não estou incluindo porque há uma quantia justa.

O problema que estou tendo é o código de exemplo deles usa o WSE 3 e nosso código atual não usa o WSE. Alguém sabe como implementar essa atualização apenas usando o código gerado automaticamente do WSDL? Eu gostaria de não ter que mudar para o material do WSE 3 agora se eu não tiver que fazer, já que essa atualização é mais um patch rápido para nos segurar até que possamos implementá-la completamente na versão atual do 3 º eles estão começando a cair 1 em 5 pedidos, no ambiente ao vivo, se eles não estão assinados, o que é uma má notícia para a nossa aplicação).

Veja um snippet da parte principal que faz a assinatura real da solicitação 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;
    }
}

E isso é chamado assim ao fazer a chamada de serviço da web real

// 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