/* * JBoss, Home of Professional Open Source * * Copyright 2013 Red Hat, Inc. and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.picketlink.test.identity.federation.web.saml.handlers; import junit.framework.Assert; import org.junit.Before; import org.junit.Test; import org.picketlink.common.constants.GeneralConstants; import org.picketlink.common.exceptions.ConfigurationException; import org.picketlink.common.exceptions.ParsingException; import org.picketlink.common.exceptions.ProcessingException; import org.picketlink.common.util.DocumentUtil; import org.picketlink.common.util.TransformerUtil; import org.picketlink.config.federation.IDPType; import org.picketlink.config.federation.ProviderType; import org.picketlink.config.federation.SPType; import org.picketlink.identity.federation.api.saml.v2.request.SAML2Request; import org.picketlink.identity.federation.api.saml.v2.response.SAML2Response; import org.picketlink.identity.federation.core.saml.v2.common.SAMLDocumentHolder; import org.picketlink.identity.federation.core.saml.v2.factories.SAML2HandlerChainFactory; import org.picketlink.identity.federation.core.saml.v2.impl.DefaultSAML2HandlerChainConfig; import org.picketlink.identity.federation.core.saml.v2.impl.DefaultSAML2HandlerConfig; import org.picketlink.identity.federation.core.saml.v2.impl.DefaultSAML2HandlerRequest; import org.picketlink.identity.federation.core.saml.v2.impl.DefaultSAML2HandlerResponse; import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2Handler; import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2Handler.HANDLER_TYPE; import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerChain; import org.picketlink.identity.federation.core.saml.v2.interfaces.SAML2HandlerChainConfig; import org.picketlink.identity.federation.core.sts.PicketLinkCoreSTS; import org.picketlink.identity.federation.saml.v2.assertion.NameIDType; import org.picketlink.identity.federation.saml.v2.protocol.AuthnRequestType; import org.picketlink.identity.federation.web.core.HTTPContext; import org.picketlink.identity.federation.web.core.IdentityServer; import org.picketlink.identity.federation.web.handlers.saml2.SAML2AuthenticationHandler; import org.picketlink.identity.federation.web.handlers.saml2.SAML2EncryptionHandler; import org.picketlink.identity.federation.web.handlers.saml2.SAML2SignatureValidationHandler; import org.picketlink.identity.federation.web.roles.DefaultRoleValidator; import org.picketlink.test.identity.federation.web.mock.MockHttpServletRequest; import org.picketlink.test.identity.federation.web.mock.MockHttpServletResponse; import org.picketlink.test.identity.federation.web.mock.MockHttpSession; import org.picketlink.test.identity.federation.web.mock.MockServletContext; import org.w3c.dom.Document; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.stream.StreamResult; import java.io.IOException; import java.io.StringWriter; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.fail; /** * @author <a href="mailto:psilva@redhat.com">Pedro Silva</a> */ public class SAML2EncryptionHandlerUnitTestCase { private static final String SERVICE_PROVIDER_URL = "http://service-provider.picketlink.org"; private static final String IDENTITY_PROVIDER_URL = "http://identity-provider.picketlink.org"; private MockServletContext servletContext = new MockServletContext(); private KeyPair keyPair; @Before public void onSetup() throws NoSuchAlgorithmException { // install the STS default configurations PicketLinkCoreSTS.instance().installDefaultConfiguration(); // register a identity server into the ServletContext servletContext.setAttribute(GeneralConstants.IDENTITY_SERVER, new IdentityServer()); // creates a random and temporary keypair for encryption and signing this.keyPair = KeyPairGenerator.getInstance("RSA").genKeyPair(); } /** * <p> * Try to issue an encrypted and signed SAML Assertion and to process it by the SP. * </p> * * @throws Exception */ @Test public void testEncryptAssertion() throws Exception { Document assertionDocument = issueSAMLAssertion(); assertNotNull(assertionDocument); System.out.println(prettyPrintDocument(assertionDocument).getBuffer().toString()); processSAMLAssertion(assertionDocument); } /** * <p> * This method asks the IDP for a new encrypted and signed SAML Assertion by sending an AuthnRequest. * </p> * * @return * * @throws ConfigurationException * @throws ProcessingException */ private Document issueSAMLAssertion() throws ConfigurationException, ProcessingException { NameIDType issuerNameID = new NameIDType(); issuerNameID.setValue(IDENTITY_PROVIDER_URL); SAML2Request samlRequest = new SAML2Request(); AuthnRequestType authnRequestType = samlRequest.createAuthnRequestType("AuthnRequest_FAKE_ID", SERVICE_PROVIDER_URL, IDENTITY_PROVIDER_URL, SERVICE_PROVIDER_URL); DefaultSAML2HandlerRequest handlerAuthnRequest = new DefaultSAML2HandlerRequest(new HTTPContext( new MockHttpServletRequest(new MockHttpSession(), "POST"), new MockHttpServletResponse(), servletContext), issuerNameID, new SAMLDocumentHolder(authnRequestType), HANDLER_TYPE.IDP); handlerAuthnRequest.addOption(GeneralConstants.SENDER_PUBLIC_KEY, getKeyPair().getPublic()); DefaultSAML2HandlerResponse handlerAuthnResponse = new DefaultSAML2HandlerResponse(); try { for (SAML2Handler handler : getIDPHandlerChain().handlers()) { handler.handleRequestType(handlerAuthnRequest, handlerAuthnResponse); } } catch (Exception e) { e.printStackTrace(); fail("Error while issuing encrypted and signed SAML Assertion."); } return handlerAuthnResponse.getResultingDocument(); } /** * <p> * Given the SAML Assertion, process it by the SP and make sure it can be decrypted and have its signature * validated. * </p> * * @param assertionDocument * * @throws ParsingException * @throws ConfigurationException * @throws ProcessingException * @throws NoSuchAlgorithmException */ private void processSAMLAssertion(Document assertionDocument) throws ParsingException, ConfigurationException, ProcessingException, NoSuchAlgorithmException { NameIDType issuerSPNameID = new NameIDType(); issuerSPNameID.setValue(IDENTITY_PROVIDER_URL); DefaultSAML2HandlerRequest handlerAssertionResponseRequest = new DefaultSAML2HandlerRequest(new HTTPContext( new MockHttpServletRequest(new MockHttpSession(), "POST"), new MockHttpServletResponse(), servletContext), issuerSPNameID, new SAMLDocumentHolder(new SAML2Response().getSAML2ObjectFromStream(DocumentUtil .getNodeAsStream(assertionDocument)), assertionDocument), HANDLER_TYPE.SP); handlerAssertionResponseRequest.addOption(GeneralConstants.DECRYPTING_KEY, getKeyPair().getPrivate()); handlerAssertionResponseRequest.addOption(GeneralConstants.SENDER_PUBLIC_KEY, getKeyPair().getPublic()); DefaultSAML2HandlerResponse handlerAssertionRequestResponse = new DefaultSAML2HandlerResponse(); try { for (SAML2Handler handler : getSPHandlerChain().handlers()) { handler.handleStatusResponseType(handlerAssertionResponseRequest, handlerAssertionRequestResponse); } } catch (Exception e) { e.printStackTrace(); fail("Error while processing the encrypted and signed SAML Assertion."); } } private SAML2HandlerChainConfig createHandlerChainConfig(ProviderType configuration) throws NoSuchAlgorithmException { SAML2HandlerChainConfig chainConfig = new DefaultSAML2HandlerChainConfig(); Map<String, Object> chainOptions = new HashMap<String, Object>(); chainOptions.put(GeneralConstants.CONFIGURATION, configuration); chainOptions.put(GeneralConstants.KEYPAIR, getKeyPair()); chainOptions.put(GeneralConstants.ROLE_VALIDATOR, new DefaultRoleValidator()); chainConfig.set(chainOptions); return chainConfig; } /** * <p> * Creates an return a random RSA {@link KeyPair} for testing. * </p> * * @return * * @throws NoSuchAlgorithmException */ private KeyPair getKeyPair() { return this.keyPair; } private SAML2HandlerChain getIDPHandlerChain() throws ConfigurationException { List<SAML2Handler> handlers = new ArrayList<SAML2Handler>(); handlers.add(createAuthenticationHandler()); handlers.add(createEncryptionHandler()); IDPType idpType = new IDPType(); idpType.setIdentityURL(IDENTITY_PROVIDER_URL); idpType.setEncrypt(true); return getHandlerChain(idpType, handlers); } private SAML2HandlerChain getSPHandlerChain() throws ConfigurationException { List<SAML2Handler> handlers = new ArrayList<SAML2Handler>(); handlers.add(createSignatureValidationHandler()); handlers.add(createAuthenticationHandler()); SPType configuration = new SPType(); configuration.setServiceURL(SERVICE_PROVIDER_URL); return getHandlerChain(configuration, handlers); } private SAML2HandlerChain getHandlerChain(ProviderType configuration, List<SAML2Handler> handlers) { SAML2HandlerChain handlerChain = null; try { SAML2HandlerChainConfig handlerChainConfig = createHandlerChainConfig(configuration); handlerChain = SAML2HandlerChainFactory.createChain(); for (SAML2Handler saml2Handler : handlers) { saml2Handler.initChainConfig(handlerChainConfig); handlerChain.add(saml2Handler); } } catch (Exception e) { e.printStackTrace(); Assert.fail("Handler chain configuration error."); } return handlerChain; } private SAML2EncryptionHandler createEncryptionHandler() throws ConfigurationException { SAML2EncryptionHandler handler = new SAML2EncryptionHandler(); DefaultSAML2HandlerConfig handlerConfig = new DefaultSAML2HandlerConfig(); handler.initHandlerConfig(handlerConfig); return handler; } private SAML2SignatureValidationHandler createSignatureValidationHandler() throws ConfigurationException { SAML2SignatureValidationHandler handler = new SAML2SignatureValidationHandler(); handler.initHandlerConfig(new DefaultSAML2HandlerConfig()); return handler; } private SAML2AuthenticationHandler createAuthenticationHandler() throws ConfigurationException { SAML2AuthenticationHandler handler = new SAML2AuthenticationHandler(); handler.initHandlerConfig(new DefaultSAML2HandlerConfig()); return handler; } private StringWriter prettyPrintDocument(Document authnRequestDocument) { StringWriter writer = new StringWriter(); try { Transformer transformer = TransformerUtil.getTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); transformer.transform(DocumentUtil.getXMLSource(authnRequestDocument), new StreamResult(writer)); } catch (Exception e) { e.printStackTrace(); fail("Error printing the document."); } finally { try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } return writer; } }