Showing posts with label OpenAM. Show all posts
Showing posts with label OpenAM. Show all posts

Thursday, August 28, 2014

OpenAM, Spring Security SAML Integration

Summary

Springteam has provided spring-security-project with all required build files.  Based on the spring-security-saml project, Single Sign On using OepnAM was demonstrated relatively easy.  Shared here are the configuration changes required to demonstrate the SSO using OpenAM, and Spring-security-saml project.

Environment

 The demonstration was done using OpenAM11.x deployed on Tomcat, and the web application from spring-security-saml projected deployed on Tomcat as shown below:

 

Integration Breakdown

 Following are the various steps involved in the integration:
  1. spring-security-saml source code
  2. Configure IdP metadata
  3. Configure truststore
  4. Compile source code
  5. Generate SP’s metadata
  6. Register Remote Service Provider
  7. Validate Spring SAML secuirty

spring-security-saml source code

Source code is available on github. Url for the project is:
https://github.com/spring-projects/spring-security-saml.  Download source code, and unzip the file to a directory of the user’s choice.  Files of interest and, the configuration files which have to be modified are:
  1. spring-security-saml-master\sample\src\main\resources\metadata\idp.xml
  2. spring-security-saml-master\sample\src\main\resources\security\samlKeystore.jks
  3. spring-security-saml-master\sample\src\main\webapp\WEB-INF\securityContext.xml

Configure Identity Provider’s Metadata

Application’s configuration file is securityContext.xml as listed above.  The source code contains a sample Identity Provider’s metadata file, idp.xml as listed above, and is configured in securityContext.xml as shown below:
 <bean id="metadata" class="org.springframework.security.saml.metadata.CachingMetadataManager">
                <bean class="org.springframework.security.saml.metadata.ExtendedMetadataDelegate">
                        <bean class="org.opensaml.saml2.metadata.provider.ResourceBackedMetadataProvider">
                                <bean class="org.opensaml.util.resource.ClasspathResource">
                                    <constructor-arg value="/metadata/idp.xml"/>
Either Spring configuration file securityContext.xml has to be modified to reflect the actual name of the Identity Provider’s metadata file, or replace idp.xml with the actual metadata of the Identity Provider.
As OpenAM is currently used as the Identity Provider, metadata retrieval from OpenAM only is mentioned.   Identity Provider metadata can be downloaded from OpenAM’s url:
https://idp.saml.org/openam/saml2/jsp/exportmetadata.jsp?entityid=<Entity Id used for>

Configure Truststore

Similar to configuring Identity Provider’s metadata, application’s truststore is configured by securityContext.xml as shown below:
<bean id="keyManager" class="org.springframework.security.saml.key.JKSKeyManager">
        <constructor-arg value="classpath:security/samlKeystore.jks"/>
        <constructor-arg type="java.lang.String" value="nalle123"/>
        <constructor-arg>
            <map>
                <entry key="apollo" value="nalle123"/>
            </map>
        </constructor-arg>
        <constructor-arg type="java.lang.String" value="apollo"/>
    </bean>
Truststore used by the application should be able to decrypt the encrypted response received from the Identity Provider, and requires that Identity Provider’s public certificate be added to the truststore.  Either change the truststore location and password to reflect a new truststore containing Identity Provider’s public certificate as against the truststore provided within the downloaded code, or just add Identity Provider’s public certificate to samlKeyStore.jks as described above.

Compile Source code

Downloaded source code contains gradle build file, and mvn pom files for compilation.  As of this writing compilation with gradle fails with the following messages:
FAILURE: Build failed with an exception.
* Where:
Build file '<path to downloaded file>\spring-security-saml-master\build.gradle' line: 22
* What went wrong:
A problem occurred evaluating root project 'spring-security-saml'.
> java.lang.String cannot be cast to org.gradle.api.artifacts.Configuration
While gradle build failed with exception, compilation with Maven resulted in a successful build, and a deployable war file spring-security-saml-master\sample\target\spring-security-saml2-sample.war.  To shorten the length of the context root of the deployed application, rename the file to a name of user’s choice, say spring.war.  Deploy the war file in a servlet container.

Generate Service Provider’s metadata

Metadata for the secured application can be downloaded from the unsecured url: http://sp.saml.org/<context root>/saml/metadata.

Register Remote Service Provider

Login to OpenAM’s admin console, and register remote service provider by using service provider’s metadata downloaded above.

Validate Spring SAML Security

By default the root directory of the web application is secured, and require authentication.  Try to access the root directory of the application, http://sp.saml.org/<context root>/.  A page requesting IdP selection is displayed.  Select the IdP from the available options, and click on submit.  The user would be redirected to the authentication page as configured on OpenAM.  After providing valid credentials, the user is successfully authenticated on the Identity Provider, and the user is redirected to the default page of the application, index.jsp which lists various SAML attributes demonstrating SAML.
Spring SAML security validation was done using X509, Username and Password, and Basic Authentication modules supported by OpenAM.

Summary

Overall the integration experience was pleasant, and great appreciation goes to the spring team for the spring-security-saml project.

Monday, November 4, 2013

Federated Authentication using OpenAM Fedlet, and Shibboleth

Summary

Federated access management using two different Identity Providers: OpenAM, and Shibboleth was required.  Towards Proof Of Concept (POC), with the idea that the Security Assertion Mark Language (SAML) compliant Identity Providers can be replaced, OpenAM’s fedlet application was selected.  Following diagram shows the high level architecture used towards the POC:

The simple concept of replacing the Identity Provider (IdP) was described in a couple of blogs, and was even mentioned in OpenAM’s documentation.  The blogs were at best were at a very high level, and was a challenge to follow.  This discussion is aimed at describing the issues encountered, and the resolutions worked during the integration.  The procedure followed is:
  • Securing Fedlet with OpenAM as IdP
  • Securing Fedlet with Shibboleth as IdP

Securing Fedlet with OpenAM as IdP

As a first step created fedlet using OpenAM’s admin console.  OpenAM sorts the list of Circle Of Trust(COT), and picks the first COT from the list while creating fedlet.  In addition OpenAM picks the hosted identity provider already configured (if one exists) as the Identity Provider for the fedlet, without providing any option to select other identity providers already configured within the OpenAM. Name, and Destination URLs are the inputs for creating fedlet as shown below:

While creating fedlet OpenAM creates a remote service provider with the url provided as input.  The fedlet thus created was deployed, configured, and tested.  Federation works successfully without any issues, hiding some of the configuration issues which are described later.  It should be noted that during fedlet creation, fedlet configuration files are copied to –Duser.home/fedlet if specified in catalina.bat, or /<user-home>/fedlet.  With self-explanatory names the files are:
  • A directory by the name debug
  • FederationConfig.properties
  • fedlet.cot
  • idp.xml
  • idp-extended.xml
  • sp.xml
  • sp-extended.xml

After accessing fedlet the first time, examining the debug files revealed some of the error messages.  The file with the name amSecurity containing the message:
ERROR: mapPk2Cert.JKSKeyProvider:
java.io.FileNotFoundException: <. . .>fedlet\keystore.jks,
clearly indicating that the file does not exist.  As a lazy developer with the first instinct, copied the keystore.jks file from OpenAM’s installation directory, and restarted tomcat.
 
Now after testing the the fedlet, FileNotFoundException in amSecurity is replaced with IOException as below:
ERROR: mapPk2Cert.JKSKeyProvider:
java.io.IOException: Keystore was tampered with, or password was incorrect

Again the message is clear that password is incorrect, but does not indicate that the required .keypass, and .storepass files are missing.  Since the keystore.jks was copied from openam directory, copied the .keypass, and .storepass files from the openam directory to fedlet directory.  Since the keystore.jks, .keypass, and .storepass are copied from the same location with the minimum expectation that it should work restarted tomcat, and accessed the fedlet again.  Now the IOException was replaced with JCEEncryption , with clueless error messages in the log files.
ERROR: JCEEncryption:: failed to decrypt data
javax.crypto.BadPaddingException: Given final block not properly padded

Remembering that the default password for the keystore was changeit, modified the passwords in .keypass, and .storepass to changeit.  Restarted tomcat, tested fedlet again.  The file amSDK contained the error message:
ERROR: JCEEncryption:: failed to decrypt data
amSDK:11/03/2013 08:08:14:519 AM EST: Thread[http-bio-18443-exec-3,5,main]
ERROR: JCEEncryption:: Unsupported version: 98

Google did not provide any leads to resolve this issue.  Replacing JCE with the latest version followed by restarting tomcat, and testing fedlet resulted with a minor difference in the error message for the Unsupported version as shown:
ERROR: JCEEncryption:: failed to decrypt data
amSDK:11/03/2013 08:08:14:519 AM EST: Thread[http-bio-18443-exec-3,5,main]
ERROR: JCEEncryption:: Unsupported version: 114

Source code did not provide any help where a comparison for a version value of 1 with some first byte was made.   Could not figure out the root cause of the problem.  While reading documentation on signing, and encrypting fedlet came across the following paragraph:

You must also set up .storepass and .keypass files using the fedletEncode.jsp page, such as http://openam.example.com:8080/fedlet/fedletEncode.jsp, to encode passwords on the Fedlet side. The passwords for the test key store and private key are both changeit.

Based on this paragraph changed passwords in .keypass, and .storepass to the encrypted passwords as suggested, restarted tomcat, accessed fedlet and noted that the log files were free from the error messages, lesson learned that the fedlet uses a different encoding mechanism than OpenAM.

Securing Fedlet with Shibboleth as IdP

Now that the log files are free from error messages, the next adventure is to replace OpenAM IdP with Shibboleth IdP.  Several steps are required for securing fedlet with Shibboleth as Identity Provider
  • Get metadata for Identity Provider
  • Getting entityId of the Identity Provider from Metadata file
  • Fedlet configuration to use new Shibboleth Identity Provider
  • Modify relyingparty.xml of Shibboleth

Get metadata for Identity Provider

/opt/shibboleth-idp/metadata/idp-metadata.xml is the metadata file for Shibboleth.  The same exposed as a URL is: https://sp.shibbolith.local:9443/idp/profile/Metadata/SAML.

Getting entityId of the Identity Provider from Metadata file

The element EntityDescriptor from the metadata file of Shibboleth:
<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:shibmd="urn:mace:shibboleth:metadata:1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" entityID="https://sp.shibbolith.local:9443/idp/shibboleth">
would provide the entityId of Shibboleth.  This entityId is used while reconfiguring fedlet configuration.

Fedlet configuration to use new Shibboleth Identity Provider

While registering Shibboleth as a Remote Identity Provider is not required, registering remote identity provider, and the reason for manually configuring the fedlet configuration is described.  OpenAM provides two ways to register Remote Identity Provider:
  • Direct registration of Remote Identity Provider
  • Importing an entity
While registering a Remote Identity Provider from OpenAM admin console requires either a URL, or a metadata file, importing an entity requires metadata file, and extended data file.  Since Shibboleth provides a single metadata file with extensions included inside the metadata file the option of importing an entity is ruled out, and we are left with the only option of registering a Remote Identity Provider.
Even after registering as Remote Identity Provider, a new fedlet can not be created with the Remote Identity Provider as the Identity Provider as OpenAM picks the hosted Identity Provider as Identity Provider for a new fedlet.  This rules out the option of creating a new fedlet with the Remote Identity Provider as the Identity Provider, leaving with the only option of modifying the fedlet configuration files to use a different remote identity provider.  

Configuration files used by Fedlet for the identity provider are:
  • fedlet.cot
  • idp.xml
  • idp-extended.xml

Modifications to Fedlet.cot

The entry sun-fm-trusted-providers contains entity ids used in the circle of trust
sun-fm-trusted-providers=<idp entity id>,<sp entity id >
Replace idp entity id with the identity provider's entityId https://sp.shibbolith.local:9443/idp/shibboleth which was obtained as described earlier, and the new entry would be:
sun-fm-trusted-providers=https://sp.shibbolith.local:9443/idp/shibboleth,https://openam-sp.shibbolith.local:18443/fedlet

Modifications to idp.xml

While idp.xml contains the basic Single Sign On related entities such as SingleSignOnService, and others, idp-extended.xml contains extended entities such as certificate information for signing, encrypting, and others.  While it may be trivial to replace the existing information in idp-extended.xml, it is a challenge, and error prone to replace the existing information in idp.xml.  As such the simple way is to remove existing single sign on related elements from idp.xml, and copy the single sign on related entities from Shibboleth's metadata file.

Following are the Single Sign On related entries to be removed: 
<ArtifactResolutionService index="0" isDefault="true" Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://openam-idp.shibbolith.local:17443/openam/ArtifactResolver/metaAlias/idp"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://openam-idp.shibbolith.local:17443/openam/IDPSloRedirect/metaAlias/idp" ResponseLocation="https://openam-idp.shibbolith.local:17443/openam/IDPSloRedirect/metaAlias/idp"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://openam-idp.shibbolith.local:17443/openam/IDPSloPOST/metaAlias/idp" ResponseLocation="https://openam-idp.shibbolith.local:17443/openam/IDPSloPOST/metaAlias/idp"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://openam-idp.shibbolith.local:17443/openam/IDPSloSoap/metaAlias/idp"/>
<ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://openam-idp.shibbolith.local:17443/openam/IDPMniRedirect/metaAlias/idp" ResponseLocation="https://openam-idp.shibbolith.local:17443/openam/IDPMniRedirect/metaAlias/idp"/>
<ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://openam-idp.shibbolith.local:17443/openam/IDPMniPOST/metaAlias/idp" ResponseLocation="https://openam-idp.shibbolith.local:17443/openam/IDPMniPOST/metaAlias/idp"/>
<ManageNameIDService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://openam-idp.shibbolith.local:17443/openam/IDPMniSoap/metaAlias/idp"/>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName</NameIDFormat>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://openam-idp.shibbolith.local:17443/openam/SSORedirect/metaAlias/idp"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://openam-idp.shibbolith.local:17443/openam/SSOPOST/metaAlias/idp"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://openam-idp.shibbolith.local:17443/openam/SSOSoap/metaAlias/idp"/>
<NameIDMappingService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://openam-idp.shibbolith.local:17443/openam/NIMSoap/metaAlias/idp"/>
<AssertionIDRequestService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://openam-idp.shibbolith.local:17443/openam/AIDReqSoap/IDPRole/metaAlias/idp"/>
<AssertionIDRequestService Binding="urn:oasis:names:tc:SAML:2.0:bindings:URI" Location="https://openam-idp.shibbolith.local:17443/openam/AIDReqUri/IDPRole/metaAlias/idp"/>

Following are the Single Sign On related elements to be copied from Shibboleth's metada:
<ArtifactResolutionService Binding="urn:oasis:names:tc:SAML:1.0:bindings:SOAP-binding" Location="https://sp.shibbolith.local:9443/idp/profile/SAML1/SOAP/ArtifactResolution" index="1"/>
<ArtifactResolutionService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://sp.shibbolith.local:9443/idp/profile/SAML2/SOAP/ArtifactResolution" index="2"/>
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://sp.shibbolith.local:9443/idp/profile/SAML2/Redirect/SLO" />
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://sp.shibbolith.local:9443/idp/profile/SAML2/POST/SLO" />
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="https://sp.shibbolith.local:9443/idp/profile/SAML2/SOAP/SLO" />
<NameIDFormat>urn:mace:shibboleth:1.0:nameIdentifier</NameIDFormat>
<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
<SingleSignOnService Binding="urn:mace:shibboleth:1.0:profiles:AuthnRequest" Location="https://sp.shibbolith.local:9443/idp/profile/Shibboleth/SSO"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="https://sp.shibbolith.local:9443/idp/profile/SAML2/POST/SSO"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST-SimpleSign" Location="https://sp.shibbolith.local:9443/idp/profile/SAML2/POST-SimpleSign/SSO"/>
<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://sp.shibbolith.local:9443/idp/profile/SAML2/Redirect/SSO"/>


In addition remove the existing element, KeyDescriptor from idp.xml.  Copy the KeyDescriptor element from Shibboleth's metdata file.  Noting slight differences in entities as described by OpenAM, and Shibboleth modify the elements KeyDescriptor, and KeyInfo as copied from Shibboleth's metdata file to read as:

<KeyDescriptor use="signing">
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">

Without these changes fedlet configuration fails, and can not access the testing link as provided by fedlet.

Modifications to Idp-extended.xml

OpenAM's handling of boolean values for the attribute hosted is confusing.  The default entry for the attribute hosted as configured in idp-extended.xml is a numeric value of "0":
<EntityConfig entityID="https://openam-idp.shibbolith.local:17443/openam" hosted="0" xmlns="urn:sun:fm:SAML:2.0:entityconfig">


Since Shibboleth Identity Provider is a remote Identity Provider, and changing the hosted attribute to a non zero did not work.  After a few iterations, only string value of "false" worked for the hosted attribute:
<EntityConfig entityID="https://openam-idp.shibbolith.local:17443/openam" hosted="false" xmlns="urn:sun:fm:SAML:2.0:entityconfig">

Replace the entityId with Shibboleth's entityID in the EntityDescriptor element:
<EntityDescriptor entityID=" https://sp.shibbolith.local:9443/idp/shibboleth" xmlns="urn:oasis:names:tc:SAML:2.0:metadata"> 

Configuring Shibboleth's relyingparty.xml

Valid enumeration values for signResponses, and signAssertions are "always", "never", and "conditional". 

Remembering that by default during fedlet configuration Service Provider is not configured to use signed authentication requests, and signed assertions as seen from the SPSSODescriptor element of sp.xml:
<SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
started with the simple case of no response signing, and no encryption as shown below: 
<rp:ProfileConfiguration xsi:type="saml:SAML2SSOProfile" includeAttributeStatement="true"
                                 assertionLifetime="PT1M" assertionProxyCount="0"
                                 signResponses="never" signAssertions="never"
                                 encryptAssertions="never" encryptNameIds="never"
                                 includeConditionsNotBefore="true"/>

When all changes as described earlier were made, fedlet testing resulted in redirecting the user to Shibboleth's(CAS) login page indicating that the Fedlet is using Shibboleth as the Identity Provider.  When valid credentials were entered following error message was displayed:
HTTP Status 500 - The signature on Assertion is not valid.
type: Status report
message: The signature on Assertion is not valid.
description: The server encountered an internal error that prevented it from fulfilling this request.

Based on the above error message changed the attributes signResponses, and signAssertions to conditional:
<rp:ProfileConfiguration xsi:type="saml:SAML2SSOProfile" includeAttributeStatement="true"
                                 assertionLifetime="PT1M" assertionProxyCount="0"
                                 signResponses="conditional" signAssertions="conditional"
                                 encryptAssertions="never" encryptNameIds="never"
                                 includeConditionsNotBefore="true"/>
  
Fedlet testing resulted in the following error message:
HTTP Status 500 - Failed to process single sign-on response.
type: Status report
message: Failed to process single sign-on response.
description: The server encountered an internal error that prevented it from fulfilling this request.

Since the signResponses, and signAssertions are not in synch between the Service Provider, and IdentityProvider, synchronized the attributes by changing the attributes AuthnRequestsSigned, and WantAssertionsSigned to true in sp.xml
<SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol"> 

Manual editing of sp.xml in fedlet configuration directory would not synchronize the same in OpenAM, and the service provider configuration would not be updated on Shibboleth.  Using OpenAM's admin console check AuthnRequestsSigned, and Want AssertionsSigned flags of Service Provider.  Restart tomcat hosting Shibboleth to synchronize with the Service provider configuration. 
Also restart tomcat hosting fedlet.

Alternate, and better ways are always possible.  But just for simplicity used this approach.

After all troubleshooting described above, fedlet worked successfully demonstrating the federated authentication between two SAML compliant service provider(OpenAM Fedlet) and identity providers(Shibboleth).

As a note following error message was received:  
ERROR: FMSigProvider.verify: The cert contained in the document is NOT the same as the one being passed in.
when the signing certificates in Shibboleth's idp-metadata.xml, and idp.xml were not in synch.