Introduction
When you use a federation binding, WCF will request an authentication token to an Identity Provider server. This Identity Provider implements a STS (Security Token Service) that generates an XML message to transport the security token. This message exchange between the application and the STS involves, in its simpler form, an RST message (Request Security Token) generated by the requesting application and a RSTR message (Request Security Token Response) generated by the STS in response to the RST.
Basically, an STS implements some actions of the WS-Trust contract, the minimum being the Issue action that generates the RSTR message in response to an incoming RST. The WCF framework doesn't implement those mechanisms but provides an implementation of the mechanisms necessary to write it. In fact, the RST generation is done in Cardspace, but no specific classes are provided in WCF for its support. As for the RSTR, some sample implementations are provided by Microsoft but there is no class support for it in the WCF API. When you setup WCF for WS-Federation, you activate the WS-* mechanism to secure the transport of the messages. The security in WCF is a very serious matter, and we are going to see how Microsoft applies the WS-* specifications.
When WSHttp* bindings are used, message security is activated, it means that WCF will use WS-* standards to secure the transport of the information, in this case the RST and the RSTR. This involves specifications like WS-SecureConversation, WS-Security that uses XML encryption (XMLEnc) and XML signature (XML Dsig). Those specifications can be found on the OASIS web site. The case we are going to analyze uses a SAML token, and the RST/RSTR messages have been generated from a STS sample provided by Microsoft which runs on the July CTP of WCF.
As I'm myself writing a full STS server that must interoperate with the Microsoft framework, and regarding the problems I was facing to integrate my STS in the WCF framework, I looked for a tool that could verify the RSTR I was creating in response to a WCF RST, but I couldn't find any. So I decided to write my own! I first started to write a RSTR verifier based on messages exchanged with an Infocard sample of the May CTP of WCF. Between the version of May and the one of July, many changes in the configuration have been made regarding the usage of WS-SecureConversation to make it as secure as possible.
The analysis of the messages requires that you have the private keys of all the certificates that are used for the encryption and the signature of the messages. The certificate of the STS is used to establish the secure conversation between the requesting application and the STS.
Background
This article doesn't use any coding, however, it assumes a good knowledge of XML and some understanding of basic cryptography like using symmetric and asymmetric algorithms for encryption and signature purposes. I just attached a small tool that helps understand the basics of cryptography used in this article.
Configuration of Cardspace
Cardspace is configured using a managed card using a username-password authentication and pointing to the address of our MEX. Cardspace requires that the MEX is sent from an HTTPS server. We configured our MEX to point to a server that hosts my STS and that provides to Cardspace the STS certificate. This configuration avoids the exchange of the certificate using the SCT protocol. Of course, any STS compliant with Cardspace can do like the ones published by Microsoft.
WCF won't allow getting the full Request and Response messages we are going to analyze. In order to get those messages, I used Cardspace on a PC and the STS was hosted on another machine. Using a tool like Ethereal, you can listen to the HTTP channel and extract the messages. If you are familiar with WCF, you know that you can trace the XML messages, however, Microsoft removes from the messages all binary data like the Nonce that are necessary to compute the derived keys.
Analyzing WS-SecureConversation
WS-SecureConversation is quite a complex specification, and is able to adapt to many security challenges. WCF implements most of the possible configurations for it at this time. In this paper, I'm going to analyze a couple of RST/RSTR that I get by running my STS implementation as an Identity Provider for Cardspace.
Secure conversation is used to protect the transportation of the RST/RSTR exchange in an XML message. It uses two concepts of security, privacy and integrity. Privacy is insured by the usage of XML encryption to cipher the data to transport in the message, and integrity is insured using XML signature to sign parts of the message. This XML security requires a certificate with its private key in the STS. The client application only needs to know the public key of this certificate. We are going to first analyze the encryption of the body of the message, then we will see how the signature is used, and finally how the STS authenticates the client. All this can help understand how WCF works, and how it is possible to write an Identity provider that interoperates with WCF at the conversation level. Then, when this level of interoperation is achieved, it is possible to build a fully interoperable Identity provider.
WSstarUtility application
In order for you to test the different concepts that are developed in this article, I wrote a simple utility application that you can use to decrypt the different elements of the SOAP messages that we are going to analyze. This application is designed to use Base 64 data which is the type of coding that is used to transport binary data over XML. This is just a simple tool I developed to make this article a bit more interactive. Beware that the user interface is very basic, and there are no checks of the inputs. However, exceptions are handled in case you enter wrong data.
For the RSA decryption, it uses the certificate that was used by the protocol to create the messages, so before using the application, don't forget to install the certificate.
Before using the application, you must read the analysis of the messages...
Following are some screenshots of this crypto application.
This screen is the main screen of the application. Each button opens a dialog box for a specific crypto function used in the analysis of the SOAP messages.
- Decrypt with RSA: decrypts data using the RSA public key algorithm (requires a certificate), used for the EncryptedKey
- Decrypt with AES: decrypts data using the symmetric AES algorithm, used for the EncryptedData
- Compute SHA: Computes the SHA1 of some data, used for the signature digest
- Compute HMACSHA1: Compute a HMAC with SHA1 digest, used for the signature
- Compute PSHA1: Compute a derived key using PSHA1
This screen shows the decryption of the master key using the data of the SOAP message. All data are entered in Base64 as this is what you get from the SOAP message. The output can be given in binary or Base64.
The above screen shows the calculation of a derived key. As for the previous one, the input keys are to be entered in Base64 format.
This screen illustrates the decryption of the body of the message. The key used is the derived key that was computed previously using the PSHA1 function. The result is there in ASCII, and is a RequestSecurity element.
Part 1: Request SOAP message containing the RST (RequestSecurityToken)
Structure of the SOAP message
The SOAP envelope is composed of two elements, a Header
and a Body
. The Header
contains information about the message such as security data, and the Body
contains the content of the message itself.
The header typically contains elements like Action
, MessageID
, To
, and the most important, the Security
element.
The Security
element contains all the necessary information to decrypt the encrypted parts of the message and to verify the signature. The list of the encrypted elements contained in the whole message, header, and body, are listed in the Security
/ReferenceList
element. This element contains the DataReference
elements that allow finding the different EncrytpedData
by their Id
property. Here is how this element looks like:
<e:ReferenceList xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:DataReference URI="#_3" />
<e:DataReference URI="#_8" />
</e:ReferenceList>
Decrypting the content of the Body element
The Body
of the SOAP message is encrypted using a session key. A typical Body
in a SOAP message is shown here:
Listing 1
<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#aes128-cbc"/>
<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:SecurityTokenReference>
</KeyInfo>
<e:CipherData>
<e:CipherValue>DjVrX8LpF3I6T5jFXG…WjG0c+taQ==</e:CipherValue>
</e:CipherData>
</e:EncryptedData>
</s:Body>
The content of the Body is encrypted in an EncryptedData
element. A complete description of the EncryptedData
element can be found in the specification of XML encryption here.
The type of this encrypted data is Content
and the attribute Algorithm
in the EncryptionMethod
element gives the encryption algorithm used. In this case, the symmetric algorithm that has been used to encrypt the data is AES 256.
<e:EncryptionMethod
Algorithm=http://www.w3.org/2001/04/xmlenc#aes128-cbc"/>
The KeyInfo
element allows resolving the key that has been used to encrypt the data with the AES algorithm. This KeyInfo
uses a SecurityTokenReference
that points to another element with an Id
equal to _1.
<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:SecurityTokenReference>
</KeyInfo>
This reference is an element that can be found under the Header
element of the SOAP message. This reference is, in our case, a DerivedKeyToken
that contains the data necessary to derive the encryption key used to encrypt the data of the body. This element is shown listing 2.
Listing 2
<c:DerivedKeyToken u:Id="_1"
xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<o:SecurityTokenReference>
<o:Reference
ValueType="http://docs.oasis-open.org/wss/
oasis-wss-soap-message-security-1.1#EncryptedKey"
URI="#uuid-4c692bc8-381a-4983-be4b-ef7dbfd00935-17"/>
</o:SecurityTokenReference>
<c:Offset>0</c:Offset>
<c:Length>16</c:Length>
<c:Nonce>KQBSz90ZzToGt9WV3lOIeA==</c:Nonce>
</c:DerivedKeyToken>
This DerivedKeyToken
element contains two useful data to compute the derived key. One is another Reference
that points this time to an EncryptedKey
element with the Id
"uuid-4c692bc8-381a-4983-be4b-ef7dbfd00935-17", and the other is a nonce value encoded in Base64 that is used to compute the derived key of the key contained in the EncryptedKey
element.
Key derivation is described in the WS-SecureConversation specification. The key derivation algorithm which is used is defined as PSHA1. This function is:
key = PSHA1(secret, label + seed)
where:
secret
is the master secret key seed
is the nonce label
is a label that can be given, or a default value is used.
When the label is not sent with the nonce, a default value defined as "WS-SecureConversation" must be used. However, Microsoft in their implementation uses a default label which is different, and its value is "WS-SecureConversationWS-SecureConversation".
To extract the derived key from the result of a PSHA1, a length and an offset are required. In our case, the derived key should be 16 bytes because it is used with an AES 128 algorithm. As no Offset
value is given in the DerivedKeyToken
, it should be set to 0.
The encrypted key is described by the element given in the following listing. This key is called the session key, and is also used to compute other derived keys. It is used as well to encrypt the Body
, and optionally the Signature
elements in the Security
element of the response message. This key is normally encrypted with an RSA algorithm that uses the public key of the STS certificate to encrypt it. This assumes that the client application that builds the RST message has the certificate of the STS, or has obtained it during a certificate negotiation.
Listing 3
<e:EncryptedKey Id="uuid-4c692bc8-381a-4983-be4b-ef7dbfd00935-17"
xmlns:e="http://www.w3.org/2001/04/xmlenc#">
<e:EncryptionMethod
Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p">
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"
xmlns="http://www.w3.org/2000/09/xmldsig#" />
</e:EncryptionMethod>
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<o:SecurityTokenReference>
<o:KeyIdentifier
ValueType="http://docs.oasis-open.org/wss/oasis-wss-
soap-message-security-1.1#ThumbprintSHA1"
EncodingType="http://docs.oasis-open.org/wss/2004/01/
oasis-200401-wss-soap-message-
security-1.0#Base64Binary">
WELxHZqkA1Xvh7A9ebgWRrW2kf0=</o:KeyIdentifier>
</o:SecurityTokenReference>
</KeyInfo>
<e:CipherData>
<e:CipherValue>XsApZde4j3w35HGt…YVHkbY0MFZIg=</e:CipherValue>
</e:CipherData>
</e:EncryptedKey>
The EncryptedKey
element is very similar to the EncryptedData
element that we have seen previously. It contains an EncryptionMethod
element that gives the encryption algorithm used to encrypt the key, and a KeyInfo
to get the encryption key used in this algorithm.
The algorithm used to encrypt the key is RSA-OAEP-MGF1P with a SHA1 digest. This is an asymmetric algorithm that requires a public key for the encryption and a private key for the decryption. It means that the KeyInfo
should describe a way for the STS to find the corresponding private key to the public key that was used. In this case, the KeyInfo
provides a reference to the certificate that was used to encrypt the key. With this reference, the STS should be able to extract the private key from its certificate store. The KeyInfo
has the following form:
<KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
<o:SecurityTokenReference>
<o:KeyIdentifier
ValueType="http://docs.oasis-open.org/wss/oasis-
wss-soap-message-security-1.1#ThumbprintSHA1"
EncodingType="http://docs.oasis-open.org/wss/2004/01/
oasis-200401-wss-soap-message-
security-1.0#Base64Binary">
WELxHZqkA1Xvh7A9ebgWRrW2kf0=
</o:KeyIdentifier>
</o:SecurityTokenReference>
</KeyInfo>
The SecurityTokenReference
of this KeyInfo
contains a KeyIdentifier
that allows finding the private key based on the thumbprint of the certificate used for the encryption. This key identifier value is a base64 of this thumbprint. This string can be converted to its binary value, and used to get the private key from the certificate store of Windows, for example, to decipher the encrypted key.
Summary to decrypt the body:
- With the thumbprint, get the STS certificate private key.
- Decrypt the Session key using RSA-OAEP-MGF1P.
- Get the derived key using the PSHA1 algorithm with the following parameters:
- Nonce
- Label=WS-SecureConversationWS-SecureConversation
- Session key
- Decrypt the
Body
encrypted content using the AES128 algorithm using the derived key previously computed. The decrypted element should be of type RequestSecurityToken
.
Decrypting the encrypted elements in the Security element of the Header
Normally, the Security
element of the Header
contains one or more EncryptedData
elements. Those elements are of type Element
. Each of them contains an element that can be a Signature
or an authentication token, generally a UsernameToken
. When certificate authentication is used, the element is not encrypted and is of type BinarySecurityToken
.
The encryption method for those elements is strictly the same used for the Body
content that we have decrypted previously. Just need to apply the same pattern to decrypt them.
It should be noted that the encryption of the signatures is not mandatory, and that they can be transmitted unencrypted in the Security
element. It depends on the policy that has been requested by the STS.
In the example I'm using, the Signature
is not encrypted and there is an EncryptedData
element of type 'element
' to be decrypted. The same derived key that was used to decrypt the Body
is to be used.
Once the data is decrypted, we get a UsernameToken
that contains a Username
and a Password
that the server can use to authenticate the requestor application. Listing 4 shows the result of the decryption.
Listing 4
<o:UsernameToken u:Id="uuid-5fbebc00-4101-4606-aa0b-b1aa2a04d6f9-34"
xmlns:o="http://docs.oasis-open.org/wss/2004/01/
oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-
200401-wss-wssecurity-utility-1.0.xsd">
<o:Username>orouit</o:Username>
<o:Password
o:Type="http://docs.oasis-open.org/wss/2004/01/oasis-
200401-wss-username-token-profile-1.0#PasswordText">
gemalto
</o:Password>
</o:UsernameToken>
Verifying the Signature
The Signature
elements are used for integrity verification. In the case we are studying, the authentication method used is username password. There is in fact only one Signature
element for this authentication method; if we were using certificate authentication, there would be another Signature
element using an RSA signature with the requestor certificate.
But, let's deal with our case which is in fact a bit simpler. The Signature
element that has been put in the Security
is used to verify the integrity of several elements of the message. The list of those elements can be configured in the MEX information that the STS sends in the first exchange. Listing 5 shows an extract of the Signature
for this sample message. To make it shorter, I just removed some of the Reference
s to the elements that are to be signed.
It is important to notice that the Signature
element could be encrypted in the Security
element. In this case, the signature is just put in a clear form. Encrypting the signature is a matter of configuration of the requestor application. The conversation layer should be able to adapt any configuration.
Listing 5
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#hmac-sha1" />
<Reference URI="#_2">
<Transforms>
<Transform
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>Ymirle7eo8IRR4QdSre7zreo0Ho=</DigestValue>
</Reference>
…
<Reference URI="#uuid-4c692bc8-381a-4983-be4b-ef7dbfd00935-18">
<Transforms>
<Transform
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>DBRKoGWw1qKmROVPT88IrjBC0QM=</DigestValue>
</Reference>
<Reference
URI="#uuid-5fbebc00-4101-4606-aa0b-b1aa2a04d6f9-34">
<Transforms>
<Transform
Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod
Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>ejz93PC9wJJpvxFW4hcj5pZvFXo=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>vdfeD+W/p++f2LBCAwNU3oVH0/0=</SignatureValue>
<KeyInfo>
<o:SecurityTokenReference>
<o:Reference URI="#_0" />
</o:SecurityTokenReference>
</KeyInfo>
</Signature>
The elements that take part in the signature process are described in the SignedInfo
element. First, we find the canonicalization method that refers to the way to serialize the SignedInfo
element before it is signed, then the signature method which is used to sign the serialized data. In this case, the C14N canonicalization is used, and the signature is a HMAC-SHA1 of the result of the canonicalization using the signing key reference in the KeyInfo
element.
Then, we find several Reference
elements that contain the URI to an element of the message, the transformations to apply on this element to get a digest, and the digest data itself. Again, the CN14 canonicalization method is used, and the digest is obtained performing a SHA1 on the canonicalized data.
The reference to the key used to sign the SignedInfo
element is given by the KeyInfo
element. The reference in the KeyInfo
points to the following DerivedKey
element:
<c:DerivedKeyToken u:Id="_0"
xmlns:c="http://schemas.xmlsoap.org/ws/2005/02/sc">
<o:SecurityTokenReference>
<o:Reference
ValueType="http://docs.oasis-open.org/wss/oasis-
wss-soap-message-security-1.1#EncryptedKey"
URI="#uuid-4c692bc8-381a-4983-be4b-ef7dbfd00935-17" />
</o:SecurityTokenReference>
<c:Offset>0</c:Offset>
<c:Length>16</c:Length>
<c:Nonce>SQHzMAYbmyZQOQ2jsyEbcg==</c:Nonce>
</c:DerivedKeyToken>
This DerivedKeyToken
element is strictly similar to the one used to get the key to decrypt the Body
and the UsernameToken
elements, and it refers to the same session key.
To verify the signature value, the STS must compute the digest value for each referenced element in the SignedInfo
and check with the ones in the original SignedInfo
. If all digests match, then the SignedInfo
element must be serialized using the C14N canonicalization method and the HASHMAC-SHA1 checked with the original one. If it matches, then the signature is fully verified.
Points of Interest
When you activate the security features of WCF, this is what happens at the protocol level for all the messages that are exchanged between a client and a server application. So far, we have only seen the request message. The response message will be discussed in the second part of this article.
Using the WCF framework to write your services spares you all the burden of this protocol. However, I thought it would be interesting for those who are dealing closer with the protocol level to have a detailed explanation of a whole exchange at this level of protocol. Security on the internet has become a priority for big companies like Microsoft. I hope that this article will help those who are willing to understand what's happening under the hood of Web service security.
The analysis of the response message is discussed in the second part of this article.