使用证书签署 WCF 消息

Sign WCF message with certificate

我正在尝试调用第三方服务。我需要用证书签署我的消息。

我使用 SOAPUI 检查了连接,它工作正常。我现在正尝试从 WCF 客户端进行调用,但无法使其正常工作。 我对此不熟悉,可能错过了一个明显的步骤。

svcutil 生成的配置如下所示:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="ServiceBinding">
          <security mode="Transport" />
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="https://url.com" binding="basicHttpBinding"
          bindingConfiguration="ServiceBinding" contract="ServiceContract"
          name="Name" />
    </client>
  </system.serviceModel>
</configuration>

我添加了一个 endpointBehaviour 来从商店获取证书。我还必须更改绑定配置,因为签名没有添加到消息中;我将安全模式更改为 TransportWithMessageCredentials 并将消息的客户端凭据类型设置为 Certificate。 修改后的配置如下(我去掉了不相关的部分):

<?xml version="1.0"?>
<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="ServiceBinding">
          <security mode="TransportWithMessageCredential">
            <message clientCredentialType="Certificate"/>
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="https://url.com" behaviorConfiguration="Behavior"
        binding="basicHttpBinding" bindingConfiguration="ServiceBinding"
        contract="ServiceContract" name="Name" />
    </client>
    <behaviors>
      <endpointBehaviors>
        <behavior name="Behavior">
          <clientCredentials>
            <clientCertificate storeName="My" x509FindType="FindByThumbprint" findValue="..."/>
          </clientCredentials>
        </behavior>
      </endpointBehaviors>
    </behaviors>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
</configuration>

当我尝试拨打电话时,出现以下错误:

Security processor was unable to find a security header in the message. This might be because the message is an unsecured fault or because there is a binding mismatch between the communicating parties. This can occur if the service is configured for security and the client is not using security.

我找到了一些关于此错误的帖子,但其中 none 可以帮助我。 我添加了 ProtectionLevel=System.Net.Security.ProtectionLevel.Signthis post.

中所示的 ServiceContractAttribute

在此先感谢您提供的任何帮助。

编辑:

检查跟踪日志后,我看到消息已发送并且我收到了有意义的响应。但是,响应中没有 header (我认为这会导致异常)。有没有一种方法可以将我的客户端配置为 检查响应中的 header?

请求跟踪:

<?xml version="1.0"?>
<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
  <System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
    <EventID>0</EventID>
    <Type>3</Type>
    <SubType Name="Information">0</SubType>
    <Level>8</Level>
    <TimeCreated SystemTime="2015-04-21T09:41:19.9227910Z"/>
    <Source Name="System.ServiceModel.MessageLogging"/>
    <Correlation ActivityID="{ba5c25df-1f0a-4375-8b15-e2160e21bc14}"/>
    <Execution ProcessName="w3wp" ProcessID="8540" ThreadID="16"/>
    <Channel/>
    <Computer>PC673</Computer>
  </System>
  <ApplicationData>
    <TraceData>
      <DataItem>
        <MessageLogTraceRecord xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace" Time="2015-04-21T11:41:19.9217909+02:00" Source="TransportSend" Type="System.ServiceModel.Security.SecurityAppliedMessage">
          <Addressing>
            <Action/>
            <To>https://url.com</To>
          </Addressing>
          <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
            <s:Header>
              <o:Security xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" s:mustUnderstand="1">
                <u:Timestamp u:Id="_0">
                  <u:Created>2015-04-21T09:41:08.287Z</u:Created>
                  <u:Expires>2015-04-21T09:46:08.287Z</u:Expires>
                </u:Timestamp>
                <o:BinarySecurityToken>
                  <!-- Removed-->
                </o:BinarySecurityToken>
                <Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
                  <SignedInfo>
                    <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                    <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
                    <Reference URI="#_0">
                      <Transforms>
                        <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
                      </Transforms>
                      <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
                      <DigestValue>zvttNRawVVcleH5WAYMCi7oBzpM=</DigestValue>
                    </Reference>
                  </SignedInfo>
                  <SignatureValue>iEMTR/xKruW1TeqJiPdXjNKG3D4ZWyfFM3T+iPy8NSCpwQIpwXmsmrOjT2N5QJoG7S+wyIm2Vsa4rmlmyYBM18rNMeN+luHuUYvNh9ammWyYgam5/mpGBmR8KJiyPSiCxCPeUWuL8z5ag2wGtTrTH/JqOsHfdobnhznvQJgPAc8YWAk6On7SkHT+nKikGv1rEfbCtOBeggbBLLzSArVOBDDOZhWRpRJLhvU6XhlYj1IbgMy4tFP8f0+SESU/UQM+0bBnt0IDwwtTQIyeheifVJINeUHe+T1Pr8qYtyo9/sLulr1vkFe0DAokv1R1WRCU5IoE38I/ptILOMvIq1s3OQ==</SignatureValue>
                  <KeyInfo>
                    <o:SecurityTokenReference>
                      <o:Reference ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" URI="#uuid-bda377d4-237a-46b7-a7c3-b7d47578c278-1"/>
                    </o:SecurityTokenReference>
                  </KeyInfo>
                </Signature>
              </o:Security>
            </s:Header>
            <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
              <!-- Valid request -->
            </s:Body>
          </s:Envelope>
        </MessageLogTraceRecord>
      </DataItem>
    </TraceData>
  </ApplicationData>
</E2ETraceEvent>

响应轨迹:

<?xml version="1.0"?>
<E2ETraceEvent xmlns="http://schemas.microsoft.com/2004/06/E2ETraceEvent">
  <System xmlns="http://schemas.microsoft.com/2004/06/windows/eventlog/system">
    <EventID>0</EventID>
    <Type>3</Type>
    <SubType Name="Information">0</SubType>
    <Level>8</Level>
    <TimeCreated SystemTime="2015-04-21T09:41:22.4049329Z"/>
    <Source Name="System.ServiceModel.MessageLogging"/>
    <Correlation ActivityID="{ba5c25df-1f0a-4375-8b15-e2160e21bc14}"/>
    <Execution ProcessName="w3wp" ProcessID="8540" ThreadID="16"/>
    <Channel/>
    <Computer>PC673</Computer>
  </System>
  <ApplicationData>
    <TraceData>
      <DataItem>
        <MessageLogTraceRecord xmlns="http://schemas.microsoft.com/2004/06/ServiceModel/Management/MessageTrace" Time="2015-04-21T11:41:22.4049329+02:00" Source="TransportReceive" Type="System.ServiceModel.Channels.BufferedMessage">
          <HttpResponse>
            <StatusCode>OK</StatusCode>
            <StatusDescription>OK</StatusDescription>
            <WebHeaders>
              <X-Backside-Transport>OK OK</X-Backside-Transport>
              <Connection>Keep-Alive</Connection>
              <Transfer-Encoding>chunked</Transfer-Encoding>
              <X-Client-IP>194.78.45.187</X-Client-IP>
              <Content-Type>text/xml</Content-Type>
              <Date>Tue, 21 Apr 2015 09:41:11 GMT</Date>
              <Server>Apache-Coyote/1.1</Server>
            </WebHeaders>
          </HttpResponse>
          <Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
            <s:Header xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"/>
            <Body>
              <!-- Meaningful response - the same I get when using SOAPUI -->
            </Body>
          </Envelope>
        </MessageLogTraceRecord>
      </DataItem>
    </TraceData>
  </ApplicationData>
</E2ETraceEvent>

wcf 中的绑定在客户端和服务器端必须相同。如果将安全模式设置为传输,则消息本身不会被加密。它只是一个安全隧道。 所以设置securitymode为message,protectionlevel为encrypt and sign.

下面是对加密选项的一个很好的概述: https://msdn.microsoft.com/en-us/library/ff650862.aspx

我通过创建自定义绑定解决了这个问题。

<binding name="ServiceBinding">
  <security authenticationMode="CertificateOverTransport"
    messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"
    requireDerivedKeys="false"
    securityHeaderLayout="Lax"
    enableUnsecuredResponse="true" />
  <textMessageEncoding messageVersion="Soap11" />
  <httpsTransport />
</binding>

这里的关键是enableUnsecuredResponse="true"。这样,我可以在不崩溃的情况下接收未签名的响应。