/* * Created on 27.11.2003 * * (c) Stabsstelle IKT-Strategie des Bundes */ package at.gv.egovernment.moa.spss.slinterface.beans; import iaik.utils.Util; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Random; import java.util.Set; import java.util.StringTokenizer; import javax.servlet.ServletContext; import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSessionBindingEvent; import javax.servlet.http.HttpSessionBindingListener; import org.apache.log4j.Logger; import org.apache.xerces.parsers.DOMParser; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import at.gv.egovernment.moa.spss.slinterface.Constants; import at.gv.egovernment.moa.spss.slinterface.DOMUtils; import at.gv.egovernment.moa.spss.slinterface.URLRewriter; import at.gv.egovernment.moa.spss.slinterface.XPathUtils; import at.gv.egovernment.moa.spss.slinterface.moainvoker.MOAInvoker; /** * @author Gregor Karlinger (mailto:gregor.karlinger@cio.gv.at) */ public class DataInfoBean implements HttpSessionBindingListener { private static Logger logger_ = Logger.getLogger(Constants.LH_BEANS_); // MOA private static final String HID_ELEM_ = "HashInputData"; private static final String B64CONT_ELEM_ = "Base64Content"; private static final String XMLCONT_ELEM_ = "XMLContent"; private static final String SIGLOC_ELEM_ = "VerifySignatureLocation"; // XMLDSIG private static final String TYPE_ATTR_ = "Type"; private static final String URI_ATTR_ = "URI"; // XHTML private static final String SRC_ATTR_ = "src"; private static final String HTML_ELEM_ = "html"; private static final String HID_URL_PREFIX_ = "/showdata?hidCount="; private static final String XPATH_ALL_IMG_ = "//" + Constants.NSPRE_XHTML_ + ":img"; private static final String XPATH_ALL_REF_ = "./" + Constants.NSPRE_DSIG_ + ":SignedInfo/" + Constants.NSPRE_DSIG_ + ":Reference"; private static final String XPATH_SIG_ENV_CONTENT_ = "/" + Constants.NSPRE_MOA_12_ + ":VerifyXMLSignatureRequest/" + Constants.NSPRE_MOA_12_ + ":VerifySignatureInfo/" + Constants.NSPRE_MOA_12_ + ":VerifySignatureEnvironment/*"; private static final String SLXHTML_TYPE_PREFIX_ = "http://www.buergerkarte.at/specifications/" + "Security-Layer/20031113?Name=SignedImage&InstanceDocRef="; private static final String ETSI_TYPE_ = "http://uri.etsi.org/01903/v1.1.1#SignedProperties"; private static final String SLMANIFEST_TYPE_ = "http://www.buergerkarte.at/specifications/Securitylayer/20020225#SignatureManifest"; ServletContext context_; String contextPath_; HttpSession session_; Properties initProps_; /** * Contains objects of type {@link HashInputDataInfo}. */ List hashInputDataInfos_; int hashInputDataCount_; /* ---------------------------------------------------------------------------------------------------- */ public DataInfoBean(Document moaRequestDoc, Document moaResponseDoc, String contextPath, HttpSession session, ServletContext context) throws Exception { context_ = context; contextPath_ = contextPath; session_ = session; initProps_ = (Properties) context_.getAttribute(Constants.WSCP_INIT_PROPS_); hashInputDataInfos_ = new ArrayList(); int hashInputDataCount_ = 0; Element moaResponseElem = moaResponseDoc.getDocumentElement(); List hidElems = DOMUtils.getChildElems(moaResponseElem, Constants.NSURI_MOA_12_, HID_ELEM_, false); String tempDir = initProps_.getProperty(Constants.IP_TEMP_DIR_); if (tempDir == null) { String message = "Init property \"" + Constants.IP_TEMP_DIR_ + "\" not set."; logger_.error(message); throw new IOException(message); } Random random = new Random(); for (int i = 0; i < hidElems.size(); i++) { // Open file for current hash input data String currHidFileNameStr = tempDir + session_.getId() + "_" + System.currentTimeMillis() + "_" + random.nextLong(); currHidFileNameStr = context_.getRealPath(currHidFileNameStr); FileOutputStream currHidFOS; try { currHidFOS = new FileOutputStream(currHidFileNameStr); } catch (IOException e) { String message = "Cannot open file \"" + currHidFileNameStr + "\"."; logger_.error(message); throw new IOException(message); } // Write HID to file Element currHidElem = (Element) hidElems.get(i); Element base64ContentElem = DOMUtils.getChildElem(currHidElem, Constants.NSURI_MOA_12_, B64CONT_ELEM_); if (base64ContentElem != null) { // HID is base64 String base64ContentText = DOMUtils.getText(base64ContentElem); byte[] content = Util.Base64Decode(base64ContentText.getBytes()); try { currHidFOS.write(content); currHidFOS.close(); } catch (IOException e) { String message = "Cannot write to file \"" + currHidFileNameStr + "\"."; logger_.error(message); throw new IOException(message); } } else { // HID is XML // TODO treatment of XML content throw new RuntimeException("XML content not support yet."); } hashInputDataInfos_.add(new HashInputDataInfo(currHidFileNameStr)); } logger_.debug("Finnished writing hash input data to files."); // Check if hids are slxhtml documents; mark them appropriately try { Map signedImages = getSignedImages(moaRequestDoc, hashInputDataInfos_); for (int i = 0; i < hashInputDataInfos_.size(); i++) { HashInputDataInfo currHid = (HashInputDataInfo) hashInputDataInfos_.get(i); FileInputStream currHidIS = new FileInputStream(currHid.filename_); checkImages(currHidIS, currHid, signedImages); } } catch (Exception e) { String message = "Performing SLXHTML checks failed."; logger_.error(message, e); throw new Exception(message, e); } logger_.debug("Finnished checking hash input data for slxhtml conformity."); } /* ---------------------------------------------------------------------------------------------------- */ public void valueBound(HttpSessionBindingEvent event) { // Do nothing. } /* ---------------------------------------------------------------------------------------------------- */ public void valueUnbound(HttpSessionBindingEvent event) { // Delete all temporary hash input data files for (int i = 0; i < hashInputDataInfos_.size(); i++) { String currFileStr = ((HashInputDataInfo) hashInputDataInfos_.get(i)).filename_; File currFile = new File(currFileStr); currFile.delete(); } } /* ---------------------------------------------------------------------------------------------------- */ public void setHashInputDataCount(int count) { hashInputDataCount_ = count; } /* ---------------------------------------------------------------------------------------------------- */ public String getHashInputDataFilename() { HashInputDataInfo currHid = (HashInputDataInfo) hashInputDataInfos_.get(hashInputDataCount_); return (currHid == null) ? null : currHid.filename_; } /* ---------------------------------------------------------------------------------------------------- */ public String getHashInputDataURL() { return (hashInputDataInfos_.size() > hashInputDataCount_) ? HID_URL_PREFIX_ + hashInputDataCount_ : null; } /* ---------------------------------------------------------------------------------------------------- */ public boolean getShowHashInputData() { HashInputDataInfo currHid = (HashInputDataInfo) hashInputDataInfos_.get(hashInputDataCount_); return (currHid == null) ? false : currHid.doShow_; } /* ---------------------------------------------------------------------------------------------------- */ public boolean getIsSLXHTMLDocument() { HashInputDataInfo currHid = (HashInputDataInfo) hashInputDataInfos_.get(hashInputDataCount_); return (currHid == null) ? false : currHid.isSLXHTMLDocument_; } /* ---------------------------------------------------------------------------------------------------- */ private Document parseSLXHTMLDocument(InputStream docIS) { DOMParser xmlParser = (DOMParser) context_.getAttribute(Constants.WSCP_XMLPARSER_); InputSource docInputSource = new InputSource(docIS); Document parsedDoc = null; try { xmlParser.parse(docInputSource); parsedDoc = xmlParser.getDocument(); } catch (Exception e) { // Exception shows that document is not a valid SLXHTML document; return null in that case logger_.debug("HashInputData is not a valid SLXHTML document.", e); return null; } Element docElem = parsedDoc.getDocumentElement(); if (docElem.getNamespaceURI() != Constants.NSURI_XHTML_ || docElem.getLocalName() != HTML_ELEM_) { return null; } return parsedDoc; } /* ---------------------------------------------------------------------------------------------------- */ private void checkImages(InputStream hidIS, HashInputDataInfo hid, Map signedImages) throws Exception { // Parse hidIS Document slxhtmlDoc = parseSLXHTMLDocument(hidIS); if (slxhtmlDoc == null) return; // Get all img elements of slxhtml document XPathUtils xpUtils = new XPathUtils(); String additionalNSPrefixes = Constants.NSPRE_XHTML_ + " " + Constants.NSURI_XHTML_; xpUtils.setupContext(XPATH_ALL_IMG_, slxhtmlDoc.getDocumentElement(), additionalNSPrefixes); NodeList imgTags = xpUtils.selectNodeSet(slxhtmlDoc); // Check if all img elements have corresponding slxhtml signed images boolean allImgsSigned = true; for (int i = 0; i < imgTags.getLength(); i++) { Element currImgElem = (Element) imgTags.item(i); String uri = currImgElem.getAttribute(SRC_ATTR_); if (!signedImages.containsKey(uri)) { allImgsSigned = false; break; } } // Mark all corresponding slxhtml signed images as not to be shown if (allImgsSigned) { for (int i = 0; i < imgTags.getLength(); i++) { Element currImgElem = (Element) imgTags.item(i); String uri = currImgElem.getAttribute(SRC_ATTR_); HashInputDataInfo currHidi = (HashInputDataInfo) signedImages.get(uri); currHidi.doShow_ = false; } } if (allImgsSigned) { // Change the src attributes of all img tags so that they refer to the temporary names for (int i = 0; i < imgTags.getLength(); i++) { Element currImgElem = (Element) imgTags.item(i); String uri = currImgElem.getAttribute(SRC_ATTR_); HashInputDataInfo currHidi = (HashInputDataInfo) signedImages.get(uri); Attr srcAttr = currImgElem.getAttributeNode(SRC_ATTR_); int slashPos = currHidi.filename_.lastIndexOf(System.getProperty("file.separator")); // Properties initProps = (Properties) context_.getAttribute(Constants.WSCP_INIT_PROPS_); // String tempDir = initProps.getProperty(Constants.IP_TEMP_DIR_); // String newSrcAttrValue = "." + tempDir + currHidi.filename_.substring(slashPos + 1); Properties initProps = (Properties) context_.getAttribute(Constants.WSCP_INIT_PROPS_); String tempDir = initProps.getProperty(Constants.IP_TEMP_DIR_); URLRewriter urlRewriter = (URLRewriter) context_.getAttribute(Constants.WSCP_URL_REWRITER_); String newSrcAttrValue = urlRewriter.rewrite( contextPath_ + tempDir + currHidi.filename_.substring(slashPos + 1), session_); srcAttr.setNodeValue(newSrcAttrValue); } // Mark hid as slxhtml document hid.isSLXHTMLDocument_ = true; // Serialize modified slxhtml document to temporary file location FileOutputStream slxhtmlFOS = new FileOutputStream(hid.filename_); MOAInvoker.serializeDocument(slxhtmlDoc, slxhtmlFOS); slxhtmlFOS.close(); } } /* ---------------------------------------------------------------------------------------------------- */ private Map getSignedImages(Document moaRequestDoc, List hashInputDataInfos) throws Exception { // Get signature from MOA request Element signatureElem = getSignature(moaRequestDoc); // Get all signature references from MOA request XPathUtils xpUtils = new XPathUtils(); String additionalNSPrefixes = Constants.NSPRE_DSIG_ + " " + Constants.NSURI_DSIG_; xpUtils.setupContext(XPATH_ALL_REF_, signatureElem, additionalNSPrefixes); NodeList dsigRefs = xpUtils.selectNodeSet(signatureElem); // Check signature references for slxhtml images HashMap imgHids = new HashMap(dsigRefs.getLength()); for (int i = 0; i < dsigRefs.getLength(); i++) { Element currRef = (Element) dsigRefs.item(i); String type = currRef.getAttribute(TYPE_ATTR_); if (type != null && type.startsWith(SLXHTML_TYPE_PREFIX_)) { String uri = currRef.getAttribute(URI_ATTR_); Set referredHids = createReferredHidsSet(type); HashInputDataInfo currHidi = (HashInputDataInfo)hashInputDataInfos.get(i); currHidi.uri_ = uri; currHidi.referredHids_ = referredHids; currHidi.isSLXHTMLImage_ = true; imgHids.put(uri, currHidi); } } // Check signature references if they refer to etsi attributes or to a SL manifest for (int i = 0; i < dsigRefs.getLength(); i++) { Element currRef = (Element) dsigRefs.item(i); String type = currRef.getAttribute(TYPE_ATTR_); if (type != null && type.equals(ETSI_TYPE_)) { HashInputDataInfo currHidi = (HashInputDataInfo)hashInputDataInfos.get(i); currHidi.doShow_ = new Boolean(initProps_.getProperty(Constants.IP_RES_SHOWETSI_).trim()).booleanValue(); } if (type != null && type.equals(SLMANIFEST_TYPE_)) { HashInputDataInfo currHidi = (HashInputDataInfo)hashInputDataInfos.get(i); currHidi.doShow_ = new Boolean(initProps_.getProperty(Constants.IP_RES_SHOWSLMAN_).trim()).booleanValue(); } } return imgHids; } /* ---------------------------------------------------------------------------------------------------- */ private Set createReferredHidsSet(String type) throws Exception { HashSet set = new HashSet(); String typeSuffix = type.substring(SLXHTML_TYPE_PREFIX_.length()); StringTokenizer tokenizer = new StringTokenizer(typeSuffix, ","); while (tokenizer.hasMoreTokens()) { try { set.add(new Integer(tokenizer.nextToken())); } catch (NumberFormatException e) { String message = "Signed image type attribute \"" + type + "\" is malformed."; logger_.error(message, e); throw new Exception(message, e); } } return set; } /* ---------------------------------------------------------------------------------------------------- */ private Element getSignature(Document moaRequestDoc) throws Exception { // Get signature environment content NodeList contentNL; try { XPathUtils xpUtils = new XPathUtils(); String addNSPrefixes = Constants.NSPRE_MOA_12_ + " " + Constants.NSURI_MOA_12_; xpUtils.setupContext(XPATH_SIG_ENV_CONTENT_, moaRequestDoc, addNSPrefixes); contentNL = xpUtils.selectNodeSet(moaRequestDoc); } catch (Exception e) { String message = "Cannot find signature environment content."; logger_.error(message); throw new Exception(message, e); } if (contentNL.getLength() == 0) { String message = "Cannot find signature environment content."; logger_.error(message); throw new Exception(message); } Element contentElem = (Element) contentNL.item(0); // Get signature environment document from signature environment content String contentElemLocName = contentElem.getLocalName(); Element sigEnvElem = null; Element oldDocElem = null; if (XMLCONT_ELEM_.equals(contentElemLocName)) { // XML content NodeList contentNodes = contentElem.getChildNodes(); for (int i = 0; i < contentNodes.getLength(); i++) { Node currContNode = (Node) contentNodes.item(i); if (currContNode.getNodeType() == Node.ELEMENT_NODE) { sigEnvElem = (Element) currContNode; oldDocElem = (Element) moaRequestDoc.replaceChild(sigEnvElem, moaRequestDoc.getDocumentElement()); break; } } } else if (B64CONT_ELEM_.equals(contentElemLocName)) { // Base64 content String base64ContStr = DOMUtils.getText(contentElem); byte[] contBytes = Util.Base64Decode(base64ContStr.getBytes()); ByteArrayInputStream contBIS = new ByteArrayInputStream(contBytes); Document sigEnvDoc; try { sigEnvDoc = DOMUtils.parseWellFormed(contBIS); } catch (Exception e) { String message = "Cannot parse signature environment from base64 content."; logger_.error(message); throw new Exception(message, e); } sigEnvElem = sigEnvDoc.getDocumentElement(); } else { // LocRef content String locRef = DOMUtils.getText(contentElem); URL locRefURL = new URL(locRef); InputStream contentIS = locRefURL.openStream(); Document sigEnvDoc; try { sigEnvDoc = DOMUtils.parseWellFormed(contentIS); } catch (Exception e) { String message = "Cannot parse signature environment from location reference content."; logger_.error(message); throw new Exception(message, e); } sigEnvElem = sigEnvDoc.getDocumentElement(); } // Get signature form signature environment document Element sigInfoElem = (Element) contentElem.getParentNode().getParentNode(); Element sigLocElem = DOMUtils.getChildElem(sigInfoElem, Constants.NSURI_MOA_12_, SIGLOC_ELEM_); String sigLocXPath = DOMUtils.getText(sigLocElem); NodeList sigElemNL; try { XPathUtils xpUtils = new XPathUtils(); xpUtils.setupContext(sigLocXPath, sigLocElem, null); sigElemNL = xpUtils.selectNodeSet(sigEnvElem); } catch (Exception e) { String message = "Cannot get signature at location \"" + sigLocXPath + "\" from signature environment."; logger_.error(message); throw new Exception(message, e); } if (sigElemNL.getLength() != 1 || ((Node) sigElemNL.item(0)).getNodeType() != Node.ELEMENT_NODE) { String message = "Cannot get signature at location \"" + sigLocXPath + "\" from signature environment."; logger_.error(message); throw new Exception(message); } if (oldDocElem != null) moaRequestDoc.replaceChild(oldDocElem, moaRequestDoc.getDocumentElement()); return (Element) sigElemNL.item(0); } }