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