/* * 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.identity.federation.web.servlets.saml; import static org.picketlink.common.util.StringUtil.isNotNull; //import java.io.*; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.StringWriter; import java.security.KeyPair; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.crypto.dsig.DigestMethod; import javax.xml.crypto.dsig.SignatureMethod; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.picketlink.config.federation.AuthPropertyType; import org.picketlink.config.federation.KeyProviderType; import org.picketlink.config.federation.KeyValueType; import org.picketlink.config.federation.MetadataProviderType; import org.picketlink.config.federation.PicketLinkType; import org.picketlink.config.federation.ProviderType; import org.picketlink.common.PicketLinkLogger; import org.picketlink.common.PicketLinkLoggerFactory; import org.picketlink.common.ErrorCodes; import org.picketlink.common.exceptions.ProcessingException; import org.picketlink.common.constants.JBossSAMLConstants; import org.picketlink.common.util.StaxUtil; import org.picketlink.identity.federation.api.saml.v2.metadata.KeyDescriptorMetaDataBuilder; import org.picketlink.identity.federation.api.util.KeyUtil; import org.picketlink.identity.federation.core.interfaces.IMetadataProvider; import org.picketlink.identity.federation.core.interfaces.TrustKeyManager; import org.picketlink.identity.federation.core.saml.md.providers.MetadataProviderUtils; import org.picketlink.identity.federation.core.saml.md.providers.SPMetadataProvider; import org.picketlink.identity.federation.core.saml.v2.writers.SAMLMetadataWriter; import org.picketlink.identity.federation.core.util.CoreConfigUtil; import org.picketlink.identity.federation.core.util.XMLSignatureUtil; import org.picketlink.identity.federation.saml.v2.metadata.AttributeAuthorityDescriptorType; import org.picketlink.identity.federation.saml.v2.metadata.AuthnAuthorityDescriptorType; import org.picketlink.identity.federation.saml.v2.metadata.EntitiesDescriptorType; import org.picketlink.identity.federation.saml.v2.metadata.EntityDescriptorType; import org.picketlink.identity.federation.saml.v2.metadata.EntityDescriptorType.EDTDescriptorChoiceType; import org.picketlink.identity.federation.saml.v2.metadata.IDPSSODescriptorType; import org.picketlink.identity.federation.saml.v2.metadata.KeyDescriptorType; import org.picketlink.identity.federation.saml.v2.metadata.PDPDescriptorType; import org.picketlink.identity.federation.saml.v2.metadata.RoleDescriptorType; import org.picketlink.identity.federation.saml.v2.metadata.SPSSODescriptorType; import org.picketlink.identity.federation.web.constants.GeneralConstants; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Metadata servlet for the SP * * Author: coluccelli@redhat.com * */ public class MetadataServletSP extends HttpServlet { private static final long serialVersionUID = 1L; //private static Logger log = Logger.getLogger(MetadataServletSP.class); private static final PicketLinkLogger log = PicketLinkLoggerFactory.getLogger(); private final boolean trace = log.isTraceEnabled(); private String configFileLocation = GeneralConstants.CONFIG_FILE_LOCATION; private transient MetadataProviderType metadataProviderType = null; private transient IMetadataProvider<?> metadataProvider = null; private transient EntitiesDescriptorType entitiesDescriptor; private transient EntityDescriptorType entityDescriptor; private String signingAlias = null; private String encryptingAlias = null; private TrustKeyManager keyManager; @SuppressWarnings("rawtypes") @Override public void init(ServletConfig config) throws ServletException { super.init(config); ServletContext context = config.getServletContext(); String configL = config.getInitParameter("configFile"); if (isNotNull(configL)) configFileLocation = configL; if (trace) log.trace("Config File Location=" + configFileLocation); InputStream is = context.getResourceAsStream(configFileLocation); if (is == null) throw new ServletException(ErrorCodes.RESOURCE_NOT_FOUND + configFileLocation + " missing"); // Look for signing alias signingAlias = config.getInitParameter("signingAlias"); encryptingAlias = config.getInitParameter("encryptingAlias"); PicketLinkType picketLinkType = MetadataProviderUtils.getPicketLinkConf(is); ProviderType providerType = MetadataProviderUtils.getProviderType(picketLinkType); metadataProviderType = providerType.getMetaDataProvider(); String fqn = metadataProviderType.getClassName(); Class<?> clazz = SecurityActions.loadClass(getClass(), fqn); try { metadataProvider = (IMetadataProvider) clazz.newInstance(); } catch (InstantiationException e) { throw new ServletException(e); } catch (IllegalAccessException e) { throw new ServletException(e); } List<KeyValueType> keyValues = metadataProviderType.getOption(); Map<String, String> options = new HashMap<String, String>(); if (keyValues != null) { for (KeyValueType kvt : keyValues) options.put(kvt.getKey(), kvt.getValue()); } //inject inputStream and other provider-specific properties String fileInjectionStr = metadataProvider.requireFileInjection(); if (isNotNull(fileInjectionStr)) { metadataProvider.injectFileStream(context.getResourceAsStream(fileInjectionStr)); }else if (metadataProvider instanceof SPMetadataProvider){ ((SPMetadataProvider)metadataProvider).setPicketLinkConf(picketLinkType); } metadataProvider.init(options); Object metadata = metadataProvider.getMetaData(); if (metadata instanceof EntitiesDescriptorType) { entitiesDescriptor = (EntitiesDescriptorType) metadata; }else if (metadata instanceof EntityDescriptorType) { entityDescriptor = (EntityDescriptorType) metadata; } else { throw new ServletException(ErrorCodes.PARSING_ERROR+"Invalid metadata type"); } // Get the trust manager information KeyProviderType keyProvider = providerType.getKeyProvider(); signingAlias = keyProvider.getSigningAlias(); String keyManagerClassName = keyProvider.getClassName(); if (keyManagerClassName == null) throw new ServletException(ErrorCodes.NULL_VALUE + "KeyManager class name"); clazz = SecurityActions.loadClass(getClass(), keyManagerClassName); try{ this.keyManager = (TrustKeyManager) clazz.newInstance(); List<AuthPropertyType> authProperties = CoreConfigUtil.getKeyProviderProperties(keyProvider); keyManager.setAuthProperties(authProperties); Certificate cert = keyManager.getCertificate(signingAlias); Element keyInfo = KeyUtil.getKeyInfo(cert); // TODO: Assume just signing key for now KeyDescriptorType keyDescriptor = KeyDescriptorMetaDataBuilder.createKeyDescriptor(keyInfo, null, 0, true, false); if (entitiesDescriptor != null) updateKeyDescriptors(entitiesDescriptor, keyDescriptor); else{ updateKeyDescriptor(entityDescriptor,keyDescriptor); } // encryption if (encryptingAlias == null) encryptingAlias = signingAlias; cert = keyManager.getCertificate(encryptingAlias); keyInfo = KeyUtil.getKeyInfo(cert); keyDescriptor = KeyDescriptorMetaDataBuilder.createKeyDescriptor(keyInfo, null, 0, false, true); if (entitiesDescriptor != null) updateKeyDescriptors(entitiesDescriptor, keyDescriptor); //TODO: IMPLEMENT FOREACH (entityDescriptor) signAndAddAttribs else{ updateKeyDescriptor(entityDescriptor,keyDescriptor); signAndAddAttribs(entityDescriptor); } }catch(Exception e){ throw new ServletException(e); } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType(JBossSAMLConstants.METADATA_MIME.get()); OutputStream os = resp.getOutputStream(); try { XMLStreamWriter streamWriter = StaxUtil.getXMLStreamWriter(os); SAMLMetadataWriter writer = new SAMLMetadataWriter(streamWriter); if (entitiesDescriptor != null) writer.writeEntitiesDescriptor(entitiesDescriptor); else writer.writeEntityDescriptor(entityDescriptor); } catch (ProcessingException e) { throw new ServletException(e); } /* * JAXBElement<?> jaxbEl = MetaDataBuilder.getObjectFactory().createEntityDescriptor(metadata); try { * MetaDataBuilder.getMarshaller().marshal(jaxbEl , os); } catch (Exception e) { throw new RuntimeException(e); } */ } private void signAndAddAttribs(EntityDescriptorType entityDescriptor) throws ServletException{ try { ByteArrayOutputStream baos = new ByteArrayOutputStream(); XMLStreamWriter streamWriter = StaxUtil.getXMLStreamWriter(baos); SAMLMetadataWriter writer = new SAMLMetadataWriter(streamWriter); writer.writeEntityDescriptor(entityDescriptor); Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new ByteArrayInputStream(baos.toByteArray())); KeyPair keyPair = new KeyPair(null, keyManager.getSigningKey()); //Sign doc Element spssoDesc = doc.getDocumentElement(); XMLSignatureUtil.sign(spssoDesc,spssoDesc.getFirstChild(),keyPair,DigestMethod.SHA1, SignatureMethod.RSA_SHA1,"",(X509Certificate) keyManager.getCertificate(signingAlias)); //extract Signature entityDescriptor.setSignature(extractSignatureFromDoc(spssoDesc)); } catch (Exception e) { throw new ServletException(e); } } private Element extractSignatureFromDoc(Element doc) { return (Element) doc.getFirstChild(); } private String getStringFromDocument(Element doc) throws TransformerException { DOMSource domSource = new DOMSource(doc); StringWriter writer = new StringWriter(); StreamResult result = new StreamResult(writer); TransformerFactory tf = TransformerFactory.newInstance(); Transformer transformer = tf.newTransformer(); transformer.transform(domSource, result); return writer.toString(); } private void updateKeyDescriptors(EntitiesDescriptorType entityId, KeyDescriptorType keyD){ List<Object> entities = entityId.getEntityDescriptor(); for (Object obj : entities){ updateKeyDescriptor((EntityDescriptorType) obj,keyD); } } private void updateKeyDescriptor(EntityDescriptorType entityD, KeyDescriptorType keyD) { List<EDTDescriptorChoiceType> objs = entityD.getChoiceType().get(0).getDescriptors(); if (objs != null) { for (EDTDescriptorChoiceType choiceTypeDesc : objs) { AttributeAuthorityDescriptorType attribDescriptor = choiceTypeDesc.getAttribDescriptor(); if (attribDescriptor != null) attribDescriptor.addKeyDescriptor(keyD); AuthnAuthorityDescriptorType authnDescriptor = choiceTypeDesc.getAuthnDescriptor(); if (authnDescriptor != null) authnDescriptor.addKeyDescriptor(keyD); IDPSSODescriptorType idpDescriptor = choiceTypeDesc.getIdpDescriptor(); if (idpDescriptor != null) idpDescriptor.addKeyDescriptor(keyD); PDPDescriptorType pdpDescriptor = choiceTypeDesc.getPdpDescriptor(); if (pdpDescriptor != null) pdpDescriptor.addKeyDescriptor(keyD); RoleDescriptorType roleDescriptor = choiceTypeDesc.getRoleDescriptor(); if (roleDescriptor != null) roleDescriptor.addKeyDescriptor(keyD); SPSSODescriptorType spDescriptorType = choiceTypeDesc.getSpDescriptor(); if (spDescriptorType != null) spDescriptorType.addKeyDescriptor(keyD); } } } }