Three Is It

Because two isn't enough and four is just too many

Gravity is a harsh mistress.
The Tick
Home Blogs Genealogy Brad's Bookshelf Subscriptions Contact Sign in
 

About the author

Brad Butts is a .NET developer and architect. He is married with children and enjoys reading, working out, and genealogy is his five minutes of spare time.
E-mail me Send mail
National Debt Clock

Recent comments

Authors

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2009

WCF, Interop, and the elusive BinarySecurityToken

If you plan to write WCF services that will be consumed by clients of technologies other than .NET--and would like to include some level of security, like message signing (via X.509 certificates)--chances are you'll have to find some way to emit a BinarySecurityToken value in your responses.

When I first configured the security of my service, I went with a custom binding and a configuration like this:

<binding name="CustomBindingForX509">
<textMessageEncoding messageVersion="Soap11" />
<security allowSerializedSigningTokenOnReply="true" authenticationMode="MutualCertificate"
requireDerivedKeys="false" securityHeaderLayout="Lax" messageProtectionOrder="EncryptBeforeSign"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
<localClientSettings detectReplays="false" />
<localServiceSettings detectReplays="false" />
<secureConversationBootstrap />
</security>
<httpTransport />
</binding>


This seemed to make sense (as far as WCF configuration can make sense, anyway): I was sharing certificates with my client (a Java Axis2 client), hence the MutualCertificate setting seemed appropriate.  I was also going for the lowest common denominator SOAP settings (as most folks in the interop space seem to recommend), so I went with those recommended SOAP settings.  This configuration had the effect of rendering my response to look as such (I only show the SOAP header section since it is what's relevant here):

<s:Header>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="uuid-5965cceb-61d9-4d3f-a503-c3f4dc7fe08a-3">
<u:Created>2008-07-17T20:33:42.153Z</u:Created>
<u:Expires>2008-07-17T20:38:42.153Z</u:Expires>
</u:Timestamp>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod>
<Reference URI="#_1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>3dcC17frvtyzp8G+kR5otzreQf0=</DigestValue>
</Reference>
<Reference URI="#uuid-5965cceb-61d9-4d3f-a503-c3f4dc7fe08a-3">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>h/DtKybbi4Q3RRtYKm26SGM2mcM=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>IRlfHoVu/JispcD5CdMCKnbHNZcSVVNNLBtXbnP3fcid+nPi1F4WNGVsHjkF6PnaIzKM/5j2Vhnxbkm1tTwFjeKelQipCHErrwXsxOKMaVKlP/2gjeiJ0K2kkEO7LIUIcmqQ9MNx/AfGr9zE4c6EPGrkbPJVYLvra5jUhypMAcM=</SignatureValue>
<KeyInfo>
<o:SecurityTokenReference>
<X509Data>
<X509IssuerSerial>
<X509IssuerName>CN=Sample Service, OU=Rampart, O=Apache, L=Colombo, S=Western, C=LK</X509IssuerName>
<X509SerialNumber>1187603713</X509SerialNumber>
</X509IssuerSerial>
</X509Data>
</o:SecurityTokenReference>
</KeyInfo>
</Signature>
</o:Security>
</s:Header>


The problem is, my client didn't like these settings and kept throwing the error "The signature verification failed".  I suspect the client didn't like this response because the MutualCertificate setting seems to want the client to look up the certificate via distinguished name or serial number to verify that the message was signed appropriately.  Java clients, and apparently a few other technologies, don't seem to work that way.

So, what to do?  Well, eventually I found this post describing a similar problem between a WCF service and an SAP NetWeaver client.  Part III of the post describes what they did to resolve the signature verification problem: namely, get the WCF service to inject a BinarySecurityToken instead of the SecurityTokenReference (with issuer name and serial number).

I'm not totally fond of their implementation--they did the old school write-a-console-app-to-host-my-WCF-service approach--but this gave me an important clue to how to inject the BinarySecurityToken: use the AsymmetricSecurityBindingElement class!  Wow, why didn't I think of that?  That's fairly...uh...obvious.

Ok, so how do I use this AsymmetricSecurityBindingElement class?  I know I don't want to write a console app just to plug in this functionality--that's what IIS is for.  Guess I'll have to go to a custom BindingElement extension (see the attachment for the code and thanks to Scott, the Microsoft tech who wrote it--and helped me with this problem):

(excerpt from the client and service configurations)
<extensions>
<bindingElementExtensions>
<add name="MySecurityBindingElement" type="MySecurityBE.AsymetricSecurityBEExtentionElement, MySecurityBE, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</bindingElementExtensions>
</extensions>
<bindings>
<customBinding>
<binding name="MyCoolBinding">
<MySecurityBindingElement/>
<textMessageEncoding messageVersion="Soap11"/>
<httpTransport/>
</binding>
</customBinding>
</bindings>



But wait, there's more!  It turns out that you don't even need to write your own custom BindingElement.  It turns out that the authentication mode MutualCertificateDuplex makes use of the AsymmetricSecurityBindingElement class.  What?  Didn't you know that?  So, all you really have to do is expose an endpoint with this kind of binding and you're set.  So, this service configuration:

<binding name="CustomBindingForX509">
<security authenticationMode="MutualCertificateDuplex"
requireDerivedKeys="false"  messageProtectionOrder="EncryptBeforeSign"
messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10"/>
<textMessageEncoding messageVersion="Soap11" />
<httpTransport  />
</binding>


Will render this response (again, I'm just including the header for brevity):

<s:Header>
<o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<u:Timestamp u:Id="uuid-39531f92-c0d4-4129-b491-6222c06d3bf5-1">
<u:Created>2008-07-25T16:03:59.959Z</u:Created>
<u:Expires>2008-07-25T16:08:59.959Z</u:Expires>
</u:Timestamp>
<o:BinarySecurityToken>
<!-- Removed-->
</o:BinarySecurityToken>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></CanonicalizationMethod>
<SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></SignatureMethod>
<Reference URI="#_1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>2x4uPP0r/Zo7auuboFg+8h0k3Yo=</DigestValue>
</Reference>
<Reference URI="#uuid-39531f92-c0d4-4129-b491-6222c06d3bf5-1">
<Transforms>
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#"></Transform>
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></DigestMethod>
<DigestValue>Z161SvyizqMC3alennK2c6FLiZg=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>EDpLDry/QuwxVuXe+/0LZrmrLkTcoZ1Ls45qvu+RebTRFfkq8HksKMN3Ip4T2begyDCLfGOTpEHfX/ohyMS7HIsxluyIwJ971kyLVt6nUZPfjqQ3iD3hCI2cCSRtNbC1p+aZDr3Tn/KbLxjWQ4aFfm7lRKbLGeVDEY5BJYyVCqY=</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-f32fc9b9-695e-4542-98bb-acf40424321b-2"></o:Reference>
</o:SecurityTokenReference>
</KeyInfo>
</Signature>
</o:Security>
</s:Header>


Note that the response now includes the BinarySecurityToken and, ironically, has removed the x509 name and serial number data.  Don't worry about that "removed" comment, the binary token was sent to my client, it's just that WCF automatically removes privacy information from its logs--and I'm pulling this data from the log files.  For more info on privacy removal, see here.

Now, my non-.NET clients are happy, but I've just hosed my .NET clients.  From what I'm told, duplex mode is no walk in the park for .NET clients.  So, what to do?  Well, I'll tell you what I plan to do: just expose another endpoint.  If you're a .NET client, you'll get a traditional MutualCertificate binding endpoint.  If you're not a .NET client, you'll get the duplex endpoint.  The custom extension is nice, but not absolutely necessary at this point.

AsymExample.zip (899.63 kb)

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: Technology Blog
Posted by Brad on Thursday, September 18, 2008 4:33 PM
Permalink | Comments (0) | Post RSSRSS comment feed

Related posts

Add comment


(Will show your Gravatar icon)  

  Country flag

[b][/b] - [i][/i] - [u][/u]- [quote][/quote]



Live preview

Monday, January 05, 2009 2:19 PM