diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java index 59d7f44d28..bf9dae94f5 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java @@ -57,6 +57,7 @@ import javax.xml.crypto.dsig.XMLSignContext; import javax.xml.crypto.dsig.XMLSignature; import javax.xml.crypto.dsig.XMLSignatureException; import javax.xml.crypto.dsig.XMLSignatureFactory; +import javax.xml.crypto.dsig.XMLValidateContext; import javax.xml.crypto.dsig.dom.DOMSignContext; import javax.xml.crypto.dsig.dom.DOMValidateContext; import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; @@ -230,9 +231,25 @@ public class SignatureInfo implements SignatureConfigurable { DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc); domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE); domValidateContext.setURIDereferencer(signatureConfig.getUriDereferencer()); + brokenJvmWorkaround(domValidateContext); XMLSignatureFactory xmlSignatureFactory = signatureConfig.getSignatureFactory(); XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext); + + // TODO: replace with property when xml-sec patch is applied + for (Reference ref : (List)xmlSignature.getSignedInfo().getReferences()) { + SignatureFacet.brokenJvmWorkaround(ref); + } + for (XMLObject xo : (List)xmlSignature.getObjects()) { + for (XMLStructure xs : (List)xo.getContent()) { + if (xs instanceof Manifest) { + for (Reference ref : (List)((Manifest)xs).getReferences()) { + SignatureFacet.brokenJvmWorkaround(ref); + } + } + } + } + boolean valid = xmlSignature.validate(domValidateContext); if (valid) { @@ -241,8 +258,6 @@ public class SignatureInfo implements SignatureConfigurable { } return valid; - } catch (ArrayIndexOutOfBoundsException e) { - throw new JvmBrokenException(e); } catch (Exception e) { LOG.log(POILogger.ERROR, "error in marshalling and validating the signature", e); return false; @@ -399,149 +414,141 @@ public class SignatureInfo implements SignatureConfigurable { @SuppressWarnings("unchecked") public DigestInfo preSign(Document document, List digestInfos) throws XMLSignatureException, MarshalException { - try { - signatureConfig.init(false); - - // it's necessary to explicitly set the mdssi namespace, but the sign() method has no - // normal way to interfere with, so we need to add the namespace under the hand ... - EventTarget target = (EventTarget)document; - EventListener creationListener = signatureConfig.getSignatureMarshalListener(); - if (creationListener != null) { - if (creationListener instanceof SignatureMarshalListener) { - ((SignatureMarshalListener)creationListener).setEventTarget(target); - } - SignatureMarshalListener.setListener(target, creationListener, true); + signatureConfig.init(false); + + // it's necessary to explicitly set the mdssi namespace, but the sign() method has no + // normal way to interfere with, so we need to add the namespace under the hand ... + EventTarget target = (EventTarget)document; + EventListener creationListener = signatureConfig.getSignatureMarshalListener(); + if (creationListener != null) { + if (creationListener instanceof SignatureMarshalListener) { + ((SignatureMarshalListener)creationListener).setEventTarget(target); } - - /* - * Signature context construction. - */ - XMLSignContext xmlSignContext = new DOMSignContext(signatureConfig.getKey(), document); - URIDereferencer uriDereferencer = signatureConfig.getUriDereferencer(); - if (null != uriDereferencer) { - xmlSignContext.setURIDereferencer(uriDereferencer); - } - - for (Map.Entry me : signatureConfig.getNamespacePrefixes().entrySet()) { - xmlSignContext.putNamespacePrefix(me.getKey(), me.getValue()); - } - xmlSignContext.setDefaultNamespacePrefix(""); - // signatureConfig.getNamespacePrefixes().get(XML_DIGSIG_NS)); - - // workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1155012 - Provider bcProv = Security.getProvider("BC"); - if (bcProv != null) { - xmlSignContext.setProperty("org.jcp.xml.dsig.internal.dom.SignatureProvider", bcProv); - } - - XMLSignatureFactory signatureFactory = signatureConfig.getSignatureFactory(); - - /* - * Add ds:References that come from signing client local files. - */ - List references = new ArrayList(); - for (DigestInfo digestInfo : safe(digestInfos)) { - byte[] documentDigestValue = digestInfo.digestValue; - - String uri = new File(digestInfo.description).getName(); - Reference reference = SignatureFacet.newReference - (uri, null, null, null, documentDigestValue, signatureConfig); - references.add(reference); - } - - /* - * Invoke the signature facets. - */ - List objects = new ArrayList(); - for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) { - LOG.log(POILogger.DEBUG, "invoking signature facet: " + signatureFacet.getClass().getSimpleName()); - signatureFacet.preSign(document, references, objects); - } - - /* - * ds:SignedInfo - */ - SignedInfo signedInfo; - try { - SignatureMethod signatureMethod = signatureFactory.newSignatureMethod - (signatureConfig.getSignatureMethodUri(), null); - CanonicalizationMethod canonicalizationMethod = signatureFactory - .newCanonicalizationMethod(signatureConfig.getCanonicalizationMethod(), - (C14NMethodParameterSpec) null); - signedInfo = signatureFactory.newSignedInfo( - canonicalizationMethod, signatureMethod, references); - } catch (GeneralSecurityException e) { - throw new XMLSignatureException(e); - } - - /* - * JSR105 ds:Signature creation - */ - String signatureValueId = signatureConfig.getPackageSignatureId() + "-signature-value"; - javax.xml.crypto.dsig.XMLSignature xmlSignature = signatureFactory - .newXMLSignature(signedInfo, null, objects, signatureConfig.getPackageSignatureId(), - signatureValueId); - - /* - * ds:Signature Marshalling. - */ - xmlSignature.sign(xmlSignContext); - - /* - * Completion of undigested ds:References in the ds:Manifests. - */ - for (XMLObject object : objects) { - LOG.log(POILogger.DEBUG, "object java type: " + object.getClass().getName()); - List objectContentList = object.getContent(); - for (XMLStructure objectContent : objectContentList) { - LOG.log(POILogger.DEBUG, "object content java type: " + objectContent.getClass().getName()); - if (!(objectContent instanceof Manifest)) continue; - Manifest manifest = (Manifest) objectContent; - List manifestReferences = manifest.getReferences(); - for (Reference manifestReference : manifestReferences) { - if (manifestReference.getDigestValue() != null) continue; - - DOMReference manifestDOMReference = (DOMReference)manifestReference; - manifestDOMReference.digest(xmlSignContext); - } - } - } - - /* - * Completion of undigested ds:References. - */ - List signedInfoReferences = signedInfo.getReferences(); - for (Reference signedInfoReference : signedInfoReferences) { - DOMReference domReference = (DOMReference)signedInfoReference; - - // ds:Reference with external digest value - if (domReference.getDigestValue() != null) continue; - - domReference.digest(xmlSignContext); - } - - /* - * Calculation of XML signature digest value. - */ - DOMSignedInfo domSignedInfo = (DOMSignedInfo)signedInfo; - ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); - domSignedInfo.canonicalize(xmlSignContext, dataStream); - byte[] octets = dataStream.toByteArray(); - - /* - * TODO: we could be using DigestOutputStream here to optimize memory - * usage. - */ - - MessageDigest md = CryptoFunctions.getMessageDigest(signatureConfig.getDigestAlgo()); - byte[] digestValue = md.digest(octets); - - - String description = signatureConfig.getSignatureDescription(); - return new DigestInfo(digestValue, signatureConfig.getDigestAlgo(), description); - } catch (ArrayIndexOutOfBoundsException e) { - throw new JvmBrokenException(e); + SignatureMarshalListener.setListener(target, creationListener, true); } + + /* + * Signature context construction. + */ + XMLSignContext xmlSignContext = new DOMSignContext(signatureConfig.getKey(), document); + URIDereferencer uriDereferencer = signatureConfig.getUriDereferencer(); + if (null != uriDereferencer) { + xmlSignContext.setURIDereferencer(uriDereferencer); + } + + for (Map.Entry me : signatureConfig.getNamespacePrefixes().entrySet()) { + xmlSignContext.putNamespacePrefix(me.getKey(), me.getValue()); + } + xmlSignContext.setDefaultNamespacePrefix(""); + // signatureConfig.getNamespacePrefixes().get(XML_DIGSIG_NS)); + + brokenJvmWorkaround(xmlSignContext); + + XMLSignatureFactory signatureFactory = signatureConfig.getSignatureFactory(); + + /* + * Add ds:References that come from signing client local files. + */ + List references = new ArrayList(); + for (DigestInfo digestInfo : safe(digestInfos)) { + byte[] documentDigestValue = digestInfo.digestValue; + + String uri = new File(digestInfo.description).getName(); + Reference reference = SignatureFacet.newReference + (uri, null, null, null, documentDigestValue, signatureConfig); + references.add(reference); + } + + /* + * Invoke the signature facets. + */ + List objects = new ArrayList(); + for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) { + LOG.log(POILogger.DEBUG, "invoking signature facet: " + signatureFacet.getClass().getSimpleName()); + signatureFacet.preSign(document, references, objects); + } + + /* + * ds:SignedInfo + */ + SignedInfo signedInfo; + try { + SignatureMethod signatureMethod = signatureFactory.newSignatureMethod + (signatureConfig.getSignatureMethodUri(), null); + CanonicalizationMethod canonicalizationMethod = signatureFactory + .newCanonicalizationMethod(signatureConfig.getCanonicalizationMethod(), + (C14NMethodParameterSpec) null); + signedInfo = signatureFactory.newSignedInfo( + canonicalizationMethod, signatureMethod, references); + } catch (GeneralSecurityException e) { + throw new XMLSignatureException(e); + } + + /* + * JSR105 ds:Signature creation + */ + String signatureValueId = signatureConfig.getPackageSignatureId() + "-signature-value"; + javax.xml.crypto.dsig.XMLSignature xmlSignature = signatureFactory + .newXMLSignature(signedInfo, null, objects, signatureConfig.getPackageSignatureId(), + signatureValueId); + + /* + * ds:Signature Marshalling. + */ + xmlSignature.sign(xmlSignContext); + + /* + * Completion of undigested ds:References in the ds:Manifests. + */ + for (XMLObject object : objects) { + LOG.log(POILogger.DEBUG, "object java type: " + object.getClass().getName()); + List objectContentList = object.getContent(); + for (XMLStructure objectContent : objectContentList) { + LOG.log(POILogger.DEBUG, "object content java type: " + objectContent.getClass().getName()); + if (!(objectContent instanceof Manifest)) continue; + Manifest manifest = (Manifest) objectContent; + List manifestReferences = manifest.getReferences(); + for (Reference manifestReference : manifestReferences) { + if (manifestReference.getDigestValue() != null) continue; + + DOMReference manifestDOMReference = (DOMReference)manifestReference; + manifestDOMReference.digest(xmlSignContext); + } + } + } + + /* + * Completion of undigested ds:References. + */ + List signedInfoReferences = signedInfo.getReferences(); + for (Reference signedInfoReference : signedInfoReferences) { + DOMReference domReference = (DOMReference)signedInfoReference; + + // ds:Reference with external digest value + if (domReference.getDigestValue() != null) continue; + + domReference.digest(xmlSignContext); + } + + /* + * Calculation of XML signature digest value. + */ + DOMSignedInfo domSignedInfo = (DOMSignedInfo)signedInfo; + ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); + domSignedInfo.canonicalize(xmlSignContext, dataStream); + byte[] octets = dataStream.toByteArray(); + + /* + * TODO: we could be using DigestOutputStream here to optimize memory + * usage. + */ + + MessageDigest md = CryptoFunctions.getMessageDigest(signatureConfig.getDigestAlgo()); + byte[] digestValue = md.digest(octets); + + + String description = signatureConfig.getSignatureDescription(); + return new DigestInfo(digestValue, signatureConfig.getDigestAlgo(), description); } /** @@ -651,10 +658,20 @@ public class SignatureInfo implements SignatureConfigurable { private static List safe(List other) { return other == null ? Collections.EMPTY_LIST : other; } - - private static class JvmBrokenException extends EncryptedDocumentException { - public JvmBrokenException(Throwable cause) { - super("\"your JVM is just too broken\" - check https://bugzilla.redhat.com/show_bug.cgi?id=1155012 if this applies to the stacktrace ...", cause); - } + + private void brokenJvmWorkaround(XMLSignContext context) { + // workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1155012 + Provider bcProv = Security.getProvider("BC"); + if (bcProv != null) { + context.setProperty("org.jcp.xml.dsig.internal.dom.SignatureProvider", bcProv); + } + } + + private void brokenJvmWorkaround(XMLValidateContext context) { + // workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1155012 + Provider bcProv = Security.getProvider("BC"); + if (bcProv != null) { + context.setProperty("org.jcp.xml.dsig.internal.dom.SignatureProvider", bcProv); + } } } diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/SignatureFacet.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/SignatureFacet.java index c21a4c8525..21922fe1c7 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/SignatureFacet.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/SignatureFacet.java @@ -150,6 +150,16 @@ public abstract class SignatureFacet implements SignatureConfigurable { reference = sigFac.newReference(uri, digestMethod, transforms, type, id, digestValue); } + brokenJvmWorkaround(reference); + + return reference; + } + + // helper method ... will be removed soon + public static void brokenJvmWorkaround(Reference reference) { + DigestMethod digestMethod = reference.getDigestMethod(); + String digestMethodUri = digestMethod.getAlgorithm(); + // workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1155012 // overwrite standard message digest, if a digest <> SHA1 is used Provider bcProv = Security.getProvider("BC"); @@ -166,7 +176,5 @@ public abstract class SignatureFacet implements SignatureConfigurable { LOG.log(POILogger.WARN, "Can't overwrite message digest (workaround for https://bugzilla.redhat.com/show_bug.cgi?id=1155012)", e); } } - - return reference; } } \ No newline at end of file diff --git a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java index 7c185721e7..3628ac75b4 100644 --- a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java +++ b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java @@ -50,7 +50,6 @@ import java.util.Iterator; import java.util.List; import java.util.TimeZone; -import org.apache.poi.EncryptedDocumentException; import org.apache.poi.POIDataSamples; import org.apache.poi.openxml4j.opc.OPCPackage; import org.apache.poi.openxml4j.opc.PackageAccess; @@ -299,7 +298,9 @@ public class TestSignatureInfo { signatureConfig.addSignatureFacet(new XAdESSignatureFacet()); signatureConfig.addSignatureFacet(new XAdESXLSignatureFacet()); - boolean mockTsp = false; + // check for internet + Process p1 = Runtime.getRuntime().exec("ping www.google.com"); + boolean mockTsp = (p1.waitFor() == 1); // http://timestamping.edelweb.fr/service/tsp // http://tsa.belgium.be/connect // http://timestamp.comodoca.com/authenticode @@ -471,14 +472,14 @@ public class TestSignatureInfo { si.confirmSignature(); boolean b = si.verifySignature(); assertTrue("Signature not correctly calculated for " + ha, b); - } catch (EncryptedDocumentException e) { - // see http://apache-poi.1045710.n5.nabble.com/org-apache-poi-poifs-crypt-TestSignatureInfo-failing-on-trunk-on-Java-6-tp5717032.html - Throwable cause = e.getCause(); - if (cause instanceof ArrayIndexOutOfBoundsException) { - LOG.log(POILogger.ERROR, "ignoring AIOOBE - hopefully a SHA2 bug ...", e); - } else { - throw e; - } +// } catch (EncryptedDocumentException e) { +// // see http://apache-poi.1045710.n5.nabble.com/org-apache-poi-poifs-crypt-TestSignatureInfo-failing-on-trunk-on-Java-6-tp5717032.html +// Throwable cause = e.getCause(); +// if (cause instanceof ArrayIndexOutOfBoundsException) { +// LOG.log(POILogger.ERROR, "ignoring AIOOBE - hopefully a SHA2 bug ...", e); +// } else { +// throw e; +// } } finally { if (pkg != null) pkg.close(); }