2

I am building a WCF web service with a customBinding endpoint and am getting stuck accepting the WS-Security header that is being sent to me by another party. We are both following a specification authored by the UK National Health Service, so I am unable to amend the requirements.

The basic structure of the <wsse:Security> header should be as follows, according to the specification:

<wsse:Security>
    <wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurityutility-1.0.xsd" wsu:Id="6CCF6A2B-11A6-11DF-86D1-236A99759561" >
        <wsu:Created>2012-06-12T09:00:00Z</wsu:Created>
        <wsu:Expires>2012-06-12T09:15:00Z</wsu:Expires>
    </wsu:Timestamp>
    <wsse:UsernameToken>
        <wsse:Username>SomeUsername</wsse:Username>
    </wsse:UsernameToken>
    <wsse:BinarySecurityToken EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wsssoap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-
200401-wss-x509-token-profile-1.0#X509v3" wsu:Id="30b91ede-35c2-11df-aac9-97f155153931 ">xxx...</wsse: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="#6CCF6A2B-11A6-11DF-86D1-236A99759561" />
            <Transforms>
                <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            </Transforms>
            <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
            <DigestValue>xxx...</DigestValue>
        </SignedInfo>
        <SignatureValue>xxx...</SignatureValue>
        <KeyInfo>
            <wsse:SecurityTokenReference>
                <wsse:Reference URI="#30b91ede-35c2-11df-aac9-97f155153931 "/>
            </wsse:SecurityTokenReference>
        </KeyInfo>
    </Signature>
</wsse:Security>

In my web service, I have tried using the following binding:

<customBinding>
    <binding name="wsHttpSoap11" >
        <textMessageEncoding messageVersion="Soap11WSAddressingAugust2004" />
        <security authenticationMode="MutualCertificate">
        </security>
        <httpTransport/>
    </binding>
</customBinding>

(The reason I am using customBinding is that I have to support both WS-Addressing and WS-Security over SOAP 1.1, and took advice from this answer.)

If I run a sample request through Fiddler, I get the following error in my WCF trace:

Cannot find a token authenticator for the 'System.IdentityModel.Tokens.UserNameSecurityToken' token type. Tokens of that type cannot be accepted according to current security settings.

I believe this is because it cannot authenticate the <UsernameToken>. If I change the binding security to:

<security authenticationMode="UserNameForCertificate">

Then I get this error:

Cannot find a token authenticator for the 'System.IdentityModel.Tokens.X509SecurityToken' token type. Tokens of that type cannot be accepted according to current security settings.

I believe this is because it now cannot authenticate the <BinarySecurityToken>!

The question, therefore, is:

  1. Are my assumptions correct about the cause of the error messages (that it can only handle one token in its current configuration)?
  2. How do I configure it to accept both tokens?

Update

Thanks to @Yaron I have now added a custom binding extension and both the UserNameSecurityToken and X509SecurityToken are validating.

However it is now failing at the stage where it verifies the XML signature. The exception returned in the HTTP response is:

Message security verification failed.

If I dig deeper into the stack trace in the service trace viewer, I see:

System.Security.Cryptography.CryptographicException...

The signature verification failed.

at System.IdentityModel.SignedXml.VerifySignature(HashAlgorithm hash, AsymmetricSignatureDeformatter deformatter) at System.IdentityModel.SignedXml.StartSignatureVerification(SecurityKey verificationKey)...

Can anyone help me figure out why this is happening? I'm kind of lost at the moment. I tried using some sample code to attempt to manually verify the signature, but it says the signature is not valid. How can I be sure whether it is or not before I go back to the supplier? Is this something that should just work? Should we be sharing some certificates somewhere along the line?

Community
  • 1
  • 1
Sir Crispalot
  • 4,792
  • 1
  • 39
  • 64

1 Answers1

4

you will need to create the binding from code.

        var b = new CustomBinding();
        var sec = (AsymmetricSecurityBindingElement)SecurityBindingElement.CreateMutualCertificateBindingElement(MessageSecurityVersion.WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10);
        sec.EndpointSupportingTokenParameters.Signed.Add(new UserNameSecurityTokenParameters());
        sec.MessageSecurityVersion =
            MessageSecurityVersion.
                WSSecurity10WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10;
        sec.IncludeTimestamp = true;
        sec.MessageProtectionOrder = System.ServiceModel.Security.MessageProtectionOrder.EncryptBeforeSign;

        b.Elements.Add(sec);
        b.Elements.Add(new TextMessageEncodingBindingElement(MessageVersion.Soap11, Encoding.UTF8));
        b.Elements.Add(new HttpTransportBindingElement());

(some of the values are estimated since I cannot tell by your post which soap version you use or if ssl is applied)

another gotcha you may run too later is that you need to have ProtectionLevel.SignOnly on your ServiceContract attributes, but this is not related to this question.

Yaron Naveh
  • 23,560
  • 32
  • 103
  • 158
  • thanks very much for your answer - I'll try this when I'm back in the office tomorrow. I did mention in my question that I'm using SOAP 1.1, does that have any effect on your sample code? – Sir Crispalot Jun 12 '12 at 19:59
  • No affect though if we addressing is also used this may have affect. If you will have any issues please publish the full soap or send it to me – Yaron Naveh Jun 12 '12 at 20:30
  • Please see my update, would appreciate any further advice. Happy to send you the full request if it would be useful. – Sir Crispalot Jun 15 '12 at 07:50
  • does the wcf trace show any inner exception? it is sometimes hidden under the main one... if no further information then build a wcf client to consume this service, using the exact same code binding. verify the client works, and then compare its soap to the actual received soap. oh wait - looking at the sample soap it looks like only the soap body is signed? wcf by default will require also the username token and timestamp to be signed. there must be some inner exception on this. if you set includeTimestamp to false you *may* get away with the timestamp requirement but you'll need to be - – Yaron Naveh Jun 15 '12 at 11:41
  • creative with the timestamp. start by finding that inner exception so we get some feedback. after you find the error also let us know if ssl is used. if so there may be a way to play with the CreateMutualCertificateBindingElement settings to turn it into a transport binding – Yaron Naveh Jun 15 '12 at 11:42
  • Thank you so much for your advice and your follow-up email, you are truly an asset to SO! Your sample code has really helped me out. I created a client as well, and I'm now very very close to getting it working. – Sir Crispalot Jun 26 '12 at 07:46
  • +1 from me for getting me past one exception. I am peeling away the next layer now. – Fenton Sep 28 '12 at 14:15