Thursday, October 27, 2011

Apache CXF STS documentation - part IV

In the previous post I covered the TokenProvider interface, which is used to generate tokens in the STS, and an implementation that ships with the STS to generate SecurityContextTokens. In this post, I will cover the other TokenProvider implementation that ships with the STS, which issues SAML Tokens (both 1.1 and 2.0).

1) The SAMLTokenProvider

The SAMLTokenProvider can issue SAML 1.1 and SAML 2.0 tokens. To request a SAML 1.1 token, the client must use one of the following Token Types:
  • http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1
  • urn:oasis:names:tc:SAML:1.0:assertion
To request a SAML 2.0 token, the client must use one of the following Token Types:
  • http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0
  • urn:oasis:names:tc:SAML:2.0:assertion
The following properties can be configured on the SAMLTokenProvider directly:
  • List<AttributeStatementProvider> attributeStatementProviders - A list of objects that can add attribute statements to the token.
  • List<AuthenticationStatementProvider> authenticationStatementProviders - A list of objects that can add authentication statements to the token.
  • List<AuthDecisionStatementProvider> authDecisionStatementProviders - A list of objects that can add authorization decision statements to the token.
  • SubjectProvider subjectProvider - An object used to add a Subject to the token.
  • ConditionsProvider conditionsProvider - An object used to add a Conditions statement to the token.
  • boolean signToken - Whether to sign the token or not. The default is true.
  • Map<String, SAMLRealm> realmMap - A map of realms to SAMLRealm objects.
We will explain each of these properties in more detail in the next few sections.

2) Realms in the TokenProviders

As explained in the previous post, the TokenProvider interface has a method that takes a realm parameter:
  • boolean canHandleToken(String tokenType, String realm) - Whether this TokenProvider implementation can provide a token of the given type, in the given realm
In other words, the TokenProvider implementation is being asked whether it can supply a token corresponding to the Token Type in a particular realm. How the STS knows what the desired realm is will be covered in a future post. However, we will explain how the realm is handled by the TokenProviders here. The SCTProvider ignores the realm in the canHandleToken method. In other words, the SCTProvider can issue a SecurityContextToken in *any* realm. If a realm is passed through via the TokenProviderParameters when creating the token, the SCTProvider will cache the token with the associated realm as a property (this was explained in the previous post).

Unlike the SCTProvider, the SAMLTokenProvider does not ignore the realm parameter to the "canHandleToken" method. Recall that the SAMLTokenProvider has a property "Map<String, SAMLRealm> realmMap". The canHandleToken method checks to see if the given realm is null, and if it is not null then the realmMap *must* contain a key which matches the given realm. So if the STS implementation is designed to issue tokens in different realms, then the realmMap of the SAMLTokenProvider must contain the corresponding realms in the key-set of the map.

The realmMap property maps realm Strings to SAMLRealm objects. The SAMLRealm class contains the following properties:
  • String issuer - the Issuer String to use in this realm
  • String signatureAlias - the keystore alias to use to retrieve the private key the SAMLTokenProvider uses to sign the generated token
In other words, if the SAMLTokenProvider is "realm aware", then it can issue tokens with an issuer name and signing key specific to a given realm. If no realm is passed to the SAMLTokenProvider, then these properties are obtained from the "system wide" properties defined in the STSPropertiesMBean object passed as part of the TokenProviderParameters, which can be set via the following methods:
  • void setSignatureUsername(String signatureUsername)
  • void setIssuer(String issuer)
Two additional properties are required when signing SAML Tokens. A password is required to access the private key in the keystore, which is supplied by a CallbackHandler instance. A WSS4J "Crypto" instance is also required which controls access to the keystore. These are both set on the STSPropertiesMBean object via:
  • void setCallbackHandler(CallbackHandler callbackHandler)
  • void setSignatureCrypto(Crypto signatureCrypto)
Note that the signature of generated SAML Tokens can be disabled, by setting the "signToken" property of the SAMLTokenProvider to "false". As per the SCTProvider, the generated SAML tokens are stored in the cache with the associated realm stored as a property.

3) Populating SAML Tokens

In the previous section we covered how a generated SAML token is signed, how to configure the key used to sign the assertion, and how to set the Issuer of the Assertion. In this section we will describe how to populate the SAML Token itself. The SAMLTokenProvider is designed to be able to issue a wide range of SAML Tokens. It does this by re-using the SAML abstraction library that ships with Apache WSS4J, which defines a collection of beans that are configured and then assembled in a CallbackHandler to create a SAML assertion.

3.1) Configure a Conditions statement

The SAMLTokenProvider has a "ConditionsProvider conditionsProvider" property, which can be used to configure the generated Conditions statement which is added to the SAML Assertion. The ConditionsProvider has a method to return a ConditionsBean object, and a method to return a lifetime in seconds. The ConditionsBean holds properties such as the not-before and not-after dates, etc. The SAMLTokenProvider ships with a default ConditionsProvider implementation that is used to insert a Conditions statement in every SAML token that is generated. This implementation uses a default lifetime of 5 minutes, and set the Audience Restriction URI of the Conditions Statement to be the received "AppliesTo" address, which is obtained from the TokenProviderParameters object.

The DefaultConditionsProvider can be configured to change the lifetime of the issued token. If you want to remove the ConditionsProvider altogether from the generation assertion, or implement a custom Conditions statement, then you must implement an instance of the ConditionsProvider interface, and set it on the SAMLTokenProvider.

3.2) Configure a Subject

The SAMLTokenProvider has a "SubjectProvider subjectProvider" property, which can be used to configure the Subject of the generated token, regardless of the version of the token. The SubjectProvider interface defines a single method to return a SubjectBean, given the token provider parameters, the parent Document of the assertion, and a secret key to use (if any). The SubjectBean contains the Subject name, name-qualifier, confirmation method, and KeyInfo element, amongst other properties. The SAMLTokenProvider ships with a default SubjectProvider implementation that is used to insert a Subject into every SAML Token that is generated.

The DefaultSubjectProvider has a single configuration method to set the subject name qualifier. It creates a subject confirmation method by checking the received key type. The subject name is the name of the principal obtained from TokenProviderParameters. Finally, a KeyInfo element is set on the SubjectBean under the following conditions:
  • If a "SymmetricKey" Key Type algorithm is specified by the client, then the secret key passed through to the SubjectProvider is encrypted with the X509Certificate of the recipient, and added to the KeyInfo element. How the provider knows the public key of the recipient will be covered later.
  • If a "PublicKey" KeyType algorithm is specified by the client, the X509Certificate that is received as part of the "UseKey" request is inserted into the KeyInfo element of the Subject.
If a "Bearer" KeyType algorithm is specified by the client, then no KeyInfo element is added to the Subject. For the "SymmetricKey" Key Type case, the SAMLTokenProvider creates a secret key using a SymmetricKeyHandler instance. The SymmetricKeyHandler first checks the key size that is supplied as part of the KeyRequirements object, by checking that it fits in between a minimum and maximum key size that can be configured. It also checks any client entropy that is supplied, as well as the computed key algorithm. It then creates some entropy and a secret key.

To add a custom Subject element to an assertion, you must create your own SubjectProvider implementation, and set it on the SAMLTokenProvider.

3.3) Adding Attribute Statements

The SAMLTokenProvider has a "List<AttributeStatementProvider> attributeStatementProviders" property, which can be used to add AttributeStatments to the generated assertion. Each object in the list adds a single Attribute statement. The AttributeStatementProvider contains a single method to return an AttributeStatementBean given the TokenProviderParameters object. This contains a SubjectBean (for SAML 1.1 assertions), and a list of AttributeBeans. The AttributeBean object holds the attribute name/qualified-name/name-format, and a list of attribute values, amongst other properties.

If no statement provider is configured in the SAMLTokenProvider, then the DefaultAttributeStatementProvider is invoked to create an Attribute statement to add to the assertion. It creates a default "authenticated" attribute, and also creates separate Attributes for any "OnBehalfOf" or "ActAs" elements that were received in the request. If the received OnBehalfOf/ActAs element was a UsernameToken, then the username is added as an Attribute. If the received element was a SAML Assertion, then the subject name is added as an Attribute. 

3.4) Adding Authentication Statements

The SAMLTokenProvider has a "List<AuthenticationStatementProvider> authenticationStatementProviders" property, which can be used to add AuthenticationStatements to the generated assertion. Each object in the list adds a single Authentication statement. The AuthenticationStatementProvider contains a single method to return an AuthenticationStatementBean given the TokenProviderParameters object. This contains a SubjectBean (for SAML 1.1 assertions), an authentication instant, authentication method, and other properties. No default implementation of the AuthenticationStatementProvider interface is provided in the STS, so if you want to issue Authentication Statements you will have to write your own.

3.5) Adding Authorization Decision Statements

The SAMLTokenProvider has a "List<AuthDecisionStatementProvider> authDecisionStatementProviders" property, which can be used to add AuthzDecisionStatements to the generated assertion. Each object in the list adds a single statement. The AuthDecisionStatementProvider  contains a single method to return an AuthDecisionStatementBean given the TokenProviderParameters object. This contains a SubjectBean (for SAML 1.1 assertions), the decision (permit/indeterminate/deny), the resource URI, a list of ActionBeans, amongst other properties. No default implementation of the AuthDecisionStatementProvider interface is provided in the STS.

Note that for SAML 1.1 tokens, the Subject is embedded in one of the Statements. When creating a SAML 1.1 Assertion, if a given Authentication/Attribute/AuthzDecision statement does not have a subject, then the standalone Subject is inserted into the statement. Finally, once a SAML token has been created, it is stored in the cache (if one is configured), with a lifetime corresponding to that of the Conditions statement. A TokenProviderResponse object is created with the DOM representation of the SAML Token, the SAML Token ID, lifetime, entropy bytes, references, etc.

Tuesday, October 25, 2011

Apache CXF STS documentation - part III

In the next couple of blog posts I will describe how to generate tokens in the new STS implementation shipped as part of Apache CXF 2.5. In this post I will detail the interface that is used for generating tokens, as well as an implementation to generate SecurityContextTokens that ships with the STS. In the next post, I will describe how to generate SAML tokens.

1) The TokenProvider interface

Security tokens are created in the STS via the TokenProvider interface. It has three methods:
  • boolean canHandleToken(String tokenType) - Whether this TokenProvider implementation can provide a token of the given type
  • boolean canHandleToken(String tokenType, String realm) - Whether this TokenProvider implementation can provide a token of the given type, in the given realm
  • TokenProviderResponse createToken(TokenProviderParameters tokenParameters) - Create a token using the given parameters
A client can request a security token from the STS by either invoking the "issue" operation and supplying a desired token type, or else calling the "validate" operation and passing a (different) token type (token transformation). Assuming that the client request is authenticated and well-formed, the STS will iterate through a list of TokenProvider implementations to see if they can "handle" the received token type. If they can, then the implementation is used to create a security token, which is returned to the client. The second "canHandleToken" method which also takes a "realm" parameter will be covered in a future post.

So to support the issuing of a particular token type in an STS deployment, it is necessary to specify a TokenProvider implementation that can handle that token type. The STS currently ships with two TokenProvider implementations, one for generating SecurityContextTokens, and one for generating SAML Assertions. Before we look at these two implementations, let's take a look at the "createToken" operation in more detail. This method takes a TokenProviderParameters instance.

2) TokenProviderParameters

The TokenProviderParameters class is nothing more than a collection of configuration properties to use in creating the token, which are populated by the STS operations using information collated from the request, or static configuration, etc. The properties of the TokenProviderParameters are:
  • STSPropertiesMBean stsProperties - A configuration MBean that holds the configuration for the STS as a whole, such as information about the private key to use to sign issued tokens, etc. This will be covered later.
  • EncryptionProperties encryptionProperties - A properties object that holds encryption information relevant to the intended recipient of the token. This will be covered later.
  • Principal principal - The current client Principal object. This can be used as the "subject" of the generated token.
  • WebServiceContext webServiceContext - The current web service context object. This allows access to the client request.
  • RequestClaimCollection requestedClaims - The requested claims in the token. This will be covered later.
  • KeyRequirements keyRequirements - A set of configuration properties relating to keys. This will be covered later.
  • TokenRequirements tokenRequirements - A set of configuration properties relating to the token. This will be covered later.
  • String appliesToAddress - The URL that corresponds to the intended recipient of the token
  • ClaimsManager claimsManager - An object that can manage claims. This will be covered later.
  • Map<String, Object> additionalProperties - Any additional (custom) properties that might be used by a TokenProvider implementation.
  • STSTokenStore tokenStore - A cache used to store tokens.
  • String realm - The realm to create the token in (this should be the same as the realm passed to "canHandleToken"). This will be covered later.
If this looks complicated then remember that the STS will take care of populating all of these properties from the request and some additional configuration. You only need to worry about the TokenProviderParameters object if you are creating your own TokenProvider implementation.

3) TokenProviderResponse

The "createToken" method returns an object of type TokenProviderResponse. Similar to the TokenProviderParameters object, this just holds a collection of objects that is parsed by the STS operation to construct a response to the client. The properties are:
  • Element token - The (DOM) token that was created by the TokenProvider.
  • String tokenId - The ID of the token
  • long lifetime - The lifetime of the token
  • byte[] entropy - Any entropy associated with the token
  • long keySize - The key size of a secret key associated with the token.
  • boolean computedKey - Whether a computed key algorithm was used in generating a secret key.
  • TokenReference attachedReference - An object which gives information how to refer to the token when it is "attached".
  • TokenReference unAttachedReference" - An object which gives information how to refer to the token when it is "unattached".
Most of these properties are optional as far as the STS operation is concerned, apart from the token and token ID. The TokenReference object contains information about how to refer to the token (direct reference vs. Key Identifier, etc.), that is used by the STS to generate the appropriate reference to return to the client. 

4) The SCTProvider

Now that we've covered the TokenProvider interface, let's look at an implementation that is shipped with the STS. The SCTProvider is used to provide a token known as a SecurityContextToken, that is defined in the WS-SecureConversation specification. A SecurityContextToken essentially consists of a String Identifier which is associated with a particular secret key. If a service provider receives a SOAP message with a digital signature which refers to a SecurityContextToken in the KeyInfo of the signature, then the service provider knows that it must somehow obtain a secret key associated with that particular Identifier to verify the signature. How this is done is "out of band" (more on this later).

To request a SecurityContextToken, the client must use one of the following Token Types:
  • http://schemas.xmlsoap.org/ws/2005/02/sc/sct
  • http://docs.oasis-open.org/ws-sx/ws-secureconversation/200512
Two properties can be configured on the SCTProvider directly:
  • long lifetime - The lifetime of the generated SCT. The default is 5 minutes.
  • boolean returnEntropy - Whether to return any entropy bytes to the client or not. The default is true.
The SCTProvider generates a secret key using the KeyRequirements object that was supplied, and constructs a SecurityContextToken with a random Identifier. It creates a CXF SecurityToken object that wraps this information, and stores it in the supplied cache using the given lifetime. The SecurityContextToken element is then returned, along with the appropriate references, lifetime element, entropy, etc.

When requesting a token from an STS, the client will typically present some entropy along with a computed key algorithm. The STS will generate some entropy of its own, and combine it with the client entropy using the computed key algorithm to generate the secret key. Alternatively, the client will present no entropy, and the STS will supply all of the entropy. Any entropy the STS generates is then returned to the client, who can recreate the secret key using its own entropy, the STS entropy, and the computed key algorithm.

This secret key is then used for the SCT use-case to encrypt/sign some part of a message. The SecurityContextToken is placed in the security header of the message, and referred to in the KeyInfo element of the signed/encrypted structure. As noted earlier, the service provider must obtain somehow the secret key corresponding to the SecurityContextToken identifier. Perhaps the service provider shares a (secured) distributed cache with an STS instance. Or perhaps the service provider sends the SCT to an STS instance to "validate" it, and receives a SAML token in response with the embedded (encrypted) secret key.

5) Token caching in the TokenProvider

Finally, we will cover token caching in a TokenProvider implementation. The SCTProvider is essentially useless without a cache, as otherwise there is no way for a third-party to know the secret key corresponding to a SecurityContextToken. Any TokenProvider implementation can cache a generated token in the STSTokenStore object supplied as part of the TokenProviderParameters. This object simply wraps the TokenStore interface in the CXF WS-Security runtime, which itself contains basic methods for adding/removing/querying CXF SecurityToken objects.

The SCTProvider creates a SecurityToken with the ID of the SCT, the secret key associated with the SCT and the client principal. If a "realm" is passed through, then this is recorded as a property of the SecurityToken (keyed via STSConstants.TOKEN_REALM). Finally, the STS ships with two STSTokenStore implementations, an in-memory implementation based on eh-cache, and an implementation that uses Hazelcast.

Friday, October 21, 2011

Apache CXF STS documentation - part II

In part I of the series of posts on the new Apache CXF STS implementation, I talked about what a Security Token Service can do, as well as the STS provider framework in CXF since the 2.4.0 release. In this part, I will leave the STS implementation to one side for the moment, and instead focus on how a client interacts with the STS in CXF.

A simple example of how a CXF client can obtain a security token from the STS is shown in the "basic" STS system test "IssueUnitTest". This test starts an instance of the new CXF STS and obtains a number of different security tokens, all done completely programmatically, i.e. with no spring configuration. The STS instance that is used for the test-cases is configured with a number of different endpoints that use different security bindings (defined in the wsdl of the STS). For the purposes of this test, the Transport binding is used:

<wsp:Policy wsu:Id="Transport_policy">
      <wsp:ExactlyOne>
         <wsp:All>
            <sp:TransportBinding
               xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
               <wsp:Policy>
                  <sp:TransportToken>
                     <wsp:Policy>
                        <sp:HttpsToken RequireClientCertificate="false"/>
                     </wsp:Policy>
                  </sp:TransportToken>
                  <sp:AlgorithmSuite>
                     <wsp:Policy>
                        <sp:Basic128 />
                     </wsp:Policy>
                  </sp:AlgorithmSuite>
                  <sp:Layout>
                     <wsp:Policy>
                        <sp:Lax />
                     </wsp:Policy>
                  </sp:Layout>
                  <sp:IncludeTimestamp />
               </wsp:Policy>
            </sp:TransportBinding>
            <sp:SignedSupportingTokens
               xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
               <wsp:Policy>
                  <sp:UsernameToken
                     sp:IncludeToken=".../AlwaysToRecipient">
                     <wsp:Policy>
                        <sp:WssUsernameToken10 />
                     </wsp:Policy>
                  </sp:UsernameToken>
               </wsp:Policy>
            </sp:SignedSupportingTokens>
            ...
         </wsp:All>
      </wsp:ExactlyOne>
   </wsp:Policy>

In other words, this security policy requires that a one-way TLS connection must be used to communicate with the STS, and that authentication is performed via a Username Token in the SOAP header.

The object that communicates with an STS in CXF is the STSClient. Typically, the user constructs an STSClient instance (normally via Spring), sets it with certain properties such as the WSDL location of the STS, what service/port to use, various crypto properties, etc, and then stores this object on the message context using the SecurityConstants tag "ws-security.sts.client". This object is then controlled by the IssuedTokenInterceptorProvider in the ws-security runtime in CXF. This interceptor provider is triggered by the "IssuedToken" policy assertion, which is typically in the WSDL of the service provider. This policy assertion informs the client that it must obtain a particular security token from an STS and include it in the service request. The IssuedTokenInterceptorProvider takes care of using the STSClient to get a Security Token from the STS, and handles how long the security token should be cached, etc.

An example of a simple IssuedToken policy that might appear in the WSDL of a service provider is as follows:

<sp:IssuedToken sp:IncludeToken=".../AlwaysToRecipient">
    <sp:RequestSecurityTokenTemplate>
        <t:TokenType>
            http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0
        </t:TokenType>
        <t:KeyType>
            http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer
        </t:KeyType>
    </sp:RequestSecurityTokenTemplate>
    ...
</sp:IssuedToken>

This policy states that the client should include a SAML 2.0 Assertion of subject confirmation method "Bearer" in the request. The client must know how to communicate with an STS to obtain such a token. This is done by providing the STSClient object with the appropriate information.

We will come back to the IssuedTokenInterceptorProvider at a later date. The IssueUnitTest referred to above uses the STSClient programmatically to obtain a security token. Let's look at the "requestSecurityToken" method called by the tests. An STSClient is instantiated via the CXF bus, and the WSDL location of the STS, plus service and port names are configured:

STSClient stsClient = new STSClient(bus);
stsClient.setWsdlLocation("https://.../SecurityTokenService/Transport?wsdl");
stsClient.setServiceName("{...}SecurityTokenService");
stsClient.setEndpointName("{...}Transport_Port");

A map is then populated with various properties and set on the STSClient. It is keyed with a different number of SecurityConstants tags. A username is supplied for use as the "user" in the UsernameToken. A CallbackHandler class is supplied to get the password to use in the UsernameToken. Compliance of the Basic Security Profile 1.1 is turned off, this is to prevent CXF throwing an exception when receiving a non-spec compliant response from a non-CXF STS:

Map<String, Object> properties = new HashMap<String, Object>();
stsClient.setProperties(properties);
properties.put(SecurityConstants.USERNAME, "alice");
properties.put(
        SecurityConstants.CALLBACK_HANDLER,
        "org.apache.cxf.systest.sts.common.CommonCallbackHandler"
);
properties.put(SecurityConstants.IS_BSP_COMPLIANT, "false");
      
If the KeyType is a "PublicKey", then an X.509 Certificate is presented to the STS in the request to embed in the generated SAML Assertion. The X.509 Certificate is obtained from the keystore defined in "clientKeystore.properties", with the alias "myclientkey". Finally, the "useCertificateForConfirmationKeyInfo" property of the STSClient means that the entire certificate is to be included in the request, instead of a KeyValue (which is the default):

if (PUBLIC_KEY_KEYTYPE.equals(keyType)) {
        properties.put(SecurityConstants.STS_TOKEN_USERNAME, "myclientkey");
        properties.put(SecurityConstants.STS_TOKEN_PROPERTIES, "clientKeystore.properties");
        stsClient.setUseCertificateForConfirmationKeyInfo(true);
}

Finally, the token type is set on the STSClient (the type of token that is being requested), as well as the KeyType (specific to a SAML Assertion), and a security token is requested, passing the endpoint address which is sent to the STS in the "AppliesTo" element:

        stsClient.setTokenType(tokenType);
        stsClient.setKeyType(keyType);
        return stsClient.requestSecurityToken(endpointAddress);

The returned SecurityToken object contains the received token as a DOM element, the ID of the received token, any reference elements that were returned - which show how to reference the token, any secret associated with the token, and the lifetime of the token.

Wednesday, October 19, 2011

Apache CXF STS documentation - part I

The forthcoming Apache CXF 2.5 release will have an STS (Security Token Service) implementation which has been donated by Talend. This is the first in a series of blog posts where I will be going into the STS implementation in detail. In this post I will be explaining what an STS is and talking about the STS provider framework in CXF.

1) What is a Security Token Service?

An informal description of a Security Token Service is that it is a web service that offers some or all of the following services (amongst others):
  • It can issue a Security Token of some sort based on presented or configured credentials.
  • It can say whether a given Security Token is valid or not
  • It can renew (extend the validity of) a given Security Token
  • It can cancel (remove the validity of) a given Security Token
  • It can transform a given Security Token into a Security Token of a different sort.
Offloading this functionality to another service greatly simplifies client and service provider functionality, as they can simply call the STS appropriately rather than have to figure out the security requirements themselves. For example, the WSDL of a service provider might state that a particular type of security token is required to access the service. A client of the service can ask an STS for a Security Token of that particular type, which is then sent to the service provider. The service provider could choose to validate the received token locally, or dispatch the token to an STS for validation. These are the two most common use-cases of an STS.

A client can communicate with the STS via a protocol defined in the WS-Trust specification. The SOAP Body of the request contains a "RequestSecurityToken" element that looks like:

<wst:RequestSecurityToken Context="..." xmlns:wst="...">
    <wst:TokenType>...</wst:TokenType>
    <wst:RequestType>...</wst:RequestType>
    <wst:SecondaryParameters>...</wst:SecondaryParameters>
    ...
</wst:RequestSecurityToken>

The Apache CXF STS implementation supports a wide range of parameters that are passed in the RequestSecurityToken element. The SOAP Body of the response from the STS will contain a "RequestSecurityTokenResponse(Collection)" element, e.g.:

<wst:RequestSecurityTokenResponseCollection xmlns:wst="...">
    <wst:RequestSecurityTokenResponse>
    ...
    </wst:RequestSecurityTokenResponse>
</wst:RequestSecurityTokenResponseCollection>

1.1 A sample request/response for issuing a Security Token

A sample client request is given here, where the client wants the STS to issue a SAML 2.0 token for the "http://cxf.apache.org:8080/service" service:

<wst:RequestSecurityToken Context="..." xmlns:wst="...">
    <wst:TokenType>
       http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0
    </wst:TokenType>
    <wst:RequestType>
        http://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue
    </wst:RequestType>
    <wsp:AppliesTo>http://cxf.apache.org:8080/service</wsp:AppliesTo>
</wst:RequestSecurityToken>

The STS responds with:

<wst:RequestSecurityTokenResponseCollection xmlns:wst="...">
    <wst:RequestSecurityTokenResponse>
         <wst:TokenType>
         http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0
         </wst:TokenType>
         <wst:RequestedSecurityToken>
             <saml2:Assertion xmlns:saml2="..." ... />
         </wst:RequestedSecurityToken>
    </wst:RequestSecurityTokenResponse>
</wst:RequestSecurityTokenResponseCollection>

2) The STS provider framework in Apache CXF

The first support for an STS in Apache CXF appeared in the 2.4.0 release with the addition of an STS provider framework in the WS-Security module. This is essentially an API that can be used to create your own STS implementation. As the STS implementation shipped in CXF 2.5 is based on this provider framework, it makes sense to examine it in more detail.

The SEI (Service Endpoint Interface) is available here. It contains the following methods that are relevant to the STS features discussed above:
  • RequestSecurityTokenResponseCollectionType issue(RequestSecurityTokenType request) - to issue a security token
  • RequestSecurityTokenResponseType issueSingle( RequestSecurityTokenType request) - to issue a security token that is not contained in a "Collection" wrapper (for legacy applications).
  • RequestSecurityTokenResponseType cancel(RequestSecurityTokenType request) - to cancel a security token
  • RequestSecurityTokenResponseType validate(RequestSecurityTokenType request) - to validate a security token
  • RequestSecurityTokenResponseType renew(RequestSecurityTokenType request) - to renew a security token
The SEI implementation handles each request by delegating it to a particular operation, which is just an interface that must be implemented by the provider framework implementation. Finally, a JAX-WS provider is available, which dispatches a request to the appropriate operation.

Significant updates to the STS Provider framework after the CXF 2.4.0 release include support for SOAP 1.2, a major bug fix to support operations other than issue,  better exception propagation, and adding support for the WS-Trust 1.4 schema. These features are all available in the Apache CXF 2.4.3 release onwards.

Tuesday, October 11, 2011

Using Kerberos with Web Services - part II

This is the second of a two-part series on using Kerberos with Web Services, with Apache WSS4J and CXF. Part I showed how to set up a KDC distribution, and how to generate client and service principals to use in some CXF system tests. The system tests showed how to obtain a Kerberos token from a KDC, package it in a BinarySecurityToken, and send it to a service endpoint for validation. In other words, part I illustrated how to use Kerberos for client authentication in a web service setting.

This article builds on part I by showing how to use the secret key associated with a Kerberos Token to secure (sign and encrypt) the request. This functionality was added as part of WSS4J 1.6.3, and the related WS-SecurityPolicy functionality was released as part of CXF 2.4.3.

1) Setting up the Kerberos system tests in Apache CXF 

If you have not done so already, follow the instructions in part I to install a Kerberos distribution and to generate client and service principals to run the CXF Kerberos system tests. The KerberosTokenTest in Apache CXF contains a number of different Kerberos tests. In this article we will examine the tests that involve obtaining a Kerberos Token, and using the associated secret key to secure some part of the request.

Firstly, make sure that the JDK has unlimited security policies installed, and then checkout the CXF trunk via:
svn co https://svn.apache.org/repos/asf/cxf/trunk/
Go into the "trunk" directory, and compile and install CXF via "mvn -Pfastinstall" (this will avoid running tests). Finally go into the WS-Security system tests in "systests/ws-security"

1.1) Installing a custom KerberosTokenDecoder


Once the client obtains an AP-REQ token from the KDC, the client also has easy access to the session key, which can be used to secure the request in some way. Unfortunately, there appears to be no easy way to obtain the session key on the receiving side. WSS4J does not support extracting a Kerberos session key on the receiving side to decrypt/verify a secured request out-of-the-box. Instead, a KerberosTokenDecoder interface is provided, which defines methods for setting the AP-REQ token and current Subject, and a method to then get a session key. An implementation must be set on the KerberosTokenValidator to obtain a session key to decrypt or verify a signed request.

To run the Kerberos system tests that require a secret key on the receiving side, download an implementation of the KerberosTokenValidator interface here, and copy it to "systests/ws-security/src/test/java/org/apache/cxf/systest/ws/kerberos/server". The implementation is based on code written by the Java Monkey, and uses internal sun APIs, and so can't be shipped in Apache CXF/WSS4J. Once this implementation has been copied into the ws-security module, then you must compile or run any tests with the "-Pnochecks" profile enabled, as otherwise the code fails checkstyle.

Open the server configuration file ("src/test/resources/org/apache/cxf/systest/ws/kerberos/server/server.xml"), and uncomment the "kerberosTicketDecoderImpl" bean, and the property of the "kerberosValidator" bean that refers to it:
<bean id="kerberosTicketDecoderImpl"   class="org.apache.cxf.systest.ws.kerberos.server.KerberosTokenDecoderImpl"/>

<bean id="kerberosValidator"
       class="org.apache.ws.security.validate.KerberosTokenValidator">
        <property name="contextName" value="bob"/>
        <property name="serviceName" value="bob@service.ws.apache.org"/>
        <property name="kerberosTokenDecoder" ref="kerberosTicketDecoderImpl"/>
</bean>
1.2) Running the tests

Open KerberosTokenTest.java and comment out the "@org.junit.Ignore" entries for the last four tests, "testKerberosOverTransportEndorsing", "testKerberosOverAsymmetricEndorsing", "testKerberosOverSymmetricProtection" and "testKerberosOverSymmetricDerivedProtection". Finally, run the tests via:
mvn -Pnochecks test -Dtest=KerberosTokenTest -Djava.security.auth.login.config=src/test/resources/kerberos.jaas
2) The tests in more detail

In this section, we'll look at the tests in more detail. 

2.1) WS-SecurityPolicy configuration

The wsdl that defines the service endpoints contains WS-SecurityPolicy expressions that define the security requirements of the endpoints. The following security policies are used for the four tests defined above:
  • testKerberosOverTransportEndorsing: A (one-way) transport binding is defined, with a KerberosToken required as an EndorsingSupportingToken. 
  • testKerberosOverAsymmetricEndorsing: An asymmetric binding is used, where a KerberosToken is required as an EndorsingSupportingToken.
  • testKerberosOverSymmetricProtection: A symmetric binding is used, where a KerberosToken is specified as a ProtectionToken of the binding.
  • testKerberosOverSymmetricDerivedProtection: The same as the previous test-case, except that any secret keys that are used must be derived.
The first two test-cases use an EndorsingSupportingToken, which means that the secret key associated with the KerberosToken is used to sign (endorse) some message part (the timestamp for the Transport binding). This illustrates proof-of-possession. For the latter two test-cases, the KerberosToken is defined as a ProtectionToken, meaning that the secret key is used to sign/encrypt the request (e.g. instead of using an X.509 Token to encrypt a session key).

2.2) Kerberos LoginModule configuration

Both the CXF client and service endpoint use JAAS to authenticate to the KDC. The JAAS file used as part of the system test is passed to the tests via the System property "java.security.auth.login.config". The client (alice) uses the following login module:
alice {
    com.sun.security.auth.module.Krb5LoginModule required
    refreshKrb5Config=true useKeyTab=true keyTab="/etc/alice.keytab"
    principal="alice";
};
and the service endpoint (bob) uses:
bob {
    com.sun.security.auth.module.Krb5LoginModule required
    refreshKrb5Config=true useKeyTab=true storeKey=true
    keyTab="/etc/bob.keytab" principal="bob/service.ws.apache.org";
};
2.3) Service endpoint configuration

The service endpoints are spring-loaded. Each endpoint definition contains the JAX-WS property "ws-security.bst.validator" which is defined in SecurityConstants. WSS4J uses Validator implementations to perform validation on received security tokens. This particular property means that BinarySecurityTokens are to be validated by the given reference, e.g.:

<jaxws:endpoint ...>
    <jaxws:properties>      
        <entry key="ws-security.bst.validator" value-ref="kerberosValidator"/>
    </jaxws:properties>
</jaxws:endpoint>
"kerberosValidator" is a KerberosTokenValidator instance given above. It requires a "contextName" property, which corresponds to the JAAS context name, as well as an optional "serviceName" property, and an optional "kerberosTokenDecoder" property to use to obtain a secret key. Combined with the JAAS properties file, this is all that is required for the service endpoint to validate a received Kerberos Token. 

2.4 Client configuration

Finally, the client must contact a KDC and obtain a Kerberos Token, once it sees that the service endpoint has a security policy that requires a KerberosToken. The client configuration is available here. A sample configuration for the Kerberos Test case is as follows:
<jaxws:client name="{...}DoubleItKerberosTransportPort"
       createdFromAPI="true">
       <jaxws:properties>
           <entry key="ws-security.kerberos.client">
               <bean class="org.apache.cxf.ws.security.kerberos.KerberosClient">
                   <constructor-arg ref="cxf"/>
                   <property name="contextName" value="alice"/>
                   <property name="serviceName" value="bob@service.ws.apache.org"/>
               </bean>           
           </entry>
       </jaxws:properties>
</jaxws:client>
The JAX-WS property "ws-security.kerberos.client" (again, defined in SecurityConstants) corresponds to a KerberosClient object. Similar to the KerberosTokenValidator on the receiving side, this is configured with a JAAS context Name and service Name.

Apache CXF STS articles

Two of my colleagues have written excellent articles on the forthcoming STS (Security Token Service) implementation in Apache CXF, complete with sample projects. See here for the article from Oliver Wulff, and here for the article from Glen Mazza.

Monday, October 10, 2011

Using Kerberos with Web Services - part I

This is the first of a two-part series on using Kerberos with Web Services, with Apache WSS4J and CXF. WSS4J 1.6.2 adds support for obtaining a Kerberos ticket from a KDC (Key Distribution Center) and converting it to a BinarySecurityToken to be inserted into the security header of a SOAP request. On the receiving side, support has been added to validate the received Kerberos ticket accordingly. CXF 2.4.2 extends the Kerberos functionality available in WSS4J 1.6.2 to add support for WS-SecurityPolicy. No support is available yet (as of CXF 2.4.2) to use a secret key to sign and encrypt message parts, this will be the subject of part II.

In this post we will talk about installing the MIT Kerberos distribution in Ubuntu, and creating the necessary credentials to run some tests. Then we will go into some system tests in CXF that show how a client can get a Kerberos AP-REQ ticket from a KDC and send it to a service provider, who then authenticates the ticket, all driven by some spring configuration and WS-SecurityPolicy.

1) Installing MIT Kerberos

1.1) Installing the product

In this section we cover how to install the MIT Kerberos distribution in Ubuntu. This is needed to run the CXF Kerberos system tests. See here for more information on using Kerberos on Ubuntu. Open a command prompt and type:
sudo apt-get install krb5-kdc krb5-admin-server
When the product is installing you'll be asked for the default Kerberos Version 5 realm. Enter:
WS.APACHE.ORG
You will then be prompted for the hostnames of Kerberos servers in the WS.APACHE.ORG Kerberos realm. As we are installing the KDC and running the tests on the same machine, we only need to enter "localhost". Similarly, enter "localhost" when prompted for the Administrative server for the Kerberos realm.

1.2) Modifying configuration

Once apt-get has finished, we need to modify the Kerberos configuration file ("sudo vi /etc/krb5.conf"):
  • Under the "[realms]" section, add a "default_domain" entry for ws.apache.org. The entire entry should look like:
    WS.APACHE.ORG = {
                 kdc = localhost
                 admin_server = localhost
                 default_domain = ws.apache.org
    }
  • Under the "[domain_realm]" section, add the following:     
ws.apache.org = WS.APACHE.ORG  
.ws.apache.org = WS.APACHE.ORG
  • Finally, add a logging section:
[logging]

        kdc = FILE:/var/log/krb5kdc.log
        admin_server = FILE:/var/log/kadmin.log
        default = FILE:/var/log/krb5lib.log
1.3) Create principals

The next step is to create some principals. Create a master key via:
sudo kdb5_util create -s
The next step is to start kadmin locally via:
sudo kadmin.local
If you run "listprincs" at the prompt you should see the ticket-granting-ticket principal "krbtgt/WS.APACHE.ORG@WS.APACHE.ORG". We will add a client principal and service principal:
addprinc alice
addprinc bob/service.ws.apache.org
"quit" the kadmin prompt, and start the KDC with "sudo krb5kdc". If you see no error messages then everything should be working correctly. To test this try to get a ticket for "alice" via "kinit alice", entering the password given when creating the "alice" principal.

1.4) Create keytabs

To avoid having to enter passwords when running the tests, we will create Keytabs. Start kadmin.local again ("sudo kadmin.local"), and enter:
ktadd -k /etc/alice.keytab alice
ktadd -k /etc/bob.keytab bob/service.ws.apache.org
To check that the keytabs were create correctly, you can inspect them with klist, e.g. "sudo klist -k /etc/alice.keytab". Finally make sure the keytabs are readable via "sudo chmod og+r /etc/*.keytab" - obviously this is not secure, but it is sufficient for this test application.

2) Running the Kerberos system tests in Apache CXF

Now that we have installed Kerberos and created the relevant principals, we can run the Kerberos system tests in Apache CXF. These tests are @Ignore'd by default. The KerberosTokenTest contains a number of different Kerberos tests. In this article we will just examine the tests that involve obtaining a Kerberos Token, and not any of the tests that involve using the secret key associated with a Kerberos Token to secure some part of the request.

Firstly, make sure that the JDK has unlimited security policies installed, and then checkout the CXF trunk via:
svn co https://svn.apache.org/repos/asf/cxf/trunk/
Go into the "trunk" directory, and compile and install CXF via "mvn -Pfastinstall" (this will avoid running tests). Finally go into the WS-Security system tests in "systests/ws-security". Open KerberosTokenTest.java and comment out the "@org.junit.Ignore" entries for the first four tests, "testKerberosOverTransport", "testKerberosOverSymmetric", "testKerberosOverSymmetricSupporting" and "testKerberosOverAsymmetric". Finally, run the tests via:

        mvn test -Dtest=KerberosTokenTest -Djava.security.auth.login.config=src/test/resources/kerberos.jaas

2.1) WS-SecurityPolicy configuration

The wsdl that defines the service endpoints contains WS-SecurityPolicy expressions that define the security requirements of the endpoints. The following security policies are used for the four tests defined above:
  • testKerberosOverTransport: A (one-way) transport binding is defined, with a KerberosToken required as a SupportingToken. Essentially, this means that the communication is secured with TLS, and authentication is handled by a Kerberos token.
  • testKerberosOverSymmetric: A symmetric binding is used, where a KerberosToken is required as a SignedSupportingToken. 
  • testKerberosOverSymmetricSupporting: A symmetric binding is used, where a KerberosToken is required as a SupportingToken.
  • testKerberosOverAsymmetric: An asymmetric binding is used, where a Kerberos token is required as a SignedSupportingToken.
The WS-SecurityPolicy expression used for a KerberosToken is:
<sp:KerberosToken sp:IncludeToken=".../Once">
    <wsp:Policy>
        <sp:WssGssKerberosV5ApReqToken11/>
    </wsp:Policy>
</sp:KerberosToken>
This means that a GSS V5 AP-REQ Token is required "once", in other words the initial invocation between the client and service endpoint must contain a token of this type encoded as a BinarySecurityToken in the security header of the request.

2.2) Kerberos LoginModule configuration

Both the CXF client and service endpoint use JAAS to authenticate to the KDC. The JAAS file used as part of the system test is passed to the tests via the System property "java.security.auth.login.config". The client (alice) uses the following login module:
alice {
    com.sun.security.auth.module.Krb5LoginModule required
    refreshKrb5Config=true useKeyTab=true keyTab="/etc/alice.keytab" 
    principal="alice";
};
and the service endpoint (bob) uses:
bob {
    com.sun.security.auth.module.Krb5LoginModule required
    refreshKrb5Config=true useKeyTab=true storeKey=true
    keyTab="/etc/bob.keytab" principal="bob/service.ws.apache.org";
}; 
2.3) Service endpoint configuration

The service endpoints are spring-loaded. Each endpoint definition contains the JAX-WS property "ws-security.bst.validator" which is defined in SecurityConstants. WSS4J uses Validator implementations to perform validation on received security tokens. This particular property means that BinarySecurityTokens are to be validated by the given reference, e.g.:
<jaxws:endpoint ...>  
    <jaxws:properties>      
        <entry key="ws-security.bst.validator" value-ref="kerberosValidator"/>
    </jaxws:properties>
</jaxws:endpoint>
"kerberosValidator" is defined as:
<bean id="kerberosValidator"
    class="org.apache.ws.security.validate.KerberosTokenValidator">
    <property name="contextName" value="bob"/>
    <property name="serviceName" value="bob@service.ws.apache.org"/>
</bean>
The KerberosTokenValidator class ships with Apache WSS4J. It requires a "contextName" property, which corresponds to the JAAS context name, as well as an optional "serviceName" property. Combined with the JAAS properties file, this is all that is required for the service endpoint to validate a received Kerberos Token. 

2.4 Client configuration

Finally, the client must contact a KDC and obtain a Kerberos Token, once it sees that the service endpoint has a security policy that requires a KerberosToken. The client configuration is available here. A sample configuration for the Kerberos Test case is as follows:
<jaxws:client name="{...}DoubleItKerberosTransportPort"
       createdFromAPI="true">
       <jaxws:properties>
           <entry key="ws-security.kerberos.client">
               <bean class="org.apache.cxf.ws.security.kerberos.KerberosClient">
                   <constructor-arg ref="cxf"/>
                   <property name="contextName" value="alice"/>
                   <property name="serviceName" value="bob@service.ws.apache.org"/>
               </bean>           
           </entry>
       </jaxws:properties>
</jaxws:client>
The JAX-WS property "ws-security.kerberos.client" (again, defined in SecurityConstants) corresponds to a KerberosClient object. Similar to the KerberosTokenValidator on the receiving side, this is configured with a JAAS context Name and service Name.

Friday, October 7, 2011

Improving decryption performance in Apache Santuario 1.5

Andreas Veithen alerted me some months back to a performance problem in Apache Santuario when decrypting messages. The issue emerged when some profiling was done on Dennis Sosnoski's test-code for measuring WS-Security performance across different web services stacks (see the original article here).

The test scenario involves deploying an Apache CXF 2.4.2 endpoint in Tomcat and repeatedly testing the "signencr" invocation defined in the article (WS-Security signing of body and headers, with timestamp and encryption of body) using a CXF client. Two types of test-runs were executed, 1000 "large" messages at 0.2 density in one run, and 10000 "small" messages at 0.05 density in another. When doing some profiling using a sampling profiler on the client, it emerged that the time it took to deserialize a decrypted XML String into a DOM element was taking around 20% of the total execution time for all WS-Security processing!

The way the default deserializing algorithm works in Apache Santuario 1.4.x is to parse the decrypted XML String into a new Document object, which is then imported into the existing Document. As Apache Xerces defers the creation of Node objects, the import operation triggers the full expansion of the DOM tree.

There are a number of alternatives to using the DocumentBuilder/importNode approach used in Santuario 1.4.x. The first approach is to use a Transformer object to transform the Source (XML String) into a placeholder Node belonging to the existing Document. This approach avoids having to explicitly import the nodes back to the existing Document. The second approach, is to use the streaming API available in the JDK 1.6 (not an option for Santuario 1.5 which must compile against the JDK 1.5).

Here are some (ad-hoc) test results. The first results show the total time for each test-run using both algorithms:
  • Large Messages:
    • Document Serializer: 119.46s
    • Transform Serializer: 115.68s
  • Small Messages:
    • Document Serializer: 222.32s
    • Transform Serializer: 216.76s
The next results show the time spent in the Serializer.deserialize() operation as a percentage of the total WS-Security processing time:
  • Large Messages:
    • Document Serializer: 19.92%
    • Transform Serializer: 18.04%
  • Small Messages:
    • Document Serializer: 24.54%
    • Transform Serializer: 18.36%
The Serializer interface is now public, and a different implementation can be set on XMLCipher. Two implementations are provided in the code, DocumentSerializer (the default algorithm) and TransformSerializer. If anyone is interested in running experiments of their own, the StreamSerializer algorithm is available here. The TransformSerializer implementation is not the default as it requires Xalan to work properly, and as this library is optional in Santuario 1.5.

Do you have any suggestions on how this could be improved further? Clearly, the time it takes to deserialize a decrypted XML String into a DOM node still takes far longer than it should. A fully StAX approach for XML Security would surely offer much improved performance - this is under development and planned for next year.

Tuesday, October 4, 2011

Apache WSS4J 1.6.3 released

Apache WSS4J 1.6.3 has been released. It can be downloaded here and the issues fixed are listed in the WSS4J JIRA.

Probably the most significant part of this release is that WSS4J now fully supports the Kerberos Token Profile 1.1. In the previous release, support was added to retrieve a Kerberos token from a KDC, and insert it into the security header of a request, and then validate it accordingly on the receiving side. In WSS4J 1.6.3, support has been added to use the secret key associated with the Kerberos token to sign and encrypt the request, and to verify and decrypt on the receiving side. I am planning on writing a series of blog posts soon about how to use Kerberos with WSS4J and CXF. The forthcoming Apache CXF 2.4.3 release will have full WS-SecurityPolicy support for working with Kerberos, based on the work done in WSS4J 1.6.3.

In addition to the Kerberos work, WSS4J 1.6.3 features an upgraded Opensaml dependency, as well as several bug fixes.