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:
- Are my assumptions correct about the cause of the error messages (that it can only handle one token in its current configuration)?
- 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?