Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
730 views
in Technique[技术] by (71.8m points)

soap - WS-Security from soapUI to WCF - binding & configuration

I need to create a WCF client to call a service that I have no control over and we have been given a wsdl only (with schemas). The web service uses the X.509 certificate with the WS-Security specification version 1.0

The web service provider has shared raw xml of the soap message to highlight the ws-security header. Using the soapUI, I have been able to create the exact same wsse-Security header as shown below:

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

In soapUI, under the "WS-Security Configurations" I added the Keystores (jks with my private cert) and Truststores (jks with CA root public key). Finally I added "Outgoing WS-Security Configurations" with the following setting.

With this WS-Security setting, the soap message adds the wsse:Security as shown above.

enter image description here

Now, we are building the WS client on the .NET with WCF, and need help with the Binding and Security settings. What binding and configuration settings can be used to have the same WS-Security header as required or shown above?

From the WSDL, I created a client-side proxy using WSCF.blue. Although, I could have also used svcutil.exe or using Add Service Reference from VS.

I have tried creating the customBinding in code as following, but when inspecting this message the wsse header is not the same as what I want.

For example, it adds a BinarySecurityToken in the Security header which is not desired.

Or the KeyIdentifier is X509SubjectKeyIdentifier instead of X509v3 for example: <o:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier" ...</o:KeyIdentifier>

It also adds additional multiple Reference under the SignedInfo, and even though it passes schema validation, I am not sure what the additional Reference is doing.

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;
}

Additionally, I am using this behaviour for the x509 cert

<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>
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

If you want to send the exact same message than you should probably use the SignedXml class to generate the signature outside of WCF. With WCF you will not be able to get the exact canonicalization algorithn / InclusiveNamespace.

Having said that most of the time producing a similar message is fine with the server. You have two possible approaches. One is to create a customMessageEncoder which will change the outgoing WCF message before it hits the network. So you can tell WCF to create a minimalist message (e.g. no timestamp) and in the encoder add the timestamp yourself (if wcf would create it then it would also sign it) and also remove/change the x.509 token references. You should be careful not to change any signed xml nodes when doing do (body, signedInfo) including whitespaces (so use preserveWhitepsaces if loading to XmlDocument).

Another approach is to try and configure WCF to send the same message:

  • to exclude the extra token reference create the x.509 params 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;
    
  • timestamp will be signed by wcf, but possibly the server does not care, or does not require a timestamp, so try requireTimestamp=true/false and see what works. You might be able to programatically disable signing for timestamps by using MessagePartSpecification.

The important in either approach is to understand the server might be more flexible than you think and to understand from its error messages what are the real show stoppers.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...