Skip to main content

Articles

Featured Products

Windows Mobile Developer Controls
Windows Mobile Developer Controls
Stay in touch using the DEVBUSS RSS feeds.
 

News

Windows Mobile Developer Controls
Windows Mobile Developer Controls

SmartPhone (SP) and WebService Enhancements (WSE) with .NETcf

Written by casey chesnut  [author's bio]  [read 47304 times]
Edited by Derek

Download the code

Page 1  Page 2  Page 3  Page 4 

X.509v3 Certificates

From playing around with the WSDK (WSE1 alpha), I still had a bad taste in my mouth from X509 ... it was a real pain. WSE2 is better in that they have more documentation and even provide some sample certificates and tools to use. It was a little tricky to get them setup correctly on the smartPhone. On the desktop, you can use the Certificate MMC plugin to import the certificates. For the client you should import the Client Private.pfx and the Server Public.cer. The .pfx files have both public/private key pairs, and the .cer files only have public keys. On the server side you should import the Server Private.pfx. I also exported the Client Private.pfx without the private key to Client Public.cer, and imported the public key for the web service as well. The Client Public.cer also comes in handy on the SmartPhone, because the System.Security.Cryptography.X509Certificate class can read them directly. NOTE it could only read .cer files exported as DER, but not B64 unless they were decoded 1st. So the SmartPhone has the client public key, the server public key, but not the client private key. This was tricky. To get the client private key, I modified the desktop  KeyPal code posted to a security newsgroup my Michel Gallant. It lets you view the crypto containers, and export keys. When looking at this, it showed the certificate crypto containers. Modified the code to export both the public and private keys. So I exported the Client Private.pfx certificate to Client Private.pvk. This blob just happens to be the same structure when you call CryptExportKey using the low-level CyrptoApi; which I just happened to wrap in the /spCrypt article. So I can read that file with the private key on the device, and import the key to do decryption and signing on the device.

Another missing piece for Certificates it the KeyIdentifier. Not exactly sure how this info is acquired yet, but I know you can manually obtain it using the WseCertificate tool that comes with WSE2. Since the samples I am targeting only require the KeyIdentifier to be sent from the client to the WS, then I will just hardcode that KeyId on the client for now.

Finally, for encrypting messages and verifying signatures, we will use the Server Public.cer certificate. So we need to know how to get the .cer file into CyrptoApi format. Luckily, Michel (again) posted this code about an hour before I needed it: DecodeCertKey. Modified that code to accept an X509Certificate and spit out the exponent and modulus to be used in an RSAParams class.

Integrity (Xml-Signature)

I was worried about getting this piece to work because of XmlCanonicalization. Until I found a Mono implementation. Instead of building signatures 1st, I started with validating them. The request trace from the WSE2 X509Signing sample validated immediately. Then I made sure that it was really working by changing the body of the message, and then the SignedInfo element, and it did not verify for both of those instances. The UsernameSigning References validated the same, but I had problems figuring out how the signature was being done. The SignatureMethod is an HMACSHA1, which requires a key. It is documented how to generate the key using the P_SHA1 algorithm from TLS, except there is no mention of the label being 'WS-Security' and the key size being 24 bytes. Fumbling around with that for a couple days and I was finally able to verify signatures from WSE. If I manually modified the signature or references, then verification would fail as expected too. NOTE this was the only point I had to resort to Anakrino / Reflector to figure out what was going on. All other code was written according the the WS specifications and viewing traces between the WSE2 clients and services. Also, UsernameToken signing is up for review because of interoperability issues between implementations.

unsReq.xml / x509Sign.xml

Next, I went about building the signatures. Started with the WSE2 X509Signing Web Service 1st. It accepted the signature from my client, and validated it with the public certificate that was sent with it. Then I went on to the UsernameSigning sample and got it to work using the key generation method explained above. This implementation emulates the full WSE and signs the Body, Creates / Expires of Timestamp, and Action / From / MessageId / To of WS-Addressing. It supports all of these by default, but also lets you specify explicitly what you want (or dont want) to be signed. Also, its untested, but should support signing with DSA as well as RSA; although I dont think WSE supports DSA yet?

xmlSigUser.xml / xmlSigX509.xml

Xml-Enc and Xml-Sig

I had previously modified the WSE2 ResponseEncryption sample to turn off signing. Went back and turned that on, so that it would send a signature out, and receive an ecrypted response. Created a SoapExtension that would add the Timestamp and Addressing headers, as well as XmlEncryption and XmlSignature if necessary. Called the WS, and it worked, doing XmlSignature for the request and XmlEncryption for the response for the same call.

xmlEncSigReq.xml / xmlEncSigRes.xml

The more observant reader would see the scam above. Its not really Xml-Enc AND Xml-Sig, the previous test was Xml-Enc OR Xml-Sig. From all the reading I did, I kept running across sections detailing the problems with doing both. e.g. do you sign 1st and then encrypt, or do you encrypt 1st and then sign the EncryptedData? So I figured I had better test this. The 1st thing I did was extend the ResponseEncryption client sample. I set it up so that it would take 2 tokens. The servers public key to encrypt the message, and the clients private key to sign it. Running the client, it made a request that was both signed and encrypted, and the WS was able to handle it and return the results. Saving off that request, I made sure that my bits could validate it. Ended up it needed to be decrypted 1st and then the signature had to be checked. Then I updated the SoapExtension to reflect this, and made sure on outgoing requests, that it would sign 1st and then encrypt. Ran it against the WSE2 sample, and it failed. Ended up having to position the EncryptedData element before the Signature element, and then it worked correctly. Then I further extended the sample so that the WS would return a signature as well, and my client was able to validate it. So the traces below show WS-Utility Timestamp, WS-Addressing, and WS-Security (XmlEncryption and XmlSignature) used at the same time! NOTE be careful that you do not sign and encrypt with the same key pair, as this lends itself to a crypto attack.

encSigBothReq.xml / encSigBothRes.xml

Programming Model

The following code shows how to use the bNb.Ws class lib for doing both XmlEnc and XmlSig (for the request and response traces immediately above). The first step you have to do is add a web reference to the WSE-enabled web service as you normally would. Then you have to decorate it with the WsExtension SoapExtension which executes the handlers for Timestamp, Addressing, XmlEncryption, XmlSignature, etc... In your code you have to deal with the X509Certificates and Key pairs used for signing and encrypting. Then you have to set static objects on the handlers which specify how to encrypt/decrypt and/or sign/validate the requests and responses. Finally you call the WS using the web reference as you normally would, and the SoapExtension does the WSE processing. NOTE this could certainly be shaped into the same client-side programming model provided by the WSE.

//get my public key to send in request
//WS will use this to verify signature and encrypt response
...steps removed...
X509Certificate cert = new X509Certificate(rawKey);

//get my private key to sign outgoing request
//also used to decrypt response from WS
...steps removed...
RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider();
rsaCsp.FromXmlString(privKey.ToXmlString(true));

//setup object to sign request and validate response
bNb.Ws.XmlSigObject xso = new bNb.Ws.XmlSigObject();
xso.BinSecTok = new bNb.Ws.BinarySecurityToken(cert, bNb.Ws.EncodingType.Base64Binary);
xso.AsymmAlg = rsaCsp;
bNb.Ws.XmlSigHandler.SigObj = xso;

//setup object to decrypt response
bNb.Ws.XmlEncObject xdo = new bNb.Ws.XmlEncObject();
xdo.RSACSP = rsaCsp; //for header
xdo.SymmAlg = new TripleDESCryptoServiceProvider(); //for body
bNb.Ws.XmlEncHandler.DecObj = xdo;

//get server public key to encrypt session key
//random session key is used to encrypt SoapBody
...steps removed...
RSACryptoServiceProvider rsaCspServ = new RSACryptoServiceProvider();
rsaCspServ.ImportParameters(rsaParam);

//setup object to encrypt request
bNb.Ws.XmlEncObject xeo = new bNb.Ws.XmlEncObject();
xeo.EncKey = new bNb.Ws.EncryptedKey();
xeo.SymmAlg = new TripleDESCryptoServiceProvider(); //random session key
xeo.RSACSP = rsaCspServ; //to encrypt session key
xeo.KeyId = "bBwPfItvKp3b6TNDq+14qs58VJQ="; //servers key id
bNb.Ws.XmlEncHandler.EncObj = xeo;

//call web service
string [] symbols = {"FABRIKAM", "CONSTOSO"};
RespEncServ.StockQuote [] sqa = respEncProxy.StockQuoteRequest(symbols);

Performance

There is definitely a performance hit with WS-Security. Especially since this is on a device with limited resources, and we are doing cryptography along with Xml processing. But as the Practical Cryptography book says 'we have enough fast unsecure systems out there'. To mitigate this, it is a must to do asynchronous calls as to not freeze the UI; as well as making chunky calls instead of being chatty. Service Pack 2 (not for SmartPhone yet) will be faster too. Also, I had to change programming models midway into this. Started out trying to work with objects as SoapHeaders, but ended up switching to XmlDocument processing. At that point I put performance to the side, and just wanted it to work. This could be sped up by switching to XmlReader / XmlWriter. Next, there is a noted processing delay (server side) when working with the X509Certificates provided with WSE2. Supposedly legit certificate will perform faster. It would be interesting to see what the cost is for using WS-Security, as compared to running without it. Also, to see what the difference is between WS-Security and SSL/TLS ...

PPC 2002 Device

Of course, I dont actually have a smartPhone, so my current concept of its speed / slowness could be entirely wrong :( But I do have a beat up PPC 2002. First, I got the /spCrypt articles unit test to run on my device (above). NOTE none of the limitations with the crypto stuff on PPC2002 have to do with the crypto that is used for WSE. Then I made this articles unit test work on my device. For the most complicated calls (both signing and encryption), it took just over 5 seconds for the call to complete. NOTE my PPC has the SP2 beta running on it. SP1 should work too, because that is what SmartPhone uses, but might be slower. Dont know if it will run without either of the .NETcf service packs installed?

Conclusion

This should give you a great start for calling the next generation web services with the .NETcf. It provides the fundamentals of WS-Routing / Addressing, DIME, and WS-Security (including Xml-Encryption and Xml-Signature).

I explicitly did not attempt some of the WSE2 samples. Avoided any that used Microsoft.Web.Services.Messaging, including: HttpSyncStockService, TcpAsyncStockService, and TcpSyncStockService. Also did not do ContentBasedRouting because all the client does is read a referralCache.config file to see what Url the router is at. CustomFilters happens on the server, but could be done client-side with SoapExtensions. PipelineHosting was out of scope.

Along with Michel for help with Certificates, I need to thank Sebastien Pouliot, whom helped me past many Cryptography and WS-Security sticking points from his contributions to Mono.

Books

These are the books I referred to while writing this

  • Professional Web Services, Gallbraith and Hankison ...
  • SecureXml, Eastlake and Niles
  • Web Services Security, O'Neill
  • Xml Security, Dournaee
  • .NET Web Services, Ballinger
  • Web Services Enhancements, Evjen
  • Programming .NET Xml Web Services, Foggon and Maharray ...
  • Kerberos: The Definitive Guide, Garman
  • WSE 2 documentation
  • and the various Web Service Specifications

Source

  • spWse (SmartPhone WinForm) unit test app for calling WSE samples
  • bNb.Ws (CE class lib) SoapHeaders and SoapExtensions for calling WSE
  • bNb.Ws.Dime (CE class lib) ported Dime Sample code from Microsoft
  • System.Security.Cryptography - see /spCrypt
  • bNb.Sec - see /spCrypt
  • /wsAnyTest (ASP .NET Web Service) for testing {any} elements in SoapHeaders

Future

I might keep working on this to start doing the higher level WS-Security stack and new GXA WS specs as they are implemented in future WSE drops. Hopefully Indigo will not forget about mobile devices ... Regardless, it is good that MS is now starting to promote mobile web services with Vodafone.

Previous Page