Tuesday, August 16, 2011

SAML token creation in Apache WSS4J 1.6

WSS4J 1.6 uses Opensaml2 to create and parse SAML Assertions. WSS4J ships with a library that wraps the Opensaml API to greatly simplify the process of creating a SAML Assertion. The user implements a CallbackHandler instance which must be able to handle a WSS4J SAMLCallback object. and set certain properties on this object. WSS4J then parses the properties that were set on the object and creates a SAML Assertion accordingly. In this blog post we will examine the process of creating a SAML Assertion by populating a SAMLCallback object in more detail.

1) Obtain a SAMLCallback object

First off, a SAMLCallback object must be obtained in the CallbackHandler implementation, e.g.:

if (callbacks[i] instanceof SAMLCallback) {
    SAMLCallback callback = (SAMLCallback) callbacks[i];
    ....
}

2) Set the SAML Version

The first thing to do is to set the desired SAML Version of the Assertion you want to create on the SAMLCallback object, e.g.:

callback.setSamlVersion(SAMLVersion.VERSION_11);

This method takes an org.opensaml.common.SAMLVersion object. For normal purposes, it will be one of the following:
  • SAMLVersion.VERSION_11
  • SAMLVersion.VERSION_20
The default value in SAMLCallback is VERSION_11.

3) Set the Issuer

The next thing to do is to set the issuer (String) name of the token, e.g.:

callback.setIssuer("sts");

4) Set the Subject

The next thing to do is to set the Subject of the Assertion. WSS4J defines a SubjectBean which encapsulates a SAML Subject. The Subject Name, NameQualifier, NameIDFormat and ConfirmationMethod Strings can be set on the SubjectBean instance. The NameIDFormat and ConfirmationMethod values that can be set are largely defined in the SAML1Constants and SAML2Constants classes. The default value for the SubjectBean NameIDFormat is SAML1Constants.NAMEID_FORMAT_UNSPECIFIED. Both constants classes contain the following values that can be used for the ConfirmationMethod value:
  •  SAML[1|2]Constants.CONF_BEARER
  •  SAML[1|2]Constants.CONF_HOLDER_KEY
  •  SAML[1|2]Constants.CONF_SENDER_VOUCHES
In addition to this, WSS4J defines a KeyInfoBean that can be set on the SubjectBean, which represents a KeyInfo structure that will be embedded in a SAML Subject. There are a number of different ways to set the KeyInfo value:
  • A DOM element can be defined for a pre-existing KeyInfo element
  • A PublicKey object can be used
  • An X.509 Certificate can be used
For the latter two cases, the KeyInfoBean contains a CERT_IDENTIFIER enum which defines how the PublicKey or X.509 Certificate will be output. The following values can be configured:
  • X509_CERT: An X509Certificate element will be output
  • X509_ISSUER_SERIAL: An X509 IssuerSerial element will be output
  • KEY_VALUE: A KeyValue element will be output
The default value in the KeyInfoBean is X509_CERT. Here is a sample that shows how to create a SubjectBean:

SubjectBean subjectBean = new SubjectBean();
subjectBean.setSubjectName("uid=joe");
subjectBean.setSubjectNameQualifier("apache.org");
subjectBean.setSubjectConfirmationMethod(
     SAML1Constants.CONF_SENDER_VOUCHES
);
KeyInfoBean keyInfo = new KeyInfoBean();
keyInfo.setCertificate(cert);
subjectBean.setKeyInfo(keyInfo);

Finally, it remains to store the SubjectBean instance in the SAMLCallback object. A SAML 2.0 Assertion contains a single Subject, and so for this case the SubjectBean instance can be set directly on the SAMLCallback, e.g.:

callback.setSubject(subjectBean);

For a SAML 1.1 Assertion, the Subject can be in a SubjectStatement (in which case it can be set directly on the SAMLCallback), or it can be embedded in one of the other statements which we will cover next.

5) Create and set a Statement

WSS4J contains a number of beans to create Statements for SAML Assertions, that can be set on a SAMLCallback object. They can be used in either SAML 1.1 or SAML 2.0 Assertions, with the caveat that SubjectBean instances are not used with statements in SAML 2.0.

a) Attribute Statements

WSS4J contains an AttributeStatementBean for creating Attribute statements. This contains a SubjectBean (for the SAML 1.1 case), and a list of AttributeBean objects. An attribute simple name, qualified name, and name format Strings can be set on the AttributeBean, as well as a list of attribute value Strings. Here is an example of creating and adding an AttributeStatement to a SAMLCallback object:

AttributeStatementBean attrBean = new AttributeStatementBean();
attrBean.setSubject(subjectBean);
AttributeBean attributeBean = new AttributeBean();
attributeBean.setSimpleName("role");
attributeBean.setAttributeValues(Collections.singletonList("user"));
attrBean.setSamlAttributes(Collections.singletonList(attributeBean));
callback.setAttributeStatementData(Collections.singletonList(attrBean));

b) Authentication Statements

WSS4J contains an AuthenticationStatementBean for creating Authentication statements. For SAML 1.1 a SubjectBean instance can be set on this object. In addition to this, an authentication instant and authentication method can be set, as well as a SubjectLocalityBean object and a session index String. Various authentication method Strings are defined in the SAML1Constants and SAML2Constants given above. Here is an example:

AuthenticationStatementBean authBean = new AuthenticationStatementBean();
authBean.setSubject(subjectBean);
SubjectLocalityBean subjectLocality = new SubjectLocalityBean();
subjectLocality.setIpAddress(subjectLocalityIpAddress);
subjectLocality.setDnsAddress(subjectLocalityDnsAddress);
authBean.setSubjectLocality(subjectLocality);
authBean.setAuthenticationMethod("Password");     callback.setAuthenticationStatementData(Collections.singletonList(authBean));

c) Authorization Decision Statements

WSS4J contains an AuthDecisionStatementBean for creating Authorization Decision Statements. For SAML 1.1 a SubjectBean instance can be set on this object. A Decision enum can be set (PERMIT, INDETERMINATE, DENY), as well as a resource String, evidence Object, and a list of ActionBeans, which in turn contain an action namespace and action contents. Here is an example:

AuthDecisionStatementBean authzBean = new AuthDecisionStatementBean();
authzBean.setSubject(subjectBean);
ActionBean actionBean = new ActionBean();
actionBean.setContents("Read");
authzBean.setActions(Collections.singletonList(actionBean));
authzBean.setResource("endpoint");
authzBean.setDecision(AuthDecisionStatementBean.Decision.PERMIT);
authzBean.setResource(resource);  callback.setAuthDecisionStatementData(Collections.singletonList(authzBean));
 
6) Create a Conditions object

Finally, a ConditionsBean object can be used to specify a set of Conditions on the SAML Assertion. This is optional, as by default a Conditions element will be generated with a NotBefore value of the present instant and a NotOnOrAfter value corresponding to 5 minutes into the future. This can be changed by creating a ConditionsBean and specifying either "notBefore" and "notAfter" dates, or else a token period in minutes in which the token is valid. It is also possible to specify an audience restriction URI on the ConditionsBean object. Here is an example:

ConditionsBean conditions = new ConditionsBean();
conditions.setTokenPeriodMinutes(10);
conditions.setAudienceURI("http://ws.apache.org/wss4j");
callback.setConditions(conditionsBean);

For some examples of CallbackHandlers used to create SAML Assertions, check out the following CallbackHandlers used in the WSS4J unit tests - SAML1AuthnHOKHandler, SAML1CallbackHandler and SAML2CallbackHandler.

1 comment:

  1. Hi.
    I am new to spring web services and SAML. I am working on implementing SAML in our web service. Is there any code sample which you can share?

    ReplyDelete