/******************************************************************************* * Copyright 2014 Federal Chancellery Austria * MOA-ID has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, and Graz University of Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. *******************************************************************************/ package at.gv.egovernment.moa.id.configuration.auth.pvp2.servlets; import java.io.IOException; import java.io.StringWriter; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.joda.time.DateTime; import org.opensaml.common.impl.SecureRandomIdentifierGenerator; import org.opensaml.common.xml.SAMLConstants; import org.opensaml.saml2.core.NameIDType; import org.opensaml.saml2.metadata.AssertionConsumerService; import org.opensaml.saml2.metadata.AttributeConsumingService; import org.opensaml.saml2.metadata.EntitiesDescriptor; import org.opensaml.saml2.metadata.EntityDescriptor; import org.opensaml.saml2.metadata.KeyDescriptor; import org.opensaml.saml2.metadata.LocalizedString; import org.opensaml.saml2.metadata.NameIDFormat; import org.opensaml.saml2.metadata.SPSSODescriptor; import org.opensaml.saml2.metadata.ServiceName; import org.opensaml.saml2.metadata.SingleLogoutService; import org.opensaml.xml.io.Marshaller; import org.opensaml.xml.io.MarshallingException; import org.opensaml.xml.security.SecurityException; import org.opensaml.xml.security.credential.Credential; import org.opensaml.xml.security.credential.UsageType; import org.opensaml.xml.security.keyinfo.KeyInfoGenerator; import org.opensaml.xml.security.x509.KeyStoreX509CredentialAdapter; import org.opensaml.xml.security.x509.X509Credential; import org.opensaml.xml.security.x509.X509KeyInfoGeneratorFactory; import org.opensaml.xml.signature.Signature; import org.opensaml.xml.signature.SignatureConstants; import org.opensaml.xml.signature.SignatureException; import org.opensaml.xml.signature.Signer; import org.w3c.dom.Document; import at.gv.egovernment.moa.id.config.webgui.exception.ConfigurationException; import at.gv.egovernment.moa.id.configuration.Constants; import at.gv.egovernment.moa.id.configuration.auth.pvp2.AttributeListBuilder; import at.gv.egovernment.moa.id.configuration.config.ConfigurationProvider; import at.gv.egovernment.moa.id.configuration.utils.SAML2Utils; import at.gv.egovernment.moa.util.MiscUtil; import lombok.extern.slf4j.Slf4j; /** * Servlet implementation class BuildMetadata */ @Slf4j public class BuildMetadata extends HttpServlet { private static final long serialVersionUID = 1L; private static final int VALIDUNTIL_IN_HOURS = 24; /** * @see HttpServlet#HttpServlet() */ public BuildMetadata() { super(); } protected static Signature getSignature(Credential credentials) { final Signature signer = SAML2Utils.createSAMLObject(Signature.class); signer.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256); signer.setCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS); signer.setSigningCredential(credentials); return signer; } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse * response) */ @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { final ConfigurationProvider config = ConfigurationProvider.getInstance(); // config.initializePVP2Login(); final SecureRandomIdentifierGenerator idGen = new SecureRandomIdentifierGenerator(); final EntitiesDescriptor spEntitiesDescriptor = SAML2Utils.createSAMLObject(EntitiesDescriptor.class); final DateTime date = new DateTime(); spEntitiesDescriptor.setValidUntil(date.plusHours(VALIDUNTIL_IN_HOURS)); final String name = config.getPVP2MetadataEntitiesName(); if (MiscUtil.isEmpty(name)) { log.info("NO Metadata EntitiesName configurated"); throw new ConfigurationException("NO Metadata EntitiesName configurated"); } spEntitiesDescriptor.setName(name); spEntitiesDescriptor.setID(idGen.generateIdentifier()); final EntityDescriptor spEntityDescriptor = SAML2Utils .createSAMLObject(EntityDescriptor.class); spEntityDescriptor.setValidUntil(date.plusDays(VALIDUNTIL_IN_HOURS)); spEntitiesDescriptor.getEntityDescriptors().add(spEntityDescriptor); String serviceURL = config.getPublicUrlPreFix(request); if (!serviceURL.endsWith("/")) { serviceURL = serviceURL + "/"; } log.debug("Set OnlineApplicationURL to " + serviceURL); spEntityDescriptor.setEntityID(serviceURL); final SPSSODescriptor spSSODescriptor = SAML2Utils .createSAMLObject(SPSSODescriptor.class); spSSODescriptor.setAuthnRequestsSigned(true); spSSODescriptor.setWantAssertionsSigned(true); final X509KeyInfoGeneratorFactory keyInfoFactory = new X509KeyInfoGeneratorFactory(); keyInfoFactory.setEmitEntityCertificate(true); final KeyInfoGenerator keyInfoGenerator = keyInfoFactory.newInstance(); final KeyStore keyStore = config.getPVP2KeyStore(); final X509Credential signingcredential = new KeyStoreX509CredentialAdapter( keyStore, config.getPVP2KeystoreMetadataKeyAlias(), config.getPVP2KeystoreMetadataKeyPassword().toCharArray()); log.debug("Set Metadata key information"); // Set MetaData Signing key final KeyDescriptor entitiesSignKeyDescriptor = SAML2Utils .createSAMLObject(KeyDescriptor.class); entitiesSignKeyDescriptor.setUse(UsageType.SIGNING); entitiesSignKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(signingcredential)); final Signature entitiesSignature = getSignature(signingcredential); spEntitiesDescriptor.setSignature(entitiesSignature); // Set AuthRequest Signing certificate final X509Credential authcredential = new KeyStoreX509CredentialAdapter( keyStore, config.getPVP2KeystoreAuthRequestKeyAlias(), config.getPVP2KeystoreAuthRequestKeyPassword().toCharArray()); final KeyDescriptor signKeyDescriptor = SAML2Utils .createSAMLObject(KeyDescriptor.class); signKeyDescriptor.setUse(UsageType.SIGNING); signKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(authcredential)); spSSODescriptor.getKeyDescriptors().add(signKeyDescriptor); // set AuthRequest encryption certificate if (MiscUtil.isNotEmpty(config.getPVP2KeystoreAuthRequestEncryptionKeyAlias())) { final X509Credential authEncCredential = new KeyStoreX509CredentialAdapter( keyStore, config.getPVP2KeystoreAuthRequestEncryptionKeyAlias(), config.getPVP2KeystoreAuthRequestEncryptionKeyPassword().toCharArray()); final KeyDescriptor encryKeyDescriptor = SAML2Utils .createSAMLObject(KeyDescriptor.class); encryKeyDescriptor.setUse(UsageType.ENCRYPTION); encryKeyDescriptor.setKeyInfo(keyInfoGenerator.generate(authEncCredential)); spSSODescriptor.getKeyDescriptors().add(encryKeyDescriptor); } else { log.warn("No Assertion Encryption-Key defined. This setting is not recommended!"); } final NameIDFormat persistentnameIDFormat = SAML2Utils.createSAMLObject(NameIDFormat.class); persistentnameIDFormat.setFormat(NameIDType.PERSISTENT); spSSODescriptor.getNameIDFormats().add(persistentnameIDFormat); final NameIDFormat transientnameIDFormat = SAML2Utils.createSAMLObject(NameIDFormat.class); transientnameIDFormat.setFormat(NameIDType.TRANSIENT); spSSODescriptor.getNameIDFormats().add(transientnameIDFormat); final NameIDFormat unspecifiednameIDFormat = SAML2Utils.createSAMLObject(NameIDFormat.class); unspecifiednameIDFormat.setFormat(NameIDType.UNSPECIFIED); spSSODescriptor.getNameIDFormats().add(unspecifiednameIDFormat); final AssertionConsumerService postassertionConsumerService = SAML2Utils.createSAMLObject(AssertionConsumerService.class); postassertionConsumerService.setIndex(0); postassertionConsumerService.setBinding(SAMLConstants.SAML2_POST_BINDING_URI); postassertionConsumerService.setLocation(serviceURL + Constants.SERVLET_PVP2ASSERTION); spSSODescriptor.getAssertionConsumerServices().add(postassertionConsumerService); // add SLO services final SingleLogoutService postBindingService = SAML2Utils.createSAMLObject(SingleLogoutService.class); postBindingService.setBinding(SAMLConstants.SAML2_POST_BINDING_URI); postBindingService.setLocation(serviceURL + Constants.SERVLET_SLO_FRONT); spSSODescriptor.getSingleLogoutServices().add(postBindingService); final SingleLogoutService redirectBindingService = SAML2Utils.createSAMLObject( SingleLogoutService.class); redirectBindingService.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI); redirectBindingService.setLocation(serviceURL + Constants.SERVLET_SLO_FRONT); spSSODescriptor.getSingleLogoutServices().add(redirectBindingService); final SingleLogoutService soapBindingService = SAML2Utils.createSAMLObject(SingleLogoutService.class); soapBindingService.setBinding(SAMLConstants.SAML2_SOAP11_BINDING_URI); soapBindingService.setLocation(serviceURL + Constants.SERVLET_SLO_BACK); spSSODescriptor.getSingleLogoutServices().add(soapBindingService); spSSODescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS); spEntityDescriptor.getRoleDescriptors().add(spSSODescriptor); spSSODescriptor.setWantAssertionsSigned(true); spSSODescriptor.setAuthnRequestsSigned(true); final AttributeConsumingService attributeService = SAML2Utils.createSAMLObject(AttributeConsumingService.class); attributeService.setIndex(0); attributeService.setIsDefault(true); final ServiceName serviceName = SAML2Utils.createSAMLObject(ServiceName.class); serviceName.setName(new LocalizedString("Default Service", "de")); attributeService.getNames().add(serviceName); attributeService.getRequestAttributes().addAll(AttributeListBuilder.getRequestedAttributes()); spSSODescriptor.getAttributeConsumingServices().add(attributeService); DocumentBuilder builder; final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); builder = factory.newDocumentBuilder(); final Document document = builder.newDocument(); final Marshaller out = org.opensaml.xml.Configuration.getMarshallerFactory().getMarshaller( spEntitiesDescriptor); out.marshall(spEntitiesDescriptor, document); Signer.signObject(entitiesSignature); final Transformer transformer = TransformerFactory.newInstance().newTransformer(); final StringWriter sw = new StringWriter(); final StreamResult sr = new StreamResult(sw); final DOMSource source = new DOMSource(document); transformer.transform(source, sr); sw.close(); final byte[] metadataXML = sw.toString().getBytes("UTF-8"); response.setContentType("text/xml"); response.setContentLength(metadataXML.length); response.getOutputStream().write(metadataXML); } catch (final ConfigurationException e) { log.warn("Configuration can not be loaded.", e); throw new ServletException("MetaData can not be created. Look into LogFiles for more details."); } catch (final NoSuchAlgorithmException e) { log.warn("Requested Algorithm could not found.", e); throw new ServletException("MetaData can not be created. Look into LogFiles for more details."); } catch (final KeyStoreException e) { log.warn("Requested KeyStoreType is not implemented.", e); throw new ServletException("MetaData can not be created. Look into LogFiles for more details."); } catch (final CertificateException e) { log.warn("KeyStore can not be opend or userd.", e); throw new ServletException("MetaData can not be created. Look into LogFiles for more details."); } catch (final SecurityException e) { log.warn("KeyStore can not be opend or used", e); throw new ServletException("MetaData can not be created. Look into LogFiles for more details."); } catch (final ParserConfigurationException e) { log.warn("PVP2 Metadata createn error", e); throw new ServletException("MetaData can not be created. Look into LogFiles for more details."); } catch (final MarshallingException e) { log.warn("PVP2 Metadata createn error", e); throw new ServletException("MetaData can not be created. Look into LogFiles for more details."); } catch (final SignatureException e) { log.warn("PVP2 Metadata can not be signed", e); throw new ServletException("MetaData can not be created. Look into LogFiles for more details."); } catch (final TransformerConfigurationException e) { log.warn("PVP2 Metadata createn error", e); throw new ServletException("MetaData can not be created. Look into LogFiles for more details."); } catch (final TransformerFactoryConfigurationError e) { log.warn("PVP2 Metadata createn error", e); throw new ServletException("MetaData can not be created. Look into LogFiles for more details."); } catch (final TransformerException e) { log.warn("PVP2 Metadata createn error", e); throw new ServletException("MetaData can not be created. Look into LogFiles for more details."); } catch (final Exception e) { log.warn("Unspecific PVP2 Metadata createn error", e); throw new ServletException("MetaData can not be created. Look into LogFiles for more details."); } } /** * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse * response) */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }