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.
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.
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.
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
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
To, and the most important, 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
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:DataReference URI="#_3" />
<e:DataReference URI="#_8" />
Decrypting the content of the Body element
Body of the SOAP message is encrypted using a session key. A typical
Body in a SOAP message is shown here:
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.
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.
<o:Reference URI="#_1" />
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.
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
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)
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.
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:
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:
- Session key
- Decrypt the
Body encrypted content using the AES128 algorithm using the derived key previously computed. The decrypted element should be of type
Decrypting the encrypted elements in the Security element of the Header
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
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.
Verifying the Signature
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
References 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.
<o:Reference URI="#_0" />
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
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
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.