worked on this so that i would have some new stuff to show for MobileDevCon (along with WS-ReliableMessaging : /cfReliable ). the WSE bits for CF that i released with the /spWSE article were made to call the WSE2 samples. but the functionality exposed was all functionality that already existed with WSE1. WSE1 already had DIME, WS-Routing (changed to WS-Addressing for v2.0), and WS-Security. WSE2 primarily introduced WS-Policy and WS-SecureConversation. so i wanted to build upon what I had already done to incorporate something that WSE2 offers, but WSE1 does not.
WS-SecureConversation was the logical choice. it builds upon WS-Security and also WS-Trust. did not have to implement WS-Trust for this though, because it is only used for service-to-service communication, and not from the client. WS-SecureConversation makes sense for small devices. if you use WS-Security from a small device, then each request must be signed and encrypted. this would involve both asymmetric and symmetric encryption for every call. Asymmetric encryption is very expensive, so this is bad. WS-SecureConversation mitigates this by doing asymmetric encryption up front to exchange a session key, and then it only needs symmetric encryption for subsequent calls. Symmetric encryption uses less resources. The cost is paid up front, to exchange security context; much in the same manner that SSL works, but at the message level instead of the wire level.
the sequence diagram above shows multiple secure messages being sent using WS-Security alone. note that RSA would be used multiple times for each message that was sent. the sequence diagram below shows RSA only being used to establish security context. after the initial call, less expensive cipher algorithms are used from then on
the 1st thing i did was get the WSE 2.0 SecureConversation sample to run. it worked right off the bat. this surprised me, because it failed for me repeatedly on my last development computer. do not know why? the next thing i did was to sniff the wire to see what communications were taking place. what happens is the client makes a signed RequestSecurityToken call to a .ASHX web service. the .ASHX is set up by using the web.config with WSE2. it is also called a SecurityTokenService (STS). meaning the client is requesting a security token. it responds with a signed and encrypted message. then the client makes a signed and encrypted call to the actual .ASMX web service itself. it returns a plain text message showing the stock values that were requested. this gave me a basic idea of what was going on, so i could start recreating it.
next order of business was to have the client call the STS. started out by looking for its URL in the sample client code and opening that in a browser.
this returned the WSDL for the STS. great! NOTE it did not generate an HTML test page as expected. so i went into the SmartDevice project and added it as a Web Reference to generate the proxy. the proxy had 2 methods: RequestSecurityToken and RequestSecurityTokenResponse. looking at the trace from the sample client again, i knew RequestSecurityToken was the one i wanted: to call
<RequestSecurityToken xmlns="http://schemas.xmlsoap.org/ws/2002/12/secext"> <TokenType>wsse:SecurityContextToken</TokenType> <RequestType>wsse:ReqIssue</RequestType> <Base> <Reference URI="#SecurityToken-027f20b6-08ba-4028-b111-4a306efe85f7" /> </Base> </RequestSecurityToken>
the next problem was that the RequestSecurityToken method from the proxy was untyped. instead of strongly typed objects, it excepted a 'ref Element ' as its parameters. all i did was create an XmlDocument, and then manually created the 3 XmlElements contained by <RequestSecurityToken/> above. when i tried to make the call though, it failed with an IndexOutOfRangeException. damn, that looked familiar. looking at the auto-generated proxy code it has the 'ref Element ' declared with the XmlAnyElementAttribute. just my luck! this bug is constantly getting in my way. from previous work, i knew that the XmlAnyElementAttribute would not work, and i would have to create strongly typed objects. mocked up a simple WebService with the typed objects as parameters (TokenType, RequestType, and Base). then i deleted my previous Web Reference, and added this one instead. then just changed its URL to point to the actual STS. also, added on the proper SoapExtension to make the outgoing request signed with my private X509Certificate. made the request and it worked.
next problem was that the response was encrypted. added on a different SoapExtension that would do XmlSignature for the request and XmlEncryption to decrypt the response, and this was the value returned in the SoapBody.
<soap:Body wsu:Id=\"Id-0bb0de32-68c2-49bc-97b4-4f91ffe49e92\"> <wsse:RequestSecurityTokenResponse> <wsse:TokenType>wsse:SecurityContextToken</wsse:TokenType> <wsse:RequestedSecurityToken> <wsse:SecurityContextToken wsu:Id=\"SecurityToken-feb27552-6eb5-4a27-a831-e1bdfca326e2\"> <wsu:Identifier>uuid:bd0271ab-eef3-461a-9787-3bda26cf81b7</wsu:Identifier> <wsu:Created>2004-03-04T21:39:33Z</wsu:Created> <wsu:Expires>2004-03-05T01:39:33Z</wsu:Expires> </wsse:SecurityContextToken> </wsse:RequestedSecurityToken> <wsse:RequestedProofToken> <xenc:EncryptedKey xmlns:xenc=\"http://www.w3.org/2001/04/xmlenc#\"> <xenc:EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#rsa-1_5\" /> <KeyInfo xmlns=\"http://www.w3.org/2000/09/xmldsig#\"> <wsse:SecurityTokenReference> <wsse:KeyIdentifier ValueType=\"wsse:X509v3\">gBfo0147lM6cKnTbbMSuMVvmFY4=</wsse:KeyIdentifier> </wsse:SecurityTokenReference> </KeyInfo> <xenc:CipherData> <xenc:CipherValue>Dkbeo9rUjqjILrAw0dAkZ8/5K45B6NkKWrrnyUa1uSX9xKsPY6aR4k3zEt7Sn/fLCD1q3YR1fc03hza1AncPRY67+vxunBZdZX311BndWwc8GLsCCh2mQaGsZ7XBGSaq2sl8r7ozgupfKYsSk25Q3poRDbg4FeQHypqM6v9hxys=</xenc:CipherValue> </xenc:CipherData> <xenc:ReferenceList /> </xenc:EncryptedKey> </wsse:RequestedProofToken> </wsse:RequestSecurityTokenResponse> </soap:Body>
NOTE that the SoapBody above had been encrypted to hide the <SecurityContextToken/>; now that it is decrypted, it still contains an <EncryptedKey/>. now we are getting somewhere! i immediately noticed that the <SecurityContextToken/> would be used in the next request from the client to the actual web service. also, i had a good feeling that the EncryptedKey was a session key returned from the STS that would be used to sign and encrypt that request to the actual web service. at this point i had to extend my previous WS-Security implementation. started out by making it extract the <RequestedSecurityToken/> and <RequestedProofToken/> elements above. also, made it decrypt the <EncryptedKey/> using my private key. this returned a 16 byte key.
CF TRACES: stsReq.xml / stsRes.xml
then i had to use the results from the call to the STS to make the request to the actual web service itself.
from looking at the sample traces previously, i knew that the request would have to be signed and encrypted. between the 2, signing has to take place 1st. so i started to extend my code for doing XmlSignature. first, i had it insert the <SecurityContextToken/> (from above) into the <Security/> element of the SoapHeader. then, instead of signing the message using the RSA cipher with my private key (asymmetric), i switched it to sign with an HMAC-SHA1 hash using the 16 byte key returned from the STS. made the request, and it worked! this really shocked me, because the call worked without being encrypted. too cool
still wanted to add Confidentiality by encrypting the message, so i extended my XmlEncryption stuff next. first, i put in the logic to add the <ReferenceList/> element beneath the <Security/> element. all <DataReference/> does it point to the fact that the SoapBody is encrypted
<xenc:ReferenceList xmlns:xenc="http://www.w3.org/2001/04/xmlenc#"> <xenc:DataReference URI="#EncryptedContent-722f5613-d30c-4956-9641-030865e786b2" /> </xenc:ReferenceList>
next, i set it up to encrypt the SoapBody using the 16 byte key from the STS. tried to make the request and it failed saying BAD_DATA. this let me know it was a cryptographic problem, and probably having to do with decryption on the server side. looking at the client traces again, i saw the difference. the full framework client was encrypting the message using the AES128 (16 bytes), and i was using TripleDes (24 bytes). NOTE the full framework TripleDES implementation supports keys of both 16 and 24 byte keys. this brings up a couple problems. 1st, AES is not implemented for CE devices, so i can only use TripleDes for symmeric encryption. 2nd, my wrapper over the CE CryptoApi (/spCrypt) only supported 24 byte TripleDes keys, yet the STS was only returning 16 byte keys. the 1st thing i did was try to get the STS to return a 24 byte key. did not find any way to accomplish this. also looked into the concept of DerivedKeys, which is spec'd out by WS-SecureConversation. the idea is that once the client and the Web Service have the security context token from the STS, then they can both derive identical keys from that original token, yet still have the additional security by using different session keys each time. deriving keys is typically not very interop friendly, so i did not go down this path long. instead, i revisited my TripleDes implementation and got it to support 16 byte keys. once this was done, i ran my test client again, and it worked! so now the message was being both signed and encrypted with the 16 byte key from the STS, and the web service was able to consume it and return its results
finally, the real test was to immediately call the web service again. meaning that the client did the heavy work up-front to establish context by using the STS. now it could make multiple requests to the same web service, using that context ... thus having a Secure Conversation. slight extension to the test client, and it worked!
CF TRACES: wsReq.xml / wsRes.xml
WS-SecureConversation makes perfect sense for small devices because it can be used to lessen the amount of expensive crypto that must be done, while still providing secure communications. if you have Xml-Signature and Xml-Encryption implementations, then it is easy to provide WS-Security as well as WS-SecureConversation.
done. i dont plan on writing anymore .NETcf & WSE articles anytime soon. got some new ideas, but not sure which i'll do next? later