Getting started with OpenSAML 4

So you have a application in Java and you have come to the realization that you need to support SAML to allow Single Sing-On of your users. Searching for SAML and Java you quickly end up in the OpenSAML library.

In this tutorial I will explain the basics of OpenSAML to get you as a beginner started. When starting to look on OpenSAML it is easy to feel daunted due to lack of documentation and guides. I how this tutorial will put you on the right track.

OpenSAML is a software library that helps you to work with the SAML framework, including

  • Creating SAML messages, such as assertions, authentication request and response messages, Single LogOut messages and much more
  • Sending messages using SAML bindings including HTTP POST, HTTP Redirect, HTTP Artifact and SOAP bindings
  • Cryptographic signing and verifying messages
  • Encrypting and decrypting messages
  • Much, much more

As you are surely staring to understand, OpenSAML is a very powerful and flexible library. This does however dome with some important caveats.

The OpenSAML library works on a very low level. Meaning you have to do a lot of stuff yourself. When you use OpenSAML for SAML communication you will more or less be creating all parts of your messages by hand, choosing what should be included. This also means that if you forget something, or implement something incorrectly, like verifying digital signatures, it will not be done and you risk opening up security holes in your code.

To be able to create a working and secure SSO service using OpenSAML you should be very familiar with the SAML specification. The specification does not only specify how SAML messages should look but also have thorough rules describing how messages must be validated and processed. In this post I explain the SAML specifications further

Starting out with the code

This is just a small example on how to get started and do something with OpenSAML. I have loads more on the blog.

Dependencies

To begin with, lets add OpenSAML to your project. OpenSAML has several different libraries and the others might be needed depending on how you use the library. But for this tutorial the below dependencies in Maven is enough.

 1<properties>
 2	<opensaml.version>4.1.1</opensaml.version>
 3	<xmlsectool.version>3.0.0</xmlsectool.version>
 4</properties>
 5<dependencies>
 6	<dependency>
 7		<groupId>org.opensaml</groupId>
 8		<artifactId>opensaml-core</artifactId>
 9		<version>${opensaml.version}</version>
10		<scope>compile</scope>
11	</dependency>
12	<dependency>
13		<groupId>org.opensaml</groupId>
14		<artifactId>opensaml-saml-api</artifactId>
15		<version>${opensaml.version}</version>
16		<scope>compile</scope>
17	</dependency>
18	<dependency>
19		<groupId>org.opensaml</groupId>
20		<artifactId>opensaml-saml-impl</artifactId>
21		<version>${opensaml.version}</version>
22		<scope>compile</scope>
23	</dependency>
24	<dependency>
25		<groupId>org.opensaml</groupId>
26		<artifactId>opensaml-soap-api</artifactId>
27		<version>${opensaml.version}</version>
28		<scope>compile</scope>
29	</dependency>
30	<dependency>
31		<groupId>org.opensaml</groupId>
32		<artifactId>opensaml-xmlsec-api</artifactId>
33		<version>${opensaml.version}</version>
34	</dependency>
35	<dependency>
36		<groupId>org.opensaml</groupId>
37		<artifactId>opensaml-security-api</artifactId>
38		<version>${opensaml.version}</version>
39	</dependency>
40	<dependency>
41		<groupId>org.opensaml</groupId>
42		<artifactId>opensaml-security-impl</artifactId>
43		<version>${opensaml.version}</version>
44	</dependency>
45	<dependency>
46		<groupId>org.opensaml</groupId>
47		<artifactId>opensaml-profile-api</artifactId>
48		<version>${opensaml.version}</version>
49	</dependency>
50	<dependency>
51		<groupId>org.opensaml</groupId>
52		<artifactId>opensaml-profile-impl</artifactId>
53		<version>${opensaml.version}</version>
54	</dependency>
55	<dependency>
56		<groupId>org.opensaml</groupId>
57		<artifactId>opensaml-messaging-api</artifactId>
58		<version>${opensaml.version}</version>
59	</dependency>
60	<dependency>
61		<groupId>org.opensaml</groupId>
62		<artifactId>opensaml-messaging-impl</artifactId>
63		<version>${opensaml.version}</version>
64	</dependency>
65	<dependency>
66		<groupId>org.opensaml</groupId>
67		<artifactId>opensaml-storage-impl</artifactId>
68		<version>${opensaml.version}</version>
69	</dependency>
70	<dependency>
71		<groupId>org.opensaml</groupId>
72		<artifactId>opensaml-xmlsec-impl</artifactId>
73		<version>${opensaml.version}</version>
74	</dependency>
75	<dependency>
76		<groupId>javax.servlet</groupId>
77		<artifactId>javax.servlet-api</artifactId>
78		<scope>provided</scope>
79		<version>3.1.0</version>
80	</dependency>
81	<dependency>
82		<groupId>net.shibboleth.tool</groupId>
83		<artifactId>xmlsectool</artifactId>
84		<version>${xmlsectool.version}</version>
85	</dependency>
86	<dependency>
87		<groupId>commons-logging</groupId>
88		<artifactId>commons-logging</artifactId>
89		<version>1.2</version>
90	</dependency>
91</dependencies>
92<repositories>
93	<repository>
94		<id>shib-release</id>
95		<url>https://build.shibboleth.net/nexus/content/repositories/releases</url>
96	</repository>
97</repositories>

Bootstrapping

To start using OpenSAML, you first always need to initiate the library. If you do not do this you will get the following error when trying to use it.

1Exception in thread "main" java.lang.NullPointerException: Cannot invoke "org.opensaml.core.xml.XMLObjectBuilder.buildObject(javax.xml.namespace.QName)" because the return value of "org.opensaml.core.xml.XMLObjectBuilderFactory.getBuilder(javax.xml.namespace.QName)" is null
2	at com.samlsecurity.opensamlSamples.gettingStartedAuthnrequest.OpenSAMLUtils.buildSAMLObject(OpenSAMLUtils.java:42)
3	at com.samlsecurity.opensamlSamples.gettingStartedAuthnrequest.GenAuthnRequest.buildAuthnRequest(GenAuthnRequest.java:91)
4	at com.samlsecurity.opensamlSamples.gettingStartedAuthnrequest.GenAuthnRequest.main(GenAuthnRequest.java:37)

Ok, so first lets define and register a parser pool

1XMLObjectProviderRegistry registry = new XMLObjectProviderRegistry();
2ConfigurationService.register(XMLObjectProviderRegistry.class, registry);
3registry.setParserPool(getParserPool());

The ParserPool is the pool of instances used for XML processing and the initiation of the parser pool is the main point of configuration for XML processing. Below is a typical secure configuration of the a parsing pool

 1BasicParserPool parserPool = new BasicParserPool();
 2parserPool.setMaxPoolSize(100);
 3parserPool.setCoalescing(true);
 4parserPool.setIgnoreComments(true);
 5parserPool.setIgnoreElementContentWhitespace(true);
 6parserPool.setNamespaceAware(true);
 7parserPool.setExpandEntityReferences(false);
 8parserPool.setXincludeAware(false);
 9
10final Map<String, Boolean> features = new HashMap<String, Boolean>();
11features.put("http://xml.org/sax/features/external-general-entities", Boolean.FALSE);
12features.put("http://xml.org/sax/features/external-parameter-entities", Boolean.FALSE);
13features.put("http://apache.org/xml/features/disallow-doctype-decl", Boolean.TRUE);
14features.put("http://apache.org/xml/features/validation/schema/normalized-value", Boolean.FALSE);
15features.put("http://javax.xml.XMLConstants/feature/secure-processing", Boolean.TRUE);
16
17parserPool.setBuilderFeatures(features);
18parserPool.setBuilderAttributes(new HashMap<String, Object>());
19
20try {
21	parserPool.initialize();
22} catch (ComponentInitializationException e) {
23	logger.error(e.getMessage(), e);
24}

First shot - Creating a authentication request

This is just a small example on how to use OpenSAML. I have loads of posts and guides on how to do more.
The following creates a SAML authentication request

 1private static AuthnRequest buildAuthnRequest() {
 2	AuthnRequest authnRequest = OpenSAMLUtils.buildSAMLObject(AuthnRequest.class);
 3	authnRequest.setIssueInstant(Instant.now());
 4	authnRequest.setDestination(IPD_SSO_DESTINATION);
 5	authnRequest.setProtocolBinding(SAMLConstants.SAML2_ARTIFACT_BINDING_URI);
 6	authnRequest.setAssertionConsumerServiceURL(SP_ASSERTION_CONSUMER_SERVICE_URL);
 7	authnRequest.setID(OpenSAMLUtils.generateSecureRandomId());
 8	authnRequest.setIssuer(buildIssuer());
 9	authnRequest.setNameIDPolicy(buildNameIdPolicy());
10	
11	return authnRequest;
12}
13	
14private static NameIDPolicy buildNameIdPolicy() {
15	NameIDPolicy nameIDPolicy = OpenSAMLUtils.buildSAMLObject(NameIDPolicy.class);
16	nameIDPolicy.setAllowCreate(true);
17	nameIDPolicy.setFormat(NameIDType.TRANSIENT);
18
19	return nameIDPolicy;
20}
21
22private static Issuer buildIssuer() {
23	Issuer issuer = OpenSAMLUtils.buildSAMLObject(Issuer.class);
24	issuer.setValue(SP_ISSUED_ID);
25
26	return issuer;
27}

Resulting in the following AuthnRequest XML

 1<?xml version="1.0" encoding="UTF-8"?>
 2<saml2p:AuthnRequest xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" 
 3		AssertionConsumerServiceURL="https://sp.example.com/assertionConsumerService"
 4		Destination="https://idp.example.com/singleSingOnService"
 5		ID="_bba9213612c50821d391aa3f95462bad"
 6		IssueInstant="2021-10-09T21:06:57.064Z"
 7		ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact" Version="2.0">
 8    <saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
 9    	IssuerEntityId
10    </saml2:Issuer>
11    <saml2p:NameIDPolicy
12    		AllowCreate="true"
13    		Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"/>
14</saml2p:AuthnRequest>

Get it on Github!

The full running sample is available on Github at https://github.com/rasmusson/OpenSAML-sample-code under getting-started-authnrequest

Just check out, run and go nuts

1git clone https://github.com/rasmusson/OpenSAML-sample-code/
2cd OpenSAML-sample-code/getting-started-authnrequest
3mvn compile exec:java -Dexec.mainClass="com.samlsecurity.opensamlSamples.gettingStartedAuthnrequest.GenAuthnRequest"

Summary

In this post we have looked on the very basics.

  • Adding OpenSAML to you project
  • Initializing OpenSAML and,
  • Producing your first SAML message

This was a very simple, minimal first example on how to use OpenSAML. On this blog you will find lots of more examples and explanations on beginner level as well as more advanced fringe cases.

Next step

Check out The Big OpenSAML Guide with links pointing to all relevant article and resources you need to continue building the OpenSAML.