Java tutorial
/* * Copyright 2011 by Graz University of Technology, Austria * MOCCA has been developed by the E-Government Innovation Center EGIZ, a joint * initiative of the Federal Chancellery Austria 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.egiz.slbinding; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import javax.xml.XMLConstants; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.UnmarshalException; import javax.xml.bind.Unmarshaller; import javax.xml.bind.ValidationEvent; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLEventReader; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xml.sax.SAXException; import at.gv.egiz.bku.utils.ClasspathURLStreamHandler; import at.gv.egiz.dom.DOMUtils; import at.gv.egiz.validation.ReportingValidationEventHandler; public class SLUnmarshaller { /** * Logging facility. */ private final Logger log = LoggerFactory.getLogger(SLUnmarshaller.class); private static class DefaultSchema { /** * Schema files required for Security Layer command validation. */ public static final String[] SCHEMA_FILES = new String[] { "classpath:at/gv/egiz/bku/slschema/xml.xsd", "classpath:at/gv/egiz/bku/slschema/xmldsig-core-schema.xsd", "classpath:at/gv/egiz/bku/slschema/Core-1.2.xsd", "classpath:at/gv/egiz/bku/slschema/Core.20020225.xsd", "classpath:at/gv/egiz/bku/slschema/Core.20020831.xsd" }; private static final Schema SCHEMA; static { try { SCHEMA = createSchema(Arrays.asList(SCHEMA_FILES)); } catch (IOException e) { Logger log = LoggerFactory.getLogger(SLUnmarshaller.class); log.error("Failed to load security layer schema.", e); throw new RuntimeException(e); } catch (SAXException e) { Logger log = LoggerFactory.getLogger(SLUnmarshaller.class); log.error("Failed to load security layer schema.", e); throw new RuntimeException(e); } } } public static Collection<String> getDefaultSchemaUrls() { return Collections.unmodifiableList(Arrays.asList(DefaultSchema.SCHEMA_FILES)); } private static Schema createSchema(Collection<String> schemaUrls) throws SAXException, IOException { Logger log = LoggerFactory.getLogger(SLUnmarshaller.class); Source[] sources = new Source[schemaUrls.size()]; Iterator<String> urls = schemaUrls.iterator(); StringBuilder sb = null; if (log.isDebugEnabled()) { sb = new StringBuilder(); sb.append("Created schema using URLs: "); } for (int i = 0; i < sources.length && urls.hasNext(); i++) { String url = urls.next(); if (url != null && url.startsWith("classpath:")) { URL schemaUrl = new URL(null, url, new ClasspathURLStreamHandler()); sources[i] = new StreamSource(schemaUrl.openStream()); } else { sources[i] = new StreamSource(url); } if (sb != null) { sb.append(url); if (urls.hasNext()) { sb.append(", "); } } } SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); Schema schema = schemaFactory.newSchema(sources); if (sb != null) { log.debug(sb.toString()); } return schema; } private static class DefaultContext { private static final String[] packageNames = { at.buergerkarte.namespaces.securitylayer._1_2_3.ObjectFactory.class.getPackage().getName(), org.w3._2000._09.xmldsig_.ObjectFactory.class.getPackage().getName(), at.buergerkarte.namespaces.cardchannel.ObjectFactory.class.getPackage().getName(), at.buergerkarte.namespaces.securitylayer._20020225_.ObjectFactory.class.getPackage().getName(), at.buergerkarte.namespaces.securitylayer._20020831_.ObjectFactory.class.getPackage().getName() }; private static final JAXBContext CONTEXT; static { try { CONTEXT = createJAXBContext(Arrays.asList(packageNames)); } catch (JAXBException e) { Logger log = LoggerFactory.getLogger(SLUnmarshaller.class); log.error("Failed to setup JAXBContext security layer request/response.", e); throw new RuntimeException(e); } } } public static Collection<String> getDefaultJAXBContextPackageNames() { return Collections.unmodifiableList(Arrays.asList(DefaultContext.packageNames)); } private static JAXBContext createJAXBContext(Collection<String> packageNames) throws JAXBException { StringBuilder contextPath = new StringBuilder(); for (String pkg : packageNames) { if (contextPath.length() > 0) { contextPath.append(':'); } contextPath.append(pkg); } return JAXBContext.newInstance(contextPath.toString()); } /** * Schema for Security Layer command validation. */ protected Schema slSchema = DefaultSchema.SCHEMA; /** * The JAXBContext. */ protected JAXBContext jaxbContext = DefaultContext.CONTEXT; /** * Returns the schema used for validation. * * @return the slSchema */ public Schema getSlSchema() { return slSchema; } /** * Sets the schema for validation. * * @param slSchema the slSchema to set */ public void setSlSchema(Schema slSchema) { this.slSchema = slSchema; } /** * Sets the schema created from the given {@code schemaUrls}. * * @param schemaUrls a collection of URLs of schema files (supports {@code classpath:} URLs) * @throws SAXException if schema creation fails * @throws IOException if an error occurs upon dereferencing the given {@code schemaUrls} */ public void setSchemaUrls(Collection<String> schemaUrls) throws SAXException, IOException { slSchema = createSchema(schemaUrls); } /** * @return the jaxbContext */ public JAXBContext getJaxbContext() { return jaxbContext; } /** * @param jaxbContext the jaxbContext to set */ public void setJaxbContext(JAXBContext jaxbContext) { this.jaxbContext = jaxbContext; } /** * Sets the JAXBContext for unmarshalling using the given {@code packageNames}. * * @param packageNames a collection of java package names * @throws JAXBException if creating the JAXBContext with the given {@code packageNames} fails */ public void setJaxbContextPackageNames(Collection<String> packageNames) throws JAXBException { this.jaxbContext = createJAXBContext(packageNames); } /** * @param source a StreamSource wrapping a Reader (!) for the marshalled Object * @return the unmarshalled Object * @throws XMLStreamException * @throws JAXBException */ public Object unmarshal(StreamSource source) throws XMLStreamException, JAXBException { Reader inputReader = source.getReader(); /* Validate XML against XXE, XEE, and SSRF * * This pre-validation step is required because com.sun.xml.stream.sjsxp-1.0.2 XML stream parser library does not * support all XML parser features to prevent these types of attacks */ if (inputReader instanceof InputStreamReader) { try { //create copy of input stream InputStreamReader isReader = (InputStreamReader) inputReader; String encoding = isReader.getEncoding(); byte[] backup = IOUtils.toByteArray(isReader, encoding); //validate input stream DOMUtils.validateXMLAgainstXXEAndSSRFAttacks(new ByteArrayInputStream(backup)); //create new inputStreamReader for reak processing inputReader = new InputStreamReader(new ByteArrayInputStream(backup), encoding); } catch (XMLStreamException e) { log.error("XML data validation FAILED with msg: " + e.getMessage(), e); throw new XMLStreamException("XML data validation FAILED with msg: " + e.getMessage(), e); } catch (IOException e) { log.error("XML data validation FAILED with msg: " + e.getMessage(), e); throw new XMLStreamException("XML data validation FAILED with msg: " + e.getMessage(), e); } } else { log.error("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); log.error( "Reader is not of type InputStreamReader -> can not make a copy of the InputStream --> extended XML validation is not possible!!! "); log.error("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); } /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * parse XML with original functionality * * This code implements the the original mocca XML processing by using * com.sun.xml.stream.sjsxp-1.0.2 XML stream parser library. Currently, this library is required to get full * security-layer specific XML processing. However, this lib does not fully support XXE, XEE and SSRF * prevention mechanisms (e.g.: XMLInputFactory.SUPPORT_DTD flag is not used) * * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ XMLInputFactory inputFactory = XMLInputFactory.newInstance(); //disallow DTD and external entities inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); inputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", false); XMLEventReader eventReader = inputFactory.createXMLEventReader(inputReader); RedirectEventFilter redirectEventFilter = new RedirectEventFilter(); XMLEventReader filteredReader = inputFactory.createFilteredReader(eventReader, redirectEventFilter); Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); ReportingValidationEventHandler validationEventHandler = new ReportingValidationEventHandler(); unmarshaller.setEventHandler(validationEventHandler); unmarshaller.setListener(new RedirectUnmarshallerListener(redirectEventFilter)); unmarshaller.setSchema(slSchema); Object object; try { log.trace("Before unmarshal()."); object = unmarshaller.unmarshal(filteredReader); log.trace("After unmarshal()."); } catch (UnmarshalException e) { if (log.isDebugEnabled()) { log.debug("Failed to unmarshal security layer message.", e); } else { log.info("Failed to unmarshal security layer message." + (e.getMessage() != null ? " " + e.getMessage() : "")); } if (validationEventHandler.getErrorEvent() != null) { ValidationEvent errorEvent = validationEventHandler.getErrorEvent(); if (e.getLinkedException() == null) { e.setLinkedException(errorEvent.getLinkedException()); } } throw e; } return object; } }