Assuming you have been following this column, or are already familiar with federated security scenarios based on WS-Trust or WS-Federation protocols, you may also be familiar with the concept of Security Assertion Markup Language (SAML). SAML describes messaging protocols for active and passive federation but also describes the most common XML-based security token format used in federation: the SAML assertion. SAML assertions (or SAML tokens) are typically issued by a security token service (STS) based on WS-Trust and WS-Federation.
In earlier installments of this column I discussed how to implement active and passive federation scenarios with Windows Identity Foundation (WIF). In most of these articles, the use of SAML assertions was implied if not explicitly stated—but I neither explored the contents of the SAML assertion nor explained how those contents relate to a claims-based security model. In this two-part article, I'll discuss common features of a SAML assertion and show you how to build one using WIF components while also commenting on some common interoperability considerations.
A Quick Review of Token Issuance
Figure 1 illustrates the primary flow of communication for both active and passive federation scenarios—that of token issuance. A request for a security token is sent to the STS typically accompanied by credentials to authenticate the subject of the call (the user). The STS (in this case, also an identity provider) authenticates the user and issues a security token for that user. The security token often contains claims describing the user and her roles, permissions, or rights in the system for which the token was issued. Figure 1 illustrates the use of a SAML assertion to achieve this. Ultimately, the SAML assertion is sent to an application or service (the relying party) and is used to authorize access to features and functionality.
For active federation scenarios, the protocol used to request the SAML assertion is WS-Trust and involves a client-side proxy as the token requestor. Passive federation scenarios are based on WS-Federation and rely on browser redirects to achieve the same result: the browser as the requestor. Both protocols describe a mechanism for safely transporting SAML assertions issued by the STS, to the relying party. Now let's talk about SAML in more detail.
The SAML Assertion
SAML describes a security assertion (token) format; a set of messaging protocols describing mechanisms for requesting or resolving assertions, or otherwise interacting with the assertion provider (a SAML authority); and a set of profiles describing the flow of communication and how to bind messages to specific protocols such as SOAP and HTTP. There are two core versions of the SAML specification: SAML 1.1 and SAML 2.0. Differences between the two are mostly syntactical changes making protocol messages and assertions incompatible between versions on the wire—this due to improvements made as the standard evolved.
Although SAML messaging protocols and profiles describe how to safely transport SAML assertions, federation based on WS-Trust and WS-Federation also supplies profiles for requesting and issuing SAML assertions. For the purpose of this article I will focus only on the SAML assertion itself. SAML 1.1 has a strong implementation base, whereas SAML 2.0 tends to be the preferred format for new system development. The two assertion formats still essentially carry the same information, but I will refer to SAML 2.0 naming conventions here as I discuss what is contained inside an assertion.
From a simple viewpoint, a SAML assertion carries statements about a subject (the user) from a SAML authority (the STS in this case). If the STS is at an identity provider, it will also authenticate the user before asserting claims about her (see earlier articles that discuss claims-based security models). At the core of the assertion are a few important elements: the issuer, the subject, and relevant statements about the subject. Figure 2 lists these and other elements of a SAML assertion, describing their purpose in summary form to provide additional context.
Note: Keep in mind that the SAML assertion has many features, and in my summary I am purposely simplifying the interpretation.
Of the three types of statements—the authentication statement, the attribute statement, and the authorization decision statement—the attribute statement is the one of interest for this discussion as it is the statement that an STS will typically produce when issuing tokens. The attribute statement holds a collection of attributes (referred to as claims when working with WIF) describing the subject of the assertion. In a scenario where the STS from Figure 1 is an identity provider, the STS would first authenticate the user associated with the request, gather claims for that user, and produce an attribute statement within the SAML assertion with the issued claims. Although it is possible to augment this with authentication and authorization statements, these are not issued in typical STS implementations based on WS-Trust and WS-Federation, so I will table these for another conversation.
Now that you know what goes into a SAML token, let's talk about how you might create one using WIF components and in the process explore some of these elements in greater detail.
Generating SAML Tokens
When you employ WIF to implement a claims-based application or service, you typically will not work directly with security tokens. In federated scenarios, when the relying party receives a message accompanied by a SAML assertion, the assertion is deserialized and its attributes hydrated into a ClaimsPrincipal. The relying party may look at claims associated with the ClaimsPrincipal to authorize access. If you build a custom STS with WIF, you are equally shielded from the gory details of producing a SAML assertion. You supply the claims you want included in an attribute statement, and the runtime handles the rest.
Despite the happy path where SAML assertions are produced and processed for you, there are a few cases that give cause to rolling some code to produce SAML assertions, including these:
- Unit testing without the need to federate with an STS
- Testing how WIF produces various SAML assertions against another application, to resolve interoperability issues
- When building a custom STS, you may need to control features of the issued SAML assertion.
- Some applications act in a token-issuing capacity, though they are not formally an STS, and must issue SAML tokens in response to incoming requests.
WIF provides a very easy object model for producing both SAML 1.1 and SAML 2.0 assertions. Figure 3 summarizes the list of relevant components.
Functionality is similar for both SAML 1.1 and SAML 2.0 assertions, but I will use SAML 2.0 to illustrate. For the remainder of this article, as I discuss producing SAML 2.0 assertions, I'll refer to them as SAML without specifying a version number.
The Saml2SecurityTokenHandler is the driver for working with SAML assertions. You can use the Saml2SecurityTokenHandler to produce a runtime representation of a SAML assertion from XML, to serialize a runtime representation to XML, and to produce a SAML assertion purely at runtime. Ultimately, a Saml2Assertion is generated that can be used to initialize a Saml2SecurityToken for runtime interaction using a claims-based programming model.
To construct a Saml2SecurityTokenHandler programmatically, you first initialize a SecurityTokenDescriptor with the required elements. Requirements vary depending on whether you're producing a SAML assertion for passive federation (bearer token) or active federation (holder-of-key tokens). Further, they can vary when interoperating with other platforms. I'll break this down per scenario.
Producing Bearer Tokens
In this section I'll describe how to create a SAML token that uses the bearer subject confirmation method—the default format for passive federation scenarios based on WS-Federation. Figure 4 illustrates the passive sign-in request to the STS and resulting sign-in response that carries the SAML 2.0 bearer token. Of interest are the public and private keys, how they are applied to signing and encryption activities, and of course the contents of the SAML token. The SAML token is signed by the STS using its private key, and the relying party will verify this signature to ensure that the token contents was not tampered with en route. In addition, the relying party will check that the token was issued by a trusted STS—checking that the signature is cryptographically related to the STS public key held by the relying party. The SAML token is also encrypted for the relying party using its public key. Only the holder of the relying party private key (presumably the relying party) can decrypt the token and view its contents.
Let's discuss how to produce the signed SAML 2 bearer token shown in Figure 5 using WIF. You can start by creating a SecurityTokenDescriptor and setting the appropriate TokenType, Lifetime, and AppliesToAddress, as follows:
descriptor.TokenType = "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0";
DateTime issueInstant = DateTime.UtcNow;
descriptor.Lifetime = new Lifetime(issueInstant, issueInstant + new TimeSpan(8, 0, 0));
descriptor.AppliesToAddress = "http://localhost/RelyingPartyApplication";
The Lifetime property is used to set the IssueInstant attribute of the token and the NotBefore and NotOnOrAfter attributes of the <Conditions> element. AppliesToAddress initializes the <AudienceRestriction> element of the <Conditions> element—so that the relying party can verify that the token was intended for it.
To initialize the <AttributeStatement>, you need only create a ClaimsIdentity with the collection of claims you want to be included as attribute name and value pairs and assign it to the Subject property:
descriptor.Subject = new ClaimsIdentity(claims);
For the TokenIssuerName, you can supply the X509 distinguished name of the STS authentication certificate—which may be the same certificate used to sign issued tokens. To supply signing credentials to the SecurityTokenDescriptor, you must first get a reference to the signing certificate as an X509Certificate2 instance. From this you can create a SecurityKeyIdentifier and produce a SigningCredentials instance, as shown in Figure 6.
Note: Certificates created with makecert.exe do not include a subject key identifier and thus cannot be used in this manner. You should produce certificates with Windows Server or purchase a certificate from Verisign or an equivalent provider.
To produce a Saml2SecurityToken from this information, create a Saml2SecurityTokenHandler and call CreateToken() passing the descriptor, as follows:
Saml2SecurityToken token = tokenHandler.CreateToken(descriptor) as Saml2SecurityToken;
You can now write this token to a memory or file stream to see output similar to Figure 5, as follows:
FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);
XmlDictionaryWriter xmlwriter = XmlDictionaryWriter.CreateTextWriter(fs);
To encrypt the bearer token for a particular relying party, you can set the EncryptingCredentials property of the descriptor. The process is similar to SigningCredentials. First you load the certificate from the certificate store and produce a security key identifier. Then you construct EncryptingCredentials from the security key and security key identifier. You must also provide an encryption algorithm, which typically defaults to that shown in Figure 7.
Producing Holder-of-Key Tokens
Producing a SAML token that uses the holder-of-key subject confirmation method is required for active federation scenarios based on WS-Trust. This type of token includes a proof key to further mitigate man-in-the-middle attacks. Figure 8 captures the highlights of this flow of communication and the related keys involved in the exchange. Compared to Figure 4, the main addition is that of the proof key, shown as produced by the STS in this scenario. The client requests a token and, as in the previous scenario, signs the token with its private key and encrypts it using the public key of the relying party. In addition, the STS produces a key pair (the proof key) and embeds the public key portion in the encrypted SAML token. The response to the request includes the private key encrypted for the client, and the client must use this key to sign requests sent to the relying party. The relying party then verifies that the proof key embedded in the SAML token indeed has a cryptographic relationship with the key used to the sign the incoming request.
Note: This is merely one of many possible ways to work with proof keys. Proof keys can be symmetric or asymmetric. Proof keys can be generated by the client, by the STS, or negotiated with key material provide by both parties. The WS-Trust specification discusses these possibilities at length, but the following discussion should at least give you an idea how to produce a SAML token that includes proof.
The code to produce a SAML holder-of-key token is identical to that discussed in the previous section, with the exception that the Proof property of the descriptor is set as shown in Figure 9. Doing this alters the <Subject> element of the token shown in Figure 5 to include a new subject confirmation method and to include <SubjectConfirmationData> with a <KeyInfo> section referencing the proof key, as shown in Figure 10.
There are alternative ways to include <KeyInfo> that are sometimes better for interoperability with Java platforms, and I'll discuss those techniques in Part 2.
In this article I showed you how to produce SAML tokens in code so that you can do things like produce test harnesses for interoperability testing or produce tokens from client applications without federating with an STS. In the next article, part 2, I will show you how to handle some interoperability issues and how to produce a custom ClientCredentials type that suppresses federation to produce SAML tokens at the client.
Michele Leroux Bustamante (firstname.lastname@example.org) is chief architect for IDesign, chief security architect for BiTKOO, a Microsoft Regional Director for San Diego, and a Microsoft MVP for Connected Systems. Her latest book is Learning WCF (O'Reilly), and she blogs at www.dasblonde.net.