From 9fcd51db225beb432f144741e1691f064b86f624 Mon Sep 17 00:00:00 2001 From: Joerg Pietschmann Date: Fri, 19 Sep 2003 16:50:56 +0000 Subject: [PATCH] SAX filter providing a MD5 checksum for ensuring XML input integrity. git-svn-id: https://svn.apache.org/repos/asf/xmlgraphics/fop/trunk@196925 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/fop/DigestFilterTestCase.java | 187 ++++++++++++++++++ .../org/apache/fop/util/DigestFilter.java | 173 ++++++++++++++++ 2 files changed, 360 insertions(+) create mode 100644 test/java/org/apache/fop/DigestFilterTestCase.java create mode 100644 test/java/org/apache/fop/util/DigestFilter.java diff --git a/test/java/org/apache/fop/DigestFilterTestCase.java b/test/java/org/apache/fop/DigestFilterTestCase.java new file mode 100644 index 000000000..a976b09d2 --- /dev/null +++ b/test/java/org/apache/fop/DigestFilterTestCase.java @@ -0,0 +1,187 @@ +/* + * $Id$ + * ============================================================================ + * The Apache Software License, Version 1.1 + * ============================================================================ + * + * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: "This product includes software + * developed by the Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "FOP" and "Apache Software Foundation" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", nor may + * "Apache" appear in their name, without prior written permission of the + * Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + * + * This software consists of voluntary contributions made by many individuals + * on behalf of the Apache Software Foundation and was originally created by + * James Tauber . For more information on the Apache + * Software Foundation, please see . + */ + +package org.apache.fop; + +import java.io.IOException; +import java.io.StringReader; +import java.security.NoSuchAlgorithmException; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParserFactory; + +import junit.framework.TestCase; + +import org.apache.fop.util.DigestFilter; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; + +/** + * Test case for digesting SAX filter. + * + */ +public class DigestFilterTestCase extends TestCase { + + private SAXParserFactory parserFactory; + + /* (non-Javadoc) + * @see junit.framework.TestCase#setUp() + */ + protected void setUp() { + parserFactory = SAXParserFactory.newInstance(); + parserFactory.setNamespaceAware(true); + } + + private boolean compareDigest(byte a[], byte b[]) { + if (a.length != b.length) { + return false; + } + for (int i = 0; i < a.length; i++) { + if (a[i] != b[i]) { + return false; + } + } + return true; + } + + private String digestToString(byte digest[]) { + StringBuffer buffer = new StringBuffer(2 * digest.length); + for (int i = 0; i < digest.length; i++) { + int val = digest[i]; + int hi = (val >> 4) & 0xF; + int lo = val & 0xF; + if (hi < 10) { + buffer.append((char) (hi + 0x30)); + } else { + buffer.append((char) (hi + 0x61 - 10)); + } + if (lo < 10) { + buffer.append((char) (lo + 0x30)); + } else { + buffer.append((char) (lo + 0x61 - 10)); + } + } + return buffer.toString(); + } + + private byte[] runTest(String input) + throws + NoSuchAlgorithmException, + ParserConfigurationException, + SAXException, + IOException { + XMLReader parser = parserFactory.newSAXParser().getXMLReader(); + DigestFilter digestFilter = new DigestFilter("MD5"); + digestFilter.setParent(parser); + digestFilter.setFeature("http://xml.org/sax/features/namespaces",true); + parser.setContentHandler(digestFilter); + InputSource inputSource = new InputSource(new StringReader(input)); + parser.parse(inputSource); + return digestFilter.getDigestValue(); + } + + public final void testLineFeed() + throws + NoSuchAlgorithmException, + ParserConfigurationException, + SAXException, + IOException { + byte lfDigest[] = runTest("\n"); + byte crlfDigest[] = runTest("\r\n"); + assertTrue( + "LF: " + + digestToString(lfDigest) + + " CRLF: " + + digestToString(crlfDigest), + compareDigest(lfDigest, crlfDigest)); + } + + public final void testAttributeOrder() + throws + NoSuchAlgorithmException, + ParserConfigurationException, + SAXException, + IOException { + byte sortDigest[] = runTest(""); + byte permutationDigest[] = runTest(""); + assertTrue( + "Sort: " + + digestToString(sortDigest) + + " permuted: " + + digestToString(permutationDigest), + compareDigest(sortDigest, permutationDigest)); + byte reverseDigest[] = runTest(""); + assertTrue( + "Sort: " + + digestToString(sortDigest) + + " permuted: " + + digestToString(reverseDigest), + compareDigest(sortDigest, reverseDigest)); + } + + public final void testNamespacePrefix() + throws + NoSuchAlgorithmException, + ParserConfigurationException, + SAXException, + IOException { + byte prefix1Digest[] = runTest(""); + byte prefix2Digest[] = runTest(""); + assertTrue( + "prefix1: " + + digestToString(prefix1Digest) + + " prefix2: " + + digestToString(prefix2Digest), + compareDigest(prefix1Digest, prefix2Digest)); + } + +} diff --git a/test/java/org/apache/fop/util/DigestFilter.java b/test/java/org/apache/fop/util/DigestFilter.java new file mode 100644 index 000000000..d6ac21d54 --- /dev/null +++ b/test/java/org/apache/fop/util/DigestFilter.java @@ -0,0 +1,173 @@ +/* + * $Id$ + * ============================================================================ + * The Apache Software License, Version 1.1 + * ============================================================================ + * + * Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modifica- + * tion, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * 3. The end-user documentation included with the redistribution, if any, must + * include the following acknowledgment: "This product includes software + * developed by the Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, if + * and wherever such third-party acknowledgments normally appear. + * + * 4. The names "FOP" and "Apache Software Foundation" must not be used to + * endorse or promote products derived from this software without prior + * written permission. For written permission, please contact + * apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", nor may + * "Apache" appear in their name, without prior written permission of the + * Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- + * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * ============================================================================ + * + * This software consists of voluntary contributions made by many individuals + * on behalf of the Apache Software Foundation and was originally created by + * James Tauber . For more information on the Apache + * Software Foundation, please see . + */ +package org.apache.fop.util; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeMap; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.helpers.XMLFilterImpl; + +/** + * SAX filter which produces a digest over the XML elements. + * Insignificant whitespace as determined by the, comments and + * processing instructions are not part of the digest. + * If the filter is namespace aware, the URI and local name for + * each element is digested, otherwise the QName. Attributes are + * sorted before the name-value pairs are digested. + * + */ +public class DigestFilter extends XMLFilterImpl { + + private MessageDigest digest; + private byte value[]; + private boolean isNamespaceAware; + + public DigestFilter(String algorithm) throws NoSuchAlgorithmException { + digest = MessageDigest.getInstance(algorithm); + } + + public byte[] getDigestValue() { + return value; + } + + public String getDigestString() { + if (value != null) { + StringBuffer buffer = new StringBuffer(2 * value.length); + for (int i = 0; i < value.length; i++) { + int val = value[i]; + int hi = (val >> 4) & 0xF; + int lo = val & 0xF; + if (hi < 10) { + buffer.append((char) (hi + 0x30)); + } else { + buffer.append((char) (hi + 0x61 - 10)); + } + if (lo < 10) { + buffer.append((char) (lo + 0x30)); + } else { + buffer.append((char) (lo + 0x61 - 10)); + } + } + return buffer.toString(); + } else { + return null; + } + } + + /* (non-Javadoc) + * @see org.xml.sax.ContentHandler#characters(char[], int, int) + */ + public void characters(char[] chars, int start, int length) + throws SAXException { + digest.update(new String(chars, start, length).getBytes()); + super.characters(chars, start, length); + } + + /* (non-Javadoc) + * @see org.xml.sax.ContentHandler#endDocument() + */ + public void endDocument() throws SAXException { + value = digest.digest(); + super.endDocument(); + } + + /* (non-Javadoc) + * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) + */ + public void startElement( + String url, + String localName, + String qName, + Attributes attr) + throws SAXException { + Map map = new TreeMap(); + if (isNamespaceAware) { + digest.update(url.getBytes()); + digest.update(localName.getBytes()); + for (int i = 0; i < attr.getLength(); i++) { + map.put( + attr.getLocalName(i) + attr.getURI(i), + attr.getValue(i)); + } + } else { + digest.update(qName.getBytes()); + for (int i = 0; i < attr.getLength(); i++) { + map.put(attr.getQName(i), attr.getValue(i)); + } + } + for (Iterator i = map.entrySet().iterator(); i.hasNext();) { + Map.Entry entry = (Map.Entry)i.next(); + digest.update(((String)entry.getKey()).getBytes()); + digest.update(((String)entry.getValue()).getBytes()); + } + super.startElement(url, localName, qName, attr); + } + + + /* (non-Javadoc) + * @see org.xml.sax.XMLReader#setFeature(java.lang.String, boolean) + */ + public void setFeature(String feature, boolean value) + throws SAXNotRecognizedException, SAXNotSupportedException { + if(feature.equals("http://xml.org/sax/features/namespaces")) { + isNamespaceAware = value; + } + super.setFeature(feature, value); + } + +} -- 2.39.5