#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
This commit is contained in:
Andreas Beeker 2020-03-01 22:20:38 +00:00
parent 3691704678
commit b04379bc4c
17 changed files with 877 additions and 808 deletions

View File

@ -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);
}
}

View File

@ -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();
}
/**
* This method tests the existence of xml signature provider in the following order:
* <ul>
* 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();
}
/**
* 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>
* </ol>
*
* For signing the classes are linked against the Santuario xmlsec, so this might
* only work for validation (not tested).
*
* @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>

View File

@ -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;
initXmlProvider();
return SignaturePartIterator::new;
}
@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;
}
private final class SignaturePartIterator implements Iterator<SignaturePart> {
Iterator<PackageRelationship> sigOrigRels;
private Iterator<PackageRelationship> sigRels;
private PackagePart sigPart;
@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 SignaturePartIterator() {
sigOrigRels = opcPackage.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN).iterator();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
@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);
}
}
};
}
/**
* Initialize the xml signing environment and the bouncycastle provider
*/
protected static synchronized void initXmlProvider() {
if (isInitialized) {
return;
return true;
}
isInitialized = true;
try {
Init.init();
RelationshipTransformService.registerDsigProvider();
CryptoFunctions.registerBouncyCastle();
} catch (Exception e) {
throw new RuntimeException("Xml & BouncyCastle-Provider initialization failed", e);
@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);
}
@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!");
}
}
}

View File

@ -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));
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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");

View File

@ -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);
}

View File

@ -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));
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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
*/

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -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()));
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 (XSSFWorkbook wb1 = new XSSFWorkbook()) {
wb1.createSheet().createRow(1).createCell(1).setCellValue("Test");
wb1.write(bos);
}
String signAct = si.getSignatureParts().iterator().next().
getSignatureDocument().getSignature().getSignatureValue().getStringValue();
assertEquals(signExp, signAct);
try (OPCPackage pkg1 = OPCPackage.open(new ByteArrayInputStream(bos.toByteArray()))) {
si.setOpcPackage(pkg1);
si.confirmSignature();
assertTrue(si.verifySignature());
bos.reset();
pkg1.save(bos);
}
pkg2.close();
wb2.close();
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());
// 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);
}