Monday, November 3, 2008

Secure Conversation with WCF

This post takes you through basics of Secure Conversation concepts through an example written in WCF.

You can download the sample solution[VS 2008] from here.

The solution contains two projects.

- StockQuoteService [The WCF service]
- StockQuoteClient

Set StockQuoteService as the 'Startup Project' and then press Ctrl+F5 to run the service.

Then run the StockQuoteClient by setting it as the 'Startup Project' and pressing Ctrl+F5.

Let's look at service and client configurations and you'll find that we use basicHttpBinding.
<!--[StockQuoteService]\app.config -->
<service name="wso2.org.stockquotes.StockQuoteService" behaviorConfiguration="stockquotebehaviour">
<endpoint address="http://localhost/stocks"
binding="basicHttpBinding"
contract="wso2.org.stockquotes.StockQuoteService" />
<endpoint contract="IMetadataExchange"
binding="mexHttpBinding"
address="mex" />
</service>
<!--[StockQuoteClient]\app.config -->
<client>
<endpoint address="http://localhost/stocks"
binding="basicHttpBinding" 
contract="StockQuoteServiceProxy.StockQuoteService" />
</client>
Now to enable logging, add following to the [StockQuoteService]\app.config.
<system.diagnostics>
<sources>
<source name="System.ServiceModel.MessageLogging" 
switchValue="Warning, ActivityTracing">
<listeners>
<add type="System.Diagnostics.DefaultTraceListener" name="Default">
<filter type="" />
</add>
<add name="ServiceModelMessageLoggingListener">
<filter type="" />
</add>
</listeners>
</source>
</sources>
<sharedListeners>
<add initializeData="c:\messages.svclog"
type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
name="ServiceModelMessageLoggingListener" traceOutputOptions="Timestamp">
<filter type="" />
</add>
</sharedListeners>
</system.diagnostics>

<system.serviceModel>
<diagnostics>
<messageLogging logEntireMessage="true" logMessagesAtTransportLevel="true" />
</diagnostics>
</system.serviceModel>
Now lets start both the service and the client.

You can view the log file with SvcTraceViewer tool comes with .NET - launch the tool and simply open the file c:\messages.svclog. You'll find everything in clear text.

Let's change the binding from basicHttpBinding to wsHttpBinding in both the client and service configuration files, delete the file c:\messages.svclog and run both the service and the client.

WCF turns on Secure Conversation by default for all bindings that support WS-Security (WsHttpBinding, NetTcpBinding, netMsmqBinding).

Once again let's open the c:\messages.svclog file in SvcTraceViewer.

Now you'll see 8 messages instead of one.

Let's go through each and every one of them to understand Secure Conversation.

There are different ways to create a Security Context Token[SCT] to establish a Secure Conversation.

One way is through a Security Token Service[STS], which I discussed in this post.

The other two ways are,

1. The SCT is created by one of the communicating parties and propagated with the message.
2. The SCT created through negotiation/exchanges.

The example which we just discussed occupies the second option, that is the SCT created through negotiation/exchanges.

For this scenario the initiating party first sends a RquestSecurityToken[RST]. That is in our case from the client to the service.
<s:Envelope xmlns:s="..." xmlns:a="...">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
<a:MessageID>urn:uuid:d55bd2a7-bae8-4751-a010-07e95fd82ee2</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">http://localhost/stocks</a:To>
</s:Header>
<s:Body>
<t:RequestSecurityToken Context="uuid-f422503c-7974-42ab-9b8a-c330727290e9-1"
xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<t:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct
<t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
<t:KeySize>256</t:KeySize>
<t:BinaryExchange 
ValueType="http://schemas.xmlsoap.org/ws/2005/02/trust/spnego" 
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">
TlRMTVNTUAABAAAAt7IY4gQABAA0AAAADAAMACgAAAAFAs4OAAAAD1BSQUJBVEgtSE9NRUhPTUU=
</t:BinaryExchange>
</t:RequestSecurityToken>
</s:Body>
</s:Envelope>
Now the service will create a RequestSecurityTokenResponse[RSTR] and will send it back to the client.
<s:Envelope xmlns:s="..." xmlns:a="...">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue
<a:RelatesTo>urn:uuid:d55bd2a7-bae8-4751-a010-07e95fd82ee2</a:RelatesTo>
</s:Header>
<s:Body>
<t:RequestSecurityTokenResponse
Context="uuid-f422503c-7974-42ab-9b8a-c330727290e9-1"
xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<t:BinaryExchange 
ValueType="http://schemas.xmlsoap.org/ws/2005/02/trust/spnego" 
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">
TlRMTVNTUAACAAAACAAIADgAAAA1wpnipKbRWTHfQGhYyuMAAAAAAIIAggBAAAAABQLODgAAAA9
IAE8ATQBFAAIACABIAE8ATQBFAAEAGABQAFIAQQBCAEEAVABIAC0ASABPAE0ARQAEABAAaABvAG0
AZQAuAGMAbwBtAAMAKgBwAHIAYQBiAGEAdABoAC0AaABvAG0AZQAuAGgAbwBtAGUALgBjAG8
AbQAFABAAaABvAG0AZQAuAGMAbwBtAAAAAAA=
</t:BinaryExchange>
</t:RequestSecurityTokenResponse>
</s:Body>
</s:Envelope>
To complete the initial handshaking process client once again sends an RSTR.
<s:Envelope xmlns:s="..." xmlns:a="">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue
<a:MessageID>urn:uuid:da46b6f3-b168-4aae-95fb-b9412cecf177</a:MessageID>
<a:ReplyTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1">http://localhost/stocks</a:To>
</s:Header>
<s:Body>
<t:RequestSecurityTokenResponse
Context="uuid-f422503c-7974-42ab-9b8a-c330727290e9-1"
xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<t:BinaryExchange 
ValueType="http://schemas.xmlsoap.org/ws/2005/02/trust/spnego" 
EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary">
TlRMTVNTUAADAAAAAAAAAEgAAAAAAAAASAAAAAAAAABIAAAAAAAA
</t:BinaryExchange>
</t:RequestSecurityTokenResponse>
</s:Body>
</s:Envelope>

Now the service will reply with the RequestedSecurityToken.
<s:Envelope xmlns:s="..." xmlns:a="...">
<s:Header>
<a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue</a:Action>
<a:RelatesTo>urn:uuid:da46b6f3-b168-4aae-95fb-b9412cecf177</a:RelatesTo>
</s:Header>
<s:Body>
<t:RequestSecurityTokenResponseCollection xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
<t:RequestSecurityTokenResponse
Context="uuid-f422503c-7974-42ab-9b8a-c330727290e9-1"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<t:TokenType>http://schemas.xmlsoap.org/ws/2005/02/sc/sct</t:TokenType>
<t:RequestedSecurityToken>
<c:SecurityContextToken u:Id="uuid-06c81281-3c66-4122-b7ef-07744a68756e-1" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<c:Identifier>
urn:uuid:05adf263-f2bb-4edf-8597-00c7c7f5e858
</c:Identifier>
</c:SecurityContextToken>
</t:RequestedSecurityToken>
<t:RequestedAttachedReference>
<o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:Reference ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct" URI="#uuid-06c81281-3c66-4122-b7ef-07744a68756e-1">
</o:Reference>
</o:SecurityTokenReference>
</t:RequestedAttachedReference>
<t:RequestedUnattachedReference>
<o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:Reference URI="urn:uuid:05adf263-f2bb-4edf-8597-00c7c7f5e858" ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct">
</o:Reference>
</o:SecurityTokenReference>
</t:RequestedUnattachedReference>
<t:RequestedProofToken>
<e:EncryptedKey xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod Algorithm="http://schemas.xmlsoap.org/2005/02/trust/spnego#GSS_Wrap">
</e:EncryptionMethod>
<e:CipherData>
<e:CipherValue>
AQAAAMaisligvVhlAAAAAINLoPtv3Zq34T52WXoXBI1YaL+V8cNjc+BxdMVKu
</e:CipherValue>
</e:CipherData>
</e:EncryptedKey>
</t:RequestedProofToken>
<t:Lifetime>
<u:Created>2008-11-03T10:11:04.437Z</u:Created>
<u:Expires>2008-11-03T10:26:04.437Z</u:Expires>
</t:Lifetime>
<t:KeySize>256</t:KeySize>
</t:RequestSecurityTokenResponse>
<t:RequestSecurityTokenResponse 
Context="uuid-f422503c-7974-42ab-9b8a-c330727290e9-1"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<t:Authenticator>
<t:CombinedHash>Zc9/3FeaG8b6/cs7jeTsHpZC8JlPjCgeun5I/4RRbd0=</t:CombinedHash>
</t:Authenticator>
</t:RequestSecurityTokenResponse>
</t:RequestSecurityTokenResponseCollection>
</s:Body>
</s:Envelope>

Now the client sends following message to the service.
<s:Envelope xmlns:s="..." xmlns:a="..." xmlns:u="...">
<s:Header>
<a:Action s:mustUnderstand="1" u:Id="_4">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/SCT</a:Action>
<a:MessageID u:Id="_5">urn:uuid:b534d3de-1bcc-42fb-81b6-e472c797281f</a:MessageID>
<a:ReplyTo u:Id="_6">
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</a:ReplyTo>
<a:To s:mustUnderstand="1" u:Id="_7">http://localhost/stocks</a:To>
<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-f422503c-7974-42ab-9b8a-c330727290e9-5">
<u:Created>2008-11-03T10:11:04.453Z</u:Created>
<u:Expires>2008-11-03T10:16:04.453Z</u:Expires>
</u:Timestamp>
<c:SecurityContextToken u:Id="uuid-06c81281-3c66-4122-b7ef-07744a68756e-1" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<c:Identifier>urn:uuid:05adf263-f2bb-4edf-8597-00c7c7f5e858</c:Identifier>
</c:SecurityContextToken>
<c:DerivedKeyToken u:Id="_0" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<o:SecurityTokenReference>
<o:Reference ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct" URI="#uuid-06c81281-3c66-4122-b7ef-07744a68756e-1"></o:Reference>
</o:SecurityTokenReference>
<c:Offset>0</c:Offset>
<c:Length>24</c:Length>
<c:Nonce>
<!-- Removed-->
</c:Nonce>
</c:DerivedKeyToken>
<c:DerivedKeyToken u:Id="_1" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<o:SecurityTokenReference>
<o:Reference ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct" URI="#uuid-06c81281-3c66-4122-b7ef-07744a68756e-1">
</o:SecurityTokenReference>
<c:Nonce>
<!-- Removed-->
</c:Nonce>
</c:DerivedKeyToken>
<e:ReferenceList xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:DataReference URI="#_3">
<e:DataReference URI="#_8">
</e:ReferenceList>
<e:EncryptedData Id="_8" Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"></e:EncryptionMethod>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<o:SecurityTokenReference>
<o:Reference URI="#_1">
</o:SecurityTokenReference>
</KeyInfo>
<e:CipherData>
<e:CipherValue>.....
</e:CipherData>
</e:EncryptedData>
</o:Security>
</s:Header>
<s:Body u:Id="_2">
<e:EncryptedData Id="_3" 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#aes256-cbc"></e:EncryptionMethod>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:Reference URI="#_1"></o:Reference>
</o:SecurityTokenReference>
</KeyInfo>
<e:CipherData>
<e:CipherValue>...</e:CipherValue>
</e:CipherData>
</e:EncryptedData>
</s:Body>
</s:Envelope>
Finally the service will repond with the following.
<s:Envelope xmlns:s="..." xmlns:a="..." xmlns:u="...">
<s:Header>
<a:Action s:mustUnderstand="1" u:Id="_4">http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/SCT
<a:RelatesTo u:Id="_5">urn:uuid:b534d3de-1bcc-42fb-81b6-e472c797281f</a:RelatesTo>
<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-06c81281-3c66-4122-b7ef-07744a68756e-6">
<u:Created>2008-11-03T10:11:04.468Z</u:Created>
<u:Expires>2008-11-03T10:16:04.468Z</u:Expires>
</u:Timestamp>
<c:DerivedKeyToken u:Id="_0" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<o:SecurityTokenReference>
<o:Reference URI="urn:uuid:05adf263-f2bb-4edf-8597-00c7c7f5e858" ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct">
</o:SecurityTokenReference>
<c:Offset>0</c:Offset>
<c:Length>24</c:Length>
<c:Nonce>
<!-- Removed--<
</c:Nonce>
</c:DerivedKeyToken>
<c:DerivedKeyToken u:Id="_1" xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<o:SecurityTokenReference>
<o:Reference URI="urn:uuid:05adf263-f2bb-4edf-8597-00c7c7f5e858" ValueType="http://schemas.xmlsoap.org/ws/2005/02/sc/sct">
</o:SecurityTokenReference>
<c:Nonce>
<!-- Removed-->
</c:Nonce>
</c:DerivedKeyToken>
<e:ReferenceList xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:DataReference URI="#_3"></e:DataReference>
<e:DataReference URI="#_6"></e:DataReference>
</e:ReferenceList>
<e:EncryptedData Id="_6" Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#aes256-cbc"></e:EncryptionMethod>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<o:SecurityTokenReference>
<o:Reference URI="#_1"></o:Reference>
</o:SecurityTokenReference>
</KeyInfo>
<e:CipherData>
<e:CipherValue>...</e:CipherValue>
</e:CipherData>
</e:EncryptedData>
</o:Security>
</s:Header>
<s:Body u:Id="_2">
<e:EncryptedData Id="_3" 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#aes256-cbc"></e:EncryptionMethod>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<o:SecurityTokenReference xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<o:Reference URI="#_1"></o:Reference>
</o:SecurityTokenReference>
</KeyInfo>
<e:CipherData>
<e:CipherValue>...</e:CipherValue>
</e:CipherData>
</e:EncryptedData>
</s:Body>
</s:Envelope>
Now the client will invoke the bussiness logic.

1 comments:

Yaron Naveh said...

Very nice!

Notice that the case you have showed here is relatively complex one as it uses the SPNego proprietary protocol to bootstrap the secure conversation. In general if negotiation is not used in WCF then one RST and one RSTR are enough.

Keep on writing interesting stuff!