You can request SAML tokens with the bearer subject confirmation
method from an external Security Token Service (STS). After obtaining
the SAML bearer token, you can then send these tokens with web services
request messages using the Java API
for XML-Based Web Services (JAX-WS) programming model and Web Services
Security APIs (WSS API).
Before you begin
This task assumes that you are familiar with the JAX-WS
programming model, the WSS API interfaces, SAML concepts, and the
use of policy sets to configure and administer web services settings.
About this task
You can request a SAML token with the bearer subject confirmation
method from an external STS and then send the SAML token in web services
request messages from a web services client using WSS APIs.
The
web services application client used in this task is a modified version
of the client code that is contained in the JaxWSServicesSamples sample
application that is available for download. Code snippets from the
sample are described in the procedure section, and a complete, ready-to-use
web services client sample is provided in the Example section.
Procedure
- Identify and obtain the web services client that you want
to use to invoke a web services provider.
Use this
client to insert SAML tokens in SOAP request messages programmatically
using WSS APIs.
The web services client used in this procedure
is a modified version of the client code that is contained in the
JaxWSServicesSamples web services sample application.
To obtain
and modify the sample web services client to add the Web Services
Security API to pass SAML tokens in SOAP request messages programmatically
using WSS APIs, complete the following steps:
- Download the JaxWSServicesSamples sample application. The JaxWSServicesSamples sample is not installed by default.
- Obtain the JaxWSServicesSamples client code.
For
example purposes, this procedure uses a modified version of the Echo
thin client sample that is included in the JaxWSServicesSamples sample.
The web services Echo thin client sample file, SampleClient.java,
is located in the src\SampleClientSei\src\com\ibm\was\wssample\sei\cli directory.
The sample class file is included in the WSSampleClientSei.jar file.
The JaxWSServicesSamples.ear enterprise
application and supporting Java archives
(JAR) files are located in the installableApps directory
within the JaxWSServicesSamples sample application.
- Deploy the JaxWSServicesSamples.ear file
onto the application server. After you deploy the JaxWSServicesSamples.ear file,
you are ready to test the sample web services client code against
the sample application.
Instead of using the web services client sample, you can
choose to add the code snippets to pass SAML tokens in SOAP request
messages programmatically using WSS APIs in your own web services
client application. The example in this procedure uses a JAX-WS web
services thin client; however, you can also use a managed client.
- Attach the SAML20 Bearer WSHTTPS default policy set to
the web services provider. This policy set is used to
protect messages using HTTPS transport. Read about configuring client
and provider bindings for the SAML Bearer token for details on how
to attach the SAML20 Bearer WSHTTPS default policy set to the web
services provider.
- Assign the SAML Bearer Provider sample default general
bindings to the sample web services provider. Read about
configuring client and provider bindings for the SAML bearer token
for details on assigning the SAML Bearer Provider sample default general
bindings to your web services application.
- Verify that the trustStoreType, trustStorePassword and
trustStorePath custom properties correspond to the trust store containing
the STS signer certificate. Using the administrative console,
complete the following steps:
- Click .
- Click gen_saml11token in the Authentication
tokens table.
- Click Callback handler.
- In the Custom Properties section, ensure that the trustStoreType,
trustStorePassword and trustStorePath custom properties correspond
to the trust store containing the STS signer certificate.
- Request the SAML token from an external STS. The
following code snippet illustrates how to request the SAML token and
assumes that an external STS is configured to accept a UsernameToken,
and to issue a SAML 2.0 token after validation:
//Request the SAML Token from external STS
WSSFactory factory = WSSFactory.getInstance();
String STS_URI = "https://externalstsserverurl:port/TrustServerWST13/services/RequestSecurityToken";
String ENDPOINT_URL = "http://localhost:9080/WSSampleSei/EchoService";
WSSGenerationContext gencont1 = factory.newWSSGenerationContext();
WSSConsumingContext concont1 = factory.newWSSConsumingContext();
HashMap<Object, Object> cbackMap1 = new HashMap<Object, Object>();
cbackMap1.put(SamlConstants.STS_ADDRESS, STS_URI);
cbackMap1.put(SamlConstants.SAML_APPLIES_TO, ENDPOINT_URL);
cbackMap1.put(SamlConstants.TRUST_CLIENT_WSTRUST_NAMESPACE, "http://docs.oasis-open.org/ws-sx/ws-trust/200512");
cbackMap1.put(SamlConstants.TRUST_CLIENT_COLLECTION_REQUEST, "false");
cbackMap1.put(SamlConstants.TOKEN_TYPE, WSSConstants.SAML.SAML20_VALUE_TYPE);
cbackMap1.put(SamlConstants.CONFIRMATION_METHOD, "Bearer");
SAMLGenerateCallbackHandler cbHandler1 = new SAMLGenerateCallbackHandler(cbackMap1);
// Add UNT to trust request
UNTGenerateCallbackHandler utCallbackHandler = new UNTGenerateCallbackHandler("testuser", "testuserpwd");
SecurityToken ut = factory.newSecurityToken(UsernameToken.class, utCallbackHandler);
gencont1.add(ut);
cbHandler1.setWSSConsumingContextForTrustClient(concont1);
cbHandler1.setWSSGenerationContextForTrustClient(gencont1);
SecurityToken samlToken = factory.newSecurityToken(SAMLToken.class, cbHandler1, "system.wss.generate.saml");
System.out.println("SAMLToken id = " + samlToken.getId());
- Add the Thin Client for JAX-WS JAR file to the class
path. Add the app_server_root/runtimes/com.ibm.jaxws.thinclient_8.5.0.jar
file to the class path. See the testing web services-enabled clients
information for more information about adding this JAR file to the
class path.
- Use the WSSFactory newSecurityToken method to request
a SAML token from an external STS.
Specify the following
method to request the SAML token:
WSSFactory newSecurityToken(SAMLToken.class, callbackHandler, "system.wss.generate.saml")
Requesting
a SAML token requires the Java security
permission wssapi.SAMLTokenFactory.newSAMLToken. Use the Policy Tool
to add the following policy statement to the Java security policy file or the application
client was.policy file:
permission java.security.SecurityPermission "wssapi.SAMLTokenFactory.newSAMLToken"
The SAMLToken.class parameter
specifies the type of security token to create.
The
callbackHandler object
contains parameters that define the characteristics of the SAML token
that you are requesting and other parameters required to reach the
STS and obtain the SAML token. The SAMLGenerateCallbackHandler object
specifies the configuration parameters described in the following
table:
Table 1. SAMLGenerateCallbackHandler
properties. This table describes the configuration parameters
for the SAMLGenerateCallbackHandler object using the bearer subject
confirmation method.Property |
Description |
Required |
SamlConstants.CONFIRMATION_METHOD |
Specifies to use the Bearer confirmation
method. |
Yes |
SamlConstants.TOKEN_TYPE |
Specifies the token type.
When
a web services client has policy set attachments, this property is
not used by Web Services Security runtime environment.
Specify
the token value type by using the valueType attribute
of the tokenGenerator binding configuration.
The example in
this procedure uses a SAML 2.0 token; however, you can also use the WSSConstants.SAML.SAML11_VALUE_TYPE value.
|
Yes |
SamlConstants.STS_ADDRESS |
Specifies the Security Token
Service address.
For the example used in this task topic, the
value of this property is set to https to specify
to use SSL to protect the SAML Token request.
You must set
the -Dcom.ibm.SSL.ConfigURL property to enable the use of SSL to protect
the SAML token request with the STS.
|
Yes |
SamlConstants.SAML_APPLIES_TO |
Specifies the target STS address
for where you want to use the SAML token. |
No |
SamlConstants.TRUST_CLIENT_COLLECTION_REQUEST |
Specifies whether to request from
the STS a single token that is enclosed in a RequestSecurityToken
(RST) element or multiple tokens in a collection of RST elements that
are enclosed in a single RequestSecurityTokenCollection (RSTC) element. The
default behavior is to request a single token that is enclosed in
a RequestSecurityToken (RST) element from the STS.
Specifying
a true value for this property indicates to request
multiple tokens in a collection of RST elements that are enclosed
in a single RequestSecurityTokenCollection (RSTC) element from the
STS.
|
No |
SamlConstants.TRUST_CLIENT_WSTRUST_NAMESPACE |
Specifies the WS-Trust namespace
that is included in the WS-Trust request. |
No |
A WSSGenerationContext instance and a WSSConsumingContext
instance are also set in the SAMLGenerateCallbackHandler object. The
WSSGenerationContext instance must contain a UNTGenerateCallbackHandler
object with the information to create the UsernameToken that you want
to send to the STS.
The
system.wss.generate.saml parameter
specifies the Java Authentication
and Authorization Service (JAAS) login module that is used to create
the SAML token. You must specify a JVM property to define a JAAS
configuration file that contains the required JAAS login configuration;
for example:
-Djava.security.auth.login.config=profile_root/properties/wsjaas_client.conf
Alternatively,
you can specify a JAAS login configuration file by setting a Java system property in the sample
client code; for example:
System.setProperty("java.security.auth.login.config", "profile_root/properties/wsjaas_client.conf");
- Obtain the token identifier of the created SAML token.
Use the following statement as a simple test for the SAML
token that you created:
System.out.println("SAMLToken id = " + samlToken.getId())
- Add the SAML token to the SOAP security header of a web
services request messages.
- Initialize the web services client and configure the
SOAPAction properties. The following code snippet illustrates
these actions:
// Initialize web services client
EchoService12PortProxy echo = new EchoService12PortProxy();
echo._getDescriptor().setEndpoint(endpointURL);
// Configure SOAPAction properties
BindingProvider bp = (BindingProvider) (echo._getDescriptor().getProxy());
Map<String, Object> requestContext = bp.getRequestContext();
requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointURL);
requestContext.put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE);
requestContext.put(BindingProvider.SOAPACTION_URI_PROPERTY, "echoOperation");
- Initialize the WSSGenerationContext. The
following code illustrates the use of the WSSGenerationContext interface
to initialize a generation context and enable you to insert the SAMLToken into
the web services request message:
// Initialize WSSGenerationContext
WSSGenerationContext gencont = factory.newWSSGenerationContext();
gencont.add(samlToken);
Specifically, the
gencont.add(samlToken) method
call specifies to put the SAML token into a request message. Use the
Policy Tool to add the following policy statement to the Java security policy file or the application
client was.policy file:
permission javax.security.auth.AuthPermission "modifyPrivateCredentials"
- Add the timestamp element in the SOAP messages security
header. The SAML20 Bearer WSHTTPS default policy set requires
web services requests and response messages to carry a timestamp element
in SOAP messages Security header. In the following code snippet, the factory.newWSSTimestamp() method
call generates the timestamp, and the gencont.add(timestamp) method
call specifies the timestamp to put into a request message:
// Add a timestamp to the request message.
WSSTimestamp timestamp = factory.newWSSTimestamp();
gencont.add(timestamp);
gencont.process(requestContext);
- Attach WSSGenerationContext object to the web services
RequestContext object. The WSSGenerationContext object
now contains all the security information that is required to format
a request message. The gencont.process(requestContext) method
call attaches the WSSGenerationContext object to the web services
RequestContext object to enable the Web Services Security runtime
environment to format the required SOAP security header; for example:
// Attaches WSSGenerationContext object to the web services RequestContext object.
gencont.process(requestContext);
- Specify SSL transport level message protection using
JVM properties.
The SAML20 Bearer WSHTTPS default policy
set requires transport-level message protection using SSL. In addition,
you can use this same property to enable protection of the SAML token
request to the STS using SSL. Specify SSL transport-level message
protection using the following JVM property:
-Dcom.ibm.SSL.ConfigURL=file:profile_root\properties\ssl.client.props
Alternatively,
you can define the SSL configuration file using a Java system property in the sample client code;
for example:
System.setProperty("com.ibm.SSL.ConfigURL", "file:profile_root/properties/ssl.client.props");
Results
You have requested a SAML token with the bearer subject
confirmation method with transport level protection from an external
STS. After obtaining the token, you sent the token with web services
request messages using the JAX-WS programming model and WSS APIs.
If
you want to request a SAML token with the bearer subject confirmation
method with message level protection from an external STS, see the
documentation for requesting SAML sender-vouches tokens from an external
STS using WSS APIs and message level protection. To use message level
protection for SAML tokens with the bearer subject confirmation method,
in the step to request the SAML token from an external STS, specify
a confirmation method of
Bearer instead of
sender-vouches;
for example:
//Request the SAML Token from external STS
WSSFactory factory = WSSFactory.getInstance();
String STS_URI = "https://externalstsserverurl:port/TrustServerWST13/services/RequestSecurityToken";
String ENDPOINT_URL = "http://localhost:9080/WSSampleSei/EchoService";
WSSGenerationContext gencont1 = factory.newWSSGenerationContext();
WSSConsumingContext concont1 = factory.newWSSConsumingContext();
HashMap<Object, Object> cbackMap1 = new HashMap<Object, Object>();
cbackMap1.put(SamlConstants.STS_ADDRESS, STS_URI);
cbackMap1.put(SamlConstants.SAML_APPLIES_TO, ENDPOINT_URL);
cbackMap1.put(SamlConstants.TRUST_CLIENT_WSTRUST_NAMESPACE, "http://docs.oasis-open.org/ws-sx/ws-trust/200512");
cbackMap1.put(SamlConstants.TRUST_CLIENT_COLLECTION_REQUEST, "false");
cbackMap1.put(SamlConstants.TOKEN_TYPE, WSSConstants.SAML.SAML11_VALUE_TYPE);
cbackMap1.put(SamlConstants.CONFIRMATION_METHOD, "Bearer");
SAMLGenerateCallbackHandler cbHandler1 = new SAMLGenerateCallbackHandler(cbackMap1);
// Add UNT to trust request
UNTGenerateCallbackHandler utCallbackHandler = new UNTGenerateCallbackHandler("testuser", "testuserpwd");
SecurityToken ut = factory.newSecurityToken(UsernameToken.class, utCallbackHandler);
gencont1.add(ut);
cbHandler1.setWSSConsumingContextForTrustClient(concont1);
cbHandler1.setWSSGenerationContextForTrustClient(gencont1);
SecurityToken samlToken = factory.newSecurityToken(SAMLToken.class, cbHandler1, "system.wss.generate.saml");
System.out.println("SAMLToken id = " + samlToken.getId());
Additionally,
the step to configure the verification of the digital signature in
the response message is optional in the case of the bearer token.
Example
The following code sample is a web services client application
that demonstrates how to request a SAML token from an external STS
and send that SAML token in web services request messages. If your
usage scenario requires SAML tokens, but does not require your application
to pass the SAML tokens using web services messages, you only need
to use the first part of the following sample code, up through the //
Initialize web services client section.
/**
* The following source code is sample code created by IBM Corporation.
* This sample code is provided to you solely for the purpose of assisting you in the
* use of the technology. The code is provided 'AS IS', without warranty or condition of
* any kind. IBM shall not be liable for any damages arising out of your use of the
* sample code, even if IBM has been advised of the possibility of such damages.
*/
package com.ibm.was.wssample.sei.cli;
import com.ibm.was.wssample.sei.echo.EchoService12PortProxy;
import com.ibm.was.wssample.sei.echo.EchoStringInput;
import com.ibm.websphere.wssecurity.wssapi.WSSFactory;
import com.ibm.websphere.wssecurity.wssapi.WSSGenerationContext;
import com.ibm.websphere.wssecurity.wssapi.WSSConsumingContext;
import com.ibm.websphere.wssecurity.wssapi.WSSTimestamp;
import com.ibm.websphere.wssecurity.callbackhandler.SAMLGenerateCallbackHandler;
import com.ibm.websphere.wssecurity.callbackhandler.UNTGenerateCallbackHandler;
import com.ibm.websphere.wssecurity.wssapi.token.UsernameToken;
import com.ibm.websphere.wssecurity.wssapi.token.SAMLToken;
import com.ibm.websphere.wssecurity.wssapi.token.SecurityToken;
import com.ibm.wsspi.wssecurity.core.token.config.WSSConstants;
import com.ibm.wsspi.wssecurity.saml.config.SamlConstants;
import java.util.Map;
import java.util.HashMap;
import javax.xml.ws.BindingProvider;
/**
* SampleClient
* main entry point for thin client JAR sample
* and worker class to communicate with the services
*/
public class SampleClient {
private String urlHost = "localhost";
private String urlPort = "9443";
private static final String CONTEXT_BASE = "/WSSampleSei/";
private static final String ECHO_CONTEXT12 = CONTEXT_BASE+"EchoService12";
private String message = "HELLO";
private String uriString = "https://" + urlHost + ":" + urlPort;
private String endpointURL = uriString + ECHO_CONTEXT12;
private String input = message;
/**
* main()
*
* see printusage() for command-line arguments
*
* @param args
*/
public static void main(String[] args) {
SampleClient sample = new SampleClient();
sample.CallService();
}
/**
* CallService Parms were already read. Now call the service proxy classes
*
*/
void CallService() {
String response = "ERROR!:";
try {
System.setProperty("java.security.auth.login.config", "profile_root/properties/wsjaas_client.conf");
System.setProperty("com.ibm.SSL.ConfigURL", "file:profile_root/properties/ssl.client.props");
//Request the SAML Token from external STS
WSSFactory factory = WSSFactory.getInstance();
String STS_URI = "https://externalstsserverurl:port/TrustServerWST13/services/RequestSecurityToken";
String ENDPOINT_URL = "http://localhost:9080/WSSampleSei/EchoService";
WSSGenerationContext gencont1 = factory.newWSSGenerationContext();
WSSConsumingContext concont1 = factory.newWSSConsumingContext();
HashMap<Object, Object> cbackMap1 = new HashMap<Object, Object>();
cbackMap1.put(SamlConstants.STS_ADDRESS, STS_URI);
cbackMap1.put(SamlConstants.SAML_APPLIES_TO, ENDPOINT_URL);
cbackMap1.put(SamlConstants.TRUST_CLIENT_WSTRUST_NAMESPACE, "http://docs.oasis-open.org/ws-sx/ws-trust/200512");
cbackMap1.put(SamlConstants.TRUST_CLIENT_COLLECTION_REQUEST, "false");
cbackMap1.put(SamlConstants.TOKEN_TYPE, WSSConstants.SAML.SAML20_VALUE_TYPE);
cbackMap1.put(SamlConstants.CONFIRMATION_METHOD, "Bearer");
SAMLGenerateCallbackHandler cbHandler1 = new SAMLGenerateCallbackHandler(cbackMap1);
// Add UNT to trust request
UNTGenerateCallbackHandler utCallbackHandler = new UNTGenerateCallbackHandler("testuser", "testuserpwd");
SecurityToken ut = factory.newSecurityToken(UsernameToken.class, utCallbackHandler);
gencont1.add(ut);
cbHandler1.setWSSConsumingContextForTrustClient(concont1);
cbHandler1.setWSSGenerationContextForTrustClient(gencont1);
SecurityToken samlToken = factory.newSecurityToken(SAMLToken.class, cbHandler1, "system.wss.generate.saml");
System.out.println("SAMLToken id = " + samlToken.getId());
// Initialize web services client
EchoService12PortProxy echo = new EchoService12PortProxy();
echo._getDescriptor().setEndpoint(endpointURL);
// Configure SOAPAction properties
BindingProvider bp = (BindingProvider) (echo._getDescriptor().getProxy());
Map<String, Object> requestContext = bp.getRequestContext();
requestContext.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpointURL);
requestContext.put(BindingProvider.SOAPACTION_USE_PROPERTY, Boolean.TRUE);
requestContext.put(BindingProvider.SOAPACTION_URI_PROPERTY, "echoOperation");
// Initialize WSSGenerationContext
WSSGenerationContext gencont = factory.newWSSGenerationContext();
gencont.add(samlToken);
// Add timestamp
WSSTimestamp timestamp = factory.newWSSTimestamp();
gencont.add(timestamp);
gencont.process(requestContext);
// Build the input object
EchoStringInput echoParm =
new com.ibm.was.wssample.sei.echo.ObjectFactory().createEchoStringInput();
echoParm.setEchoInput(input);
System.out.println(">> CLIENT: SEI Echo to " + endpointURL);
// Prepare to consume timestamp in response message.
WSSConsumingContext concont = factory.newWSSConsumingContext();
concont.add(WSSConsumingContext.TIMESTAMP);
concont.process(requestContext);
// Call the service
response = echo.echoOperation(echoParm).getEchoResponse();
System.out.println(">> CLIENT: SEI Echo invocation complete.");
System.out.println(">> CLIENT: SEI Echo response is: " + response);
} catch (Exception e) {
System.out.println(">> CLIENT: ERROR: SEI Echo EXCEPTION.");
e.printStackTrace();
}
}
}
When this web services client application sample
runs correctly, you receive messages like the following messages:
SAMLToken id = _191EBC44865015D9AB1270745072344
Retrieving document at 'file:profile_root/.../wsdl/'.
>> CLIENT: SEI Echo to https://localhost:9443/WSSampleSei/EchoService12
>> CLIENT: SEI Echo invocation complete.
>> CLIENT: SEI Echo response is: SOAP12==>>HELLO