/******************************************************************************* * 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.demoOA.servlet.pvp2; import java.io.IOException; import java.io.StringWriter; import java.security.KeyStore; import java.security.NoSuchAlgorithmException; 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.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.Signer; import org.w3c.dom.Document; import at.gv.egovernment.moa.id.demoOA.Configuration; import at.gv.egovernment.moa.id.demoOA.Constants; import at.gv.egovernment.moa.id.demoOA.exception.ConfigurationException; import at.gv.egovernment.moa.id.demoOA.utils.AttributeListBuilder; import at.gv.egovernment.moa.id.demoOA.utils.SAML2Utils; import at.gv.egovernment.moa.util.MiscUtil; import lombok.extern.slf4j.Slf4j; @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 Configuration config = Configuration.getInstance(); 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()); // set period of validity for metadata information final DateTime validUntil = new DateTime(); spEntitiesDescriptor.setValidUntil(validUntil.plusDays(7)); final EntityDescriptor spEntityDescriptor = SAML2Utils .createSAMLObject(EntityDescriptor.class); spEntityDescriptor.setValidUntil(date.plusDays(VALIDUNTIL_IN_HOURS)); spEntitiesDescriptor.getEntityDescriptors().add(spEntityDescriptor); // set OA-ID (PublicURL Prefix) as identifier 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()) || MiscUtil.isNotEmpty(config.getPVP2KeystoreAuthRequestEncryptionKeyPassword())) { 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)); // set encryption methode // EncryptionMethod encMethode = SAML2Utils.createSAMLObject(EncryptionMethod.class); // encMethode.setAlgorithm(EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM); // encryKeyDescriptor.getEncryptionMethods().add(encMethode); // // EncryptionMethod keyencMethode = SAML2Utils.createSAMLObject(EncryptionMethod.class); // keyencMethode.setAlgorithm(EncryptionConstants.ALGO_ID_KEYTRANSPORT_RSAOAEP); // encryKeyDescriptor.getEncryptionMethods().add(keyencMethode); 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); // set HTTP-POST Binding assertion consumer service 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); // set HTTP-Redirect Binding assertion consumer service final AssertionConsumerService redirectassertionConsumerService = SAML2Utils.createSAMLObject(AssertionConsumerService.class); redirectassertionConsumerService.setIndex(1); redirectassertionConsumerService.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI); redirectassertionConsumerService.setLocation(serviceURL + Constants.SERVLET_PVP2ASSERTION); spSSODescriptor.getAssertionConsumerServices().add(redirectassertionConsumerService); // set Single Log-Out service final SingleLogoutService sloService = SAML2Utils.createSAMLObject(SingleLogoutService.class); sloService.setBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI); sloService.setLocation(serviceURL + Constants.SERVLET_PVPSINGLELOGOUT); spSSODescriptor.getSingleLogoutServices().add(sloService); spSSODescriptor.addSupportedProtocol(SAMLConstants.SAML20P_NS); spEntityDescriptor.getRoleDescriptors().add(spSSODescriptor); 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); // set attributes which are requested attributeService.getRequestAttributes().addAll(AttributeListBuilder.getRequestedAttributes()); spSSODescriptor.getAttributeConsumingServices().add(attributeService); // build metadata 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 String metadataXML = sw.toString(); response.setContentType("text/xml"); response.getOutputStream().write(metadataXML.getBytes()); response.getOutputStream().close(); } 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 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 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 { } }