Page 1
Page 2
Page 3
Page 4
WoTe
Write once, Test everywhere !?!? ...
I knew C# looked familiar :) The encryption to support WSE
entails having an enhanced PROV_RSA_FULL provider found
on most Pocket PC PCs, including PPC 2002, but
not on the 2002 emulator. For PPC 2000, this can be added
by installing the enhanced encryption pack. Although my
Sys.Sec.Crypt library supports the DSA algorithm with
the PROV_DSS_DH provider found on SP 2003 and PPC 2003,
it is not yet necessary for making WSE2 calls. I extended
the .NETcf crypto bits to look for if CryptoApi, the Enhanced
Provider, and DSA are available.
I have tested the /spCrypt libraries
on the PPC 2003 and CE emulators and got working results.
Same goes for this articles unit tests with SP1 installed.
Initial testing on my PPC 2002 device (Audiovox Maestro)
showed some missing functions with Guid and Random # generation
that can be overcome by using different methods. There was
an unnecessary Crypto call being made (CryptProtectData)
that resulted in a MissingMethodException. One way
of doing HmacSha1 would throw a BAD_ALGID when destroying
the key, but HMAC can also be done with multiple SHA1 calls.
And RC2 symmetric encryption did not interop with the full
framework, although it does on the emulators for SP 2003,
PPC 2003, and CE .NET. So the variables are Device, OS,
CryptoApi, and Service Pack ... and this should all be tested
to get the codebase to the lowest common denominator. To
help with this, I wrote a WoTe class to find Device, OS,
and CLR info.
- Device - CE, PocketPC, SmartPhone
- OS - PPC 2000, PPC 2002, PPC 2003,
SP 2002, SP 2003, CE 4.1, CE 4.2
- CLR and ServicePack - .NETcf, SP1,
SP2, ... come on Whidbey!
- CryptoApi - Base RSA Provider, Enhanced
RSA Provider, DSA Provider
My current rule of thumb is to write
all class libs as Windows CE projects. Then put a thin SmartPhone
UI over that for testing. For kicks, I make PPC and CE WinForms
project and run the SP UI in those environments as well.
It ports with no changes, the only thing is that the menu
operation is funny with a stylus. For even more kicks I
will run those apps directly on a desktop computer.
Hopefully device and desktop .resx files will be made compatible
in Whidbey to make this easier. VS .NET could stand to do
a better job of importing existing 'Web References' as well.
Below are screen shots of the SmartPhone WSE unit test app
running on a PPC 2003 emulator, CE .NET emulator, and
on a desktop computer respectively.



WS-Security
This is the lowest level security specification
for web services. There are a number of other security
specifications which build upon this one (e.g. WS-Policy,
WS-Trust, WS-SecureConversation). This one is the core,
so it has to be built 1st. WS-Security is primarily used
for 3 different scenarios: Authentication, Confidentiality,
and Integrity. I will tackle these in this same order.
Authentication (Security
Tokens)
This is actually the reason I began
writing this article. 1st the Microsoft.com web service
went public. It is basically a 'hello world' web service
meaning it does not expose much functionality yet; but what
made it interesting is that it required a UsernameToken
SoapHeader to access the service. 2nd a Perl implementation
of UsernameToken came out before a .NET one did, other than
the WSE. Who cares if you are on the full framework, because
you have the WSE, but .NETcf was out of luck. So I threw
together a quick SoapHeader implementation and threw it
out to the newsgroups. That was the decision point when
I decided to attempt WSE for .NETcf again. NOTE you will
have to supply your own username and password to access
the service.
<Security xmlns="http://schemas.xmlsoap.org/ws/2002/07/secext" soap:mustUnderstand="1">
<UsernameToken d4p1:Id="SecurityToken-2506db16-f1c8-2129-7f36-13b576716003"
xmlns:d4p1="http://schemas.xmlsoap.org/ws/2002/07/utility">
<Username>Admin</Username>
<Password Type="wsse:PasswordDigest">ABC...=</Password>
<Nonce>yHZfWUJgSUMiUXcEdsPce2YXK6I=</Nonce>
<d4p1:Created>2003-11-08T00:05:19Z</d4p1:Created>
</UsernameToken>
</Security>
UsernameToken - Had already got it working
against Microsoft.com, so I knew it was at least partially
working. Next, I attempted to call the WSE2 sample. Before
calling it, I modified the sample app to make sure it did
not have to pass a Signature, and it worked without one.
When I tried to call it, it kept throwing a SoapFault with
'The Soap Header Security was not understood'. Looking at
the trace of the message from the WSE2 client, the 'wsse'
namespace version was /2002/12/ instead of /2002/07/, so
I upgraded mine and it worked. To test it more, I ran it
against the WSE1 UsernameSigning sample, and it worked fine.
I did have to modify the WS IsValid() method, so that it
only looked for a UsernameToken SecurityToken, and not a
Signature SecurityElement.
<Security xmlns="http://schemas.xmlsoap.org/ws/2002/12/secext" soap:mustUnderstand="1">
...
</Security>
BinaryToken - this is another type of
Security Token that can be passed. For this one, all I did
was read in an X509v3 certificate that was stored as a file
on the device. Then I got the raw data and base64 encoded
it. Passed it to an echo web service I wrote that just
read the certificate and spit back the name. It worked with
WSE2. Make sure you only pass around public certificates
using this method.
<Security xmlns="http://schemas.xmlsoap.org/ws/2002/12/secext" soap:mustUnderstand="1">
<BinarySecurityToken d4p1:Id="SecurityToken-acbfa8b0-ad6e-9164-3c25-4838ee2c2a68"
ValueType="wsse:X509v3" EncodingType="wsse:Base64Binary"
xmlns:d4p1="http://schemas.xmlsoap.org/ws/2002/07/utility">
ABC...==
</BinarySecurityToken>
</Security>
Other Security options include Kerberos
and custom (binary & xml) tokens. I'd been reading about
Kerberos and wanted to attempt it, but it requires
my environment to have a domain, which I dont have the time
to setup :(. My code would have to be extended to support
these.
Confidentiality (Xml-Encryption)
The next level of difficulty is XmlEncryption.
Started out with SymmetricEncryption from WSE1, because
that sample does not exist for WSE2. Initially, the
sample just encrypted the request, and the response came
back in the clear. For the request, the SoapHeader needed
a <Security\SecurityTokenReference> to be added along
with the <EncryptedData> in the SoapBody. Made a SoapExtension
to do this. The client application has to set some static objects on
the SoapExtension that tells it how it wants to encrypt
and decrypt, and the SoapExtension handles the rest. At
1st, the web service could not decrypt my messages, and
was throwing back an XmlException. To test that my System.Security.Cryptography
was really compatible, I grabbed the encrypted message from
the WSE1 client trace, and decrypted it. This revealed that
the messages were getting IV's appended to them, per the
XENC spec when I looked it up. Added this logic to my code,
and I got a successful response from the WebService. Then
I extended the WS to send back an encrypted response as
well, and updated the XmlEncryption SoapExtension do decrypt
it.
xmlEncSymmReq.xml
/ xmlEncSymmRes.xml
Then I went to the WSE2 ResponseEncryption
sample. In this one, the request sends a X509 certificate
to the WS as a BinarySecurityToken in the SoapHeader; and
the response returns an EncryptedKey in the SoapHeader,
with EncryptedData in the SoapBody. For X509 Certificates,
see the section below. The X509 cert has the public key
that the WS uses to encrypt the response. The service creates
a TripleDes session key to encrypt the SoapBody. That session
key is then encrypted with the RSA public key from the certificate.
On the client side, it discovers the EncryptedKey 1st, and
uses the corresponding RSA private key to decrypt. That
session key is then loaded into a TripleDesCryptoServiceProvider
to decrypt the actual SoapBody. Works great. I did have
to mod the web service to not require an XmlSignature at
this time, and just look for the BinarySecurityToken directly.
xmlEncRes.xml
The final one is the WSE2
AsymmetricEncryption sample. The ResponseEncryption sample
also uses Asymmetric encryption, but only the Response.
This is the same, except it is for building the request,
while the response comes back in plain text. One other difference
is that instead of sending the public key as a BinarySecurityToken,
it sends a KeyIdentifier. So the client generates a session
key. That session key is encrypted with the servers public
key and added as an EncryptedKey to the SoapHeader. That
session key is also used to encrypt the SoapBody and replace
it with an EncryptedData element.
xmlEncAsymmReq.xml
Right now it supports symmetric and
asymmetric encryption. EncryptedKeys and EncryptedData.
Named keys, X509 certificates, and known KeyIdentifiers.
Encrypting the entire message, and also just a portion of
the message using my LameXpath implementation (does
anybody have a good Xpath imp. for .NETcf?). It would have
to be extended to support referenced keys and arbitrary
data, as well as multiple EncryptedKey and EncryptedData
sections in a single message.
Previous Page
Next Page