Chapter 7. Authentication
Abstract
This chapter describes how to use policies to configure authentication in a Apache CXF application. Currently, the only credentials type supported in the SOAP layer is the WS-Security UsernameToken.
7.1. Introduction to Authentication
Overview
In Apache CXF, an application can be set up to use authentication through a combination of policy assertions in the WSDL contract and configuration settings in Blueprint XML.
Remember, you can also use the HTTPS protocol as the basis for authentication and, in some cases, this might be easier to configure. See Section 3.1, “Authentication Alternatives”.
Steps to set up authentication
In outline, you need to perform the following steps to set up an application to use authentication:
- Add a supporting tokens policy to an endpoint in the WSDL contract. This has the effect of requiring the endpoint to include a particular type of token (client credentials) in its request messages.
- On the client side, provide credentials to send by configuring the relevant endpoint in Blueprint XML.
- (Optional) On the client side, if you decide to provide passwords using a callback handler, implement the callback handler in Java.
- On the server side, associate a callback handler class with the endpoint in Blueprint XML. The callback handler is then responsible for authenticating the credentials received from remote clients.
7.2. Specifying an Authentication Policy
Overview
If you want an endpoint to support authentication, associate a supporting tokens policy assertion with the relevant endpoint binding. There are several different kinds of supporting tokens policy assertions, whose elements all have names of the form *SupportingTokens
(for example, SupportingTokens
, SignedSupportingTokens
, and so on). For a complete list, see the section called “SupportingTokens assertions”.
Associating a supporting tokens assertion with an endpoint has the following effects:
-
Messages to or from the endpoint are required to include the specified token type (where the token’s direction is specified by the
sp:IncludeToken
attribute). - Depending on the particular type of supporting tokens element you use, the endpoint might be required to sign and/or encrypt the token.
The supporting tokens assertion implies that the runtime will check that these requirements are satisified. But the WS-SecurityPolicy policies do not define the mechanism for providing credentials to the runtime. You must use Blueprint XML configuration to specify the credentials (see Section 7.3, “Providing Client Credentials”).
Syntax
The *SupportingTokens
elements (that is, all elements with the SupportingTokens
suffix—see the section called “SupportingTokens assertions”) have the following syntax:
<sp:SupportingTokensElement xmlns:sp="..." ... > <wsp:Policy xmlns:wsp="..."> [Token Assertion]+ <sp:AlgorithmSuite ... > ... </sp:AlgorithmSuite> ? ( <sp:SignedParts ... > ... </sp:SignedParts> | <sp:SignedElements ... > ... </sp:SignedElements> | <sp:EncryptedParts ... > ... </sp:EncryptedParts> | <sp:EncryptedElements ... > ... </sp:EncryptedElements> | ) * ... </wsp:Policy> ... </sp:SupportingTokensElement>
Where SupportingTokensElement stands for one of the supporting token elements, *SupportingTokens
.Typically, if you simply want to include a token (or tokens) in the security header, you would include one or more token assertions, [Token Assertion]
, in the policy. In particular, this is all that is required for authentication.
If the token is of an appropriate type (for example, an X.509 certificate or a symmetric key), you could theoretically also use it to sign or encrypt specific parts of the current message using the sp:AlgorithmSuite
, sp:SignedParts
, sp:SignedElements
, sp:EncryptedParts
, and sp:EncryptedElements
elements. This functionality is currently not supported by Apache CXF, however.
Sample policy
Example 7.1, “Example of a Supporting Tokens Policy” shows an example of a policy that requires a WS-Security UsernameToken token (which contains username/password credentials) to be included in the security header. In addition, because the token is specified inside an sp:SignedSupportingTokens
element, the policy requires that the token is signed. This example uses a transport binding, so it is the underlying transport that is responsible for signing the message.
For example, if the underlying transport is HTTPS, the SSL/TLS protocol (configured with an appropriate algorithm suite) is responsible for signing the entire message, including the security header that contains the specified token. This is sufficient to satisfy the requirement that the supporting token is signed.
Example 7.1. Example of a Supporting Tokens Policy
<wsp:Policy wsu:Id="UserNameOverTransport_IPingService_policy"> <wsp:ExactlyOne> <wsp:All> <sp:TransportBinding> ... </sp:TransportBinding> <sp:SignedSupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"> <wsp:Policy> <sp:UsernameToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient"> <wsp:Policy> <sp:WssUsernameToken10/> </wsp:Policy> </sp:UsernameToken> </wsp:Policy> </sp:SignedSupportingTokens> ... </wsp:All> </wsp:ExactlyOne> </wsp:Policy>
Where the presence of the sp:WssUsernameToken10
sub-element indicates that the UsernameToken header should conform to version 1.0 of the WS-Security UsernameToken specification.
Token types
In principle, you can specify any of the WS-SecurityPolicy token types in a supporting tokens assertion. For SOAP-level authentication, however, only the sp:UsernameToken
token type is relevant.
sp:UsernameToken
In the context of a supporting tokens assertion, this element specifies that a WS-Security UsernameToken is to be included in the security SOAP header. Essentially, a WS-Security UsernameToken is used to send username/password credentials in the WS-Security SOAP header. The sp:UsernameToken
element has the following syntax:
<sp:UsernameToken sp:IncludeToken="xs:anyURI"? xmlns:sp="..." ... > ( <sp:Issuer>wsa:EndpointReferenceType</sp:Issuer> | <sp:IssuerName>xs:anyURI</sp:IssuerName> ) ? <wst:Claims Dialect="..."> ... </wst:Claims> ? <wsp:Policy xmlns:wsp="..."> ( <sp:NoPassword ... /> | <sp:HashPassword ... /> ) ? ( <sp:RequireDerivedKeys /> | <sp:RequireImpliedDerivedKeys ... /> | <sp:RequireExplicitDerivedKeys ... /> ) ? ( <sp:WssUsernameToken10 ... /> | <sp:WssUsernameToken11 ... /> ) ? ... </wsp:Policy> ... </sp:UsernameToken>
The sub-elements of sp:UsernameToken
are all optional and are not needed for ordinary authentication. Normally, the only part of this syntax that is relevant is the sp:IncludeToken
attribute.
Currently, in the sp:UsernameToken
syntax, only the sp:WssUsernameToken10
sub-element is supported in Apache CXF.
sp:IncludeToken attribute
The value of the sp:IncludeToken
must match the WS-SecurityPolicy version from the enclosing policy. The current version is 1.2, but legacy WSDL might use version 1.1. Valid values of the sp:IncludeToken
attribute are as follows:
- Never
The token MUST NOT be included in any messages sent between the initiator and the recipient; rather, an external reference to the token should be used. Valid URI values are:
1.2
http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Never
1.1
http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Never
- Once
The token MUST be included in only one message sent from the initiator to the recipient. References to the token MAY use an internal reference mechanism. Subsequent related messages sent between the recipient and the initiator may refer to the token using an external reference mechanism. Valid URI values are:
1.2
http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Once
1.1
http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Once
- AlwaysToRecipient
The token MUST be included in all messages sent from initiator to the recipient. The token MUST NOT be included in messages sent from the recipient to the initiator. Valid URI values are:
1.2
http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient
1.1
http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient
- AlwaysToInitiator
The token MUST be included in all messages sent from the recipient to the initiator. The token MUST NOT be included in messages sent from the initiator to the recipient. Valid URI values are:
1.2
http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToInitiator
1.1
http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToInitiator
- Always
The token MUST be included in all messages sent between the initiator and the recipient. This is the default behavior. Valid URI values are:
1.2
http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/Always
1.1
http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Always
SupportingTokens assertions
The following kinds of supporting tokens assertions are supported:
- the section called “sp:SupportingTokens”.
- the section called “sp:SignedSupportingTokens”.
- the section called “sp:EncryptedSupportingTokens”.
- the section called “sp:SignedEncryptedSupportingTokens”.
- the section called “sp:EndorsingSupportingTokens”.
- the section called “sp:SignedEndorsingSupportingTokens”.
- the section called “sp:EndorsingEncryptedSupportingTokens”.
- the section called “sp:SignedEndorsingEncryptedSupportingTokens”.
sp:SupportingTokens
This element requires a token (or tokens) of the specified type to be included in the wsse:Security
header. No additional requirements are imposed.
This policy does not explicitly require the tokens to be signed or encrypted. It is normally essential, however, to protect tokens by signing and encryption.
sp:SignedSupportingTokens
This element requires a token (or tokens) of the specified type to be included in the wsse:Security
header. In addition, this policy requires that the token is signed, in order to guarantee token integrity.
This policy does not explicitly require the tokens to be encrypted. It is normally essential, however, to protect tokens both by signing and encryption.
sp:EncryptedSupportingTokens
This element requires a token (or tokens) of the specified type to be included in the wsse:Security
header. In addition, this policy requires that the token is encrypted, in order to guarantee token confidentiality.
This policy does not explicitly require the tokens to be signed. It is normally essential, however, to protect tokens both by signing and encryption.
sp:SignedEncryptedSupportingTokens
This element requires a token (or tokens) of the specified type to be included in the wsse:Security
header. In addition, this policy requires that the token is both signed and encrypted, in order to guarantee token integrity and confidentiality.
sp:EndorsingSupportingTokens
An endorsing supporting token is used to sign the message signature (primary signature). This signature is known as an endorsing signature or secondary signature. Hence, by applying an endorsing supporting tokens policy, you can have a chain of signatures: the primary signature, which signs the message itself, and the secondary signature, which signs the primary signature.
If you are using a transport binding (for example, HTTPS), the message signature is not actually part of the SOAP message, so it is not possible to sign the message signature in this case. If you specify this policy with a transport binding, the endorsing token signs the timestamp instead.
This policy does not explicitly require the tokens to be signed or encrypted. It is normally essential, however, to protect tokens by signing and encryption.
sp:SignedEndorsingSupportingTokens
This policy is the same as the endorsing supporting tokens policy, except that the tokens are required to be signed, in order to guarantee token integrity.
This policy does not explicitly require the tokens to be encrypted. It is normally essential, however, to protect tokens both by signing and encryption.
sp:EndorsingEncryptedSupportingTokens
This policy is the same as the endorsing supporting tokens policy, except that the tokens are required to be encrypted, in order to guarantee token confidentiality.
This policy does not explicitly require the tokens to be signed. It is normally essential, however, to protect tokens both by signing and encryption.
sp:SignedEndorsingEncryptedSupportingTokens
This policy is the same as the endorsing supporting tokens policy, except that the tokens are required to be signed and encrypted, in order to guarantee token integrity and confidentiality.
7.3. Providing Client Credentials
Overview
There are essentially two approaches to providing UsernameToken
client credentials: you can either set both the username and the password directly in the client’s Blueprint XML configuration; or you can set the username in the client’s configuration and implement a callback handler to provide passwords programmatically. The latter approach (by programming) has the advantage that passwords are easier to hide from view.
Client credentials properties
Table 7.1, “Client Credentials Properties” shows the properties you can use to specify WS-Security username/password credentials on a client’s request context in Blueprint XML.
Properties | Description |
---|---|
| Specifies the username for UsernameToken policy assertions. |
| Specifies the password for UsernameToken policy assertions. If not specified, the password is obtained by calling the callback handler. |
| Specifies the class name of the WSS4J callback handler that retrieves passwords for UsernameToken policy assertions. Note that the callback handler can also handle other kinds of security events. |
Configuring client credentials in Blueprint XML
To configure username/password credentials in a client’s request context in Blueprint XML, set the security.username
and security.password
properties as follows:
<beans ... > <jaxws:client name="{NamespaceName}LocalPortName" createdFromAPI="true"> <jaxws:properties> <entry key="security.username" value="Alice"/> <entry key="security.password" value="abcd!1234"/> </jaxws:properties> </jaxws:client> ... </beans>
If you prefer not to store the password directly in Blueprint XML (which might potentially be a security hazard), you can provide passwords using a callback handler instead.
Programming a callback handler for passwords
If you want to use a callback handler to provide passwords for the UsernameToken header, you must first modify the client configuration in Blueprint XML, replacing the security.password
setting by a security.callback-handler
setting, as follows:
<beans ... > <jaxws:client name="{NamespaceName}LocalPortName" createdFromAPI="true"> <jaxws:properties> <entry key="security.username" value="Alice"/> <entry key="security.callback-handler" value="interop.client.UTPasswordCallback"/> </jaxws:properties> </jaxws:client> ... </beans>
In the preceding example, the callback handler is implemented by the UTPasswordCallback
class. You can write a callback handler by implementing the javax.security.auth.callback.CallbackHandler
interface, as shown in Example 7.2, “Callback Handler for UsernameToken Passwords”.
Example 7.2. Callback Handler for UsernameToken Passwords
package interop.client;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.apache.ws.security.WSPasswordCallback;
public class UTPasswordCallback implements CallbackHandler {
private Map<String, String> passwords =
new HashMap<String, String>();
public UTPasswordCallback() {
passwords.put("Alice", "ecilA");
passwords.put("Frank", "invalid-password");
//for MS clients
passwords.put("abcd", "dcba");
}
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
WSPasswordCallback pc = (WSPasswordCallback)callbacks[i];
String pass = passwords.get(pc.getIdentifier());
if (pass != null) {
pc.setPassword(pass);
return;
}
}
throw new IOException();
}
// Add an alias/password pair to the callback mechanism.
public void setAliasPassword(String alias, String password) {
passwords.put(alias, password);
}
}
The callback functionality is implemented by the CallbackHandler.handle()
method. In this example, it assumed that the callback objects passed to the handle()
method are all of org.apache.ws.security.WSPasswordCallback type (in a more realistic example, you would check the type of the callback objects).
A more realistic implementation of a client callback handler would probably consist of prompting the user to enter their password.
WSPasswordCallback class
When a CallbackHandler
is called in a Apache CXF client for the purpose of setting a UsernameToken
password, the corresponding WSPasswordCallback
object has the USERNAME_TOKEN
usage code.
For more details about the WSPasswordCallback
class, see org.apache.ws.security.WSPasswordCallback.
The WSPasswordCallback
class defines several different usage codes, as follows:
- USERNAME_TOKEN
Obtain the password for UsernameToken credentials. This usage code is used both on the client side (to obtain a password to send to the server) and on the server side (to obtain a password in order to compare it with the password received from the client).
On the server side, this code is set in the following cases:
-
Digest password—if the UsernameToken contains a digest password, the callback must return the corresponding password for the given user name (given by
WSPasswordCallback.getIdentifier()
). Verification of the password (by comparing with the digest password) is done by the WSS4J runtime. - Plaintext password—implemented the same way as the digest password case (since Apache CXF 2.4.0).
Custom password type—if
getHandleCustomPasswordTypes()
istrue
onorg.apache.ws.security.WSSConfig
, this case is implemented the same way as the digest password case (since Apache CXF 2.4.0). Otherwise, an exception is thrown.If no
Password
element is included in a received UsernameToken on the server side, the callback handler is not called (since Apache CXF 2.4.0).
-
Digest password—if the UsernameToken contains a digest password, the callback must return the corresponding password for the given user name (given by
- DECRYPT
-
Need a password to retrieve a private key from a Java keystore, where
WSPasswordCallback.getIdentifier()
gives the alias of the keystore entry. WSS4J uses this private key to decrypt the session (symmetric) key. - SIGNATURE
-
Need a password to retrieve a private key from a Java keystore, where
WSPasswordCallback.getIdentifier()
gives the alias of the keystore entry. WSS4J uses this private key to produce a signature. - SECRET_KEY
-
Need a secret key for encryption or signature on the outbound side, or for decryption or verification on the inbound side. The callback handler must set the key using the
setKey(byte[])
method. - SECURITY_CONTEXT_TOKEN
-
Need the key for a
wsc:SecurityContextToken
, which you provide by calling thesetKey(byte[])
method. - CUSTOM_TOKEN
-
Need a token as a DOM element. For example, this is used for the case of a reference to a SAML Assertion or SecurityContextToken that is not in the message. The callback handler must set the token using the
setCustomToken(Element)
method. - KEY_NAME
- (Obsolete) Since Apache CXF 2.4.0, this usage code is obsolete.
- USERNAME_TOKEN_UNKNOWN
- (Obsolete) Since Apache CXF 2.4.0, this usage code is obsolete.
- UNKNOWN
- Not used by WSS4J.
7.4. Authenticating Received Credentials
Overview
On the server side, you can verify that received credentials are authentic by registering a callback handler with the Apache CXF runtime. You can either write your own custom code to perform credentials verification or you can implement a callback handler that integrates with a third-party enterprise security system (for example, an LDAP server).
Configuring a server callback handler in Blueprint XML
To configure a server callback handler that verifies UsernameToken
credentials received from clients, set the security.callback-handler
property in the server’s Blueprint XML configuration, as follows:
<beans ... > <jaxws:endpoint id="UserNameOverTransport" address="https://localhost:9001/UserNameOverTransport" serviceName="interop:PingService10" endpointName="interop:UserNameOverTransport_IPingService" implementor="interop.server.UserNameOverTransport" depends-on="tls-settings"> <jaxws:properties> <entry key="security.username" value="Alice"/> <entry key="security.callback-handler" value="interop.client.UTPasswordCallback"/> </jaxws:properties> </jaxws:endpoint> ... </beans>
In the preceding example, the callback handler is implemented by the UTPasswordCallback
class.
Implementing the callback handler to check passwords
To implement a callback handler for checking passwords on the server side, implement the javax.security.auth.callback.CallbackHandler
interface. The general approach to implementing the CallbackHandler
interface for a server is similar to implementing a CallbackHandler
for a client. The interpretation given to the returned password on the server side is different, however: the password from the callback handler is compared against the received client password in order to verify the client’s credentials.
For example, you could use the sample implementation shown in Example 7.2, “Callback Handler for UsernameToken Passwords” to obtain passwords on the server side. On the server side, the WSS4J runtime would compare the password obtained from the callback with the password in the received client credentials. If the two passwords match, the credentials are successfully verified.
A more realistic implementation of a server callback handler would involve writing an integration with a third-party database that is used to store security data (for example, integration with an LDAP server).