Я закончил тем, что обновил код для использования WCF, так как это то, что есть в текущей версии разработчика, над которой я работал. Затем я использовал некоторый код, который был размещен на форумах Amazon, но немного облегчил его использование.
ОБНОВЛЕНИЕ: новый, более простой в использовании код, который позволяет вам по-прежнему использовать настройки конфигурации для всего
В предыдущем коде, который я опубликовал, и то, что я видел в другом месте, когда создается сервисный объект, одно из переопределений конструктора используется, чтобы указать ему использовать HTTPS, дать ему URL-адрес HTTPS и вручную присоединить инспектор сообщений это будет делать подписание. Недостатком неиспользования конструктора по умолчанию является то, что вы теряете возможность конфигурировать сервис через файл конфигурации.
С тех пор я переделал этот код, чтобы вы могли продолжить использовать конструктор по умолчанию, без параметров и настроить сервис через файл конфигурации. Преимущество этого заключается в том, что вам не нужно перекомпилировать свой код, чтобы использовать его, или вносить изменения после развертывания, например, в maxStringContentLength (что и послужило причиной этого изменения, а также обнаружить недостатки при выполнении всего этого в коде) , Я также немного обновил часть подписи, чтобы вы могли сказать, какой алгоритм хеширования использовать, а также регулярное выражение для извлечения Action.
Эти два изменения связаны с тем, что не все веб-сервисы от Amazon используют один и тот же алгоритм хеширования, и действие может потребоваться извлечь по-разному. Это означает, что вы можете повторно использовать один и тот же код для каждого типа службы, просто изменив содержимое файла конфигурации.
public class SigningExtension : BehaviorExtensionElement
{
public override Type BehaviorType
{
get { return typeof(SigningBehavior); }
}
[ConfigurationProperty("actionPattern", IsRequired = true)]
public string ActionPattern
{
get { return this["actionPattern"] as string; }
set { this["actionPattern"] = value; }
}
[ConfigurationProperty("algorithm", IsRequired = true)]
public string Algorithm
{
get { return this["algorithm"] as string; }
set { this["algorithm"] = value; }
}
[ConfigurationProperty("algorithmKey", IsRequired = true)]
public string AlgorithmKey
{
get { return this["algorithmKey"] as string; }
set { this["algorithmKey"] = value; }
}
protected override object CreateBehavior()
{
var hmac = HMAC.Create(Algorithm);
if (hmac == null)
{
throw new ArgumentException(string.Format("Algorithm of type ({0}) is not supported.", Algorithm));
}
if (string.IsNullOrEmpty(AlgorithmKey))
{
throw new ArgumentException("AlgorithmKey cannot be null or empty.");
}
hmac.Key = Encoding.UTF8.GetBytes(AlgorithmKey);
return new SigningBehavior(hmac, ActionPattern);
}
}
public class SigningBehavior : IEndpointBehavior
{
private HMAC algorithm;
private string actionPattern;
public SigningBehavior(HMAC algorithm, string actionPattern)
{
this.algorithm = algorithm;
this.actionPattern = actionPattern;
}
public void Validate(ServiceEndpoint endpoint)
{
}
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
}
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
}
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.MessageInspectors.Add(new SigningMessageInspector(algorithm, actionPattern));
}
}
public class SigningMessageInspector : IClientMessageInspector
{
private readonly HMAC Signer;
private readonly Regex ActionRegex;
public SigningMessageInspector(HMAC algorithm, string actionPattern)
{
Signer = algorithm;
ActionRegex = new Regex(actionPattern);
}
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
var operation = GetOperation(request.Headers.Action);
var timeStamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ");
var toSignBytes = Encoding.UTF8.GetBytes(operation + timeStamp);
var sigBytes = Signer.ComputeHash(toSignBytes);
var signature = Convert.ToBase64String(sigBytes);
request.Headers.Add(MessageHeader.CreateHeader("AWSAccessKeyId", Helpers.NameSpace, Helpers.AWSAccessKeyId));
request.Headers.Add(MessageHeader.CreateHeader("Timestamp", Helpers.NameSpace, timeStamp));
request.Headers.Add(MessageHeader.CreateHeader("Signature", Helpers.NameSpace, signature));
return null;
}
private string GetOperation(string request)
{
var match = ActionRegex.Match(request);
var val = match.Groups["action"];
return val.Value;
}
}
Чтобы использовать это, вам не нужно вносить какие-либо изменения в существующий код, вы даже можете поместить код подписи во всю другую сборку, если это будет необходимо. Вам просто нужно настроить раздел конфигурации следующим образом (примечание: номер версии важен, без совпадения код не будет загружаться или запускаться)
<system.serviceModel>
<extensions>
<behaviorExtensions>
<add name="signer" type="WebServices.Amazon.SigningExtension, AmazonExtensions, Version=1.3.11.7, Culture=neutral, PublicKeyToken=null" />
</behaviorExtensions>
</extensions>
<behaviors>
<endpointBehaviors>
<behavior name="AWSECommerceBehaviors">
<signer algorithm="HMACSHA256" algorithmKey="..." actionPattern="\w:\/\/.+/(?<action>.+)" />
</behavior>
</endpointBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="AWSECommerceServiceBinding" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536">
<readerQuotas maxDepth="32" maxStringContentLength="16384" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<security mode="Transport">
<transport clientCredentialType="None" proxyCredentialType="None" realm="" />
<message clientCredentialType="UserName" algorithmSuite="Default" />
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="https://ecs.amazonaws.com/onca/soap?Service=AWSECommerceService" behaviorConfiguration="AWSECommerceBehaviors" binding="basicHttpBinding" bindingConfiguration="AWSECommerceServiceBinding" contract="WebServices.Amazon.AWSECommerceServicePortType" name="AWSECommerceServicePort" />
</client>
</system.serviceModel>