从soapUI到WCF的WS-Security - 绑定&配置

时间:2015-09-21 20:04:05

标签: wcf soap soapui ws-security

我需要创建一个WCF客户端来调用我无法控制的服务,并且我们只获得了一个wsdl(带有模式)。 Web服务使用X.509证书和WS-Security规范版本1.0

Web服务提供者已共享soap消息的原始xml以突出显示ws-security标头。使用soapUI,我能够创建完全相同的wsse-Security标头,如下所示:

<soapenv:Envelope xmlns:oas="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:oas1="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xd="http://www.w3.org/2000/09/xmldsig#">
  <soapenv:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
      <ds:Signature Id="SIG-32" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:SignedInfo>
          <ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"/>
          <ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
          <ds:Reference URI="#id-31">
            <ds:Transforms>
              <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#">
                <InclusiveNamespaces PrefixList="oas oas1 urn urn1 urn2 urn3 xd" xmlns="http://www.w3.org/2001/10/xml-exc-c14n#"/>
              </ds:Transform>
            </ds:Transforms>
            <ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
            <ds:DigestValue>ucSFZEOTHpe/IOlPVWtU+1xT4sM=</ds:DigestValue>
          </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue>
          d4CKqie==
        </ds:SignatureValue>
        <ds:KeyInfo Id="KI-9A8D1F611E86CFB79E144316684667546">
          <wsse:SecurityTokenReference oas:Id="STR-9A8D1F611E86CFB79E144316684667547">
            <wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary"
                                ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">5lAB5TaqeFwo23mRVm31LngBT1dQMf94mxeVkyKog==
            </wsse:KeyIdentifier>
          </wsse:SecurityTokenReference>
        </ds:KeyInfo>
      </ds:Signature>

      <oas:Timestamp oas:Id="TS-30">
        <oas:Created>2015-09-25T07:40:46.670Z</oas:Created>
        <oas:Expires>2015-09-26T11:27:26.670Z</oas:Expires>
      </oas:Timestamp>
    </wsse:Security>
  </soapenv:Header>

  <soapenv:Body oas:Id="id-31">
    ...
  </soapenv:Body>
</soapenv:Envelope>

在soapUI中,在&#34; WS-Security配置&#34;我添加了Keystores(带有我的私有证书的jks)和Truststores(带有CA root公钥的jks)。最后,我添加了#34;传出WS-Security配置&#34;使用以下设置。

使用此WS-Security设置,soap消息会添加wsse:Security,如上所示。

enter image description here

现在,我们正在使用WCF在.NET上构建WS客户端,并需要有关Binding和Security设置的帮助。可以使用哪些绑定和配置设置来获得上面所示或显示的相同WS-Security标头?

在WSDL中,我使用WSCF.blue创建了一个客户端代理。虽然,我也可以使用svcutil.exe或使用VS的添加服务引用。

我尝试在代码中创建customBinding,如下所示,但在检查此消息时,wsse标题与我想要的不同。

例如,它会在BinarySecurityToken标题中添加Security,这是不可取的。

KeyIdentifierX509SubjectKeyIdentifier而不是X509v3,例如: <o:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier" ...</o:KeyIdentifier>

它还在Reference下添加了多个SignedInfo,即使它通过了架构验证,我也不确定其他Reference正在做什么。

internal static Binding GetCustomBinding()
{
    CustomBinding myBinding = new CustomBinding();

    AsymmetricSecurityBindingElement asBindingElement = new AsymmetricSecurityBindingElement();

    //Have tried these also
    //asBindingElement.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12;
    //asBindingElement.MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;
    //WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10

    asBindingElement.MessageSecurityVersion = MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10;
    asBindingElement.InitiatorTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters { InclusionMode = SecurityTokenInclusionMode.Never };

    asBindingElement.RecipientTokenParameters = new System.ServiceModel.Security.Tokens.X509SecurityTokenParameters 
    { 
        //X509ReferenceStyle = X509KeyIdentifierClauseType.SubjectKeyIdentifier,
        InclusionMode = SecurityTokenInclusionMode.Never
    };


    asBindingElement.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.SignBeforeEncrypt;
    //asBindingElement.SecurityHeaderLayout = SecurityHeaderLayout.Strict;
    asBindingElement.SecurityHeaderLayout = SecurityHeaderLayout.LaxTimestampLast;
    asBindingElement.EnableUnsecuredResponse = true;
    asBindingElement.IncludeTimestamp = true;
    asBindingElement.SetKeyDerivation(false);
    asBindingElement.DefaultAlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Basic128Rsa15;

    asBindingElement.EndpointSupportingTokenParameters.Signed.Add(new X509SecurityTokenParameters());

    myBinding.Elements.Add(asBindingElement);

    myBinding.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8));
    //myBinding.Elements.Add(new MtomMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8));

    HttpsTransportBindingElement httpsBindingElement = new HttpsTransportBindingElement();
    httpsBindingElement.RequireClientCertificate = true;
    myBinding.Elements.Add(httpsBindingElement);

    return myBinding;
}

此外,我将此行为用于x509证书

<behaviors>
  <endpointBehaviors>
    <behavior name="MyBehavior">
      <clientCredentials>
        <clientCertificate findValue="xxx"
          storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint" />
        <serviceCertificate>
          <defaultCertificate findValue="xxx"
            storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint" />
          <authentication certificateValidationMode="None" revocationMode="NoCheck"
            trustedStoreLocation="LocalMachine" />
        </serviceCertificate>
      </clientCredentials>
    </behavior>
  </endpointBehaviors>
</behaviors>

1 个答案:

答案 0 :(得分:3)

如果要发送完全相同的消息,则应该使用SignedXml类在WCF之外生成签名。使用WCF,您将无法获得精确的规范化算法/ InclusiveNamespace。

已经说过,大多数时候产生类似的消息对服务器来说都很好。你有两种可能的方法。一个是create a customMessageEncoder,它将在它到达网络之前更改传出的WCF消息。所以你可以告诉WCF创建一个极简主义的消息(例如没有时间戳),并在编码器中自己添加时间戳(如果wcf会创建它,那么它也会签名)并且还删除/更改x.509令牌引用。在执行do(body,signedInfo)(包括空格)时应该注意不要更改任何已签名的xml节点(因此如果加载到XmlDocument则使用preserveWhitepsaces)。

另一种方法是尝试配置WCF以发送相同的消息:

  • 要排除额外的令牌引用,请创建x.509参数like this

            X509SecurityTokenParameters x509Params = new X509SecurityTokenParameters();
            x509Params.X509ReferenceStyle =
                X509KeyIdentifierClauseType.SubjectKeyIdentifier;
            x509Params.RequireDerivedKeys = false;
            ;
            x509Params.InclusionMode = SecurityTokenInclusionMode.Never;
            x509Params.ReferenceStyle = SecurityTokenReferenceStyle.Internal;
            x509Params.X509ReferenceStyle = X509KeyIdentifierClauseType.Any;
            ((AsymmetricSecurityBindingElement) sec).InitiatorTokenParameters = x509Params;
    
  • 时间戳将由wcf签名,但服务器可能不在乎,或者不需要时间戳,因此请尝试requireTimestamp=true/false并查看哪些有效。您可以使用MessagePartSpecification以编程方式停用signing for timestamps

这两种方法中的重要一点是要了解服务器可能比您想象的更灵活,并从错误消息中了解什么是真正的显示停止。