wcfdigital-signaturebea

How to make WCF client sign SecurityTokenReference:Reference


I need to create a WCF client who calls a bea webservice.

I keep getting this response from the webservice:

Could not validate signature using any of the supported token types

So I turn my attention to the signature part of the client<->service communication:

Part of the wsdl from the webservice:

<s0:Policy s1:Id="Sign.xml">
<wssp:Integrity xmlns:wls="http://www.bea.com/wls90/security/policy/wsee#part"
                xmlns:wssp="http://www.bea.com/wls90/security/policy"
                xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  <wssp:SignatureAlgorithm URI="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
  <wssp:CanonicalizationAlgorithm URI="http://www.w3.org/2001/10/xml-exc-c14n#"/>
  <wssp:Target>
    <wssp:DigestAlgorithm URI="http://www.w3.org/2000/09/xmldsig#sha1"/>
    <wssp:MessageParts Dialect="http://www.bea.com/wls90/security/policy/wsee#part"> 
    wls:SystemHeaders()
  </wssp:MessageParts>
  </wssp:Target>
  <wssp:Target>
    <wssp:DigestAlgorithm URI="http://www.w3.org/2000/09/xmldsig#sha1"/>
    <wssp:MessageParts Dialect="http://www.bea.com/wls90/security/policy/wsee#part"> 
    wls:SecurityHeader(wsu:Timestamp)
  </wssp:MessageParts>
  </wssp:Target>
  <wssp:Target>
    <wssp:DigestAlgorithm URI="http://www.w3.org/2000/09/xmldsig#sha1"/>
    <wssp:MessageParts Dialect="http://schemas.xmlsoap.org/2002/12/wsse#part">
  wsp:Body()
  </wssp:MessageParts>
  </wssp:Target>
  <wssp:SupportedTokens>
    <wssp:SecurityToken 
      IncludeInMessage="true" 
      TokenType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">
      <wssp:TokenIssuer>REMOVED</wssp:TokenIssuer>
    </wssp:SecurityToken>
  </wssp:SupportedTokens>
</wssp:Integrity>
<wssp:MessageAge Age="60" xmlns:wssp="http://www.bea.com/wls90/security/policy"/>

From the wsdl i understand that I must sign:

  1. SystemHeaders (my real problem)
  2. Timestamp (did that)
  3. Body (did that, and also encrypted it)

I have working code (using microsoft.web.services3) who produce this soap:

<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
    <SignedInfo>
      <ds:CanonicalizationMethod 
        Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"
        xmlns:ds="http://www.w3.org/2000/09/xmldsig#" />
      <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1" />
      <Reference URI="#Timestamp-fc23bf88-381b-4f2b-b992-ff07b41b5c38"> <!--This is the timestamp-->
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <DigestValue>Removed</DigestValue>
      </Reference>
      <Reference URI="#Id-4b4f1377-eac0-4db0-b334-384d7b14e286"> <!--This is the encrypted body-->
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <DigestValue>Removed</DigestValue>
      </Reference>
      <Reference URI="#SecurityToken-dcb8a392-5907-4432-80c6-cbe8f29a6117"> <!--This is the SecurityTokenReference:Reference-->
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
        <DigestValue>Removed</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>Removed</SignatureValue>
    <KeyInfo>
      <wsse:SecurityTokenReference>
        <wsse:Reference 
          URI="#SecurityToken-dcb8a392-5907-4432-80c6-cbe8f29a6117" 
          ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3" />
      </wsse:SecurityTokenReference>
    </KeyInfo>
  </Signature>
</wsse:Security>
</soap:Header>
<soap:Body wsu:Id="Id-4b4f1377-eac0-4db0-b334-384d7b14e286">
<xenc:EncryptedData 
 Id="Enc-8b5b4ef4-1c12-409b-8159-dec2889a8fa8" 
 Type="http://www.w3.org/2001/04/xmlenc#Content" 
 xmlns:xenc="http://www.w3.org/2001/04/xmlenc#">
  <xenc:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
  <xenc:CipherData>
    <xenc:CipherValue>Removed<xenc:CipherValue>
  </xenc:CipherData>
</xenc:EncryptedData>
</soap:Body>

I have promised to make this work using WCF, so microsoft.web.services3 is not an option. Sorry.

I have created the proxy using svcutil. No sweat. Only change I have made to the proxy by hand is that I have appended

, ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign

to the System.ServiceModel.ServiceContractAttribute

My current code produces this sign part:

<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="#_1"> <!--This is the body-->
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
        <DigestValue>Removed</DigestValue>
      </Reference>
      <Reference URI="#uuid-e7f22d2b-5a91-421a-aced-df7ab8a92f8d-1"> <!--This is the timestamp-->
        <Transforms>
          <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"/>
        </Transforms>
        <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
        <DigestValue>Removed</DigestValue>
      </Reference>
    </SignedInfo>
    <SignatureValue>Removed</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-3b0e28b3-d47f-4eb2-ab0a-77f94dd76af0-2"/>
      </o:SecurityTokenReference>
    </KeyInfo>
  </Signature>
</o:Security>
</s:Header>
<s:Body u:Id="_1" 
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
      xmlns:xsd="http://www.w3.org/2001/XMLSchema">
 <e:EncryptedData 
  Id="_2" 
  Type="http://www.w3.org/2001/04/xmlenc#Content" 
  xmlns:e="http://www.w3.org/2001/04/xmlenc#">
  <e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"/>
  <e:CipherData><e:CipherValue>Removed</e:CipherValue></e:CipherData>
</e:EncryptedData>
</s:Body>
</s:Envelope>

from my app.config:

  <system.serviceModel>
    <bindings>
      <customBinding>
        <binding name="testBinding">
          <textMessageEncoding maxReadPoolSize="64" maxWritePoolSize="16" 
                               messageVersion="Soap11" writeEncoding="utf-8">
            <readerQuotas maxDepth="32" maxStringContentLength="8192" 
                          maxArrayLength="16384" maxBytesPerRead="4096" 
                          maxNameTableCharCount="16384"/>
          </textMessageEncoding>
          <httpTransport manualAddressing="false" maxBufferPoolSize="524288" 
                         maxReceivedMessageSize="65536" allowCookies="false" 
                         authenticationScheme="Anonymous" bypassProxyOnLocal="false" 
                         hostNameComparisonMode="StrongWildcard" keepAliveEnabled="true" 
                         maxBufferSize="65536" proxyAuthenticationScheme="Anonymous" 
                         realm="" transferMode="Buffered" 
                         unsafeConnectionNtlmAuthentication="false" useDefaultWebProxy="true"/>
        </binding>
      </customBinding>
    </bindings>
    <client>
      <endpoint address="http://url.com/testservice/testservicePort" binding="customBinding" 
                bindingConfiguration="testBinding" 
                contract="testservicePortType" 
                name="testservicePort"/>
    </client>
  </system.serviceModel>

I configure the CustomBinding in code like this:

    private static CustomBinding CreateCustomBinding()
    {
        var customBinding = new CustomBinding();

        SecurityBindingElement securityBindingElement = SecurityBindingElement.CreateMutualCertificateBindingElement(
                MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10);

        AsymmetricSecurityBindingElement asymmetricSecurityBindingElement =
            (AsymmetricSecurityBindingElement)securityBindingElement;

        asymmetricSecurityBindingElement.SetKeyDerivation(false);

        asymmetricSecurityBindingElement.EnableUnsecuredResponse = true;

        asymmetricSecurityBindingElement.AllowInsecureTransport = true;
        asymmetricSecurityBindingElement.AllowSerializedSigningTokenOnReply = true;

        asymmetricSecurityBindingElement.DefaultAlgorithmSuite = SecurityAlgorithmSuite.TripleDesRsa15;

        asymmetricSecurityBindingElement.IncludeTimestamp = true;
        asymmetricSecurityBindingElement.MessageProtectionOrder = MessageProtectionOrder.SignBeforeEncrypt;
        asymmetricSecurityBindingElement.RequireSignatureConfirmation = false;

        asymmetricSecurityBindingElement.SecurityHeaderLayout = SecurityHeaderLayout.LaxTimestampFirst;

        customBinding.Elements.Clear();

        customBinding.Elements.Add(asymmetricSecurityBindingElement);

        customBinding.Elements.Add(new TextMessageEncodingBindingElement()
        {
            MessageVersion = MessageVersion.CreateVersion(EnvelopeVersion.Soap11,
            AddressingVersion.None),
            WriteEncoding = new System.Text.UTF8Encoding()
        });

        HttpTransportBindingElement httpbinding = new HttpTransportBindingElement();
        httpbinding.AuthenticationScheme = AuthenticationSchemes.Anonymous;
        httpbinding.MaxReceivedMessageSize = 1024 * 1024;
        customBinding.Elements.Add(httpbinding);
        return customBinding;
    }

I have tried to understand what happens in the microsoft.web.services3 code who works (I havent written it), and it seems like the author completely rewrites the securityheader. This doesnt seem like the best solution (but maybe the only?)

Could anyone help me?


Solution

  • Finally figured it out :-)

    Used this post:

    How to make WCF Client conform to specific WS-Security - sign UsernameToken and SecurityTokenReference

    Have read it several times before i wrote the question above (hence the titles are so similar), and could really not see why it should work. But it does!

    My custom binding now looks like this:

    System.ServiceModel.Channels.AsymmetricSecurityBindingElement 
      asymmetricSecurityBindingElement = new AsymmetricSecurityBindingElement();
    asymmetricSecurityBindingElement.MessageSecurityVersion = 
              MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;
    
    asymmetricSecurityBindingElement.InitiatorTokenParameters = new
      System.ServiceModel.Security.Tokens.X509SecurityTokenParameters 
        { InclusionMode = SecurityTokenInclusionMode.Never };
    asymmetricSecurityBindingElement.RecipientTokenParameters = new
      System.ServiceModel.Security.Tokens.X509SecurityTokenParameters
        { InclusionMode = SecurityTokenInclusionMode.Never };
    asymmetricSecurityBindingElement.MessageProtectionOrder =
      System.ServiceModel.Security.MessageProtectionOrder.SignBeforeEncrypt;
    
    asymmetricSecurityBindingElement.SecurityHeaderLayout = SecurityHeaderLayout.LaxTimestampFirst;
    asymmetricSecurityBindingElement.EnableUnsecuredResponse = true;
    asymmetricSecurityBindingElement.IncludeTimestamp = true;
    
    asymmetricSecurityBindingElement.SetKeyDerivation(false);
    asymmetricSecurityBindingElement.DefaultAlgorithmSuite = 
      System.ServiceModel.Security.SecurityAlgorithmSuite.TripleDesRsa15;
    
    asymmetricSecurityBindingElement.EndpointSupportingTokenParameters.Signed.Add(
      new X509SecurityTokenParameters());
    
    customBinding.Elements.Clear();
    customBinding.Elements.Add(asymmetricSecurityBindingElement);