While playing with the WSE Security Settings Wizard I discovered that the generated policy requires a DerivedKeyToken to be used to sign the messages rather than the original security tokens. This is a good thing, but isn't obvious from the wizard screens. I thought I'd provide some background on what derived keys are, why they are useful and how to ensure your WSE services use them through code or policy.
Derived Keys: what are they and why are they useful?
Using a derived key is a good thing as it means a different key is used to sign and/or encrypt each message. Changing the key each time makes it more difficult to perform a ciphertext-only attack. The DerivedKey can use many different algorithms to generate the key. WSE 2.0 uses the algorithm defined in the WS-SecureConversation specification, which creates the derived key by performing a SHA1 hash over the original key (in the case below, a reference to the key), along with the combination of a label and a nonce value (a unique value that's seen only once). Here's what a DerivedKeyToken based upon a UsernameToken looks like on the wire:
<wssc:DerivedKeyToken wsu:Id="SecurityToken-d06a92f7-990c-43a4-a996-f8f3a359e450" wssc:Algorithm="http://schemas.xmlsoap.org/ws/2004/04/security/sc/dk/p_sha1" xmlns:wssc="http://schemas.xmlsoap.org/ws/2004/04/sc">
<wsse:SecurityTokenReference>
<wsse:Reference URI="#SecurityToken-6c059c7a-c748-44b1-b957-4b927dd2a8f3" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#UsernameToken" />
</wsse:SecurityTokenReference>
<wssc:Generation>0</wssc:Generation>
<wssc:Length>16</wssc:Length>
<wssc:Label>WS-SecureConversation</wssc:Label>
<wsse:Nonce>mDRVPaA7343zsrMjD+CatA==</wsse:Nonce>
</wssc:DerivedKeyToken>
See Martin Gudgin's MSDN article on Using WS-Trust and WS-SecureConversation for further information.
Ensuring compliance with the WSE generated policy
To comply with the server-side policy that the WSE Security Settings Wizard generates you can either create a derived token in your code or use a send-side policy. The advantage of using the send-side policy is that it requires fewer lines of code and provides the potential to change the security configuration in future without having to recompile the code.
Signing a message using a DerivedKeyToken with code
Creating a DerivedKeyToken requires two more lines of code than would be required to sign a message with a standard user name token. The first extra line creates the DerivedKeyToken and the second line adds it to the RequestSoapContext.Security.Tokens collection so that it will appear on the wire when WSE sends the message. Here's the code:
// Create the username token and add it to the message headers
UsernameToken usernameToken = new UsernameToken("TechedClient", "TechEd2004!", PasswordOption.SendPlainText );
proxy.RequestSoapContext.Security.Tokens.Add( usernameToken );
// Create a derived token, based on our username token
DerivedKeyToken derivedToken = new DerivedKeyToken( usernameToken );
// Add the derived key token to the message headers
proxy.RequestSoapContext.Security.Tokens.Add( derivedToken );
// Sign the message with the derived key token.
proxy.RequestSoapContext.Security.Elements.Add( new MessageSignature( derivedToken ) );
Signing a message using a DerivedKeyToken with send-side Policy
The simplest way to ensure that a message is sent using a DerivedKeyToken based on a UsernameToken is to use the WSE Security Setting Tool to create a policy file on the client specifying that output messages should be signed with a UsernameToken. When WSE sends the message through its pipeline the PolicyEnforcementOutputFilter will create a DerivedToken based on the UsernameToken and sign the message with it. For this to work, WSE needs to know where to find the UsernameToken to base the DerivedKeyToken on. WSE first searches for a matching token in the SoapContext.Security.Tokens collection and if it doesn't find any there it looks in the PolicyEnforcementSecurityTokenCache.
So you can either use the first two lines of the sample code above, or the following:
// Create the username token and add it to the message headers
UsernameToken usernameToken = new UsernameToken("TechedClient", "TechEd2004!", PasswordOption.SendPlainText );
PolicyEnforcementSecurityTokenCache.GlobalCache.Add( usernameToken );
The advantage of using the PolicyEnforcementSecurityTokenCache is that the username token can more easily be attached to any message leaving the client, regardless of which service is being used (e.g. it is independent of the webservice the message is being sent to, unlike the SoapContext which is related to a specific webservice address). The second advantage is that if you later decide to sign with X509SecurityTokens rather than UsernameTokens then you wouldn't need to change any code or recompile (the UsernameToken in the GlobalCache would simply not be used). I'll write more about this topic in a future post.