How to: Create a Security Token Service

A security token service implements the protocol defined in the WS-Trust specification. This protocol defines message formats and message exchange patterns for issuing, renewing, canceling, and validating security tokens. A given security token service provides one or more of these capabilities. This topic looks at the most common scenario: implementing token issuance.

Issuing Tokens

WS-Trust defines message formats, based on the RequestSecurityToken XML Schema definition language (XSD) schema element, and RequestSecurityTokenResponse XSD schema element for performing token issuance. In addition, it defines the associated Action Uniform Resource Identifiers (URIs). The action URI associated with the RequestSecurityToken message is http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue. The action URI associated with the RequestSecurityTokenResponse message is http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue.

Request Message Structure

The issue request message structure typically consists of the following items:

  • A request type URI with a value of http://schemas.xmlsoap.org/ws/2005/02/trust/Issue.

  • A token type URI. For Security Assertions Markup Language (SAML) 1.1 tokens, the value of this URI is http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1.

  • A key size value that indicates the number of bits in the key to be associated with the issued token.

  • A key type URI. For symmetric keys, the value of this URI is http://schemas.xmlsoap.org/ws/2005/02/trust/SymmetricKey.

In addition, a couple of other items might be present:

  • Key material provided by the client.

  • Scope information that indicates the target service that the issued token will be used with.

The security token service uses the information in the issue request message when it constructs the Issue Response message.

Response Message Structure

The issue response message structure typically consists of the following items;

  • The issued security token, for example, a SAML 1.1 assertion.

  • A proof token associated with the security token. For symmetric keys, this is often an encrypted form of the key material.

  • References to the issued security token. Typically, the security token service returns a reference that can be used when the issued token appears in a subsequent message sent by the client and another that can be used when the token is not present in subsequent messages.

In addition, a couple of other items might be present:

  • Key material provided by the security token service.

  • The algorithm needed to compute the shared key.

  • Lifetime information for the issued token.

Processing Request Messages

The security token service processes the issue request by examining the various pieces of the request message and ensuring that it can issue a token that satisfies the request. The security token service must determine the following before it constructs the token to be issued:

  • The request really is a request for a token to be issued.

  • The security token service supports the requested token type.

  • The requester is authorized to make the request.

  • The security token service can meet the requester's expectations with respect to key material.

Two vital parts of constructing a token are determining what key to sign the token with and what key to encrypt the shared key with. The token needs to be signed so that when the client presents the token to the target service, that service can determine that the token was issued by a security token service that it trusts. The key material needs to be encrypted in such a way that the target service can decrypt that key material.

Signing a SAML assertion involves creating a SigningCredentials instance. The constructor for this class takes the following:

  • A SecurityKey for the key to use to sign the SAML assertion.

  • A string identifying the signature algorithm to use.

  • A string identifying the digest algorithm to use.

  • Optionally, a SecurityKeyIdentifier that identifies the key to use to sign the assertion.

void AddSigningCredentials(SamlAssertion assertion, SecurityKey signingKey)
{
    SigningCredentials sc = new SigningCredentials(signingKey,
        SecurityAlgorithms.RsaSha1Signature, SecurityAlgorithms.Sha1Digest);
    assertion.SigningCredentials = sc;
}
Sub AddSigningCredentials(ByVal assertion As SamlAssertion, _
    ByVal signingKey As SecurityKey)
    Dim sc As New SigningCredentials(signingKey, _
    SecurityAlgorithms.RsaSha1Signature, SecurityAlgorithms.Sha1Digest)
    assertion.SigningCredentials = sc

End Sub

Encrypting the shared key involves taking the key material and encrypting it with a key that the target service can use to decrypt the shared key. Typically, the public key of the target service is used.

byte[] EncryptKey(byte[] plainTextKey, SecurityKey encryptingKey)
{
    return encryptingKey.EncryptKey(SecurityAlgorithms.RsaOaepKeyWrap, plainTextKey);
}
Function EncryptKey(ByVal plainTextKey() As Byte, _
        ByVal encryptingKey As SecurityKey) As Byte()
    Return encryptingKey.EncryptKey(SecurityAlgorithms.RsaOaepKeyWrap, plainTextKey)
End Function

In addition, a SecurityKeyIdentifier for the encrypted key is needed.

SecurityKeyIdentifier GetKeyIdentifierForEncryptedKey(byte[] encryptedKey,
    SecurityToken encryptingToken)
{
    SecurityKeyIdentifier encryptingKeyIdentifier = new SecurityKeyIdentifier(encryptingToken.CreateKeyIdentifierClause<X509ThumbprintKeyIdentifierClause>());
    return new SecurityKeyIdentifier(new EncryptedKeyIdentifierClause(encryptedKey, SecurityAlgorithms.RsaOaepKeyWrap, encryptingKeyIdentifier));
}
Function GetKeyIdentifierForEncryptedKey(ByVal encryptedKey() _
 As Byte, ByVal encryptingToken As SecurityToken) _
    As SecurityKeyIdentifier
    Dim encryptingKeyIdentifier As New SecurityKeyIdentifier( _
        encryptingToken.CreateKeyIdentifierClause(Of X509ThumbprintKeyIdentifierClause)())
    Return New SecurityKeyIdentifier(New EncryptedKeyIdentifierClause( _
        encryptedKey, SecurityAlgorithms.RsaOaepKeyWrap, encryptingKeyIdentifier))
End Function

This SecurityKeyIdentifier is then used to create a SamlSubject as part of the SamlToken.

SamlSubject CreateSamlSubjectForProofKey(SecurityKeyIdentifier proofKeyIdentifier)
{
    List<string> confirmations = new List<string>();

    confirmations.Add("urn:oasis:names:tc:SAML:1.0:cm:holder-of-key");

    return new SamlSubject(null, null, "IssuerName", confirmations, null, proofKeyIdentifier);
}
Function CreateSamlSubjectForProofKey( _
    ByVal proofKeyIdentifier As SecurityKeyIdentifier) As SamlSubject
    Dim confirmations As List(Of String) = New List(Of String)()
    confirmations.Add("urn:oasis:names:tc:SAML:1.0:cm:holder-of-key")
    Return New SamlSubject(Nothing, Nothing, "IssuerName", _
        confirmations, Nothing, proofKeyIdentifier)
End Function

For more information, see Federation Sample.

Creating Response Messages

Once the security token service processes the issue request and constructs the token to be issued along with the proof key, the response message needs to be constructed, including at least the requested token, the proof token, and the issued token references. The issued token is typically a SamlSecurityToken created from the SamlAssertion, as shown in the following example.

SecurityToken CreateIssuedToken(SamlAssertion assertion)
{
    return new SamlSecurityToken(assertion);
}
Function CreateIssuedToken(ByVal assertion As SamlAssertion) As SecurityToken
    Return New SamlSecurityToken(assertion)
End Function

In the case where the security token service provides the shared key material, the proof token is constructed by creating a BinarySecretSecurityToken.

BinarySecretSecurityToken CreateProofToken(byte[] proofKey)
{
    return new BinarySecretSecurityToken(proofKey);
}
Function CreateProofToken(ByVal proofKey() As Byte) As BinarySecretSecurityToken
    Return New BinarySecretSecurityToken(proofKey)

End Function

For more information about how to construct the proof token when the client and the security token service both provide key material for the shared key, see Federation Sample.

The issued token references are constructed by creating instances of the SecurityKeyIdentifierClause class.

SecurityKeyIdentifierClause CreateTokenReference(SamlSecurityToken token)
{
    return token.CreateKeyIdentifierClause<SamlAssertionKeyIdentifierClause>();
}
Function CreateTokenReference(ByVal token As SamlSecurityToken) _
    As SecurityKeyIdentifierClause
    Return token.CreateKeyIdentifierClause( _
    Of SamlAssertionKeyIdentifierClause)()
End Function

These various values are then serialized into the response message returned to the client.

Example

For full code for a security token service, see Federation Sample.

See also