diff options
author | Andreas Beeker <kiwiwings@apache.org> | 2020-03-01 22:20:38 +0000 |
---|---|---|
committer | Andreas Beeker <kiwiwings@apache.org> | 2020-03-01 22:20:38 +0000 |
commit | b04379bc4ca1faffe2b834f26e247ba74317ce38 (patch) | |
tree | e859045db0b4ae5314123c5b1671122cce7b46f6 | |
parent | 369170467880ccab6c4cc9aeeae1caae15d0a469 (diff) | |
download | poi-b04379bc4ca1faffe2b834f26e247ba74317ce38.tar.gz poi-b04379bc4ca1faffe2b834f26e247ba74317ce38.zip |
#64186 - Decrease usage of ThreadLocals in XML Signature API
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1874671 13f79535-47bb-0310-9956-ffa450edef68
17 files changed, 875 insertions, 806 deletions
diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/OOXMLURIDereferencer.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/OOXMLURIDereferencer.java index b4061622e0..b809b06ef5 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/OOXMLURIDereferencer.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/OOXMLURIDereferencer.java @@ -18,9 +18,9 @@ /* ==================================================================== This product contains an ASLv2 licensed version of the OOXML signer package from the eID Applet project - http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt Copyright (C) 2008-2014 FedICT. - ================================================================= */ + ================================================================= */ package org.apache.poi.poifs.crypt.dsig; @@ -42,33 +42,29 @@ import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackagePartName; import org.apache.poi.openxml4j.opc.PackagingURIHelper; -import org.apache.poi.poifs.crypt.dsig.SignatureConfig.SignatureConfigurable; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; /** * JSR105 URI dereferencer for Office Open XML documents. */ -public class OOXMLURIDereferencer implements URIDereferencer, SignatureConfigurable { +public class OOXMLURIDereferencer implements URIDereferencer { private static final POILogger LOG = POILogFactory.getLogger(OOXMLURIDereferencer.class); - private SignatureConfig signatureConfig; + private SignatureInfo signatureInfo; private URIDereferencer baseUriDereferencer; - public void setSignatureConfig(SignatureConfig signatureConfig) { - this.signatureConfig = signatureConfig; + public void setSignatureInfo(SignatureInfo signatureInfo) { + this.signatureInfo = signatureInfo; + baseUriDereferencer = signatureInfo.getSignatureFactory().getURIDereferencer(); } public Data dereference(URIReference uriReference, XMLCryptoContext context) throws URIReferenceException { - if (baseUriDereferencer == null) { - baseUriDereferencer = signatureConfig.getSignatureFactory().getURIDereferencer(); + if (uriReference == null) { + throw new NullPointerException("URIReference cannot be null - call setSignatureInfo(...) before"); } - - if (null == uriReference) { - throw new NullPointerException("URIReference cannot be null"); - } - if (null == context) { + if (context == null) { throw new NullPointerException("XMLCryptoContext cannot be null"); } @@ -82,7 +78,7 @@ public class OOXMLURIDereferencer implements URIDereferencer, SignatureConfigura PackagePart part = findPart(uri); if (part == null) { LOG.log(POILogger.DEBUG, "cannot resolve, delegating to base DOM URI dereferencer", uri); - return this.baseUriDereferencer.dereference(uriReference, context); + return baseUriDereferencer.dereference(uriReference, context); } InputStream dataStream; @@ -116,15 +112,14 @@ public class OOXMLURIDereferencer implements URIDereferencer, SignatureConfigura LOG.log(POILogger.DEBUG, "illegal part name (expected)", uri); return null; } - + PackagePartName ppn; try { ppn = PackagingURIHelper.createPartName(path); + return signatureInfo.getOpcPackage().getPart(ppn); } catch (InvalidFormatException e) { LOG.log(POILogger.WARN, "illegal part name (not expected)", uri); return null; } - - return signatureConfig.getOpcPackage().getPart(ppn); } } diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java index 0fcacaf376..f122b36296 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureConfig.java @@ -27,12 +27,15 @@ import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.UUID; +import java.util.function.Supplier; +import java.util.stream.Collectors; import javax.xml.crypto.URIDereferencer; import javax.xml.crypto.dsig.CanonicalizationMethod; @@ -53,11 +56,12 @@ import org.apache.poi.poifs.crypt.dsig.services.SignaturePolicyService; import org.apache.poi.poifs.crypt.dsig.services.TSPTimeStampService; import org.apache.poi.poifs.crypt.dsig.services.TimeStampService; import org.apache.poi.poifs.crypt.dsig.services.TimeStampServiceValidator; +import org.apache.poi.util.Internal; import org.apache.poi.util.LocaleUtil; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; +import org.apache.poi.util.Removal; import org.apache.xml.security.signature.XMLSignature; -import org.w3c.dom.events.EventListener; /** * This class bundles the configuration options used for the existing @@ -73,11 +77,16 @@ public class SignatureConfig { private static final POILogger LOG = POILogFactory.getLogger(SignatureConfig.class); private static final String DigestMethod_SHA224 = "http://www.w3.org/2001/04/xmldsig-more#sha224"; private static final String DigestMethod_SHA384 = "http://www.w3.org/2001/04/xmldsig-more#sha384"; + private static final String XMLSEC_SANTUARIO = "org.apache.jcp.xml.dsig.internal.dom.XMLDSigRI"; + private static final String XMLSEC_JDK = "org.jcp.xml.dsig.internal.dom.XMLDSigRI"; + private static final List<Supplier<SignatureFacet>> DEFAULT_FACETS = Arrays.asList( + OOXMLSignatureFacet::new, + KeyInfoSignatureFacet::new, + XAdESSignatureFacet::new, + Office2010SignatureFacet::new + ); - public interface SignatureConfigurable { - void setSignatureConfig(SignatureConfig signatureConfig); - } private ThreadLocal<OPCPackage> opcPackage = new ThreadLocal<>(); private ThreadLocal<XMLSignatureFactory> signatureFactory = new ThreadLocal<>(); @@ -94,7 +103,7 @@ public class SignatureConfig { * the optional signature policy service used for XAdES-EPES. */ private SignaturePolicyService signaturePolicyService; - private URIDereferencer uriDereferencer; + private URIDereferencer uriDereferencer = new OOXMLURIDereferencer(); private String canonicalizationMethod = CanonicalizationMethod.INCLUSIVE; private boolean includeEntireCertificateChain = true; @@ -161,7 +170,7 @@ public class SignatureConfig { * with certain namespaces, so this EventListener is used to interfere * with the marshalling process. */ - private EventListener signatureMarshalListener; + private SignatureMarshalListener signatureMarshalListener = new SignatureMarshalDefaultListener(); /** * Map of namespace uris to prefix @@ -181,61 +190,11 @@ public class SignatureConfig { */ private boolean allowMultipleSignatures = false; - - /** - * Inits and checks the config object. - * If not set previously, complex configuration properties also get - * created/initialized via this initialization call. - * - * @param onlyValidation if true, only a subset of the properties - * is initialized, which are necessary for validation. If false, - * also the other properties needed for signing are been taken care of - */ - protected void init(boolean onlyValidation) { - if (opcPackage == null) { - throw new EncryptedDocumentException("opcPackage is null"); - } - if (uriDereferencer == null) { - uriDereferencer = new OOXMLURIDereferencer(); - } - if (uriDereferencer instanceof SignatureConfigurable) { - ((SignatureConfigurable)uriDereferencer).setSignatureConfig(this); - } - if (namespacePrefixes.isEmpty()) { - /* - * OOo doesn't like ds namespaces so per default prefixing is off. - */ - // namespacePrefixes.put(XML_DIGSIG_NS, ""); - namespacePrefixes.put(OO_DIGSIG_NS, "mdssi"); - namespacePrefixes.put(XADES_132_NS, "xd"); - } - - if (onlyValidation) { - return; - } - - if (signatureMarshalListener == null) { - signatureMarshalListener = new SignatureMarshalListener(); - } - - if (signatureMarshalListener instanceof SignatureConfigurable) { - ((SignatureConfigurable)signatureMarshalListener).setSignatureConfig(this); - } - - if (tspService != null) { - tspService.setSignatureConfig(this); - } - - if (signatureFacets.isEmpty()) { - addSignatureFacet(new OOXMLSignatureFacet()); - addSignatureFacet(new KeyInfoSignatureFacet()); - addSignatureFacet(new XAdESSignatureFacet()); - addSignatureFacet(new Office2010SignatureFacet()); - } - - for (SignatureFacet sf : signatureFacets) { - sf.setSignatureConfig(this); - } + public SignatureConfig() { + // OOo doesn't like ds namespaces so per default prefixing is off. + // namespacePrefixes.put(XML_DIGSIG_NS, ""); + namespacePrefixes.put(OO_DIGSIG_NS, "mdssi"); + namespacePrefixes.put(XADES_132_NS, "xd"); } /** @@ -249,7 +208,11 @@ public class SignatureConfig { * @return the list of facets, may be empty when the config object is not initialized */ public List<SignatureFacet> getSignatureFacets() { - return signatureFacets; + if (signatureFacets.isEmpty()) { + return DEFAULT_FACETS.stream().map(Supplier::get).collect(Collectors.toList()); + } else { + return signatureFacets; + } } /** @@ -275,14 +238,22 @@ public class SignatureConfig { /** * @return the opc package to be used by this thread, stored as thread-local + * + * @deprecated in POI 4.1.3 - use {@link SignatureInfo#setOpcPackage(OPCPackage)} instead */ + @Deprecated + @Removal(version = "5.0.0") public OPCPackage getOpcPackage() { return opcPackage.get(); } /** * @param opcPackage the opc package to be handled by this thread, stored as thread-local + * + * @deprecated in POI 4.1.3 - use {@link SignatureInfo#setOpcPackage(OPCPackage)} instead */ + @Deprecated + @Removal(version = "5.0.0") public void setOpcPackage(OPCPackage opcPackage) { this.opcPackage.set(opcPackage); } @@ -378,14 +349,22 @@ public class SignatureConfig { /** * @return the dereferencer used for Reference/@URI attributes, defaults to {@link OOXMLURIDereferencer} + * + * @deprecated in POI 4.1.3 - use {@link SignatureInfo#getUriDereferencer()} instead */ + @Deprecated + @Removal(version = "5.0.0") public URIDereferencer getUriDereferencer() { return uriDereferencer; } /** * @param uriDereferencer the dereferencer used for Reference/@URI attributes + * + * @deprecated in POI 4.1.3 - use {@link SignatureInfo#setUriDereferencer(URIDereferencer)} instead */ + @Deprecated + @Removal(version = "5.0.0") public void setUriDereferencer(URIDereferencer uriDereferencer) { this.uriDereferencer = uriDereferencer; } @@ -753,11 +732,10 @@ public class SignatureConfig { /** - * @return the event listener which is active while xml structure for - * the signature is created. + * @return the event listener which is active while xml structure for the signature is created. * Defaults to {@link SignatureMarshalListener} */ - public EventListener getSignatureMarshalListener() { + public SignatureMarshalListener getSignatureMarshalListener() { return signatureMarshalListener; } @@ -765,7 +743,7 @@ public class SignatureConfig { * @param signatureMarshalListener the event listener watching the xml structure * generation for the signature */ - public void setSignatureMarshalListener(EventListener signatureMarshalListener) { + public void setSignatureMarshalListener(SignatureMarshalListener signatureMarshalListener) { this.signatureMarshalListener = signatureMarshalListener; } @@ -898,85 +876,91 @@ public class SignatureConfig { /** * @param signatureFactory the xml signature factory, saved as thread-local + * + * @deprecated in POI 4.1.3 - use {@link SignatureInfo#setSignatureFactory(XMLSignatureFactory)} */ + @Deprecated + @Removal(version = "5.0.0") public void setSignatureFactory(XMLSignatureFactory signatureFactory) { this.signatureFactory.set(signatureFactory); } /** * @return the xml signature factory (thread-local) + * + * @deprecated in POI 4.1.3 - will be handled by SignatureInfo internally */ + @Deprecated + @Removal(version = "5.0.0") public XMLSignatureFactory getSignatureFactory() { - XMLSignatureFactory sigFac = signatureFactory.get(); - if (sigFac == null) { - sigFac = XMLSignatureFactory.getInstance("DOM", getProvider()); - setSignatureFactory(sigFac); - } - return sigFac; + return signatureFactory.get(); } /** * @param keyInfoFactory the key factory, saved as thread-local + * + * @deprecated in POI 4.1.3 - use {@link SignatureInfo#setKeyInfoFactory(KeyInfoFactory)} */ + @Deprecated + @Removal(version = "5.0.0") public void setKeyInfoFactory(KeyInfoFactory keyInfoFactory) { this.keyInfoFactory.set(keyInfoFactory); } /** * @return the key factory (thread-local) + * + * @deprecated in POI 4.1.3 - will be handled by SignatureInfo internally */ + @Deprecated + @Removal(version = "5.0.0") public KeyInfoFactory getKeyInfoFactory() { - KeyInfoFactory keyFac = keyInfoFactory.get(); - if (keyFac == null) { - keyFac = KeyInfoFactory.getInstance("DOM", getProvider()); - setKeyInfoFactory(keyFac); - } - return keyFac; + return keyInfoFactory.get(); + } + + /** + * Helper method to set provider + * @param provider the provider + * @deprecated in POI 4.1.3 - use {@link SignatureInfo#setProvider(Provider)} + */ + @Internal + @Deprecated + @Removal(version = "5.0.0") + public void setProvider(Provider provider) { + this.provider.set(provider); + } + + /** + * @return the cached provider or null if not set before + * + * @deprecated in POI 4.1.3 - will be handled by SignatureInfo internally + */ + @Deprecated + @Removal(version = "5.0.0") + public Provider getProvider() { + return provider.get(); } /** - * This method tests the existence of xml signature provider in the following order: - * <ul> + * Determine the possible classes for XMLSEC. + * The order is + * <ol> * <li>the class pointed to by the system property "jsr105Provider"</li> * <li>the Santuario xmlsec provider</li> * <li>the JDK xmlsec provider</li> - * </ul> - * - * For signing the classes are linked against the Santuario xmlsec, so this might - * only work for validation (not tested). + * </ol> * - * @return the xml dsig provider + * @return a list of possible XMLSEC provider class names */ - public Provider getProvider() { - Provider prov = provider.get(); - if (prov == null) { - String[] dsigProviderNames = { - System.getProperty("jsr105Provider"), - // Santuario xmlsec - "org.apache.jcp.xml.dsig.internal.dom.XMLDSigRI", - // JDK xmlsec - "org.jcp.xml.dsig.internal.dom.XMLDSigRI" - }; - for (String pn : dsigProviderNames) { - if (pn == null) { - continue; - } - try { - prov = (Provider)Class.forName(pn).newInstance(); - break; - } catch (Exception e) { - LOG.log(POILogger.DEBUG, "XMLDsig-Provider '"+pn+"' can't be found - trying next."); - } - } - } - - if (prov == null) { - throw new RuntimeException("JRE doesn't support default xml signature provider - set jsr105Provider system property!"); - } - - return prov; + public static String[] getProviderNames() { + // need to check every time, as the system property might have been changed in the meantime + String sysProp = System.getProperty("jsr105Provider"); + return (sysProp == null || "".equals(sysProp)) + ? new String[]{XMLSEC_SANTUARIO, XMLSEC_JDK} + : new String[]{sysProp, XMLSEC_SANTUARIO, XMLSEC_JDK}; } + /** * @return the cannonicalization method for XAdES-XL signing. * Defaults to <code>EXCLUSIVE</code> 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 c4f25f1222..806ab91d65 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 @@ -31,6 +31,7 @@ import java.io.IOException; import java.io.OutputStream; import java.security.GeneralSecurityException; import java.security.PrivateKey; +import java.security.Provider; import java.util.ArrayList; import java.util.Base64; import java.util.HashMap; @@ -38,6 +39,8 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.stream.Stream; import javax.xml.crypto.MarshalException; import javax.xml.crypto.URIDereferencer; @@ -52,6 +55,7 @@ import javax.xml.crypto.dsig.XMLObject; import javax.xml.crypto.dsig.XMLSignatureException; import javax.xml.crypto.dsig.XMLSignatureFactory; import javax.xml.crypto.dsig.dom.DOMSignContext; +import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory; import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; import org.apache.jcp.xml.dsig.internal.dom.DOMReference; @@ -70,7 +74,6 @@ import org.apache.poi.openxml4j.opc.PackagingURIHelper; import org.apache.poi.openxml4j.opc.TargetMode; import org.apache.poi.poifs.crypt.CryptoFunctions; import org.apache.poi.poifs.crypt.HashAlgorithm; -import org.apache.poi.poifs.crypt.dsig.SignatureConfig.SignatureConfigurable; import org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet; import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService; import org.apache.poi.util.POILogFactory; @@ -84,6 +87,7 @@ import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.w3c.dom.events.EventListener; import org.w3c.dom.events.EventTarget; +import org.w3c.dom.events.MutationEvent; /** @@ -153,20 +157,16 @@ import org.w3c.dom.events.EventTarget; * <li>and slf4j-api (tested against 1.7.30)</li> * </ul> */ -public class SignatureInfo implements SignatureConfigurable { +public class SignatureInfo { private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class); - private static boolean isInitialized; private SignatureConfig signatureConfig; - - - /** - * Constructor initializes xml signature environment, if it hasn't been initialized before - */ - public SignatureInfo() { - initXmlProvider(); - } + private OPCPackage opcPackage; + private Provider provider; + private XMLSignatureFactory signatureFactory; + private KeyInfoFactory keyInfoFactory; + private URIDereferencer uriDereferencer; /** * @return the signature config @@ -178,30 +178,45 @@ public class SignatureInfo implements SignatureConfigurable { /** * @param signatureConfig the signature config, needs to be set before a SignatureInfo object is used */ - @Override public void setSignatureConfig(SignatureConfig signatureConfig) { this.signatureConfig = signatureConfig; } + public void setOpcPackage(OPCPackage opcPackage) { + this.opcPackage = opcPackage; + } + + public OPCPackage getOpcPackage() { + return opcPackage; + } + + public URIDereferencer getUriDereferencer() { + return uriDereferencer; + } + + public void setUriDereferencer(URIDereferencer uriDereferencer) { + this.uriDereferencer = uriDereferencer; + } + /** * @return true, if first signature part is valid */ public boolean verifySignature() { + initXmlProvider(); // http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html - for (SignaturePart sp : getSignatureParts()){ - // only validate first part - return sp.validate(); - } - return false; + // only validate first part + Iterator<SignaturePart> iter = getSignatureParts().iterator(); + return iter.hasNext() && iter.next().validate(); } /** * add the xml signature to the document * - * @throws XMLSignatureException - * @throws MarshalException + * @throws XMLSignatureException if the signature can't be calculated + * @throws MarshalException if the document can't be serialized */ public void confirmSignature() throws XMLSignatureException, MarshalException { + initXmlProvider(); final Document document = DocumentHelper.createDocument(); final DOMSignContext xmlSignContext = createXMLSignContext(document); @@ -223,6 +238,7 @@ public class SignatureInfo implements SignatureConfigurable { * @return the initialized signature context */ public DOMSignContext createXMLSignContext(final Document document) { + initXmlProvider(); return new DOMSignContext(signatureConfig.getKey(), document); } @@ -231,10 +247,10 @@ public class SignatureInfo implements SignatureConfigurable { * Sign (encrypt) the digest with the private key. * Currently only rsa is supported. * - * @param digest the hashed input * @return the encrypted hash */ public String signDigest(final DOMSignContext xmlSignContext, final DOMSignedInfo signedInfo) { + initXmlProvider(); final PrivateKey key = signatureConfig.getKey(); final HashAlgorithm algo = signatureConfig.getDigestAlgo(); @@ -243,12 +259,12 @@ public class SignatureInfo implements SignatureConfigurable { if (algo.hashSize*4/3 > BASE64DEFAULTLENGTH && !XMLUtils.ignoreLineBreaks()) { - throw new EncryptedDocumentException("The hash size of the choosen hash algorithm ("+algo+" = "+algo.hashSize+" bytes), "+ + throw new EncryptedDocumentException("The hash size of the chosen hash algorithm ("+algo+" = "+algo.hashSize+" bytes), "+ "will motivate XmlSec to add linebreaks to the generated digest, which results in an invalid signature (... at least "+ "for Office) - please persuade it otherwise by adding '-Dorg.apache.xml.security.ignoreLineBreaks=true' to the JVM "+ "system properties."); } - + try (final DigestOutputStream dos = getDigestStream(algo, key)) { dos.init(); @@ -277,78 +293,61 @@ public class SignatureInfo implements SignatureConfigurable { * the parts can be validated independently. */ public Iterable<SignaturePart> getSignatureParts() { - signatureConfig.init(true); - return new Iterable<SignaturePart>() { - @Override - public Iterator<SignaturePart> iterator() { - return new Iterator<SignaturePart>() { - OPCPackage pkg = signatureConfig.getOpcPackage(); - Iterator<PackageRelationship> sigOrigRels = - pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN).iterator(); - Iterator<PackageRelationship> sigRels; - PackagePart sigPart; - - @Override - public boolean hasNext() { - while (sigRels == null || !sigRels.hasNext()) { - if (!sigOrigRels.hasNext()) { - return false; - } - sigPart = pkg.getPart(sigOrigRels.next()); - LOG.log(POILogger.DEBUG, "Digital Signature Origin part", sigPart); - try { - sigRels = sigPart.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE).iterator(); - } catch (InvalidFormatException e) { - LOG.log(POILogger.WARN, "Reference to signature is invalid.", e); - } - } - return true; - } + initXmlProvider(); + return SignaturePartIterator::new; + } - @Override - public SignaturePart next() { - PackagePart sigRelPart = null; - do { - try { - if (!hasNext()) { - throw new NoSuchElementException(); - } - sigRelPart = sigPart.getRelatedPart(sigRels.next()); - LOG.log(POILogger.DEBUG, "XML Signature part", sigRelPart); - } catch (InvalidFormatException e) { - LOG.log(POILogger.WARN, "Reference to signature is invalid.", e); - } - } while (sigRelPart == null); - return new SignaturePart(sigRelPart, signatureConfig); - } + private final class SignaturePartIterator implements Iterator<SignaturePart> { + Iterator<PackageRelationship> sigOrigRels; + private Iterator<PackageRelationship> sigRels; + private PackagePart sigPart; - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; + private SignaturePartIterator() { + sigOrigRels = opcPackage.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN).iterator(); + } + + @Override + public boolean hasNext() { + while (sigRels == null || !sigRels.hasNext()) { + if (!sigOrigRels.hasNext()) { + return false; + } + sigPart = opcPackage.getPart(sigOrigRels.next()); + LOG.log(POILogger.DEBUG, "Digital Signature Origin part", sigPart); + try { + sigRels = sigPart.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE).iterator(); + } catch (InvalidFormatException e) { + LOG.log(POILogger.WARN, "Reference to signature is invalid.", e); + } } - }; - } + return true; + } - /** - * Initialize the xml signing environment and the bouncycastle provider - */ - protected static synchronized void initXmlProvider() { - if (isInitialized) { - return; + @Override + public SignaturePart next() { + PackagePart sigRelPart = null; + do { + try { + if (!hasNext()) { + throw new NoSuchElementException(); + } + sigRelPart = sigPart.getRelatedPart(sigRels.next()); + LOG.log(POILogger.DEBUG, "XML Signature part", sigRelPart); + } catch (InvalidFormatException e) { + LOG.log(POILogger.WARN, "Reference to signature is invalid.", e); + } + } while (sigRelPart == null); + return new SignaturePart(sigRelPart, SignatureInfo.this); } - isInitialized = true; - try { - Init.init(); - RelationshipTransformService.registerDsigProvider(); - CryptoFunctions.registerBouncyCastle(); - } catch (Exception e) { - throw new RuntimeException("Xml & BouncyCastle-Provider initialization failed", e); + @Override + public void remove() { + throw new UnsupportedOperationException(); } } + + /** * Helper method for adding informations before the signing. * Normally {@link #confirmSignature()} is sufficient to be used. @@ -356,35 +355,18 @@ public class SignatureInfo implements SignatureConfigurable { @SuppressWarnings("unchecked") public DOMSignedInfo preSign(final DOMSignContext xmlSignContext) throws XMLSignatureException, MarshalException { - signatureConfig.init(false); - final Document document = (Document)xmlSignContext.getParent(); - // 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); - } + registerEventListener(document); - /* - * Signature context construction. - */ - URIDereferencer uriDereferencer = signatureConfig.getUriDereferencer(); - if (null != uriDereferencer) { + // Signature context construction. + if (uriDereferencer != null) { xmlSignContext.setURIDereferencer(uriDereferencer); } signatureConfig.getNamespacePrefixes().forEach(xmlSignContext::putNamespacePrefix); xmlSignContext.setDefaultNamespacePrefix(""); - // signatureConfig.getNamespacePrefixes().get(XML_DIGSIG_NS)); - - XMLSignatureFactory signatureFactory = signatureConfig.getSignatureFactory(); /* * Add ds:References that come from signing client local files. @@ -397,7 +379,7 @@ public class SignatureInfo implements SignatureConfigurable { List<XMLObject> objects = new ArrayList<>(); for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) { LOG.log(POILogger.DEBUG, "invoking signature facet: " + signatureFacet.getClass().getSimpleName()); - signatureFacet.preSign(document, references, objects); + signatureFacet.preSign(this, document, references, objects); } /* @@ -471,6 +453,35 @@ public class SignatureInfo implements SignatureConfigurable { return (DOMSignedInfo)signedInfo; } + // 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 ... + protected void registerEventListener(Document document) { + final SignatureMarshalListener sml = signatureConfig.getSignatureMarshalListener(); + if (sml == null) { + return; + } + + EventTarget target = (EventTarget)document; + + final EventListener[] el = { null }; + el[0] = (e) -> { + if (!(e instanceof MutationEvent)) { + return; + } + + MutationEvent mutEvt = (MutationEvent) e; + EventTarget et = mutEvt.getTarget(); + if (!(et instanceof Element)) { + return; + } + + sml.handleElement(this, (Element) et, target, el[0]); + }; + + SignatureMarshalListener.setListener(target, el[0], true); + } + + /** * Helper method for adding informations after the signing. * Normally {@link #confirmSignature()} is sufficient to be used. @@ -492,7 +503,7 @@ public class SignatureInfo implements SignatureConfigurable { /* * Insert signature value into the ds:SignatureValue element */ - final Element signatureNode = getDsigElement(document, "SignatureValue"); + final Element signatureNode = getDsigElement(document, "SignatureValue"); if (signatureNode == null) { throw new RuntimeException("preSign has to be called before postSign"); } @@ -502,7 +513,7 @@ public class SignatureInfo implements SignatureConfigurable { * Allow signature facets to inject their own stuff. */ for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) { - signatureFacet.postSign(document); + signatureFacet.postSign(this, document); } writeDocument(document); @@ -512,7 +523,7 @@ public class SignatureInfo implements SignatureConfigurable { * Write XML signature into the OPC package * * @param document the xml signature document - * @throws MarshalException + * @throws MarshalException if the document can't be serialized */ protected void writeDocument(Document document) throws MarshalException { XmlOptions xo = new XmlOptions(); @@ -527,23 +538,21 @@ public class SignatureInfo implements SignatureConfigurable { * Copy the original OOXML content to the signed OOXML package. During * copying some files need to changed. */ - OPCPackage pkg = signatureConfig.getOpcPackage(); - try { // <Default Extension="sigs" ContentType="application/vnd.openxmlformats-package.digital-signature-origin"/> final DSigRelation originDesc = DSigRelation.ORIGIN_SIGS; PackagePartName originPartName = PackagingURIHelper.createPartName(originDesc.getFileName(0)); - PackagePart originPart = pkg.getPart(originPartName); + PackagePart originPart = opcPackage.getPart(originPartName); if (originPart == null) { // touch empty marker file - originPart = pkg.createPart(originPartName, originDesc.getContentType()); - pkg.addRelationship(originPartName, TargetMode.INTERNAL, originDesc.getRelation()); + originPart = opcPackage.createPart(originPartName, originDesc.getContentType()); + opcPackage.addRelationship(originPartName, TargetMode.INTERNAL, originDesc.getRelation()); } // <Override PartName="/_xmlsignatures/sig1.xml" ContentType="application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"/> final DSigRelation sigDesc = DSigRelation.SIG; - int nextSigIdx = pkg.getUnusedPartIndex(sigDesc.getDefaultFileName()); + int nextSigIdx = opcPackage.getUnusedPartIndex(sigDesc.getDefaultFileName()); if (!signatureConfig.isAllowMultipleSignatures()) { PackageRelationshipCollection prc = originPart.getRelationshipsByType(sigDesc.getRelation()); @@ -558,16 +567,16 @@ public class SignatureInfo implements SignatureConfigurable { } } - pkg.removePart(pkg.getPart(pn)); + opcPackage.removePart(opcPackage.getPart(pn)); } nextSigIdx = 1; } PackagePartName sigPartName = PackagingURIHelper.createPartName(sigDesc.getFileName(nextSigIdx)); - PackagePart sigPart = pkg.getPart(sigPartName); + PackagePart sigPart = opcPackage.getPart(sigPartName); if (sigPart == null) { - sigPart = pkg.createPart(sigPartName, sigDesc.getContentType()); + sigPart = opcPackage.createPart(sigPartName, sigDesc.getContentType()); originPart.addRelationship(sigPartName, TargetMode.INTERNAL, sigDesc.getRelation()); } else { sigPart.clear(); @@ -590,7 +599,123 @@ public class SignatureInfo implements SignatureConfigurable { } LOG.log(POILogger.WARN, "Signature element '"+localName+"' was "+(sigValNl.getLength() == 0 ? "not found" : "multiple times")); - + return null; } + + public void setProvider(Provider provider) { + this.provider = provider; + } + + public void setSignatureFactory(XMLSignatureFactory signatureFactory) { + this.signatureFactory = signatureFactory; + } + + public XMLSignatureFactory getSignatureFactory() { + return signatureFactory; + } + + public void setKeyInfoFactory(KeyInfoFactory keyInfoFactory) { + this.keyInfoFactory = keyInfoFactory; + } + + public KeyInfoFactory getKeyInfoFactory() { + return keyInfoFactory; + } + + + + /** + * Initialize the xml signing environment and the bouncycastle provider + */ + @SuppressWarnings("deprecation") + protected void initXmlProvider() { + if (opcPackage == null) { + opcPackage = signatureConfig.getOpcPackage(); + } + if (provider == null) { + provider = signatureConfig.getProvider(); + if (provider == null) { + provider = XmlProviderInitSingleton.getInstance().findProvider(); + } + } + if (signatureFactory == null) { + signatureFactory = signatureConfig.getSignatureFactory(); + if (signatureFactory == null) { + signatureFactory = XMLSignatureFactory.getInstance("DOM", provider); + } + } + if (keyInfoFactory == null) { + keyInfoFactory = signatureConfig.getKeyInfoFactory(); + if (keyInfoFactory == null) { + keyInfoFactory = KeyInfoFactory.getInstance("DOM", provider); + } + } + if (uriDereferencer == null) { + uriDereferencer = signatureConfig.getUriDereferencer(); + if (uriDereferencer == null) { + uriDereferencer = new OOXMLURIDereferencer(); + } + } + if (uriDereferencer instanceof OOXMLURIDereferencer) { + ((OOXMLURIDereferencer)uriDereferencer).setSignatureInfo(this); + } + } + + private static final class XmlProviderInitSingleton { + + // Bill Pugh Singleton + private static class SingletonHelper { + private static final XmlProviderInitSingleton INSTANCE = new XmlProviderInitSingleton(); + } + + public static XmlProviderInitSingleton getInstance(){ + return SingletonHelper.INSTANCE; + } + + private XmlProviderInitSingleton() { + try { + Init.init(); + RelationshipTransformService.registerDsigProvider(); + CryptoFunctions.registerBouncyCastle(); + } catch (Exception e) { + throw new RuntimeException("Xml & BouncyCastle-Provider initialization failed", e); + } + } + + /** + * This method tests the existence of xml signature provider in the following order: + * <ul> + * <li>the class pointed to by the system property "jsr105Provider"</li> + * <li>the Santuario xmlsec provider</li> + * <li>the JDK xmlsec provider</li> + * </ul> + * + * For signing the classes are linked against the Santuario xmlsec, so this might + * only work for validation (not tested). + * + * @return the xml dsig provider + */ + public Provider findProvider() { + return + Stream.of(SignatureConfig.getProviderNames()) + .map(this::getProvider) + .filter(Objects::nonNull).findFirst() + .orElseThrow(this::providerNotFound); + } + + private Provider getProvider(String className) { + try { + return (Provider)Class.forName(className).newInstance(); + } catch (Exception e) { + LOG.log(POILogger.DEBUG, "XMLDsig-Provider '"+className+"' can't be found - trying next."); + return null; + } + } + + private RuntimeException providerNotFound() { + return new RuntimeException("JRE doesn't support default xml signature provider - set jsr105Provider system property!"); + } + } + } diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalDefaultListener.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalDefaultListener.java new file mode 100644 index 0000000000..24acda22ed --- /dev/null +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalDefaultListener.java @@ -0,0 +1,63 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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.apache.poi.poifs.crypt.dsig; + +import static org.apache.poi.poifs.crypt.dsig.SignatureMarshalListener.setListener; +import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.OO_DIGSIG_NS; +import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.XML_NS; + +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.events.EventListener; +import org.w3c.dom.events.EventTarget; + +/** + * This listener class is used, to modify the to be digested xml document, + * e.g. to register id attributes or set prefixes for registered namespaces + */ +public class SignatureMarshalDefaultListener implements SignatureMarshalListener { + @Override + public void handleElement(SignatureInfo signatureInfo, Element el, EventTarget target, EventListener parentListener) { + if (el.hasAttribute("Id")) { + el.setIdAttribute("Id", true); + } + + setListener(target, parentListener, false); + if (OO_DIGSIG_NS.equals(el.getNamespaceURI())) { + String parentNS = el.getParentNode().getNamespaceURI(); + if (!OO_DIGSIG_NS.equals(parentNS) && !el.hasAttributeNS(XML_NS, "mdssi")) { + el.setAttributeNS(XML_NS, "xmlns:mdssi", OO_DIGSIG_NS); + } + } + setPrefix(signatureInfo, el); + setListener(target, parentListener, true); + } + + protected static void setPrefix(SignatureInfo signatureInfo, Node el) { + String prefix = signatureInfo.getSignatureConfig().getNamespacePrefixes().get(el.getNamespaceURI()); + if (prefix != null && el.getPrefix() == null) { + el.setPrefix(prefix); + } + + NodeList nl = el.getChildNodes(); + for (int i=0; i<nl.getLength(); i++) { + setPrefix(signatureInfo, nl.item(i)); + } + } +} diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java index 017953f570..34210f4cf5 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureMarshalListener.java @@ -17,85 +17,25 @@ package org.apache.poi.poifs.crypt.dsig; -import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.OO_DIGSIG_NS; -import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.XML_NS; - -import org.apache.poi.poifs.crypt.dsig.SignatureConfig.SignatureConfigurable; import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.w3c.dom.events.Event; import org.w3c.dom.events.EventListener; import org.w3c.dom.events.EventTarget; -import org.w3c.dom.events.MutationEvent; /** - * This listener class is used, to modify the to be digested xml document, + * This listener interface is used, to modify the to be digested xml document, * e.g. to register id attributes or set prefixes for registered namespaces */ -public class SignatureMarshalListener implements EventListener, SignatureConfigurable { - ThreadLocal<EventTarget> target = new ThreadLocal<>(); - SignatureConfig signatureConfig; - public void setEventTarget(EventTarget target) { - this.target.set(target); - } - - @Override - public void handleEvent(Event e) { - if (!(e instanceof MutationEvent)) { - return; - } - MutationEvent mutEvt = (MutationEvent)e; - EventTarget et = mutEvt.getTarget(); - if (!(et instanceof Element)) { - return; - } - handleElement((Element)et); - } - - public void handleElement(Element el) { - EventTarget target = this.target.get(); - - if (el.hasAttribute("Id")) { - el.setIdAttribute("Id", true); - } - - setListener(target, this, false); - if (OO_DIGSIG_NS.equals(el.getNamespaceURI())) { - String parentNS = el.getParentNode().getNamespaceURI(); - if (!OO_DIGSIG_NS.equals(parentNS) && !el.hasAttributeNS(XML_NS, "mdssi")) { - el.setAttributeNS(XML_NS, "xmlns:mdssi", OO_DIGSIG_NS); - } - } - setPrefix(el); - setListener(target, this, true); - } +public interface SignatureMarshalListener { + void handleElement(SignatureInfo signatureInfo, Element el, EventTarget target, EventListener parentListener); // helper method to keep it in one place - public static void setListener(EventTarget target, EventListener listener, boolean enabled) { - String type = "DOMSubtreeModified"; - boolean useCapture = false; + static void setListener(EventTarget target, EventListener listener, boolean enabled) { + final String type = "DOMSubtreeModified"; + final boolean DONT_USE_CAPTURE = false; if (enabled) { - target.addEventListener(type, listener, useCapture); + target.addEventListener(type, listener, DONT_USE_CAPTURE); } else { - target.removeEventListener(type, listener, useCapture); + target.removeEventListener(type, listener, DONT_USE_CAPTURE); } } - - protected void setPrefix(Node el) { - String prefix = signatureConfig.getNamespacePrefixes().get(el.getNamespaceURI()); - if (prefix != null && el.getPrefix() == null) { - el.setPrefix(prefix); - } - - NodeList nl = el.getChildNodes(); - for (int i=0; i<nl.getLength(); i++) { - setPrefix(nl.item(i)); - } - } - - @Override - public void setSignatureConfig(SignatureConfig signatureConfig) { - this.signatureConfig = signatureConfig; - } }
\ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java index dab69bd250..d120d655e1 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignaturePart.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.function.Consumer; import javax.xml.crypto.MarshalException; +import javax.xml.crypto.URIDereferencer; import javax.xml.crypto.dsig.XMLSignature; import javax.xml.crypto.dsig.XMLSignatureException; import javax.xml.crypto.dsig.XMLSignatureFactory; @@ -54,40 +55,40 @@ import org.xml.sax.SAXException; public class SignaturePart { private static final POILogger LOG = POILogFactory.getLogger(SignaturePart.class); - private static final String XMLSEC_VALIDATE_MANIFEST = "org.jcp.xml.dsig.validateManifests"; + private static final String XMLSEC_VALIDATE_MANIFEST = "org.jcp.xml.dsig.validateManifests"; + - private final PackagePart signaturePart; - private final SignatureConfig signatureConfig; + private final SignatureInfo signatureInfo; private X509Certificate signer; private List<X509Certificate> certChain; - - /* package */ SignaturePart(final PackagePart signaturePart, final SignatureConfig signatureConfig) { + + /* package */ SignaturePart(final PackagePart signaturePart, final SignatureInfo signatureInfo) { this.signaturePart = signaturePart; - this.signatureConfig = signatureConfig; + this.signatureInfo = signatureInfo; } - + /** * @return the package part containing the signature */ public PackagePart getPackagePart() { return signaturePart; } - + /** * @return the signer certificate */ public X509Certificate getSigner() { return signer; } - + /** * @return the certificate chain of the signer */ public List<X509Certificate> getCertChain() { return certChain; } - + /** * Helper method for examining the xml signature * @@ -102,7 +103,7 @@ public class SignaturePart { /** * @return true, when the xml signature is valid, false otherwise - * + * * @throws EncryptedDocumentException if the signature can't be extracted or if its malformed */ public boolean validate() { @@ -117,14 +118,16 @@ public class SignaturePart { for (int i=0; i<length; i++) { ((Element)nl.item(i)).setIdAttribute("Id", true); } - + DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc); domValidateContext.setProperty(XMLSEC_VALIDATE_MANIFEST, Boolean.TRUE); - domValidateContext.setURIDereferencer(signatureConfig.getUriDereferencer()); - XMLSignatureFactory xmlSignatureFactory = signatureConfig.getSignatureFactory(); + URIDereferencer uriDereferencer = signatureInfo.getUriDereferencer(); + domValidateContext.setURIDereferencer(uriDereferencer); + + XMLSignatureFactory xmlSignatureFactory = signatureInfo.getSignatureFactory(); XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext); - + boolean valid = xmlSignature.validate(domValidateContext); if (valid) { @@ -132,7 +135,7 @@ public class SignaturePart { certChain = keySelector.getCertChain(); extractConfig(doc, xmlSignature); } - + return valid; } catch (IOException e) { String s = "error in reading document"; @@ -158,6 +161,7 @@ public class SignaturePart { } private void extractConfig(final Document doc, final XMLSignature xmlSignature) throws XPathExpressionException { + SignatureConfig signatureConfig = signatureInfo.getSignatureConfig(); if (!signatureConfig.isUpdateConfigOnValidate()) { return; } @@ -168,7 +172,7 @@ public class SignaturePart { final XPath xpath = XPathHelper.getFactory().newXPath(); xpath.setNamespaceContext(new XPathNSContext()); - final Map<String,Consumer<String>> m = new HashMap(); + final Map<String,Consumer<String>> m = new HashMap<>(); m.put("//mdssi:SignatureTime/mdssi:Value", signatureConfig::setExecutionTime); m.put("//xd:ClaimedRole", signatureConfig::setXadesRole); m.put("//dsss:SignatureComments", signatureConfig::setSignatureDescription); @@ -185,7 +189,7 @@ public class SignaturePart { final Map<String,String> nsMap = new HashMap<>(); { - signatureConfig.getNamespacePrefixes().forEach((k,v) -> nsMap.put(v,k)); + signatureInfo.getSignatureConfig().getNamespacePrefixes().forEach((k,v) -> nsMap.put(v,k)); nsMap.put("dsss", MS_DIGSIG_NS); nsMap.put("ds", XML_DIGSIG_NS); } @@ -193,6 +197,8 @@ public class SignaturePart { public String getNamespaceURI(String prefix) { return nsMap.get(prefix); } + @SuppressWarnings("rawtypes") + @Override public Iterator getPrefixes(String val) { return null; } diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/EnvelopedSignatureFacet.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/EnvelopedSignatureFacet.java index 12ba42c14c..d37cc2ce77 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/EnvelopedSignatureFacet.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/EnvelopedSignatureFacet.java @@ -18,12 +18,15 @@ /* ==================================================================== This product contains an ASLv2 licensed version of the OOXML signer package from the eID Applet project - http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt Copyright (C) 2008-2014 FedICT. - ================================================================= */ + ================================================================= */ package org.apache.poi.poifs.crypt.dsig.facets; +import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacetHelper.newReference; +import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacetHelper.newTransform; + import java.util.ArrayList; import java.util.List; @@ -33,28 +36,29 @@ import javax.xml.crypto.dsig.Transform; import javax.xml.crypto.dsig.XMLObject; import javax.xml.crypto.dsig.XMLSignatureException; +import org.apache.poi.poifs.crypt.dsig.SignatureInfo; import org.w3c.dom.Document; /** * Signature Facet implementation to create enveloped signatures. - * + * * @author Frank Cornelis - * */ -public class EnvelopedSignatureFacet extends SignatureFacet { +public class EnvelopedSignatureFacet implements SignatureFacet { @Override - public void preSign(Document document + public void preSign(SignatureInfo signatureInfo + , Document document , List<Reference> references , List<XMLObject> objects) throws XMLSignatureException { List<Transform> transforms = new ArrayList<>(); - Transform envelopedTransform = newTransform(CanonicalizationMethod.ENVELOPED); + Transform envelopedTransform = newTransform(signatureInfo, CanonicalizationMethod.ENVELOPED); transforms.add(envelopedTransform); - Transform exclusiveTransform = newTransform(CanonicalizationMethod.EXCLUSIVE); + Transform exclusiveTransform = newTransform(signatureInfo, CanonicalizationMethod.EXCLUSIVE); transforms.add(exclusiveTransform); - Reference reference = newReference("", transforms, null, null, null); + Reference reference = newReference(signatureInfo, "", transforms, null, null, null); references.add(reference); } } diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/KeyInfoSignatureFacet.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/KeyInfoSignatureFacet.java index e7797e942d..0943a8a596 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/KeyInfoSignatureFacet.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/KeyInfoSignatureFacet.java @@ -18,9 +18,9 @@ /* ==================================================================== This product contains an ASLv2 licensed version of the OOXML signer package from the eID Applet project - http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt Copyright (C) 2008-2014 FedICT. - ================================================================= */ + ================================================================= */ package org.apache.poi.poifs.crypt.dsig.facets; @@ -29,7 +29,6 @@ import java.security.KeyException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; -import java.util.Map; import javax.xml.crypto.MarshalException; import javax.xml.crypto.XMLStructure; @@ -41,6 +40,8 @@ import javax.xml.crypto.dsig.keyinfo.KeyValue; import javax.xml.crypto.dsig.keyinfo.X509Data; import org.apache.jcp.xml.dsig.internal.dom.DOMKeyInfo; +import org.apache.poi.poifs.crypt.dsig.SignatureConfig; +import org.apache.poi.poifs.crypt.dsig.SignatureInfo; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; import org.w3c.dom.Document; @@ -50,21 +51,20 @@ import org.w3c.dom.NodeList; /** * Signature Facet implementation that adds ds:KeyInfo to the XML signature. - * + * * @author Frank Cornelis - * */ -public class KeyInfoSignatureFacet extends SignatureFacet { +public class KeyInfoSignatureFacet implements SignatureFacet { private static final POILogger LOG = POILogFactory.getLogger(KeyInfoSignatureFacet.class); - + @Override - public void postSign(Document document) + public void postSign(SignatureInfo signatureInfo, Document document) throws MarshalException { LOG.log(POILogger.DEBUG, "postSign"); NodeList nl = document.getElementsByTagNameNS(XML_DIGSIG_NS, "Object"); - + /* * Make sure we insert right after the ds:SignatureValue element, just * before the first ds:Object element. @@ -74,8 +74,9 @@ public class KeyInfoSignatureFacet extends SignatureFacet { /* * Construct the ds:KeyInfo element using JSR 105. */ - KeyInfoFactory keyInfoFactory = signatureConfig.getKeyInfoFactory(); + KeyInfoFactory keyInfoFactory = signatureInfo.getKeyInfoFactory(); List<Object> x509DataObjects = new ArrayList<>(); + SignatureConfig signatureConfig = signatureInfo.getSignatureConfig(); X509Certificate signingCertificate = signatureConfig.getSigningCertificateChain().get(0); List<XMLStructure> keyInfoContent = new ArrayList<>(); @@ -107,7 +108,7 @@ public class KeyInfoSignatureFacet extends SignatureFacet { keyInfoContent.add(x509Data); } KeyInfo keyInfo = keyInfoFactory.newKeyInfo(keyInfoContent); - DOMKeyInfo domKeyInfo = (DOMKeyInfo)keyInfo; + DOMKeyInfo domKeyInfo = (DOMKeyInfo)keyInfo; Key key = new Key() { private static final long serialVersionUID = 1L; @@ -133,7 +134,7 @@ public class KeyInfoSignatureFacet extends SignatureFacet { DOMStructure domStructure = new DOMStructure(n); domKeyInfo.marshal(domStructure, domSignContext); - + // move keyinfo into the right place if (nextSibling != null) { NodeList kiNl = document.getElementsByTagNameNS(XML_DIGSIG_NS, "KeyInfo"); diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java index 5d4ccbd869..0eff7e3186 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java @@ -24,20 +24,21 @@ package org.apache.poi.poifs.crypt.dsig.facets; +import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacetHelper.newReference; +import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacetHelper.newTransform; + import java.net.URI; import java.net.URISyntaxException; -import java.text.DateFormat; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Set; import javax.xml.XMLConstants; +import javax.xml.crypto.URIReference; import javax.xml.crypto.XMLStructure; import javax.xml.crypto.dom.DOMStructure; import javax.xml.crypto.dsig.CanonicalizationMethod; @@ -48,7 +49,10 @@ import javax.xml.crypto.dsig.SignatureProperty; import javax.xml.crypto.dsig.Transform; import javax.xml.crypto.dsig.XMLObject; import javax.xml.crypto.dsig.XMLSignatureException; +import javax.xml.crypto.dsig.XMLSignatureFactory; +import com.microsoft.schemas.office.x2006.digsig.CTSignatureInfoV1; +import com.microsoft.schemas.office.x2006.digsig.SignatureInfoV1Document; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.openxml4j.opc.ContentTypes; import org.apache.poi.openxml4j.opc.OPCPackage; @@ -58,9 +62,10 @@ import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; import org.apache.poi.openxml4j.opc.PackagingURIHelper; import org.apache.poi.openxml4j.opc.TargetMode; +import org.apache.poi.poifs.crypt.dsig.SignatureConfig; +import org.apache.poi.poifs.crypt.dsig.SignatureInfo; import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService; import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService.RelationshipTransformParameterSpec; -import org.apache.poi.util.LocaleUtil; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; import org.openxmlformats.schemas.xpackage.x2006.digitalSignature.CTSignatureTime; @@ -68,58 +73,58 @@ import org.openxmlformats.schemas.xpackage.x2006.digitalSignature.SignatureTimeD import org.w3c.dom.Document; import org.w3c.dom.Element; -import com.microsoft.schemas.office.x2006.digsig.CTSignatureInfoV1; -import com.microsoft.schemas.office.x2006.digsig.SignatureInfoV1Document; - /** * Office OpenXML Signature Facet implementation. * * @see <a href="http://msdn.microsoft.com/en-us/library/cc313071.aspx">[MS-OFFCRYPTO]: Office Document Cryptography Structure</a> */ -public class OOXMLSignatureFacet extends SignatureFacet { +public class OOXMLSignatureFacet implements SignatureFacet { private static final POILogger LOG = POILogFactory.getLogger(OOXMLSignatureFacet.class); private static final String ID_PACKAGE_OBJECT = "idPackageObject"; @Override public void preSign( - Document document + SignatureInfo signatureInfo + , Document document , List<Reference> references , List<XMLObject> objects) throws XMLSignatureException { LOG.log(POILogger.DEBUG, "pre sign"); - addManifestObject(document, references, objects); - addSignatureInfo(document, references, objects); + addManifestObject(signatureInfo, document, references, objects); + addSignatureInfo(signatureInfo, document, references, objects); } protected void addManifestObject( - Document document + SignatureInfo signatureInfo + , Document document , List<Reference> references , List<XMLObject> objects) throws XMLSignatureException { + final XMLSignatureFactory sigFac = signatureInfo.getSignatureFactory(); + List<Reference> manifestReferences = new ArrayList<>(); - addManifestReferences(manifestReferences); - Manifest manifest = getSignatureFactory().newManifest(manifestReferences); + addManifestReferences(signatureInfo, manifestReferences); + Manifest manifest = sigFac.newManifest(manifestReferences); List<XMLStructure> objectContent = new ArrayList<>(); objectContent.add(manifest); - addSignatureTime(document, objectContent); + addSignatureTime(signatureInfo, document, objectContent); - XMLObject xo = getSignatureFactory().newXMLObject(objectContent, ID_PACKAGE_OBJECT, null, null); + XMLObject xo = sigFac.newXMLObject(objectContent, ID_PACKAGE_OBJECT, null, null); objects.add(xo); - Reference reference = newReference("#"+ID_PACKAGE_OBJECT, null, XML_DIGSIG_NS+"Object", null, null); + Reference reference = newReference(signatureInfo, "#"+ID_PACKAGE_OBJECT, null, XML_DIGSIG_NS+"Object", null, null); references.add(reference); } @SuppressWarnings("resource") - protected void addManifestReferences(List<Reference> manifestReferences) + protected void addManifestReferences(SignatureInfo signatureInfo, List<Reference> manifestReferences) throws XMLSignatureException { - - OPCPackage ooxml = signatureConfig.getOpcPackage(); - List<PackagePart> relsEntryNames = ooxml.getPartsByContentType(ContentTypes.RELATIONSHIPS_PART); + OPCPackage opcPackage = signatureInfo.getOpcPackage(); + List<PackagePart> relsEntryNames = opcPackage.getPartsByContentType(ContentTypes.RELATIONSHIPS_PART); Set<String> digestedPartNames = new HashSet<>(); for (PackagePart pp : relsEntryNames) { @@ -127,7 +132,7 @@ public class OOXMLSignatureFacet extends SignatureFacet { PackageRelationshipCollection prc; try { - prc = new PackageRelationshipCollection(ooxml); + prc = new PackageRelationshipCollection(opcPackage); prc.parseRelationshipsPart(pp); } catch (InvalidFormatException e) { throw new XMLSignatureException("Invalid relationship descriptor: "+pp.getPartName().getName(), e); @@ -163,7 +168,7 @@ public class OOXMLSignatureFacet extends SignatureFacet { String contentType; try { PackagePartName relName = PackagingURIHelper.createPartName(partName); - PackagePart pp2 = ooxml.getPart(relName); + PackagePart pp2 = opcPackage.getPart(relName); contentType = pp2.getContentType(); } catch (InvalidFormatException e) { throw new XMLSignatureException(e); @@ -176,26 +181,22 @@ public class OOXMLSignatureFacet extends SignatureFacet { } String uri = partName + "?ContentType=" + contentType; - Reference reference = newReference(uri, null, null, null, null); + Reference reference = newReference(signatureInfo, uri, null, null, null, null); manifestReferences.add(reference); } if (parameterSpec.hasSourceIds()) { List<Transform> transforms = new ArrayList<>(); - transforms.add(newTransform(RelationshipTransformService.TRANSFORM_URI, parameterSpec)); - transforms.add(newTransform(CanonicalizationMethod.INCLUSIVE)); + transforms.add(newTransform(signatureInfo, RelationshipTransformService.TRANSFORM_URI, parameterSpec)); + transforms.add(newTransform(signatureInfo, CanonicalizationMethod.INCLUSIVE)); String uri = normalizePartName(pp.getPartName().getURI(), baseUri) + "?ContentType=application/vnd.openxmlformats-package.relationships+xml"; - Reference reference = newReference(uri, transforms, null, null, null); + Reference reference = newReference(signatureInfo, uri, transforms, null, null, null); manifestReferences.add(reference); } } - - manifestReferences.sort(new Comparator<Reference>() { - public int compare(Reference o1, Reference o2) { - return o1.getURI().compareTo(o2.getURI()); - } - }); + + manifestReferences.sort(Comparator.comparing(URIReference::getURI)); } /** @@ -217,7 +218,9 @@ public class OOXMLSignatureFacet extends SignatureFacet { } - protected void addSignatureTime(Document document, List<XMLStructure> objectContent) { + protected void addSignatureTime(SignatureInfo signatureInfo, Document document, List<XMLStructure> objectContent) { + SignatureConfig signatureConfig = signatureInfo.getSignatureConfig(); + XMLSignatureFactory sigFac = signatureInfo.getSignatureFactory(); /* * SignatureTime */ @@ -230,20 +233,25 @@ public class OOXMLSignatureFacet extends SignatureFacet { Element n = (Element)document.importNode(ctTime.getDomNode(),true); List<XMLStructure> signatureTimeContent = new ArrayList<>(); signatureTimeContent.add(new DOMStructure(n)); - SignatureProperty signatureTimeSignatureProperty = getSignatureFactory() + SignatureProperty signatureTimeSignatureProperty = sigFac .newSignatureProperty(signatureTimeContent, "#" + signatureConfig.getPackageSignatureId(), "idSignatureTime"); List<SignatureProperty> signaturePropertyContent = new ArrayList<>(); signaturePropertyContent.add(signatureTimeSignatureProperty); - SignatureProperties signatureProperties = getSignatureFactory() + SignatureProperties signatureProperties = sigFac .newSignatureProperties(signaturePropertyContent, null); objectContent.add(signatureProperties); } - protected void addSignatureInfo(Document document, - List<Reference> references, - List<XMLObject> objects) + protected void addSignatureInfo( + SignatureInfo signatureInfo + , Document document + , List<Reference> references + , List<XMLObject> objects) throws XMLSignatureException { + SignatureConfig signatureConfig = signatureInfo.getSignatureConfig(); + XMLSignatureFactory sigFac = signatureInfo.getSignatureFactory(); + List<XMLStructure> objectContent = new ArrayList<>(); SignatureInfoV1Document sigV1 = SignatureInfoV1Document.Factory.newInstance(); @@ -259,20 +267,20 @@ public class OOXMLSignatureFacet extends SignatureFacet { List<XMLStructure> signatureInfoContent = new ArrayList<>(); signatureInfoContent.add(new DOMStructure(n)); - SignatureProperty signatureInfoSignatureProperty = getSignatureFactory() + SignatureProperty signatureInfoSignatureProperty = sigFac .newSignatureProperty(signatureInfoContent, "#" + signatureConfig.getPackageSignatureId(), "idOfficeV1Details"); List<SignatureProperty> signaturePropertyContent = new ArrayList<>(); signaturePropertyContent.add(signatureInfoSignatureProperty); - SignatureProperties signatureProperties = getSignatureFactory() + SignatureProperties signatureProperties = sigFac .newSignatureProperties(signaturePropertyContent, null); objectContent.add(signatureProperties); String objectId = "idOfficeObject"; - objects.add(getSignatureFactory().newXMLObject(objectContent, objectId, null, null)); + objects.add(sigFac.newXMLObject(objectContent, objectId, null, null)); - Reference reference = newReference("#" + objectId, null, XML_DIGSIG_NS+"Object", null, null); + Reference reference = newReference(signatureInfo, "#" + objectId, null, XML_DIGSIG_NS+"Object", null, null); references.add(reference); } diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/Office2010SignatureFacet.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/Office2010SignatureFacet.java index 59ba63c3da..f9ea3a9662 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/Office2010SignatureFacet.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/Office2010SignatureFacet.java @@ -18,9 +18,9 @@ /* ==================================================================== This product contains an ASLv2 licensed version of the OOXML signer package from the eID Applet project - http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt Copyright (C) 2008-2014 FedICT. - ================================================================= */ + ================================================================= */ package org.apache.poi.poifs.crypt.dsig.facets; @@ -28,6 +28,7 @@ import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; import javax.xml.crypto.MarshalException; +import org.apache.poi.poifs.crypt.dsig.SignatureInfo; import org.apache.xmlbeans.XmlException; import org.etsi.uri.x01903.v13.QualifyingPropertiesType; import org.etsi.uri.x01903.v13.UnsignedPropertiesType; @@ -38,17 +39,15 @@ import org.w3c.dom.NodeList; /** * Work-around for Office2010 to accept the XAdES-BES/EPES signature. - * - * xades:UnsignedProperties/xades:UnsignedSignatureProperties needs to be - * present. - * + * + * xades:UnsignedProperties/xades:UnsignedSignatureProperties needs to be present. + * * @author Frank Cornelis - * */ -public class Office2010SignatureFacet extends SignatureFacet { +public class Office2010SignatureFacet implements SignatureFacet { @Override - public void postSign(Document document) + public void postSign(SignatureInfo signatureInfo, Document document) throws MarshalException { // check for XAdES-BES NodeList nl = document.getElementsByTagNameNS(XADES_132_NS, "QualifyingProperties"); @@ -62,7 +61,7 @@ public class Office2010SignatureFacet extends SignatureFacet { } catch (XmlException e) { throw new MarshalException(e); } - + // create basic XML container structure UnsignedPropertiesType unsignedProps = qualProps.getUnsignedProperties(); if (unsignedProps == null) { @@ -72,7 +71,7 @@ public class Office2010SignatureFacet extends SignatureFacet { if (unsignedSigProps == null) { /* unsignedSigProps = */ unsignedProps.addNewUnsignedSignatureProperties(); } - + Node n = document.importNode(qualProps.getDomNode().getFirstChild(), true); nl.item(0).getParentNode().replaceChild(n, nl.item(0)); } 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 29852f0044..cc1a803ce6 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 @@ -18,70 +18,57 @@ /* ==================================================================== This product contains an ASLv2 licensed version of the OOXML signer package from the eID Applet project - http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt Copyright (C) 2008-2014 FedICT. - ================================================================= */ + ================================================================= */ package org.apache.poi.poifs.crypt.dsig.facets; -import java.security.GeneralSecurityException; import java.util.List; import javax.xml.XMLConstants; import javax.xml.crypto.MarshalException; -import javax.xml.crypto.dsig.DigestMethod; import javax.xml.crypto.dsig.Reference; -import javax.xml.crypto.dsig.Transform; import javax.xml.crypto.dsig.XMLObject; import javax.xml.crypto.dsig.XMLSignature; import javax.xml.crypto.dsig.XMLSignatureException; -import javax.xml.crypto.dsig.XMLSignatureFactory; -import javax.xml.crypto.dsig.spec.TransformParameterSpec; import org.apache.poi.openxml4j.opc.PackageNamespaces; -import org.apache.poi.poifs.crypt.dsig.SignatureConfig; -import org.apache.poi.poifs.crypt.dsig.SignatureConfig.SignatureConfigurable; -import org.apache.poi.util.POILogFactory; -import org.apache.poi.util.POILogger; +import org.apache.poi.poifs.crypt.dsig.SignatureInfo; +import org.apache.poi.util.Internal; import org.w3c.dom.Document; /** * JSR105 Signature Facet base class. */ -public abstract class SignatureFacet implements SignatureConfigurable { +@Internal +public interface SignatureFacet { - private static final POILogger LOG = POILogFactory.getLogger(SignatureFacet.class); - - public static final String XML_NS = XMLConstants.XMLNS_ATTRIBUTE_NS_URI; - public static final String XML_DIGSIG_NS = XMLSignature.XMLNS; - public static final String OO_DIGSIG_NS = PackageNamespaces.DIGITAL_SIGNATURE; - public static final String MS_DIGSIG_NS = "http://schemas.microsoft.com/office/2006/digsig"; - public static final String XADES_132_NS = "http://uri.etsi.org/01903/v1.3.2#"; - public static final String XADES_141_NS = "http://uri.etsi.org/01903/v1.4.1#"; - - protected SignatureConfig signatureConfig; - - @Override - public void setSignatureConfig(SignatureConfig signatureConfig) { - this.signatureConfig = signatureConfig; - } + String XML_NS = XMLConstants.XMLNS_ATTRIBUTE_NS_URI; + String XML_DIGSIG_NS = XMLSignature.XMLNS; + String OO_DIGSIG_NS = PackageNamespaces.DIGITAL_SIGNATURE; + String MS_DIGSIG_NS = "http://schemas.microsoft.com/office/2006/digsig"; + String XADES_132_NS = "http://uri.etsi.org/01903/v1.3.2#"; + String XADES_141_NS = "http://uri.etsi.org/01903/v1.4.1#"; /** * This method is being invoked by the XML signature service engine during * pre-sign phase. Via this method a signature facet implementation can add * signature facets to an XML signature. - * + * + * @param signatureInfo the signature info object holding the OPCPackage and other document related data * @param document the signature document to be used for imports * @param references list of reference definitions * @param objects objects to be signed/included in the signature document * @throws XMLSignatureException */ - public void preSign( - Document document + default void preSign( + SignatureInfo signatureInfo + , Document document , List<Reference> references , List<XMLObject> objects ) throws XMLSignatureException { - // empty + } /** @@ -89,62 +76,12 @@ public abstract class SignatureFacet implements SignatureConfigurable { * the post-sign phase. Via this method a signature facet can extend the XML * signatures with for example key information. * + * @param signatureInfo the signature info object holding the OPCPackage and other document related data * @param document the signature document to be modified * @throws MarshalException */ - public void postSign(Document document) throws MarshalException { - // empty - } + default void postSign(SignatureInfo signatureInfo, Document document) throws MarshalException { - protected XMLSignatureFactory getSignatureFactory() { - return signatureConfig.getSignatureFactory(); - } - - protected Transform newTransform(String canonicalizationMethod) throws XMLSignatureException { - return newTransform(canonicalizationMethod, null); - } - - protected Transform newTransform(String canonicalizationMethod, TransformParameterSpec paramSpec) - throws XMLSignatureException { - try { - return getSignatureFactory().newTransform(canonicalizationMethod, paramSpec); - } catch (GeneralSecurityException e) { - throw new XMLSignatureException("unknown canonicalization method: "+canonicalizationMethod, e); - } - } - - protected Reference newReference(String uri, List<Transform> transforms, String type, String id, byte[] digestValue) - throws XMLSignatureException { - return newReference(uri, transforms, type, id, digestValue, signatureConfig); } - public static Reference newReference( - String uri - , List<Transform> transforms - , String type - , String id - , byte[] digestValue - , SignatureConfig signatureConfig) - throws XMLSignatureException { - // the references appear in the package signature or the package object - // so we can use the default digest algorithm - String digestMethodUri = signatureConfig.getDigestMethodUri(); - XMLSignatureFactory sigFac = signatureConfig.getSignatureFactory(); - DigestMethod digestMethod; - try { - digestMethod = sigFac.newDigestMethod(digestMethodUri, null); - } catch (GeneralSecurityException e) { - throw new XMLSignatureException("unknown digest method uri: "+digestMethodUri, e); - } - - Reference reference; - if (digestValue == null) { - reference = sigFac.newReference(uri, digestMethod, transforms, type, id); - } else { - reference = sigFac.newReference(uri, digestMethod, transforms, type, id, digestValue); - } - - - return reference; - } }
\ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/SignatureFacetHelper.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/SignatureFacetHelper.java new file mode 100644 index 0000000000..e60771f563 --- /dev/null +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/SignatureFacetHelper.java @@ -0,0 +1,75 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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.apache.poi.poifs.crypt.dsig.facets; + +import java.security.GeneralSecurityException; +import java.util.List; + +import javax.xml.crypto.dsig.DigestMethod; +import javax.xml.crypto.dsig.Reference; +import javax.xml.crypto.dsig.Transform; +import javax.xml.crypto.dsig.XMLSignatureException; +import javax.xml.crypto.dsig.XMLSignatureFactory; +import javax.xml.crypto.dsig.spec.TransformParameterSpec; + +import org.apache.poi.poifs.crypt.dsig.SignatureConfig; +import org.apache.poi.poifs.crypt.dsig.SignatureInfo; +import org.apache.poi.util.Internal; + +@Internal +final class SignatureFacetHelper { + private SignatureFacetHelper() {} + + static Transform newTransform(SignatureInfo signatureInfo, String canonicalizationMethod) throws XMLSignatureException { + return newTransform(signatureInfo, canonicalizationMethod, null); + } + + static Transform newTransform(SignatureInfo signatureInfo, String canonicalizationMethod, TransformParameterSpec paramSpec) + throws XMLSignatureException { + try { + return signatureInfo.getSignatureFactory().newTransform(canonicalizationMethod, paramSpec); + } catch (GeneralSecurityException e) { + throw new XMLSignatureException("unknown canonicalization method: "+canonicalizationMethod, e); + } + } + + static Reference newReference( + SignatureInfo signatureInfo + , String uri + , List<Transform> transforms + , String type + , String id + , byte[] digestValue) + throws XMLSignatureException { + // the references appear in the package signature or the package object + // so we can use the default digest algorithm + SignatureConfig signatureConfig = signatureInfo.getSignatureConfig(); + String digestMethodUri = signatureConfig.getDigestMethodUri(); + XMLSignatureFactory sigFac = signatureInfo.getSignatureFactory(); + DigestMethod digestMethod; + try { + digestMethod = sigFac.newDigestMethod(digestMethodUri, null); + } catch (GeneralSecurityException e) { + throw new XMLSignatureException("unknown digest method uri: "+digestMethodUri, e); + } + + return (digestValue == null) + ? sigFac.newReference(uri, digestMethod, transforms, type, id) + : sigFac.newReference(uri, digestMethod, transforms, type, id, digestValue); + } +} diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESSignatureFacet.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESSignatureFacet.java index c8f04e8824..ce96f16a91 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESSignatureFacet.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESSignatureFacet.java @@ -18,13 +18,15 @@ /* ==================================================================== This product contains an ASLv2 licensed version of the OOXML signer package from the eID Applet project - http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt Copyright (C) 2008-2014 FedICT. - ================================================================= */ + ================================================================= */ package org.apache.poi.poifs.crypt.dsig.facets; import static java.util.Collections.singletonList; +import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacetHelper.newReference; +import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacetHelper.newTransform; import java.security.MessageDigest; import java.security.cert.CertificateEncodingException; @@ -48,6 +50,7 @@ import javax.xml.crypto.dsig.XMLSignatureException; import org.apache.poi.poifs.crypt.CryptoFunctions; import org.apache.poi.poifs.crypt.HashAlgorithm; import org.apache.poi.poifs.crypt.dsig.SignatureConfig; +import org.apache.poi.poifs.crypt.dsig.SignatureInfo; import org.apache.poi.poifs.crypt.dsig.services.SignaturePolicyService; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; @@ -77,36 +80,39 @@ import org.w3c.dom.Node; * XAdES Signature Facet. Implements XAdES v1.4.1 which is compatible with XAdES * v1.3.2. The implemented XAdES format is XAdES-BES/EPES. It's up to another * part of the signature service to upgrade the XAdES-BES to a XAdES-X-L. - * + * * This implementation has been tested against an implementation that * participated multiple ETSI XAdES plugtests. - * + * * @author Frank Cornelis * @see <a href="http://en.wikipedia.org/wiki/XAdES">XAdES</a> - * + * */ -public class XAdESSignatureFacet extends SignatureFacet { +public class XAdESSignatureFacet implements SignatureFacet { private static final POILogger LOG = POILogFactory.getLogger(XAdESSignatureFacet.class); private static final String XADES_TYPE = "http://uri.etsi.org/01903#SignedProperties"; - + private final Map<String, String> dataObjectFormatMimeTypes = new HashMap<>(); @Override public void preSign( - Document document + SignatureInfo signatureInfo + , Document document , List<Reference> references , List<XMLObject> objects) throws XMLSignatureException { LOG.log(POILogger.DEBUG, "preSign"); + SignatureConfig signatureConfig = signatureInfo.getSignatureConfig(); + // QualifyingProperties QualifyingPropertiesDocument qualDoc = QualifyingPropertiesDocument.Factory.newInstance(); QualifyingPropertiesType qualifyingProperties = qualDoc.addNewQualifyingProperties(); qualifyingProperties.setTarget("#" + signatureConfig.getPackageSignatureId()); - + // SignedProperties SignedPropertiesType signedProperties = qualifyingProperties.addNewSignedProperties(); signedProperties.setId(signatureConfig.getXadesSignatureId()); @@ -115,35 +121,37 @@ public class XAdESSignatureFacet extends SignatureFacet { SignedSignaturePropertiesType signedSignatureProperties = signedProperties.addNewSignedSignatureProperties(); // SigningTime - addSigningTime(signedSignatureProperties); + addSigningTime(signatureInfo, signedSignatureProperties); // SigningCertificate - addCertificate(signedSignatureProperties); + addCertificate(signatureInfo, signedSignatureProperties); // ClaimedRole - addXadesRole(signedSignatureProperties); + addXadesRole(signatureInfo, signedSignatureProperties); // XAdES-EPES - addPolicy(signedSignatureProperties); + addPolicy(signatureInfo, signedSignatureProperties); // DataObjectFormat addMimeTypes(signedProperties); // add XAdES ds:Object - objects.add(addXadesObject(document, qualifyingProperties)); + objects.add(addXadesObject(signatureInfo, document, qualifyingProperties)); // add XAdES ds:Reference - references.add(addXadesReference()); + references.add(addXadesReference(signatureInfo)); } - private void addSigningTime(SignedSignaturePropertiesType signedSignatureProperties) { + private void addSigningTime(SignatureInfo signatureInfo, SignedSignaturePropertiesType signedSignatureProperties) { + SignatureConfig signatureConfig = signatureInfo.getSignatureConfig(); Calendar xmlGregorianCalendar = Calendar.getInstance(TimeZone.getTimeZone("Z"), Locale.ROOT); xmlGregorianCalendar.setTime(signatureConfig.getExecutionTime()); xmlGregorianCalendar.clear(Calendar.MILLISECOND); signedSignatureProperties.setSigningTime(xmlGregorianCalendar); } - private void addCertificate(SignedSignaturePropertiesType signedSignatureProperties) { + private void addCertificate(SignatureInfo signatureInfo, SignedSignaturePropertiesType signedSignatureProperties) { + SignatureConfig signatureConfig = signatureInfo.getSignatureConfig(); List<X509Certificate> chain = signatureConfig.getSigningCertificateChain(); if (chain == null || chain.isEmpty()) { throw new RuntimeException("no signing certificate chain available"); @@ -153,7 +161,8 @@ public class XAdESSignatureFacet extends SignatureFacet { setCertID(certId, signatureConfig, signatureConfig.isXadesIssuerNameNoReverseOrder(), chain.get(0)); } - private void addXadesRole(SignedSignaturePropertiesType signedSignatureProperties) { + private void addXadesRole(SignatureInfo signatureInfo, SignedSignaturePropertiesType signedSignatureProperties) { + SignatureConfig signatureConfig = signatureInfo.getSignatureConfig(); String role = signatureConfig.getXadesRole(); if (role == null || role.isEmpty()) { return; @@ -168,7 +177,8 @@ public class XAdESSignatureFacet extends SignatureFacet { insertXChild(claimedRole, roleString); } - private void addPolicy(SignedSignaturePropertiesType signedSignatureProperties) { + private void addPolicy(SignatureInfo signatureInfo, SignedSignaturePropertiesType signedSignatureProperties) { + SignatureConfig signatureConfig = signatureInfo.getSignatureConfig(); SignaturePolicyService policyService = signatureConfig.getSignaturePolicyService(); if (policyService == null) { if (signatureConfig.isXadesSignaturePolicyImplied()) { @@ -221,22 +231,23 @@ public class XAdESSignatureFacet extends SignatureFacet { }); } - private XMLObject addXadesObject(Document document, QualifyingPropertiesType qualifyingProperties) { + private XMLObject addXadesObject(SignatureInfo signatureInfo, Document document, QualifyingPropertiesType qualifyingProperties) { Node qualDocElSrc = qualifyingProperties.getDomNode(); Node qualDocEl = document.importNode(qualDocElSrc, true); List<XMLStructure> xadesObjectContent = Arrays.asList(new DOMStructure(qualDocEl)); - return getSignatureFactory().newXMLObject(xadesObjectContent, null, null, null); + return signatureInfo.getSignatureFactory().newXMLObject(xadesObjectContent, null, null, null); } - private Reference addXadesReference() throws XMLSignatureException { - List<Transform> transforms = singletonList(newTransform(CanonicalizationMethod.INCLUSIVE)); - return newReference("#"+signatureConfig.getXadesSignatureId(), transforms, XADES_TYPE, null, null); + private Reference addXadesReference(SignatureInfo signatureInfo) throws XMLSignatureException { + SignatureConfig signatureConfig = signatureInfo.getSignatureConfig(); + List<Transform> transforms = singletonList(newTransform(signatureInfo, CanonicalizationMethod.INCLUSIVE)); + return newReference(signatureInfo, "#"+signatureConfig.getXadesSignatureId(), transforms, XADES_TYPE, null, null); } /** * Gives back the JAXB DigestAlgAndValue data structure. * - * @param digestAlgAndValue the parent for the new digest element + * @param digestAlgAndValue the parent for the new digest element * @param data the data to be digested * @param digestAlgo the digest algorithm */ @@ -246,7 +257,7 @@ public class XAdESSignatureFacet extends SignatureFacet { HashAlgorithm digestAlgo) { DigestMethodType digestMethod = digestAlgAndValue.addNewDigestMethod(); digestMethod.setAlgorithm(SignatureConfig.getDigestMethodUri(digestAlgo)); - + MessageDigest messageDigest = CryptoFunctions.getMessageDigest(digestAlgo); byte[] digestValue = messageDigest.digest(data); digestAlgAndValue.setDigestValue(digestValue); @@ -264,7 +275,7 @@ public class XAdESSignatureFacet extends SignatureFacet { * Make sure the DN is encoded using the same order as present * within the certificate. This is an Office2010 work-around. * Should be reverted back. - * + * * XXX: not correct according to RFC 4514. */ // TODO: check if issuerName is different on getTBSCertificate @@ -283,14 +294,14 @@ public class XAdESSignatureFacet extends SignatureFacet { throw new RuntimeException("certificate encoding error: " + e.getMessage(), e); } - DigestAlgAndValueType certDigest = certId.addNewCertDigest(); + DigestAlgAndValueType certDigest = certId.addNewCertDigest(); setDigestAlgAndValue(certDigest, encodedCertificate, signatureConfig.getXadesDigestAlgo()); } /** * Adds a mime-type for the given ds:Reference (referred via its @URI). This * information is added via the xades:DataObjectFormat element. - * + * * @param dsReferenceUri * @param mimetype */ diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java index e00de87b7c..60b9e26e67 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java @@ -18,9 +18,9 @@ /* ==================================================================== This product contains an ASLv2 licensed version of the OOXML signer package from the eID Applet project - http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt Copyright (C) 2008-2014 FedICT. - ================================================================= */ + ================================================================= */ package org.apache.poi.poifs.crypt.dsig.facets; @@ -47,6 +47,8 @@ import java.util.UUID; import javax.xml.crypto.MarshalException; +import org.apache.poi.poifs.crypt.dsig.SignatureConfig; +import org.apache.poi.poifs.crypt.dsig.SignatureInfo; import org.apache.poi.poifs.crypt.dsig.services.RevocationData; import org.apache.poi.util.IOUtils; import org.apache.poi.util.POILogFactory; @@ -73,18 +75,18 @@ import org.w3c.dom.NodeList; /** * XAdES-X-L v1.4.1 signature facet. This signature facet implementation will * upgrade a given XAdES-BES/EPES signature to XAdES-X-L. - * + * * We don't inherit from XAdESSignatureFacet as we also want to be able to use * this facet out of the context of a signature creation. This signature facet * assumes that the signature is already XAdES-BES/EPES compliant. - * + * * This implementation has been tested against an implementation that * participated multiple ETSI XAdES plugtests. - * + * * @author Frank Cornelis * @see XAdESSignatureFacet */ -public class XAdESXLSignatureFacet extends SignatureFacet { +public class XAdESXLSignatureFacet implements SignatureFacet { private static final POILogger LOG = POILogFactory.getLogger(XAdESXLSignatureFacet.class); @@ -99,9 +101,11 @@ public class XAdESXLSignatureFacet extends SignatureFacet { } @Override - public void postSign(Document document) throws MarshalException { + public void postSign(SignatureInfo signatureInfo, Document document) throws MarshalException { LOG.log(POILogger.DEBUG, "XAdES-X-L post sign phase"); + SignatureConfig signatureConfig = signatureInfo.getSignatureConfig(); + QualifyingPropertiesDocument qualDoc = null; QualifyingPropertiesType qualProps = null; @@ -127,18 +131,18 @@ public class XAdESXLSignatureFacet extends SignatureFacet { if (unsignedSigProps == null) { unsignedSigProps = unsignedProps.addNewUnsignedSignatureProperties(); } - + // create the XAdES-T time-stamp NodeList nlSigVal = document.getElementsByTagNameNS(XML_DIGSIG_NS, "SignatureValue"); if (nlSigVal.getLength() != 1) { throw new IllegalArgumentException("SignatureValue is not set."); } - + RevocationData tsaRevocationDataXadesT = new RevocationData(); LOG.log(POILogger.DEBUG, "creating XAdES-T time-stamp"); XAdESTimeStampType signatureTimeStamp = createXAdESTimeStamp - (Collections.singletonList(nlSigVal.item(0)), tsaRevocationDataXadesT); + (signatureInfo, Collections.singletonList(nlSigVal.item(0)), tsaRevocationDataXadesT); // marshal the XAdES-T extension unsignedSigProps.addNewSignatureTimeStamp().set(signatureTimeStamp); @@ -158,7 +162,7 @@ public class XAdESXLSignatureFacet extends SignatureFacet { } // XAdES-C: complete certificate refs - CompleteCertificateRefsType completeCertificateRefs = + CompleteCertificateRefsType completeCertificateRefs = unsignedSigProps.addNewCompleteCertificateRefs(); CertIDListType certIdList = completeCertificateRefs.addNewCertRefs(); @@ -176,7 +180,7 @@ public class XAdESXLSignatureFacet extends SignatureFacet { } // XAdES-C: complete revocation refs - CompleteRevocationRefsType completeRevocationRefs = + CompleteRevocationRefsType completeRevocationRefs = unsignedSigProps.addNewCompleteRevocationRefs(); RevocationData revocationData = signatureConfig.getRevocationDataService() .getRevocationData(certChain); @@ -212,22 +216,22 @@ public class XAdESXLSignatureFacet extends SignatureFacet { for (byte[] ocsp : revocationData.getOCSPs()) { try { OCSPRefType ocspRef = ocspRefs.addNewOCSPRef(); - + DigestAlgAndValueType digestAlgAndValue = ocspRef.addNewDigestAlgAndValue(); XAdESSignatureFacet.setDigestAlgAndValue(digestAlgAndValue, ocsp, signatureConfig.getDigestAlgo()); - + OCSPIdentifierType ocspIdentifier = ocspRef.addNewOCSPIdentifier(); - + OCSPResp ocspResp = new OCSPResp(ocsp); - + BasicOCSPResp basicOcspResp = (BasicOCSPResp)ocspResp.getResponseObject(); - + Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Z"), Locale.ROOT); cal.setTime(basicOcspResp.getProducedAt()); ocspIdentifier.setProducedAt(cal); - + ResponderIDType responderId = ocspIdentifier.addNewResponderID(); - + RespID respId = basicOcspResp.getResponderId(); ResponderID ocspResponderId = respId.toASN1Primitive(); DERTaggedObject derTaggedObject = (DERTaggedObject)ocspResponderId.toASN1Primitive(); @@ -247,7 +251,7 @@ public class XAdESXLSignatureFacet extends SignatureFacet { } // marshal XAdES-C - + // XAdES-X Type 1 timestamp List<Node> timeStampNodesXadesX1 = new ArrayList<>(); timeStampNodesXadesX1.add(nlSigVal.item(0)); @@ -258,7 +262,7 @@ public class XAdESXLSignatureFacet extends SignatureFacet { RevocationData tsaRevocationDataXadesX1 = new RevocationData(); LOG.log(POILogger.DEBUG, "creating XAdES-X time-stamp"); XAdESTimeStampType timeStampXadesX1 = createXAdESTimeStamp - (timeStampNodesXadesX1, tsaRevocationDataXadesX1); + (signatureInfo, timeStampNodesXadesX1, tsaRevocationDataXadesX1); if (tsaRevocationDataXadesX1.hasRevocationDataEntries()) { ValidationDataType timeStampXadesX1ValidationData = createValidationData(tsaRevocationDataXadesX1); insertXChild(unsignedSigProps, timeStampXadesX1ValidationData); @@ -277,7 +281,7 @@ public class XAdESXLSignatureFacet extends SignatureFacet { throw new RuntimeException("certificate encoding error: " + e.getMessage(), e); } } - + RevocationValuesType revocationValues = unsignedSigProps.addNewRevocationValues(); createRevocationValues(revocationValues, revocationData); @@ -330,18 +334,21 @@ public class XAdESXLSignatureFacet extends SignatureFacet { } private XAdESTimeStampType createXAdESTimeStamp( + SignatureInfo signatureInfo, List<Node> nodeList, RevocationData revocationData) { + SignatureConfig signatureConfig = signatureInfo.getSignatureConfig(); byte[] c14nSignatureValueElement = getC14nValue(nodeList, signatureConfig.getXadesCanonicalizationMethod()); - return createXAdESTimeStamp(c14nSignatureValueElement, revocationData); + return createXAdESTimeStamp(signatureInfo, c14nSignatureValueElement, revocationData); } - private XAdESTimeStampType createXAdESTimeStamp(byte[] data, RevocationData revocationData) { + private XAdESTimeStampType createXAdESTimeStamp(SignatureInfo signatureInfo, byte[] data, RevocationData revocationData) { + SignatureConfig signatureConfig = signatureInfo.getSignatureConfig(); // create the time-stamp byte[] timeStampToken; try { - timeStampToken = signatureConfig.getTspService().timeStamp(data, revocationData); + timeStampToken = signatureConfig.getTspService().timeStamp(signatureInfo, data, revocationData); } catch (Exception e) { throw new RuntimeException("error while creating a time-stamp: " + e.getMessage(), e); diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TSPTimeStampService.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TSPTimeStampService.java index c7fd85c468..925f463077 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TSPTimeStampService.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TSPTimeStampService.java @@ -18,9 +18,9 @@ /* ==================================================================== This product contains an ASLv2 licensed version of the OOXML signer package from the eID Applet project - http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt Copyright (C) 2008-2014 FedICT. - ================================================================= */ + ================================================================= */ package org.apache.poi.poifs.crypt.dsig.services; @@ -47,6 +47,7 @@ import java.util.Map; import org.apache.poi.poifs.crypt.CryptoFunctions; import org.apache.poi.poifs.crypt.HashAlgorithm; import org.apache.poi.poifs.crypt.dsig.SignatureConfig; +import org.apache.poi.poifs.crypt.dsig.SignatureInfo; import org.apache.poi.util.HexDump; import org.apache.poi.util.IOUtils; import org.apache.poi.util.POILogFactory; @@ -72,16 +73,14 @@ import org.bouncycastle.tsp.TimeStampToken; /** * A TSP time-stamp service implementation. - * + * * @author Frank Cornelis - * + * */ public class TSPTimeStampService implements TimeStampService { private static final POILogger LOG = POILogFactory.getLogger(TSPTimeStampService.class); - private SignatureConfig signatureConfig; - /** * Maps the digest algorithm to corresponding OID value. */ @@ -97,8 +96,9 @@ public class TSPTimeStampService implements TimeStampService { } @SuppressWarnings({"unchecked","squid:S2647"}) - public byte[] timeStamp(byte[] data, RevocationData revocationData) - throws Exception { + public byte[] timeStamp(SignatureInfo signatureInfo, byte[] data, RevocationData revocationData) throws Exception { + SignatureConfig signatureConfig = signatureInfo.getSignatureConfig(); + // digest the message MessageDigest messageDigest = CryptoFunctions.getMessageDigest(signatureConfig.getTspDigestAlgo()); byte[] digest = messageDigest.digest(data); @@ -170,7 +170,7 @@ public class TSPTimeStampService implements TimeStampService { huc.disconnect(); } - if (!contentType.startsWith(signatureConfig.isTspOldProtocol() + if (!contentType.startsWith(signatureConfig.isTspOldProtocol() ? "application/timestamp-response" : "application/timestamp-reply" )) { @@ -178,7 +178,7 @@ public class TSPTimeStampService implements TimeStampService { // dump the first few bytes ": " + HexDump.dump(bos.toByteArray(), 0, 0, 200)); } - + if (bos.size() == 0) { throw new RuntimeException("Content-Length is zero"); } @@ -209,7 +209,7 @@ public class TSPTimeStampService implements TimeStampService { // TSP signer certificates retrieval Collection<X509CertificateHolder> certificates = timeStampToken.getCertificates().getMatches(null); - + X509CertificateHolder signerCert = null; Map<X500Name, X509CertificateHolder> certificateMap = new HashMap<>(); for (X509CertificateHolder certificate : certificates) { @@ -245,7 +245,7 @@ public class TSPTimeStampService implements TimeStampService { BcDigestCalculatorProvider calculator = new BcDigestCalculatorProvider(); BcRSASignerInfoVerifierBuilder verifierBuilder = new BcRSASignerInfoVerifierBuilder(nameGen, sigAlgoFinder, hashAlgoFinder, calculator); SignerInformationVerifier verifier = verifierBuilder.build(holder); - + timeStampToken.validate(verifier); // verify TSP signer certificate @@ -258,8 +258,4 @@ public class TSPTimeStampService implements TimeStampService { return timeStampToken.getEncoded(); } - - public void setSignatureConfig(SignatureConfig signatureConfig) { - this.signatureConfig = signatureConfig; - } }
\ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TimeStampService.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TimeStampService.java index ede39ac357..72561af9bd 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TimeStampService.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TimeStampService.java @@ -18,28 +18,26 @@ /* ==================================================================== This product contains an ASLv2 licensed version of the OOXML signer package from the eID Applet project - http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt Copyright (C) 2008-2014 FedICT. - ================================================================= */ + ================================================================= */ package org.apache.poi.poifs.crypt.dsig.services; -import org.apache.poi.poifs.crypt.dsig.SignatureConfig.SignatureConfigurable; - +import org.apache.poi.poifs.crypt.dsig.SignatureInfo; /** * Interface for a time-stamp service. - * + * * @author Frank Cornelis - * */ -public interface TimeStampService extends SignatureConfigurable { +public interface TimeStampService { /** * Gives back the encoded time-stamp token for the given array of data * bytes. We assume that the time-stamp token itself contains its full * certificate chain required for proper validation. - * + * * @param data * the data to be time-stamped. * @param revocationData @@ -49,6 +47,5 @@ public interface TimeStampService extends SignatureConfigurable { * @throws Exception * in case something went wrong. */ - byte[] timeStamp(byte[] data, RevocationData revocationData) - throws Exception; + byte[] timeStamp(SignatureInfo signatureInfo, byte[] data, RevocationData revocationData) throws Exception; } 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 e621f51e0d..1d9e154542 100644 --- a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java +++ b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java @@ -104,27 +104,18 @@ import org.apache.poi.xssf.streaming.SXSSFWorkbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.xmlbeans.SystemProperties; import org.apache.xmlbeans.XmlObject; -import org.bouncycastle.asn1.DERIA5String; import org.bouncycastle.asn1.DEROctetString; -import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x500.X500Name; -import org.bouncycastle.asn1.x509.AuthorityInformationAccess; import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.CRLNumber; -import org.bouncycastle.asn1.x509.CRLReason; -import org.bouncycastle.asn1.x509.DistributionPoint; -import org.bouncycastle.asn1.x509.DistributionPointName; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.Extensions; -import org.bouncycastle.asn1.x509.GeneralName; -import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; import org.bouncycastle.cert.X509CRLHolder; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cert.X509ExtensionUtils; @@ -141,7 +132,6 @@ import org.bouncycastle.cert.ocsp.OCSPReqBuilder; import org.bouncycastle.cert.ocsp.OCSPResp; import org.bouncycastle.cert.ocsp.OCSPRespBuilder; import org.bouncycastle.cert.ocsp.Req; -import org.bouncycastle.cert.ocsp.RevokedStatus; import org.bouncycastle.crypto.params.RSAKeyParameters; import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory; import org.bouncycastle.openssl.PEMParser; @@ -196,7 +186,7 @@ public class TestSignatureInfo { String additionalJar = System.getProperty("additionaljar"); //System.out.println("Having: " + additionalJar); Assume.assumeTrue("Not running TestSignatureInfo because we are testing with additionaljar set to " + additionalJar, - additionalJar == null || additionalJar.trim().length() == 0); + additionalJar == null || additionalJar.trim().length() == 0); System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true"); @@ -207,98 +197,97 @@ public class TestSignatureInfo { @Ignore("This test is very sensitive, it breaks with every little change to the produced XML") @Test public void bug61182() throws Exception { - String pfxInput = - "H4sIAAAAAAAAAFXTfzzTeRwH8P2uGRmG6hKSmJh9a2HsuPy60VnHCEU6v86sieZH2Jr2qFl+s+ZHJ5tfUcfKb4uho/OjiFq1qTv5ceFyp0PqEK"+ - "fH4+66++Pz+Dwer9fj8f7r9cRzEd4QMBTPRWxDIM14ZN47NfAWsJgL34Bx4at4Lvwdngvd9b8KqgbjQpGbMXzzgRGovytVFTBEzIXU47kQCd4U"+ - "ofJPvHl8JwyTjRS55hbKoor3UJLDE1i/PcPKCBAIDATjQlKiK67XjVYdcnkZgD2txroiAUb8W9dtn57DvTsbM+3wIsdocXDEN7TdPKgaSl+tU1"+ - "xq9oqiB5yMaZCPho8uUEbFU9U6u3N7lEMLTJGeA0RfX+5FMRrpXPFrbrlJ8uNUCE2H247P28Ckyfqlsy32yeKg/HTbH5JpqUDNw2B32+SaiRw7"+ - "ofRMePUpaAoK7KYgmd5ZIc0rLLYjJBfOWCb28xlrGhbpJvdToFdqt5PXVjEz5YOJ6g7W0fskuKW9/iZP0yLEVpR9XkkHmb6tfpcE8YwCdWNCan"+ - "LvAsco25JdF1j2/FLAMVU79HdOex07main90dy40511OZtTGZ+TdVd3lKZ7D3clEg9hLESHwSNnZ6239X4yLM4xYSElQ/hqSbwdmiozYG9PhF2"+ - "Zf0XaZnxzTK0Iot+rJ3kYoxWTLE8DR9leV62Ywbtlg4mapYOxb3lT7fQ1x4EQ44flh2oFWSPLR8LMbsc6jzJsV6OZ3TrODjHEdw9W+8OD32vd8"+ - "XQ6iCaIHcrSOn6qS0TKLr786234eeSAhvAQbEsVn7vrvc/487Be/O2e/+5Y5zRq2zAtz6pfcNyraJNDqMW1inNkgJ3t3VESbZ3pNzyl3KHILs0"+ - "51dY6msDYSlWhw40TglXxj9rw95O6gFWIuN012W/vhS50jpKXcao4gc1aLaXtJXxirbRkpZ/0e7a0pD6TDa7+GxEdEEML3VGo9udD5YUKhU3y7"+ - "SzWAgN6WIEIglq7LilvCjqIVLIfg8CvVGL9f5iSsCDf5hef4vMxbyvcjINuy06gZu+iPYOWNxjfrwKGYzoqqotK2aywgYVrPMh0JovfkDuN95n"+ - "MdVlYHbN1Mnn4TxAwuv+u3AkBlDZvRUUCwoDMUGxeMNPhTaAgWl60xhhBgCBaEMgAACReMAav7n3x598IDYJ9GxGXRAwaPOT/kfO/1AgPqLQkp"+ - "MiIVaHthnUS4v2y32e2BjdMPyIImUTBW3cV3R5tjVQm0MOm+D2C5+bBW9vHLjLR4lun4toQiY3Ls/v4bES/OJ4EmpZk5xhL9i5ClofYZNEsxFn"+ - "An/q821Tg+Cq9Er4XYGQe8ogjjLJ2b7dUsJ3auFQFNUJF7Ke7yUL2EeYYxl6vz5l4q5u8704mRbFts1E1eWMp6WIy91GPrsVlRGvtuNERfrjfE"+ - "YtzUI3Flcv65zJUbUBEzUnTS0fEYso2XyToAl8kb251mUY2o2lJzv5dp/1htmcjeeP2MjxC+3S45ljx7jd52Pv9XAat+ryiauFOF7YgztkoWWD"+ - "h62tplPH1bzDV+d0NLdaE5AfVJ09HuUYTFS+iggtvT5Euyk+unj4N2XvzW91n+GNjtgWfKOHmkinUPvYRh70Jv+wlPJrVaT8mL7GxJLqDC9jbv"+ - "Gznoiae6es+wQejnk3XjU366MrK/zXxngBYj9J6NnXc9mMiTFLX8WqQ8iTelTAFs2NJzPoDzrBUz4JFIEOa6Dja6dULc68g1jFDTeEHZyra7RZ"+ - "2ElqGDEqcNRo3SNX6feMy9EF1GOyZK0Sa87KwjKw8aM68dpsIYjfLcTXaZ6atg0BKfMnl6axeUGEaIFSP7rzj9wjzumRbG3jgUVp2lX5AK/tsO"+ - "7R4TQX/9/H6RiN34c9KldmPZZGANXzzTajZS9mR2OSvlJ+F4AgSko4htrMAKFTBu51/5SWNsO1vlRaaG48ZRJ+8PzuHQMdvS36gNpRPi7jhF1S"+ - "H3B2ycI4y0VURv6SrqJNUY/X645ZFJQ+eBO+ptG7o8axf1dcqh2beiQk+GRTeZ37LVeUlaeo9vl1/+8tyBfyT2v5lFC5E19WdKIyCuZe7r99Px"+ - "D/Od4Qj0TA92+DQnbCQTCMy/wwse9O4gsEebkkpPIP5GBV3Q0YBsj75XE0uSFQ1tCZSW8bNa9MUJZ/nPBfExohHlgGAAA="; + final String pfxInput = + "H4sIAAAAAAAAAFXTfzzTeRwH8P2uGRmG6hKSmJh9a2HsuPy60VnHCEU6v86sieZH2Jr2qFl+s+ZHJ5tfUcfKb4uho/OjiFq1qTv5ceFyp0PqEK"+ + "fH4+66++Pz+Dwer9fj8f7r9cRzEd4QMBTPRWxDIM14ZN47NfAWsJgL34Bx4at4Lvwdngvd9b8KqgbjQpGbMXzzgRGovytVFTBEzIXU47kQCd4U"+ + "ofJPvHl8JwyTjRS55hbKoor3UJLDE1i/PcPKCBAIDATjQlKiK67XjVYdcnkZgD2txroiAUb8W9dtn57DvTsbM+3wIsdocXDEN7TdPKgaSl+tU1"+ + "xq9oqiB5yMaZCPho8uUEbFU9U6u3N7lEMLTJGeA0RfX+5FMRrpXPFrbrlJ8uNUCE2H247P28Ckyfqlsy32yeKg/HTbH5JpqUDNw2B32+SaiRw7"+ + "ofRMePUpaAoK7KYgmd5ZIc0rLLYjJBfOWCb28xlrGhbpJvdToFdqt5PXVjEz5YOJ6g7W0fskuKW9/iZP0yLEVpR9XkkHmb6tfpcE8YwCdWNCan"+ + "LvAsco25JdF1j2/FLAMVU79HdOex07main90dy40511OZtTGZ+TdVd3lKZ7D3clEg9hLESHwSNnZ6239X4yLM4xYSElQ/hqSbwdmiozYG9PhF2"+ + "Zf0XaZnxzTK0Iot+rJ3kYoxWTLE8DR9leV62Ywbtlg4mapYOxb3lT7fQ1x4EQ44flh2oFWSPLR8LMbsc6jzJsV6OZ3TrODjHEdw9W+8OD32vd8"+ + "XQ6iCaIHcrSOn6qS0TKLr786234eeSAhvAQbEsVn7vrvc/487Be/O2e/+5Y5zRq2zAtz6pfcNyraJNDqMW1inNkgJ3t3VESbZ3pNzyl3KHILs0"+ + "51dY6msDYSlWhw40TglXxj9rw95O6gFWIuN012W/vhS50jpKXcao4gc1aLaXtJXxirbRkpZ/0e7a0pD6TDa7+GxEdEEML3VGo9udD5YUKhU3y7"+ + "SzWAgN6WIEIglq7LilvCjqIVLIfg8CvVGL9f5iSsCDf5hef4vMxbyvcjINuy06gZu+iPYOWNxjfrwKGYzoqqotK2aywgYVrPMh0JovfkDuN95n"+ + "MdVlYHbN1Mnn4TxAwuv+u3AkBlDZvRUUCwoDMUGxeMNPhTaAgWl60xhhBgCBaEMgAACReMAav7n3x598IDYJ9GxGXRAwaPOT/kfO/1AgPqLQkp"+ + "MiIVaHthnUS4v2y32e2BjdMPyIImUTBW3cV3R5tjVQm0MOm+D2C5+bBW9vHLjLR4lun4toQiY3Ls/v4bES/OJ4EmpZk5xhL9i5ClofYZNEsxFn"+ + "An/q821Tg+Cq9Er4XYGQe8ogjjLJ2b7dUsJ3auFQFNUJF7Ke7yUL2EeYYxl6vz5l4q5u8704mRbFts1E1eWMp6WIy91GPrsVlRGvtuNERfrjfE"+ + "YtzUI3Flcv65zJUbUBEzUnTS0fEYso2XyToAl8kb251mUY2o2lJzv5dp/1htmcjeeP2MjxC+3S45ljx7jd52Pv9XAat+ryiauFOF7YgztkoWWD"+ + "h62tplPH1bzDV+d0NLdaE5AfVJ09HuUYTFS+iggtvT5Euyk+unj4N2XvzW91n+GNjtgWfKOHmkinUPvYRh70Jv+wlPJrVaT8mL7GxJLqDC9jbv"+ + "Gznoiae6es+wQejnk3XjU366MrK/zXxngBYj9J6NnXc9mMiTFLX8WqQ8iTelTAFs2NJzPoDzrBUz4JFIEOa6Dja6dULc68g1jFDTeEHZyra7RZ"+ + "2ElqGDEqcNRo3SNX6feMy9EF1GOyZK0Sa87KwjKw8aM68dpsIYjfLcTXaZ6atg0BKfMnl6axeUGEaIFSP7rzj9wjzumRbG3jgUVp2lX5AK/tsO"+ + "7R4TQX/9/H6RiN34c9KldmPZZGANXzzTajZS9mR2OSvlJ+F4AgSko4htrMAKFTBu51/5SWNsO1vlRaaG48ZRJ+8PzuHQMdvS36gNpRPi7jhF1S"+ + "H3B2ycI4y0VURv6SrqJNUY/X645ZFJQ+eBO+ptG7o8axf1dcqh2beiQk+GRTeZ37LVeUlaeo9vl1/+8tyBfyT2v5lFC5E19WdKIyCuZe7r99Px"+ + "D/Od4Qj0TA92+DQnbCQTCMy/wwse9O4gsEebkkpPIP5GBV3Q0YBsj75XE0uSFQ1tCZSW8bNa9MUJZ/nPBfExohHlgGAAA="; + + // Unix + final String unixSignExp = + "QkqTFQZjXagjRAoOWKpAGa8AR0rKqkSfBtfSWqtjBmTgyjarn+t2POHkpySIpheHAbg+90GKSH88ACMtPHbG7q" + + "FL4gtgAD9Kjew6j16j0IRBwy145UlPrSLFMfF7YF7UlU1k1LBkIlRJ6Fv4MAJl6XspuzZOZIUmHZrWrdxycUQ="; + + // Windows + final String winSignExp = + "GmAlL7+bT1r3FsMHJOp3pKg8betblYieZTjhMIrPZPRBbSzjO7KsYRGNtr0aOE3qr8xzyYJN6/8QdF5X7pUEUc" + + "2m8ctrm7s5o2vZTkAqk9ENJGDjBPXX7TnuVOiVeL1cJdtjHC2QpjtRwkFR+B54G6b1OXLOFuQpP3vqR3+/XXE="; + + // Mac + final String macSignExp = + "NZedY/LNTYU4nAUEUhIOg5+fKdgVtzRXKmdD3v+47E7Mb84oeiUGv9cCEE91DU3StF/JFIhjOJqavOzKnCsNcz" + + "NJ4j/inggUl1OJUsicqIGQnA7E8vzWnN1kf5lINgJLv+0PyrrX9sQZbItzxUpgqyOFYcD0trid+31nRt4wtaA="; + + Calendar cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC); cal.clear(); cal.setTimeZone(LocaleUtil.TIMEZONE_UTC); cal.set(2017, Calendar.JULY, 1); - SignatureConfig signatureConfig = prepareConfig("test", "CN=Test", pfxInput); + SignatureConfig signatureConfig = prepareConfig(pfxInput); signatureConfig.setExecutionTime(cal.getTime()); SignatureInfo si = new SignatureInfo(); si.setSignatureConfig(signatureConfig); - XSSFWorkbook wb1 = new XSSFWorkbook(); - wb1.createSheet().createRow(1).createCell(1).setCellValue("Test"); ByteArrayOutputStream bos = new ByteArrayOutputStream(100000); - wb1.write(bos); - wb1.close(); - - OPCPackage pkg1 = OPCPackage.open(new ByteArrayInputStream(bos.toByteArray())); + try (XSSFWorkbook wb1 = new XSSFWorkbook()) { + wb1.createSheet().createRow(1).createCell(1).setCellValue("Test"); + wb1.write(bos); + } - signatureConfig.setOpcPackage(pkg1); - si.confirmSignature(); - assertTrue(si.verifySignature()); - bos.reset(); - pkg1.save(bos); - pkg1.close(); - - XSSFWorkbook wb2 = new XSSFWorkbook(new ByteArrayInputStream(bos.toByteArray())); - assertEquals("Test", wb2.getSheetAt(0).getRow(1).getCell(1).getStringCellValue()); - OPCPackage pkg2 = wb2.getPackage(); - signatureConfig.setOpcPackage(pkg2); - assertTrue(si.verifySignature()); - - // xmlbeans adds line-breaks depending on the system setting, so we get different - // test results on Unix/Mac/Windows - // if the xml documents eventually change, this test needs to be run with the - // separator set to the various system configurations - String sep = SystemProperties.getProperty( "line.separator" ); - String signExp; - assumeTrue("Hashes only known for Windows/Unix/Mac", sep == null || "\n".equals(sep) || "\r\n".equals(sep) || "\r".equals(sep)); - if (sep == null || "\n".equals(sep)) { - // Unix - signExp = - "QkqTFQZjXagjRAoOWKpAGa8AR0rKqkSfBtfSWqtjBmTgyjarn+t2POHkpySIpheHAbg+90GKSH88ACMtPHbG7q"+ - "FL4gtgAD9Kjew6j16j0IRBwy145UlPrSLFMfF7YF7UlU1k1LBkIlRJ6Fv4MAJl6XspuzZOZIUmHZrWrdxycUQ="; - } else if ("\r\n".equals(sep)){ - // Windows - signExp = - "GmAlL7+bT1r3FsMHJOp3pKg8betblYieZTjhMIrPZPRBbSzjO7KsYRGNtr0aOE3qr8xzyYJN6/8QdF5X7pUEUc"+ - "2m8ctrm7s5o2vZTkAqk9ENJGDjBPXX7TnuVOiVeL1cJdtjHC2QpjtRwkFR+B54G6b1OXLOFuQpP3vqR3+/XXE="; - } else { - // Mac - signExp = - "NZedY/LNTYU4nAUEUhIOg5+fKdgVtzRXKmdD3v+47E7Mb84oeiUGv9cCEE91DU3StF/JFIhjOJqavOzKnCsNcz"+ - "NJ4j/inggUl1OJUsicqIGQnA7E8vzWnN1kf5lINgJLv+0PyrrX9sQZbItzxUpgqyOFYcD0trid+31nRt4wtaA="; + try (OPCPackage pkg1 = OPCPackage.open(new ByteArrayInputStream(bos.toByteArray()))) { + si.setOpcPackage(pkg1); + si.confirmSignature(); + assertTrue(si.verifySignature()); + bos.reset(); + pkg1.save(bos); } - String signAct = si.getSignatureParts().iterator().next(). - getSignatureDocument().getSignature().getSignatureValue().getStringValue(); - assertEquals(signExp, signAct); + try (XSSFWorkbook wb2 = new XSSFWorkbook(new ByteArrayInputStream(bos.toByteArray()))) { + assertEquals("Test", wb2.getSheetAt(0).getRow(1).getCell(1).getStringCellValue()); + OPCPackage pkg2 = wb2.getPackage(); + si.setOpcPackage(pkg2); + assertTrue(si.verifySignature()); - pkg2.close(); - wb2.close(); + // xmlbeans adds line-breaks depending on the system setting, so we get different + // test results on Unix/Mac/Windows + // if the xml documents eventually change, this test needs to be run with the + // separator set to the various system configurations + String sep = SystemProperties.getProperty("line.separator"); + String signExp; + assumeTrue("Hashes only known for Windows/Unix/Mac", sep == null || "\n".equals(sep) || "\r\n".equals(sep) || "\r".equals(sep)); + signExp = (sep == null || "\n".equals(sep)) ? unixSignExp : ("\r\n".equals(sep)) ? winSignExp : macSignExp; + + String signAct = si.getSignatureParts().iterator().next(). + getSignatureDocument().getSignature().getSignatureValue().getStringValue(); + assertEquals(signExp, signAct); + } } @Test public void office2007prettyPrintedRels() throws Exception { try (OPCPackage pkg = OPCPackage.open(testdata.getFile("office2007prettyPrintedRels.docx"), PackageAccess.READ)) { SignatureConfig sic = new SignatureConfig(); - sic.setOpcPackage(pkg); SignatureInfo si = new SignatureInfo(); + si.setOpcPackage(pkg); si.setSignatureConfig(sic); boolean isValid = si.verifySignature(); assertTrue(isValid); @@ -315,19 +304,19 @@ public class TestSignatureInfo { }; for (String testFile : testFiles) { - OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ); - SignatureConfig sic = new SignatureConfig(); - sic.setOpcPackage(pkg); - SignatureInfo si = new SignatureInfo(); - si.setSignatureConfig(sic); List<X509Certificate> result = new ArrayList<>(); - for (SignaturePart sp : si.getSignatureParts()) { - if (sp.validate()) { - result.add(sp.getSigner()); + try (OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ)) { + SignatureConfig sic = new SignatureConfig(); + SignatureInfo si = new SignatureInfo(); + si.setOpcPackage(pkg); + si.setSignatureConfig(sic); + for (SignaturePart sp : si.getSignatureParts()) { + if (sp.validate()) { + result.add(sp.getSigner()); + } } + pkg.revert(); } - pkg.revert(); - pkg.close(); assertNotNull(result); assertTrue(result.isEmpty()); } @@ -345,14 +334,14 @@ public class TestSignatureInfo { "ms-office-2010-signed.pptx", "ms-office-2010-signed.xlsx", "Office2010-SP1-XAdES-X-L.docx", - "signed.docx", + "signed.docx" }; for (String testFile : testFiles) { try (OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ)) { SignatureConfig sic = new SignatureConfig(); - sic.setOpcPackage(pkg); SignatureInfo si = new SignatureInfo(); + si.setOpcPackage(pkg); si.setSignatureConfig(sic); List<X509Certificate> result = new ArrayList<>(); for (SignaturePart sp : si.getSignatureParts()) { @@ -378,8 +367,8 @@ public class TestSignatureInfo { String testFile = "hello-world-signed-twice.docx"; try (OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ)) { SignatureConfig sic = new SignatureConfig(); - sic.setOpcPackage(pkg); SignatureInfo si = new SignatureInfo(); + si.setOpcPackage(pkg); si.setSignatureConfig(sic); List<X509Certificate> result = new ArrayList<>(); for (SignaturePart sp : si.getSignatureParts()) { @@ -404,9 +393,9 @@ public class TestSignatureInfo { @Test public void testSignSpreadsheet() throws Exception { String testFile = "hello-world-unsigned.xlsx"; - OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE); - sign(pkg, "Test", "CN=Test", 1); - pkg.close(); + try (OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE)) { + sign(pkg); + } } private static class CommitableWorkbook extends XSSFWorkbook { @@ -423,7 +412,7 @@ public class TestSignatureInfo { // sign & validate String testFile = "hello-world-unsigned.xlsx"; try (OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE)) { - sign(pkg, "Test", "CN=Test", 1); + sign(pkg); // manipulate try (CommitableWorkbook wb = new CommitableWorkbook(pkg)) { @@ -436,8 +425,8 @@ public class TestSignatureInfo { // validate SignatureConfig sic = new SignatureConfig(); - sic.setOpcPackage(pkg); SignatureInfo si = new SignatureInfo(); + si.setOpcPackage(pkg); si.setSignatureConfig(sic); boolean b = si.verifySignature(); assertFalse("signature should be broken", b); @@ -449,14 +438,14 @@ public class TestSignatureInfo { @Test public void testSignSpreadsheetWithSignatureInfo() throws Exception { - initKeyPair("Test", "CN=Test"); + initKeyPair(); String testFile = "hello-world-unsigned.xlsx"; try (OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE)) { SignatureConfig sic = new SignatureConfig(); - sic.setOpcPackage(pkg); sic.setKey(keyPair.getPrivate()); sic.setSigningCertificateChain(Collections.singletonList(x509)); SignatureInfo si = new SignatureInfo(); + si.setOpcPackage(pkg); si.setSignatureConfig(sic); // hash > sha1 doesn't work in excel viewer ... si.confirmSignature(); @@ -481,12 +470,11 @@ public class TestSignatureInfo { try (OPCPackage pkg = OPCPackage.open(copy(sigCopy), PackageAccess.READ_WRITE)) { - initKeyPair("Test", "CN=Test"); + initKeyPair(); final X509CRL crl = generateCrl(x509, keyPair.getPrivate()); // setup SignatureConfig signatureConfig = new SignatureConfig(); - signatureConfig.setOpcPackage(pkg); signatureConfig.setKey(keyPair.getPrivate()); /* @@ -529,17 +517,9 @@ public class TestSignatureInfo { } if (mockTsp) { - TimeStampService tspService = new TimeStampService() { - @Override - public byte[] timeStamp(byte[] data, RevocationData revocationData) { - revocationData.addCRL(crl); - return "time-stamp-token".getBytes(LocaleUtil.CHARSET_1252); - } - - @Override - public void setSignatureConfig(SignatureConfig config) { - // empty on purpose - } + TimeStampService tspService = (signatureInfo, data, revocationData) -> { + revocationData.addCRL(crl); + return "time-stamp-token".getBytes(LocaleUtil.CHARSET_1252); }; signatureConfig.setTspService(tspService); } else { @@ -555,8 +535,7 @@ public class TestSignatureInfo { final RevocationData revocationData = new RevocationData(); revocationData.addCRL(crl); - OCSPResp ocspResp = createOcspResp(x509, false, - x509, x509, keyPair.getPrivate(), "SHA1withRSA", cal.getTimeInMillis()); + OCSPResp ocspResp = createOcspResp(x509, x509, x509, keyPair.getPrivate(), cal.getTimeInMillis()); revocationData.addOCSP(ocspResp.getEncoded()); RevocationDataService revocationDataService = revocationChain -> revocationData; @@ -564,33 +543,31 @@ public class TestSignatureInfo { // operate SignatureInfo si = new SignatureInfo(); + si.setOpcPackage(pkg); si.setSignatureConfig(signatureConfig); try { si.confirmSignature(); } catch (RuntimeException e) { - pkg.close(); // only allow a ConnectException because of timeout, we see this in Jenkins from time to time... if (e.getCause() == null) { throw e; } if ((e.getCause() instanceof ConnectException) || (e.getCause() instanceof SocketTimeoutException)) { Assume.assumeFalse("Only allowing ConnectException with 'timed out' as message here, but had: " + e, - e.getCause().getMessage().contains("timed out")); + e.getCause().getMessage().contains("timed out")); } else if (e.getCause() instanceof IOException) { Assume.assumeFalse("Only allowing IOException with 'Error contacting TSP server' as message here, but had: " + e, - e.getCause().getMessage().contains("Error contacting TSP server")); + e.getCause().getMessage().contains("Error contacting TSP server")); } else if (e.getCause() instanceof RuntimeException) { Assume.assumeFalse("Only allowing RuntimeException with 'This site is cur' as message here, but had: " + e, - e.getCause().getMessage().contains("This site is cur")); + e.getCause().getMessage().contains("This site is cur")); } throw e; } // verify Iterator<SignaturePart> spIter = si.getSignatureParts().iterator(); - assertTrue("Had: " + si.getSignatureConfig().getOpcPackage(). - getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN), - spIter.hasNext()); + assertTrue("Had: " + pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN), spIter.hasNext()); SignaturePart sp = spIter.next(); boolean valid = sp.validate(); assertTrue(valid); @@ -627,10 +604,10 @@ public class TestSignatureInfo { try (OPCPackage pkg = OPCPackage.open(new ByteArrayInputStream(bos.toByteArray()))) { SignatureConfig signatureConfig = new SignatureConfig(); - signatureConfig.setOpcPackage(pkg); signatureConfig.setUpdateConfigOnValidate(true); SignatureInfo si = new SignatureInfo(); + si.setOpcPackage(pkg); si.setSignatureConfig(signatureConfig); assertTrue(si.verifySignature()); @@ -667,13 +644,7 @@ public class TestSignatureInfo { conn.connect(); if (fireRequest) { - InputStream is = null; - try { - is = conn.getInputStream(); - } finally { - IOUtils.closeQuietly(is); - } - + conn.getInputStream().close(); } /* if connecting is possible we return true here */ return null; @@ -692,9 +663,9 @@ public class TestSignatureInfo { public void testCertChain() throws Exception { KeyStore keystore = KeyStore.getInstance("PKCS12"); String password = "test"; - InputStream is = testdata.openResourceAsStream("chaintest.pfx"); - keystore.load(is, password.toCharArray()); - is.close(); + try (InputStream is = testdata.openResourceAsStream("chaintest.pfx")) { + keystore.load(is, password.toCharArray()); + } Key key = keystore.getKey("poitest", password.toCharArray()); Certificate[] chainList = keystore.getCertificateChain("poitest"); @@ -714,9 +685,9 @@ public class TestSignatureInfo { Calendar oldCal = LocaleUtil.getLocaleCalendar(2007, 7, 1); signatureConfig.setExecutionTime(oldCal.getTime()); signatureConfig.setDigestAlgo(HashAlgorithm.sha1); - signatureConfig.setOpcPackage(pkg); SignatureInfo si = new SignatureInfo(); + si.setOpcPackage(pkg); si.setSignatureConfig(signatureConfig); si.confirmSignature(); @@ -735,7 +706,7 @@ public class TestSignatureInfo { @Test public void testNonSha1() throws Exception { String testFile = "hello-world-unsigned.xlsx"; - initKeyPair("Test", "CN=Test"); + initKeyPair(); SignatureConfig signatureConfig = new SignatureConfig(); signatureConfig.setKey(keyPair.getPrivate()); @@ -745,13 +716,10 @@ public class TestSignatureInfo { , HashAlgorithm.sha384, HashAlgorithm.sha512, HashAlgorithm.ripemd160}; for (HashAlgorithm ha : testAlgo) { - OPCPackage pkg = null; - try { - signatureConfig.setDigestAlgo(ha); - pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE); - signatureConfig.setOpcPackage(pkg); - + signatureConfig.setDigestAlgo(ha); + try (OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE)) { SignatureInfo si = new SignatureInfo(); + si.setOpcPackage(pkg); si.setSignatureConfig(signatureConfig); si.confirmSignature(); @@ -759,10 +727,6 @@ public class TestSignatureInfo { assertTrue("Signature not correctly calculated for " + ha, b); } catch (EncryptedDocumentException e) { Assume.assumeTrue(e.getMessage().startsWith("Export Restrictions")); - } finally { - if (pkg != null) { - pkg.close(); - } } } } @@ -776,20 +740,18 @@ public class TestSignatureInfo { wb1.removeSheetAt(0); ByteArrayOutputStream os = new ByteArrayOutputStream(); wb1.write(os); - wb1.close(); - try (OPCPackage pkg = OPCPackage.open(new ByteArrayInputStream(os.toByteArray()))) { - initKeyPair("Test", "CN=Test"); + try (OPCPackage pkg = OPCPackage.open(new ByteArrayInputStream(os.toByteArray()))) { + initKeyPair(); SignatureConfig signatureConfig = new SignatureConfig(); signatureConfig.setKey(keyPair.getPrivate()); signatureConfig.setSigningCertificateChain(Collections.singletonList(x509)); - signatureConfig.setOpcPackage(pkg); SignatureInfo si = new SignatureInfo(); + si.setOpcPackage(pkg); si.setSignatureConfig(signatureConfig); si.confirmSignature(); assertTrue("invalid signature", si.verifySignature()); - } } } @@ -833,8 +795,8 @@ public class TestSignatureInfo { private void verifyPkg63011(File tpl, boolean multi) throws InvalidFormatException, IOException { try (OPCPackage pkg = OPCPackage.open(tpl, PackageAccess.READ)) { SignatureConfig sic = new SignatureConfig(); - sic.setOpcPackage(pkg); SignatureInfo si = new SignatureInfo(); + si.setOpcPackage(pkg); si.setSignatureConfig(sic); List<X509Certificate> result = new ArrayList<>(); for (SignaturePart sp : si.getSignatureParts()) { @@ -860,7 +822,7 @@ public class TestSignatureInfo { } private void signPkg63011(OPCPackage pkg, String pemFile, boolean multi) - throws IOException, CertificateException, XMLSignatureException, MarshalException { + throws IOException, CertificateException, XMLSignatureException, MarshalException { assertNotNull(pkg); initKeyFromPEM(testdata.getFile(pemFile)); @@ -869,9 +831,9 @@ public class TestSignatureInfo { config.setSigningCertificateChain(Collections.singletonList(x509)); config.setExecutionTime(cal.getTime()); config.setAllowMultipleSignatures(multi); - config.setOpcPackage(pkg); SignatureInfo si = new SignatureInfo(); + si.setOpcPackage(pkg); si.setSignatureConfig(config); si.confirmSignature(); } @@ -881,9 +843,9 @@ public class TestSignatureInfo { SignatureConfig sic = new SignatureConfig(); final File file = testdata.getFile("PPT2016withComment.pptx"); try (final OPCPackage pkg = OPCPackage.open(file, PackageAccess.READ)) { - sic.setOpcPackage(pkg); sic.setUpdateConfigOnValidate(true); SignatureInfo si = new SignatureInfo(); + si.setOpcPackage(pkg); si.setSignatureConfig(sic); assertTrue(si.verifySignature()); } @@ -897,8 +859,8 @@ public class TestSignatureInfo { assertEquals(CanonicalizationMethod.INCLUSIVE, sic.getCanonicalizationMethod()); } - private SignatureConfig prepareConfig(String alias, String signerDn, String pfxInput) throws Exception { - initKeyPair(alias, signerDn, pfxInput); + private SignatureConfig prepareConfig(String pfxInput) throws Exception { + initKeyPair(pfxInput); SignatureConfig signatureConfig = new SignatureConfig(); signatureConfig.setKey(keyPair.getPrivate()); @@ -909,11 +871,13 @@ public class TestSignatureInfo { return signatureConfig; } - private void sign(OPCPackage pkgCopy, String alias, String signerDn, int signerCount) throws Exception { - SignatureConfig signatureConfig = prepareConfig(alias, signerDn, null); - signatureConfig.setOpcPackage(pkgCopy); + private void sign(OPCPackage pkgCopy) throws Exception { + int signerCount = 1; + + SignatureConfig signatureConfig = prepareConfig(null); SignatureInfo si = new SignatureInfo(); + si.setOpcPackage(pkgCopy); si.setSignatureConfig(signatureConfig); final Document document = DocumentHelper.createDocument(); @@ -933,7 +897,7 @@ public class TestSignatureInfo { si.postSign(xmlSignContext, signatureValue); // verify: signature - si.getSignatureConfig().setOpcPackage(pkgCopy); + si.setOpcPackage(pkgCopy); List<X509Certificate> result = new ArrayList<>(); for (SignaturePart sp : si.getSignatureParts()) { if (sp.validate()) { @@ -943,24 +907,25 @@ public class TestSignatureInfo { assertEquals(signerCount, result.size()); } - private void initKeyPair(String alias, String subjectDN) throws Exception { - initKeyPair(alias, subjectDN, null); + private void initKeyPair() throws Exception { + initKeyPair(null); } - private void initKeyPair(String alias, String subjectDN, String pfxInput) throws Exception { + private void initKeyPair(String pfxInput) throws Exception { + final String alias = "Test"; final char[] password = "test".toCharArray(); File file = new File("build/test.pfx"); KeyStore keystore = KeyStore.getInstance("PKCS12"); if (pfxInput != null) { - InputStream fis = new ByteArrayInputStream(RawDataUtil.decompress(pfxInput)); - keystore.load(fis, password); - fis.close(); + try (InputStream fis = new ByteArrayInputStream(RawDataUtil.decompress(pfxInput))) { + keystore.load(fis, password); + } } else if (file.exists()) { - InputStream fis = new FileInputStream(file); - keystore.load(fis, password); - fis.close(); + try (InputStream fis = new FileInputStream(file)) { + keystore.load(fis, password); + } } else { keystore.load(null, password); } @@ -977,15 +942,14 @@ public class TestSignatureInfo { Date notAfter = cal2.getTime(); KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature); - x509 = generateCertificate(keyPair.getPublic(), subjectDN - , notBefore, notAfter, null, keyPair.getPrivate(), true, 0, null, null, keyUsage); + x509 = generateCertificate(keyPair.getPublic(), notBefore, notAfter, keyPair.getPrivate(), keyUsage); keystore.setKeyEntry(alias, keyPair.getPrivate(), password, new Certificate[]{x509}); if (pfxInput == null) { - FileOutputStream fos = new FileOutputStream(file); - keystore.store(fos, password); - fos.close(); + try (FileOutputStream fos = new FileOutputStream(file)) { + keystore.store(fos, password); + } } } } @@ -1038,24 +1002,18 @@ public class TestSignatureInfo { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); SecureRandom random = new SecureRandom(); keyPairGenerator.initialize(new RSAKeyGenParameterSpec(1024, - RSAKeyGenParameterSpec.F4), random); + RSAKeyGenParameterSpec.F4), random); return keyPairGenerator.generateKeyPair(); } private static X509Certificate generateCertificate(PublicKey subjectPublicKey, - String subjectDn, Date notBefore, Date notAfter, - X509Certificate issuerCertificate, PrivateKey issuerPrivateKey, - boolean caFlag, int pathLength, String crlUri, String ocspUri, - KeyUsage keyUsage) - throws IOException, OperatorCreationException, CertificateException - { - String signatureAlgorithm = "SHA1withRSA"; - X500Name issuerName; - if (issuerCertificate != null) { - issuerName = new X509CertificateHolder(issuerCertificate.getEncoded()).getIssuer(); - } else { - issuerName = new X500Name(subjectDn); - } + Date notBefore, Date notAfter, + PrivateKey issuerPrivateKey, + KeyUsage keyUsage) + throws IOException, OperatorCreationException, CertificateException { + final String signatureAlgorithm = "SHA1withRSA"; + final String subjectDn = "CN=Test"; + X500Name issuerName = new X500Name(subjectDn); RSAPublicKey rsaPubKey = (RSAPublicKey)subjectPublicKey; RSAKeyParameters rsaSpec = new RSAKeyParameters(false, rsaPubKey.getModulus(), rsaPubKey.getPublicExponent()); @@ -1077,47 +1035,13 @@ public class TestSignatureInfo { X509ExtensionUtils exUtils = new X509ExtensionUtils(digestCalc); SubjectKeyIdentifier subKeyId = exUtils.createSubjectKeyIdentifier(subjectPublicKeyInfo); - AuthorityKeyIdentifier autKeyId = (issuerCertificate != null) - ? exUtils.createAuthorityKeyIdentifier(new X509CertificateHolder(issuerCertificate.getEncoded())) - : exUtils.createAuthorityKeyIdentifier(subjectPublicKeyInfo); + AuthorityKeyIdentifier autKeyId = exUtils.createAuthorityKeyIdentifier(subjectPublicKeyInfo); certificateGenerator.addExtension(Extension.subjectKeyIdentifier, false, subKeyId); certificateGenerator.addExtension(Extension.authorityKeyIdentifier, false, autKeyId); - if (caFlag) { - BasicConstraints bc; - - if (-1 == pathLength) { - bc = new BasicConstraints(true); - } else { - bc = new BasicConstraints(pathLength); - } - certificateGenerator.addExtension(Extension.basicConstraints, false, bc); - } - - if (null != crlUri) { - int uri = GeneralName.uniformResourceIdentifier; - DERIA5String crlUriDer = new DERIA5String(crlUri); - GeneralName gn = new GeneralName(uri, crlUriDer); - - DERSequence gnDer = new DERSequence(gn); - GeneralNames gns = GeneralNames.getInstance(gnDer); - - DistributionPointName dpn = new DistributionPointName(0, gns); - DistributionPoint distp = new DistributionPoint(dpn, null, null); - DERSequence distpDer = new DERSequence(distp); - certificateGenerator.addExtension(Extension.cRLDistributionPoints, false, distpDer); - } - - if (null != ocspUri) { - int uri = GeneralName.uniformResourceIdentifier; - GeneralName ocspName = new GeneralName(uri, ocspUri); - - AuthorityInformationAccess authorityInformationAccess = - new AuthorityInformationAccess(X509ObjectIdentifiers.ocspAccessMethod, ocspName); - - certificateGenerator.addExtension(Extension.authorityInfoAccess, false, authorityInformationAccess); - } + BasicConstraints bc = new BasicConstraints(0); + certificateGenerator.addExtension(Extension.basicConstraints, false, bc); if (null != keyUsage) { certificateGenerator.addExtension(Extension.keyUsage, true, keyUsage); @@ -1158,10 +1082,10 @@ public class TestSignatureInfo { } private static OCSPResp createOcspResp(X509Certificate certificate, - boolean revoked, X509Certificate issuerCertificate, - X509Certificate ocspResponderCertificate, - PrivateKey ocspResponderPrivateKey, String signatureAlgorithm, - long nonceTimeinMillis) + X509Certificate issuerCertificate, + X509Certificate ocspResponderCertificate, + PrivateKey ocspResponderPrivateKey, + long nonceTimeinMillis) throws Exception { DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder() .setProvider("BC").build().get(CertificateID.HASH_SHA1); @@ -1192,9 +1116,6 @@ public class TestSignatureInfo { for (Req ocspRequest : requestList) { CertificateID certificateID = ocspRequest.getCertID(); CertificateStatus certificateStatus = CertificateStatus.GOOD; - if (revoked) { - certificateStatus = new RevokedStatus(new Date(), CRLReason.privilegeWithdrawn); - } basicOCSPRespBuilder.addResponse(certificateID, certificateStatus); } |