/* * Copyright 2012 SURFnet bv, The Netherlands * * 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.surfnet.oaaas.conext; import java.io.IOException; import java.util.Arrays; import java.util.Properties; import nl.surfnet.spring.security.opensaml.AssertionConsumer; import nl.surfnet.spring.security.opensaml.AssertionConsumerImpl; import nl.surfnet.spring.security.opensaml.KeyStore; import nl.surfnet.spring.security.opensaml.Provisioner; import nl.surfnet.spring.security.opensaml.SAMLMessageHandler; import nl.surfnet.spring.security.opensaml.SAMLMessageHandlerImpl; import nl.surfnet.spring.security.opensaml.SecurityPolicyDelegate; import nl.surfnet.spring.security.opensaml.SignatureSecurityPolicyRule; import nl.surfnet.spring.security.opensaml.crypt.KeyStoreCredentialResolverDelegate; import nl.surfnet.spring.security.opensaml.xml.SAML2ValidatorSuite; import org.apache.velocity.app.VelocityEngine; import org.opensaml.DefaultBootstrap; import org.opensaml.common.binding.decoding.SAMLMessageDecoder; import org.opensaml.common.binding.security.IssueInstantRule; import org.opensaml.common.binding.security.MessageReplayRule; import org.opensaml.saml2.binding.decoding.HTTPPostSimpleSignDecoder; import org.opensaml.security.SAMLSignatureProfileValidator; import org.opensaml.util.storage.MapBasedStorageService; import org.opensaml.util.storage.ReplayCache; import org.opensaml.util.storage.ReplayCacheEntry; import org.opensaml.ws.security.SecurityPolicyResolver; import org.opensaml.ws.security.provider.StaticSecurityPolicyResolver; import org.opensaml.xml.ConfigurationException; import org.opensaml.xml.parse.BasicParserPool; import org.opensaml.xml.security.credential.CredentialResolver; import org.springframework.ui.velocity.VelocityEngineFactoryBean; /** * Context that wires the required OpenSAML configuration. * All methods can be overridden to customize behavior. */ public class OpenSAMLContext { private static final String DEFAULT_ASSERTION_CONSUMER_URI = "/assertionConsumerService"; private long replayCacheDuration; private int maxParserPoolSize; private String entityId; private int clockSkew; private int newExpires; private String assertionConsumerURI; private String idpEntityId; private String idpCertificate; private Provisioner provisioner; private SAMLMessageHandlerImpl samlMessageHandler; private final SAML2ValidatorSuite validatorSuite; private String idpUrl; private String spPrivateKey; private String spCertificate; public OpenSAMLContext(Properties properties, Provisioner provisioner) { // Bootstrap openSAML try { DefaultBootstrap.bootstrap(); } catch (ConfigurationException e) { throw new RuntimeException(e); } replayCacheDuration = Long.parseLong(properties.getProperty("replayCacheDuration", "14400000")); maxParserPoolSize = Integer.parseInt(properties.getProperty("maxParserPoolSize", "2")); entityId = properties.getProperty("entityId", "no-property-named-entityId"); clockSkew = Integer.parseInt(properties.getProperty("clockSkew", "90")); newExpires = Integer.parseInt(properties.getProperty("newExpires", "300")); assertionConsumerURI = properties.getProperty("assertionConsumerURI", DEFAULT_ASSERTION_CONSUMER_URI); idpEntityId = properties.getProperty("idpEntityId", "no-property-named-idpEntityId"); idpCertificate = properties.getProperty("idpCertificate", "no-property-named-idpCertificate"); idpUrl = properties.getProperty("idpUrl", "no-property-named-idpUrl"); spPrivateKey = properties.getProperty("spPrivateKey", "no-property-named-spPrivateKey"); spCertificate = properties.getProperty("spCertificate", "no-property-named-spCertificate"); this.provisioner = provisioner; samlMessageHandler = new SAMLMessageHandlerImpl(samlMessageDecoder(), securityPolicyResolver()); samlMessageHandler.setEntityId(entityId); samlMessageHandler.setVelocityEngine(velocityEngine()); samlMessageHandler.setNeedsSigning(true); validatorSuite = new SAML2ValidatorSuite(); } protected VelocityEngine velocityEngine() { final VelocityEngineFactoryBean velocityEngineFactoryBean = new VelocityEngineFactoryBean(); velocityEngineFactoryBean.setPreferFileSystemAccess(false); Properties velocityEngineProperties = new Properties(); velocityEngineProperties.setProperty("resource.loader", "classpath"); velocityEngineProperties.setProperty("classpath.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); velocityEngineFactoryBean.setVelocityProperties(velocityEngineProperties); try { return velocityEngineFactoryBean.createVelocityEngine(); } catch (IOException e) { throw new RuntimeException("Unable to create velocity engine instance"); } } public String assertionConsumerUri() { return assertionConsumerURI; } protected ReplayCache replayCache() { return new ReplayCache(new MapBasedStorageService<String,ReplayCacheEntry>(), replayCacheDuration); } protected MessageReplayRule messageReplayRule() { return new MessageReplayRule(replayCache()); } protected IssueInstantRule issueInstantRule() { return new IssueInstantRule(clockSkew, newExpires); } protected CredentialResolver keyStoreCredentialResolver() { final KeyStoreCredentialResolverDelegate keyStoreCredentialResolverDelegate = new KeyStoreCredentialResolverDelegate(); keyStoreCredentialResolverDelegate.setKeyStore(keyStore()); return keyStoreCredentialResolverDelegate; } protected SignatureSecurityPolicyRule signatureBuilder() { final SignatureSecurityPolicyRule signatureSecurityPolicyRule = new SignatureSecurityPolicyRule(new SAMLSignatureProfileValidator()); signatureSecurityPolicyRule.setCredentialResolver(keyStoreCredentialResolver()); try { signatureSecurityPolicyRule.afterPropertiesSet(); } catch (Exception e) { throw new RuntimeException(e); } return signatureSecurityPolicyRule; } protected SecurityPolicyDelegate securityPolicy() { return new SecurityPolicyDelegate(Arrays.asList(signatureBuilder(), issueInstantRule(), messageReplayRule())); } protected SecurityPolicyResolver securityPolicyResolver() { return new StaticSecurityPolicyResolver(securityPolicy()); } protected SAMLMessageDecoder samlMessageDecoder() { final BasicParserPool basicParserPool = new BasicParserPool(); basicParserPool.setMaxPoolSize(maxParserPoolSize); return new HTTPPostSimpleSignDecoder(basicParserPool); } public SAMLMessageHandler samlMessageHandler() { return this.samlMessageHandler; } public AssertionConsumer assertionConsumer() { final AssertionConsumerImpl assertionConsumer = new AssertionConsumerImpl(); assertionConsumer.setProvisioner(provisioner); return assertionConsumer; } protected KeyStore keyStore() { final KeyStore keyStore = new KeyStore(); keyStore.addCertificate(idpEntityId, idpCertificate); keyStore.addPrivateKey(entityId(), spPrivateKey, spCertificate, "somepass"); return keyStore; } public String entityId() { return entityId; } public SAML2ValidatorSuite validatorSuite() { return validatorSuite; } public String getIdpUrl() { return idpUrl; } }