mirror of https://github.com/apache/poi.git
reorganization, xmlsignatureservice is now in signatureinfo
git-svn-id: https://svn.apache.org/repos/asf/poi/branches/xml_signature@1626107 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
9fec16c622
commit
1fd5289bf6
|
@ -24,48 +24,88 @@
|
||||||
|
|
||||||
package org.apache.poi.poifs.crypt.dsig;
|
package org.apache.poi.poifs.crypt.dsig;
|
||||||
|
|
||||||
|
import static org.apache.xml.security.signature.XMLSignature.ALGO_ID_MAC_HMAC_RIPEMD160;
|
||||||
|
import static org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
|
||||||
|
import static org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256;
|
||||||
|
import static org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA384;
|
||||||
|
import static org.apache.xml.security.signature.XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.PrivateKey;
|
import java.security.NoSuchProviderException;
|
||||||
import java.security.Provider;
|
import java.security.Provider;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.xml.crypto.MarshalException;
|
import javax.xml.crypto.MarshalException;
|
||||||
|
import javax.xml.crypto.URIDereferencer;
|
||||||
|
import javax.xml.crypto.XMLStructure;
|
||||||
|
import javax.xml.crypto.dsig.CanonicalizationMethod;
|
||||||
|
import javax.xml.crypto.dsig.DigestMethod;
|
||||||
|
import javax.xml.crypto.dsig.Manifest;
|
||||||
|
import javax.xml.crypto.dsig.Reference;
|
||||||
|
import javax.xml.crypto.dsig.SignatureMethod;
|
||||||
|
import javax.xml.crypto.dsig.SignedInfo;
|
||||||
|
import javax.xml.crypto.dsig.XMLObject;
|
||||||
|
import javax.xml.crypto.dsig.XMLSignContext;
|
||||||
import javax.xml.crypto.dsig.XMLSignature;
|
import javax.xml.crypto.dsig.XMLSignature;
|
||||||
|
import javax.xml.crypto.dsig.XMLSignatureException;
|
||||||
import javax.xml.crypto.dsig.XMLSignatureFactory;
|
import javax.xml.crypto.dsig.XMLSignatureFactory;
|
||||||
|
import javax.xml.crypto.dsig.dom.DOMSignContext;
|
||||||
import javax.xml.crypto.dsig.dom.DOMValidateContext;
|
import javax.xml.crypto.dsig.dom.DOMValidateContext;
|
||||||
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
|
import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
|
||||||
|
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
import javax.xml.parsers.ParserConfigurationException;
|
||||||
|
import javax.xml.transform.TransformerException;
|
||||||
|
import javax.xml.transform.TransformerFactoryConfigurationError;
|
||||||
|
|
||||||
|
import org.apache.jcp.xml.dsig.internal.dom.DOMReference;
|
||||||
|
import org.apache.jcp.xml.dsig.internal.dom.DOMSignedInfo;
|
||||||
import org.apache.poi.EncryptedDocumentException;
|
import org.apache.poi.EncryptedDocumentException;
|
||||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
||||||
import org.apache.poi.openxml4j.opc.OPCPackage;
|
import org.apache.poi.openxml4j.opc.OPCPackage;
|
||||||
|
import org.apache.poi.openxml4j.opc.PackageNamespaces;
|
||||||
import org.apache.poi.openxml4j.opc.PackagePart;
|
import org.apache.poi.openxml4j.opc.PackagePart;
|
||||||
|
import org.apache.poi.openxml4j.opc.PackagePartName;
|
||||||
import org.apache.poi.openxml4j.opc.PackageRelationship;
|
import org.apache.poi.openxml4j.opc.PackageRelationship;
|
||||||
import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
|
import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
|
||||||
import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
|
import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
|
||||||
|
import org.apache.poi.openxml4j.opc.PackagingURIHelper;
|
||||||
|
import org.apache.poi.openxml4j.opc.TargetMode;
|
||||||
import org.apache.poi.poifs.crypt.ChainingMode;
|
import org.apache.poi.poifs.crypt.ChainingMode;
|
||||||
import org.apache.poi.poifs.crypt.CipherAlgorithm;
|
import org.apache.poi.poifs.crypt.CipherAlgorithm;
|
||||||
import org.apache.poi.poifs.crypt.CryptoFunctions;
|
import org.apache.poi.poifs.crypt.CryptoFunctions;
|
||||||
import org.apache.poi.poifs.crypt.HashAlgorithm;
|
import org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet;
|
||||||
import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService;
|
import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService;
|
||||||
import org.apache.poi.poifs.crypt.dsig.services.XmlSignatureService;
|
|
||||||
import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;
|
import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;
|
||||||
import org.apache.poi.util.DocumentHelper;
|
import org.apache.poi.util.DocumentHelper;
|
||||||
import org.apache.poi.util.POILogFactory;
|
import org.apache.poi.util.POILogFactory;
|
||||||
import org.apache.poi.util.POILogger;
|
import org.apache.poi.util.POILogger;
|
||||||
import org.apache.xml.security.Init;
|
import org.apache.xml.security.Init;
|
||||||
import org.apache.xmlbeans.XmlCursor;
|
import org.apache.xml.security.utils.Base64;
|
||||||
import org.apache.xmlbeans.XmlException;
|
import org.apache.xmlbeans.XmlException;
|
||||||
import org.apache.xmlbeans.XmlObject;
|
import org.apache.xmlbeans.XmlOptions;
|
||||||
|
import org.w3.x2000.x09.xmldsig.SignatureDocument;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
|
import org.w3c.dom.Element;
|
||||||
import org.w3c.dom.Node;
|
import org.w3c.dom.Node;
|
||||||
import org.w3c.dom.NodeList;
|
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;
|
||||||
|
import org.xml.sax.SAXException;
|
||||||
|
|
||||||
public class SignatureInfo {
|
public class SignatureInfo {
|
||||||
|
|
||||||
|
@ -104,12 +144,16 @@ public class SignatureInfo {
|
||||||
private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class);
|
private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class);
|
||||||
private static boolean isInitialized = false;
|
private static boolean isInitialized = false;
|
||||||
|
|
||||||
private final OPCPackage pkg;
|
private SignatureInfoConfig signatureConfig;
|
||||||
|
|
||||||
public SignatureInfo(OPCPackage pkg) {
|
public SignatureInfoConfig getSignatureConfig() {
|
||||||
this.pkg = pkg;
|
return signatureConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSignatureConfig(SignatureInfoConfig signatureConfig) {
|
||||||
|
this.signatureConfig = signatureConfig;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean verifySignature() {
|
public boolean verifySignature() {
|
||||||
initXmlProvider();
|
initXmlProvider();
|
||||||
// http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html
|
// http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html
|
||||||
|
@ -117,40 +161,27 @@ public class SignatureInfo {
|
||||||
return getSignersAndValidate(signers, true);
|
return getSignersAndValidate(signers, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void confirmSignature(PrivateKey key, X509Certificate x509)
|
public void confirmSignature()
|
||||||
throws NoSuchAlgorithmException, IOException, MarshalException, ParserConfigurationException, XmlException {
|
throws NoSuchAlgorithmException, IOException, MarshalException, ParserConfigurationException, XmlException, InvalidAlgorithmParameterException, NoSuchProviderException, XMLSignatureException, TransformerFactoryConfigurationError, TransformerException, SAXException, URISyntaxException {
|
||||||
confirmSignature(key, x509, HashAlgorithm.sha1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void confirmSignature(PrivateKey key, X509Certificate x509, HashAlgorithm hashAlgo)
|
|
||||||
throws NoSuchAlgorithmException, IOException, MarshalException, ParserConfigurationException, XmlException {
|
|
||||||
SignatureInfoConfig signatureConfig = new SignatureInfoConfig();
|
|
||||||
signatureConfig.setOpcPackage(pkg);
|
|
||||||
signatureConfig.setDigestAlgo(hashAlgo);
|
|
||||||
signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
|
|
||||||
signatureConfig.setKey(key);
|
|
||||||
signatureConfig.addDefaultFacets();
|
|
||||||
XmlSignatureService signatureService = new XmlSignatureService(signatureConfig);
|
|
||||||
|
|
||||||
Document document = DocumentHelper.createDocument();
|
Document document = DocumentHelper.createDocument();
|
||||||
|
|
||||||
// operate
|
// operate
|
||||||
DigestInfo digestInfo = signatureService.preSign(document, null);
|
DigestInfo digestInfo = preSign(document, null);
|
||||||
|
|
||||||
// setup: key material, signature value
|
// setup: key material, signature value
|
||||||
byte[] signatureValue = signDigest(key, hashAlgo, digestInfo.digestValue);
|
byte[] signatureValue = signDigest(digestInfo.digestValue);
|
||||||
|
|
||||||
// operate: postSign
|
// operate: postSign
|
||||||
signatureService.postSign(document, signatureValue);
|
postSign(document, signatureValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] signDigest(PrivateKey key, HashAlgorithm hashAlgo, byte digest[]) {
|
public byte[] signDigest(byte digest[]) {
|
||||||
Cipher cipher = CryptoFunctions.getCipher(key, CipherAlgorithm.rsa
|
Cipher cipher = CryptoFunctions.getCipher(signatureConfig.getKey(), CipherAlgorithm.rsa
|
||||||
, ChainingMode.ecb, null, Cipher.ENCRYPT_MODE, "PKCS1Padding");
|
, ChainingMode.ecb, null, Cipher.ENCRYPT_MODE, "PKCS1Padding");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ByteArrayOutputStream digestInfoValueBuf = new ByteArrayOutputStream();
|
ByteArrayOutputStream digestInfoValueBuf = new ByteArrayOutputStream();
|
||||||
digestInfoValueBuf.write(getHashMagic(hashAlgo));
|
digestInfoValueBuf.write(getHashMagic());
|
||||||
digestInfoValueBuf.write(digest);
|
digestInfoValueBuf.write(digest);
|
||||||
byte[] digestInfoValue = digestInfoValueBuf.toByteArray();
|
byte[] digestInfoValue = digestInfoValueBuf.toByteArray();
|
||||||
byte[] signatureValue = cipher.doFinal(digestInfoValue);
|
byte[] signatureValue = cipher.doFinal(digestInfoValue);
|
||||||
|
@ -175,15 +206,12 @@ public class SignatureInfo {
|
||||||
allValid = false;
|
allValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SignatureInfoConfig signatureConfig = new SignatureInfoConfig();
|
|
||||||
signatureConfig.setOpcPackage(pkg);
|
|
||||||
|
|
||||||
for (PackagePart signaturePart : signatureParts) {
|
for (PackagePart signaturePart : signatureParts) {
|
||||||
KeyInfoKeySelector keySelector = new KeyInfoKeySelector();
|
KeyInfoKeySelector keySelector = new KeyInfoKeySelector();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Document doc = DocumentHelper.readDocument(signaturePart.getInputStream());
|
Document doc = DocumentHelper.readDocument(signaturePart.getInputStream());
|
||||||
XmlSignatureService.registerIds(doc);
|
registerIds(doc);
|
||||||
|
|
||||||
DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc);
|
DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc);
|
||||||
domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE);
|
domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE);
|
||||||
|
@ -209,6 +237,7 @@ public class SignatureInfo {
|
||||||
|
|
||||||
protected List<PackagePart> getSignatureParts(boolean onlyFirst) {
|
protected List<PackagePart> getSignatureParts(boolean onlyFirst) {
|
||||||
List<PackagePart> packageParts = new ArrayList<PackagePart>();
|
List<PackagePart> packageParts = new ArrayList<PackagePart>();
|
||||||
|
OPCPackage pkg = signatureConfig.getOpcPackage();
|
||||||
|
|
||||||
PackageRelationshipCollection sigOrigRels = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
|
PackageRelationshipCollection sigOrigRels = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
|
||||||
for (PackageRelationship rel : sigOrigRels) {
|
for (PackageRelationship rel : sigOrigRels) {
|
||||||
|
@ -260,31 +289,6 @@ public class SignatureInfo {
|
||||||
throw new RuntimeException("JRE doesn't support default xml signature provider - set jsr105Provider system property!");
|
throw new RuntimeException("JRE doesn't support default xml signature provider - set jsr105Provider system property!");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void insertXChild(XmlObject root, XmlObject child) {
|
|
||||||
XmlCursor rootCursor = root.newCursor();
|
|
||||||
insertXChild(rootCursor, child);
|
|
||||||
rootCursor.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void insertXChild(XmlCursor rootCursor, XmlObject child) {
|
|
||||||
rootCursor.toEndToken();
|
|
||||||
XmlCursor childCursor = child.newCursor();
|
|
||||||
childCursor.toNextToken();
|
|
||||||
childCursor.moveXml(rootCursor);
|
|
||||||
childCursor.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
// public static void setPrefix(XmlObject xobj, String ns, String prefix) {
|
|
||||||
// XmlCursor cur;
|
|
||||||
// for (cur = xobj.newCursor(); cur.hasNextToken(); cur.toNextToken()) {
|
|
||||||
// if (cur.isStart()) {
|
|
||||||
// Element el = (Element)cur.getDomNode();
|
|
||||||
// if (ns.equals(el.getNamespaceURI())) el.setPrefix(prefix);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// cur.dispose();
|
|
||||||
// }
|
|
||||||
|
|
||||||
public static void setPrefix(Node el, String ns, String prefix) {
|
public static void setPrefix(Node el, String ns, String prefix) {
|
||||||
if (ns.equals(el.getNamespaceURI())) el.setPrefix(prefix);
|
if (ns.equals(el.getNamespaceURI())) el.setPrefix(prefix);
|
||||||
NodeList nl = el.getChildNodes();
|
NodeList nl = el.getChildNodes();
|
||||||
|
@ -293,8 +297,8 @@ public class SignatureInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static byte[] getHashMagic(HashAlgorithm hashAlgo) {
|
protected byte[] getHashMagic() {
|
||||||
switch (hashAlgo) {
|
switch (signatureConfig.getDigestAlgo()) {
|
||||||
case sha1: return SHA1_DIGEST_INFO_PREFIX;
|
case sha1: return SHA1_DIGEST_INFO_PREFIX;
|
||||||
// sha224: return SHA224_DIGEST_INFO_PREFIX;
|
// sha224: return SHA224_DIGEST_INFO_PREFIX;
|
||||||
case sha256: return SHA256_DIGEST_INFO_PREFIX;
|
case sha256: return SHA256_DIGEST_INFO_PREFIX;
|
||||||
|
@ -303,9 +307,22 @@ public class SignatureInfo {
|
||||||
case ripemd128: return RIPEMD128_DIGEST_INFO_PREFIX;
|
case ripemd128: return RIPEMD128_DIGEST_INFO_PREFIX;
|
||||||
case ripemd160: return RIPEMD160_DIGEST_INFO_PREFIX;
|
case ripemd160: return RIPEMD160_DIGEST_INFO_PREFIX;
|
||||||
// case ripemd256: return RIPEMD256_DIGEST_INFO_PREFIX;
|
// case ripemd256: return RIPEMD256_DIGEST_INFO_PREFIX;
|
||||||
default: throw new EncryptedDocumentException("Hash algorithm "+hashAlgo+" not supported for signing.");
|
default: throw new EncryptedDocumentException("Hash algorithm "+signatureConfig.getDigestAlgo()+" not supported for signing.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected String getSignatureMethod() {
|
||||||
|
switch (signatureConfig.getDigestAlgo()) {
|
||||||
|
case sha1: return ALGO_ID_SIGNATURE_RSA_SHA1;
|
||||||
|
case sha256: return ALGO_ID_SIGNATURE_RSA_SHA256;
|
||||||
|
case sha384: return ALGO_ID_SIGNATURE_RSA_SHA384;
|
||||||
|
case sha512: return ALGO_ID_SIGNATURE_RSA_SHA512;
|
||||||
|
case ripemd160: return ALGO_ID_MAC_HMAC_RIPEMD160;
|
||||||
|
default: throw new EncryptedDocumentException("Hash algorithm "+signatureConfig.getDigestAlgo()+" not supported for signing.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static synchronized void initXmlProvider() {
|
public static synchronized void initXmlProvider() {
|
||||||
if (isInitialized) return;
|
if (isInitialized) return;
|
||||||
|
@ -319,4 +336,281 @@ public class SignatureInfo {
|
||||||
throw new RuntimeException("Xml & BouncyCastle-Provider initialization failed", e);
|
throw new RuntimeException("Xml & BouncyCastle-Provider initialization failed", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public DigestInfo preSign(Document document, List<DigestInfo> digestInfos)
|
||||||
|
throws ParserConfigurationException, NoSuchAlgorithmException,
|
||||||
|
InvalidAlgorithmParameterException, MarshalException,
|
||||||
|
javax.xml.crypto.dsig.XMLSignatureException,
|
||||||
|
TransformerFactoryConfigurationError, TransformerException,
|
||||||
|
IOException, SAXException, NoSuchProviderException, XmlException, URISyntaxException {
|
||||||
|
SignatureInfo.initXmlProvider();
|
||||||
|
|
||||||
|
// 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 ...
|
||||||
|
final EventTarget et = (EventTarget)document;
|
||||||
|
EventListener myModificationListener = new EventListener() {
|
||||||
|
@Override
|
||||||
|
public void handleEvent(Event e) {
|
||||||
|
if (e instanceof MutationEvent) {
|
||||||
|
MutationEvent mutEvt = (MutationEvent)e;
|
||||||
|
if (mutEvt.getTarget() instanceof Element) {
|
||||||
|
Element el = (Element)mutEvt.getTarget();
|
||||||
|
if ("idPackageObject".equals(el.getAttribute("Id"))) {
|
||||||
|
et.removeEventListener("DOMSubtreeModified", this, false);
|
||||||
|
el.setAttributeNS(XmlNS, "xmlns:mdssi", PackageNamespaces.DIGITAL_SIGNATURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
et.addEventListener("DOMSubtreeModified", myModificationListener, false);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Signature context construction.
|
||||||
|
*/
|
||||||
|
XMLSignContext xmlSignContext = new DOMSignContext(signatureConfig.getKey(), document);
|
||||||
|
URIDereferencer uriDereferencer = signatureConfig.getUriDereferencer();
|
||||||
|
if (null != uriDereferencer) {
|
||||||
|
xmlSignContext.setURIDereferencer(uriDereferencer);
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlSignContext.putNamespacePrefix(
|
||||||
|
"http://schemas.openxmlformats.org/package/2006/digital-signature",
|
||||||
|
"mdssi");
|
||||||
|
|
||||||
|
String sigNsPrefix = signatureConfig.getSignatureNamespacePrefix();
|
||||||
|
if (sigNsPrefix != null) {
|
||||||
|
/*
|
||||||
|
* OOo doesn't like ds namespaces so per default prefixing is off.
|
||||||
|
*/
|
||||||
|
xmlSignContext.putNamespacePrefix(XmlDSigNS, sigNsPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
XMLSignatureFactory signatureFactory = SignatureInfo.getSignatureFactory();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add ds:References that come from signing client local files.
|
||||||
|
*/
|
||||||
|
List<Reference> references = new ArrayList<Reference>();
|
||||||
|
for (DigestInfo digestInfo : safe(digestInfos)) {
|
||||||
|
byte[] documentDigestValue = digestInfo.digestValue;
|
||||||
|
|
||||||
|
DigestMethod digestMethod = signatureFactory.newDigestMethod(
|
||||||
|
digestInfo.hashAlgo.xmlSignUri, null);
|
||||||
|
|
||||||
|
String uri = new File(digestInfo.description).getName();
|
||||||
|
|
||||||
|
Reference reference = signatureFactory.newReference
|
||||||
|
(uri, digestMethod, null, null, null, documentDigestValue);
|
||||||
|
references.add(reference);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Invoke the signature facets.
|
||||||
|
*/
|
||||||
|
List<XMLObject> objects = new ArrayList<XMLObject>();
|
||||||
|
for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) {
|
||||||
|
LOG.log(POILogger.DEBUG, "invoking signature facet: " + signatureFacet.getClass().getSimpleName());
|
||||||
|
signatureFacet.preSign(document, signatureFactory, references, objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ds:SignedInfo
|
||||||
|
*/
|
||||||
|
SignatureMethod signatureMethod = signatureFactory.newSignatureMethod(getSignatureMethod(), null);
|
||||||
|
CanonicalizationMethod canonicalizationMethod = signatureFactory
|
||||||
|
.newCanonicalizationMethod(signatureConfig.getCanonicalizationMethod(),
|
||||||
|
(C14NMethodParameterSpec) null);
|
||||||
|
SignedInfo signedInfo = signatureFactory.newSignedInfo(
|
||||||
|
canonicalizationMethod, signatureMethod, references);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* JSR105 ds:Signature creation
|
||||||
|
*/
|
||||||
|
String signatureValueId = signatureConfig.getPackageSignatureId() + "-signature-value";
|
||||||
|
javax.xml.crypto.dsig.XMLSignature xmlSignature = signatureFactory
|
||||||
|
.newXMLSignature(signedInfo, null, objects, signatureConfig.getPackageSignatureId(),
|
||||||
|
signatureValueId);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ds:Signature Marshalling.
|
||||||
|
*/
|
||||||
|
xmlSignContext.setDefaultNamespacePrefix(signatureConfig.getSignatureNamespacePrefix());
|
||||||
|
// xmlSignContext.putNamespacePrefix(PackageNamespaces.DIGITAL_SIGNATURE, "mdssi");
|
||||||
|
xmlSignature.sign(xmlSignContext);
|
||||||
|
|
||||||
|
registerIds(document);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Completion of undigested ds:References in the ds:Manifests.
|
||||||
|
*/
|
||||||
|
for (XMLObject object : objects) {
|
||||||
|
LOG.log(POILogger.DEBUG, "object java type: " + object.getClass().getName());
|
||||||
|
List<XMLStructure> objectContentList = object.getContent();
|
||||||
|
for (XMLStructure objectContent : objectContentList) {
|
||||||
|
LOG.log(POILogger.DEBUG, "object content java type: " + objectContent.getClass().getName());
|
||||||
|
if (!(objectContent instanceof Manifest)) continue;
|
||||||
|
Manifest manifest = (Manifest) objectContent;
|
||||||
|
List<Reference> manifestReferences = manifest.getReferences();
|
||||||
|
for (Reference manifestReference : manifestReferences) {
|
||||||
|
if (manifestReference.getDigestValue() != null) continue;
|
||||||
|
|
||||||
|
DOMReference manifestDOMReference = (DOMReference)manifestReference;
|
||||||
|
manifestDOMReference.digest(xmlSignContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Completion of undigested ds:References.
|
||||||
|
*/
|
||||||
|
List<Reference> signedInfoReferences = signedInfo.getReferences();
|
||||||
|
for (Reference signedInfoReference : signedInfoReferences) {
|
||||||
|
DOMReference domReference = (DOMReference)signedInfoReference;
|
||||||
|
|
||||||
|
// ds:Reference with external digest value
|
||||||
|
if (domReference.getDigestValue() != null) continue;
|
||||||
|
|
||||||
|
domReference.digest(xmlSignContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculation of XML signature digest value.
|
||||||
|
*/
|
||||||
|
DOMSignedInfo domSignedInfo = (DOMSignedInfo)signedInfo;
|
||||||
|
ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
|
||||||
|
domSignedInfo.canonicalize(xmlSignContext, dataStream);
|
||||||
|
byte[] octets = dataStream.toByteArray();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: we could be using DigestOutputStream here to optimize memory
|
||||||
|
* usage.
|
||||||
|
*/
|
||||||
|
|
||||||
|
MessageDigest jcaMessageDigest = CryptoFunctions.getMessageDigest(signatureConfig.getDigestAlgo());
|
||||||
|
byte[] digestValue = jcaMessageDigest.digest(octets);
|
||||||
|
|
||||||
|
|
||||||
|
String description = signatureConfig.getSignatureDescription();
|
||||||
|
return new DigestInfo(digestValue, signatureConfig.getDigestAlgo(), description);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postSign(Document document, byte[] signatureValue)
|
||||||
|
throws IOException, MarshalException, ParserConfigurationException, XmlException {
|
||||||
|
LOG.log(POILogger.DEBUG, "postSign");
|
||||||
|
SignatureInfo.initXmlProvider();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check ds:Signature node.
|
||||||
|
*/
|
||||||
|
String signatureId = signatureConfig.getPackageSignatureId();
|
||||||
|
if (!signatureId.equals(document.getDocumentElement().getAttribute("Id"))) {
|
||||||
|
throw new RuntimeException("ds:Signature not found for @Id: " + signatureId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Insert signature value into the ds:SignatureValue element
|
||||||
|
*/
|
||||||
|
NodeList sigValNl = document.getElementsByTagNameNS(XmlDSigNS, "SignatureValue");
|
||||||
|
if (sigValNl.getLength() != 1) {
|
||||||
|
throw new RuntimeException("preSign has to be called before postSign");
|
||||||
|
}
|
||||||
|
sigValNl.item(0).setTextContent(Base64.encode(signatureValue));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allow signature facets to inject their own stuff.
|
||||||
|
*/
|
||||||
|
for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) {
|
||||||
|
signatureFacet.postSign(document, signatureConfig.getSigningCertificateChain());
|
||||||
|
}
|
||||||
|
|
||||||
|
registerIds(document);
|
||||||
|
writeDocument(document);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void writeDocument(Document document) throws IOException, XmlException {
|
||||||
|
XmlOptions xo = new XmlOptions();
|
||||||
|
Map<String,String> namespaceMap = new HashMap<String,String>();
|
||||||
|
for (SignatureFacet sf : signatureConfig.getSignatureFacets()) {
|
||||||
|
Map<String,String> sfm = sf.getNamespacePrefixMapping();
|
||||||
|
if (sfm != null) {
|
||||||
|
namespaceMap.putAll(sfm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xo.setSaveSuggestedPrefixes(namespaceMap);
|
||||||
|
xo.setUseDefaultNamespace();
|
||||||
|
|
||||||
|
LOG.log(POILogger.DEBUG, "output signed Office OpenXML document");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copy the original OOXML content to the signed OOXML package. During
|
||||||
|
* copying some files need to changed.
|
||||||
|
*/
|
||||||
|
OPCPackage pkg = signatureConfig.getOpcPackage();
|
||||||
|
|
||||||
|
PackagePartName sigPartName, sigsPartName;
|
||||||
|
try {
|
||||||
|
// <Override PartName="/_xmlsignatures/sig1.xml" ContentType="application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"/>
|
||||||
|
sigPartName = PackagingURIHelper.createPartName("/_xmlsignatures/sig1.xml");
|
||||||
|
// <Default Extension="sigs" ContentType="application/vnd.openxmlformats-package.digital-signature-origin"/>
|
||||||
|
sigsPartName = PackagingURIHelper.createPartName("/_xmlsignatures/origin.sigs");
|
||||||
|
} catch (InvalidFormatException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
String sigContentType = "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml";
|
||||||
|
PackagePart sigPart = pkg.getPart(sigPartName);
|
||||||
|
if (sigPart == null) {
|
||||||
|
sigPart = pkg.createPart(sigPartName, sigContentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStream os = sigPart.getOutputStream();
|
||||||
|
SignatureDocument sigDoc = SignatureDocument.Factory.parse(document);
|
||||||
|
sigDoc.save(os, xo);
|
||||||
|
os.close();
|
||||||
|
|
||||||
|
String sigsContentType = "application/vnd.openxmlformats-package.digital-signature-origin";
|
||||||
|
PackagePart sigsPart = pkg.getPart(sigsPartName);
|
||||||
|
if (sigsPart == null) {
|
||||||
|
// touch empty marker file
|
||||||
|
sigsPart = pkg.createPart(sigsPartName, sigsContentType);
|
||||||
|
}
|
||||||
|
|
||||||
|
PackageRelationshipCollection relCol = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
|
||||||
|
for (PackageRelationship pr : relCol) {
|
||||||
|
pkg.removeRelationship(pr.getId());
|
||||||
|
}
|
||||||
|
pkg.addRelationship(sigsPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
|
||||||
|
|
||||||
|
sigsPart.addRelationship(sigPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the resulting document needs to be tweaked before it can be digested -
|
||||||
|
* this applies to the verification and signing step
|
||||||
|
*
|
||||||
|
* @param doc
|
||||||
|
*/
|
||||||
|
private static void registerIds(Document doc) {
|
||||||
|
NodeList nl = doc.getElementsByTagNameNS(XmlDSigNS, "Object");
|
||||||
|
registerIdAttribute(nl);
|
||||||
|
nl = doc.getElementsByTagNameNS("http://uri.etsi.org/01903/v1.3.2#", "SignedProperties");
|
||||||
|
registerIdAttribute(nl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerIdAttribute(NodeList nl) {
|
||||||
|
for (int i=0; i<nl.getLength(); i++) {
|
||||||
|
Element el = (Element)nl.item(i);
|
||||||
|
if (el.hasAttribute("Id")) {
|
||||||
|
el.setIdAttribute("Id", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> List<T> safe(List<T> other) {
|
||||||
|
return other == null ? Collections.EMPTY_LIST : other;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,10 @@ import java.security.cert.X509Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import javax.xml.crypto.URIDereferencer;
|
import javax.xml.crypto.URIDereferencer;
|
||||||
|
import javax.xml.crypto.dsig.CanonicalizationMethod;
|
||||||
|
|
||||||
import org.apache.poi.openxml4j.opc.OPCPackage;
|
import org.apache.poi.openxml4j.opc.OPCPackage;
|
||||||
import org.apache.poi.poifs.crypt.HashAlgorithm;
|
import org.apache.poi.poifs.crypt.HashAlgorithm;
|
||||||
|
@ -31,8 +33,8 @@ import org.apache.poi.poifs.crypt.dsig.facets.KeyInfoSignatureFacet;
|
||||||
import org.apache.poi.poifs.crypt.dsig.facets.OOXMLSignatureFacet;
|
import org.apache.poi.poifs.crypt.dsig.facets.OOXMLSignatureFacet;
|
||||||
import org.apache.poi.poifs.crypt.dsig.facets.Office2010SignatureFacet;
|
import org.apache.poi.poifs.crypt.dsig.facets.Office2010SignatureFacet;
|
||||||
import org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet;
|
import org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet;
|
||||||
import org.apache.poi.poifs.crypt.dsig.facets.SignaturePolicyService;
|
|
||||||
import org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet;
|
import org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet;
|
||||||
|
import org.apache.poi.poifs.crypt.dsig.services.SignaturePolicyService;
|
||||||
import org.apache.poi.poifs.crypt.dsig.spi.AddressDTO;
|
import org.apache.poi.poifs.crypt.dsig.spi.AddressDTO;
|
||||||
import org.apache.poi.poifs.crypt.dsig.spi.IdentityDTO;
|
import org.apache.poi.poifs.crypt.dsig.spi.IdentityDTO;
|
||||||
|
|
||||||
|
@ -48,7 +50,21 @@ public class SignatureInfoConfig {
|
||||||
private AddressDTO address;
|
private AddressDTO address;
|
||||||
private byte[] photo;
|
private byte[] photo;
|
||||||
private SignaturePolicyService signaturePolicyService;
|
private SignaturePolicyService signaturePolicyService;
|
||||||
private URIDereferencer uriDereferencer;
|
private URIDereferencer uriDereferencer;
|
||||||
|
private String signatureNamespacePrefix;
|
||||||
|
private String canonicalizationMethod = CanonicalizationMethod.INCLUSIVE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The signature Id attribute value used to create the XML signature. A
|
||||||
|
* <code>null</code> value will trigger an automatically generated signature Id.
|
||||||
|
*/
|
||||||
|
private String packageSignatureId = "idPackageSignature";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives back the human-readable description of what the citizen will be
|
||||||
|
* signing. The default value is "Office OpenXML Document".
|
||||||
|
*/
|
||||||
|
private String signatureDescription = "Office OpenXML Document";
|
||||||
|
|
||||||
public SignatureInfoConfig() {
|
public SignatureInfoConfig() {
|
||||||
OOXMLURIDereferencer uriDereferencer = new OOXMLURIDereferencer();
|
OOXMLURIDereferencer uriDereferencer = new OOXMLURIDereferencer();
|
||||||
|
@ -148,8 +164,7 @@ public class SignatureInfoConfig {
|
||||||
public SignaturePolicyService getSignaturePolicyService() {
|
public SignaturePolicyService getSignaturePolicyService() {
|
||||||
return signaturePolicyService;
|
return signaturePolicyService;
|
||||||
}
|
}
|
||||||
public void setSignaturePolicyService(
|
public void setSignaturePolicyService(SignaturePolicyService signaturePolicyService) {
|
||||||
SignaturePolicyService signaturePolicyService) {
|
|
||||||
this.signaturePolicyService = signaturePolicyService;
|
this.signaturePolicyService = signaturePolicyService;
|
||||||
}
|
}
|
||||||
public URIDereferencer getUriDereferencer() {
|
public URIDereferencer getUriDereferencer() {
|
||||||
|
@ -158,6 +173,30 @@ public class SignatureInfoConfig {
|
||||||
public void setUriDereferencer(URIDereferencer uriDereferencer) {
|
public void setUriDereferencer(URIDereferencer uriDereferencer) {
|
||||||
this.uriDereferencer = uriDereferencer;
|
this.uriDereferencer = uriDereferencer;
|
||||||
}
|
}
|
||||||
|
public String getSignatureDescription() {
|
||||||
|
return signatureDescription;
|
||||||
|
}
|
||||||
|
public void setSignatureDescription(String signatureDescription) {
|
||||||
|
this.signatureDescription = signatureDescription;
|
||||||
|
}
|
||||||
|
public String getSignatureNamespacePrefix() {
|
||||||
|
return signatureNamespacePrefix;
|
||||||
|
}
|
||||||
|
public void setSignatureNamespacePrefix(String signatureNamespacePrefix) {
|
||||||
|
this.signatureNamespacePrefix = signatureNamespacePrefix;
|
||||||
|
}
|
||||||
|
public String getCanonicalizationMethod() {
|
||||||
|
return canonicalizationMethod;
|
||||||
|
}
|
||||||
|
public void setCanonicalizationMethod(String canonicalizationMethod) {
|
||||||
|
this.canonicalizationMethod = canonicalizationMethod;
|
||||||
|
}
|
||||||
|
public String getPackageSignatureId() {
|
||||||
|
return packageSignatureId;
|
||||||
|
}
|
||||||
|
public void setPackageSignatureId(String packageSignatureId) {
|
||||||
|
this.packageSignatureId = (packageSignatureId != null)
|
||||||
|
? packageSignatureId
|
||||||
|
: "xmldsig-" + UUID.randomUUID();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import javax.xml.crypto.dsig.XMLObject;
|
||||||
import javax.xml.crypto.dsig.XMLSignatureFactory;
|
import javax.xml.crypto.dsig.XMLSignatureFactory;
|
||||||
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
|
import javax.xml.crypto.dsig.spec.TransformParameterSpec;
|
||||||
|
|
||||||
import org.apache.poi.poifs.crypt.HashAlgorithm;
|
import org.apache.poi.poifs.crypt.dsig.SignatureInfoConfig;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -26,24 +26,10 @@ import org.w3c.dom.Document;
|
||||||
*/
|
*/
|
||||||
public class EnvelopedSignatureFacet implements SignatureFacet {
|
public class EnvelopedSignatureFacet implements SignatureFacet {
|
||||||
|
|
||||||
private final HashAlgorithm hashAlgo;
|
private SignatureInfoConfig signatureConfig;
|
||||||
|
|
||||||
/**
|
public EnvelopedSignatureFacet(SignatureInfoConfig signatureConfig) {
|
||||||
* Default constructor. Digest algorithm will be SHA-1.
|
this.signatureConfig = signatureConfig;
|
||||||
*/
|
|
||||||
public EnvelopedSignatureFacet() {
|
|
||||||
this(HashAlgorithm.sha1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main constructor.
|
|
||||||
*
|
|
||||||
* @param hashAlgo
|
|
||||||
* the digest algorithm to be used within the ds:Reference
|
|
||||||
* element. Possible values: "SHA-1", "SHA-256, or "SHA-512".
|
|
||||||
*/
|
|
||||||
public EnvelopedSignatureFacet(HashAlgorithm hashAlgo) {
|
|
||||||
this.hashAlgo = hashAlgo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -52,14 +38,12 @@ public class EnvelopedSignatureFacet implements SignatureFacet {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preSign(Document document,
|
public void preSign(Document document
|
||||||
XMLSignatureFactory signatureFactory,
|
, XMLSignatureFactory signatureFactory
|
||||||
String signatureId,
|
, List<Reference> references
|
||||||
List<X509Certificate> signingCertificateChain,
|
, List<XMLObject> objects)
|
||||||
List<Reference> references, List<XMLObject> objects)
|
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
DigestMethod digestMethod = signatureFactory.newDigestMethod(signatureConfig.getDigestAlgo().xmlSignUri, null);
|
||||||
DigestMethod digestMethod = signatureFactory.newDigestMethod(
|
|
||||||
this.hashAlgo.xmlSignUri, null);
|
|
||||||
|
|
||||||
List<Transform> transforms = new ArrayList<Transform>();
|
List<Transform> transforms = new ArrayList<Transform>();
|
||||||
Transform envelopedTransform = signatureFactory
|
Transform envelopedTransform = signatureFactory
|
||||||
|
|
|
@ -173,12 +173,11 @@ public class KeyInfoSignatureFacet implements SignatureFacet {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preSign(Document document,
|
public void preSign(
|
||||||
XMLSignatureFactory signatureFactory,
|
Document document
|
||||||
String signatureId,
|
, XMLSignatureFactory signatureFactory
|
||||||
List<X509Certificate> signingCertificateChain,
|
, List<Reference> references
|
||||||
List<Reference> references,
|
, List<XMLObject> objects
|
||||||
List<XMLObject> objects
|
|
||||||
) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||||
// empty
|
// empty
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,20 +104,20 @@ public class OOXMLSignatureFacet implements SignatureFacet {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preSign(Document document,
|
public void preSign(
|
||||||
XMLSignatureFactory signatureFactory,
|
Document document
|
||||||
String signatureId,
|
, XMLSignatureFactory signatureFactory
|
||||||
List<X509Certificate> signingCertificateChain,
|
, List<Reference> references
|
||||||
List<Reference> references, List<XMLObject> objects)
|
, List<XMLObject> objects)
|
||||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, IOException, URISyntaxException, XmlException {
|
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, IOException, URISyntaxException, XmlException {
|
||||||
LOG.log(POILogger.DEBUG, "pre sign");
|
LOG.log(POILogger.DEBUG, "pre sign");
|
||||||
addManifestObject(document, signatureFactory, signatureId, references, objects);
|
addManifestObject(document, signatureFactory, references, objects);
|
||||||
addSignatureInfo(document, signatureFactory, signatureId, references, objects);
|
addSignatureInfo(document, signatureFactory, references, objects);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addManifestObject(Document document,
|
private void addManifestObject(Document document,
|
||||||
XMLSignatureFactory signatureFactory,
|
XMLSignatureFactory signatureFactory,
|
||||||
String signatureId, List<Reference> references,
|
List<Reference> references,
|
||||||
List<XMLObject> objects) throws NoSuchAlgorithmException,
|
List<XMLObject> objects) throws NoSuchAlgorithmException,
|
||||||
InvalidAlgorithmParameterException, IOException, URISyntaxException, XmlException {
|
InvalidAlgorithmParameterException, IOException, URISyntaxException, XmlException {
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ public class OOXMLSignatureFacet implements SignatureFacet {
|
||||||
List<XMLStructure> objectContent = new ArrayList<XMLStructure>();
|
List<XMLStructure> objectContent = new ArrayList<XMLStructure>();
|
||||||
objectContent.add(manifest);
|
objectContent.add(manifest);
|
||||||
|
|
||||||
addSignatureTime(document, signatureFactory, signatureId, objectContent);
|
addSignatureTime(document, signatureFactory, objectContent);
|
||||||
|
|
||||||
XMLObject xo = signatureFactory.newXMLObject(objectContent, objectId, null, null);
|
XMLObject xo = signatureFactory.newXMLObject(objectContent, objectId, null, null);
|
||||||
objects.add(xo);
|
objects.add(xo);
|
||||||
|
@ -225,7 +225,6 @@ public class OOXMLSignatureFacet implements SignatureFacet {
|
||||||
|
|
||||||
private void addSignatureTime(Document document,
|
private void addSignatureTime(Document document,
|
||||||
XMLSignatureFactory signatureFactory,
|
XMLSignatureFactory signatureFactory,
|
||||||
String signatureId,
|
|
||||||
List<XMLStructure> objectContent) {
|
List<XMLStructure> objectContent) {
|
||||||
/*
|
/*
|
||||||
* SignatureTime
|
* SignatureTime
|
||||||
|
@ -247,7 +246,7 @@ public class OOXMLSignatureFacet implements SignatureFacet {
|
||||||
List<XMLStructure> signatureTimeContent = new ArrayList<XMLStructure>();
|
List<XMLStructure> signatureTimeContent = new ArrayList<XMLStructure>();
|
||||||
signatureTimeContent.add(new DOMStructure(n));
|
signatureTimeContent.add(new DOMStructure(n));
|
||||||
SignatureProperty signatureTimeSignatureProperty = signatureFactory
|
SignatureProperty signatureTimeSignatureProperty = signatureFactory
|
||||||
.newSignatureProperty(signatureTimeContent, "#" + signatureId,
|
.newSignatureProperty(signatureTimeContent, "#" + signatureConfig.getPackageSignatureId(),
|
||||||
"idSignatureTime");
|
"idSignatureTime");
|
||||||
List<SignatureProperty> signaturePropertyContent = new ArrayList<SignatureProperty>();
|
List<SignatureProperty> signaturePropertyContent = new ArrayList<SignatureProperty>();
|
||||||
signaturePropertyContent.add(signatureTimeSignatureProperty);
|
signaturePropertyContent.add(signatureTimeSignatureProperty);
|
||||||
|
@ -258,10 +257,10 @@ public class OOXMLSignatureFacet implements SignatureFacet {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSignatureInfo(Document document,
|
private void addSignatureInfo(Document document,
|
||||||
XMLSignatureFactory signatureFactory,
|
XMLSignatureFactory signatureFactory,
|
||||||
String signatureId, List<Reference> references,
|
List<Reference> references,
|
||||||
List<XMLObject> objects) throws NoSuchAlgorithmException,
|
List<XMLObject> objects)
|
||||||
InvalidAlgorithmParameterException {
|
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||||
List<XMLStructure> objectContent = new ArrayList<XMLStructure>();
|
List<XMLStructure> objectContent = new ArrayList<XMLStructure>();
|
||||||
|
|
||||||
SignatureInfoV1Document sigV1 = SignatureInfoV1Document.Factory.newInstance();
|
SignatureInfoV1Document sigV1 = SignatureInfoV1Document.Factory.newInstance();
|
||||||
|
@ -273,7 +272,7 @@ public class OOXMLSignatureFacet implements SignatureFacet {
|
||||||
List<XMLStructure> signatureInfoContent = new ArrayList<XMLStructure>();
|
List<XMLStructure> signatureInfoContent = new ArrayList<XMLStructure>();
|
||||||
signatureInfoContent.add(new DOMStructure(n));
|
signatureInfoContent.add(new DOMStructure(n));
|
||||||
SignatureProperty signatureInfoSignatureProperty = signatureFactory
|
SignatureProperty signatureInfoSignatureProperty = signatureFactory
|
||||||
.newSignatureProperty(signatureInfoContent, "#" + signatureId,
|
.newSignatureProperty(signatureInfoContent, "#" + signatureConfig.getPackageSignatureId(),
|
||||||
"idOfficeV1Details");
|
"idOfficeV1Details");
|
||||||
|
|
||||||
List<SignatureProperty> signaturePropertyContent = new ArrayList<SignatureProperty>();
|
List<SignatureProperty> signaturePropertyContent = new ArrayList<SignatureProperty>();
|
||||||
|
|
|
@ -54,12 +54,11 @@ import org.w3c.dom.NodeList;
|
||||||
public class Office2010SignatureFacet implements SignatureFacet {
|
public class Office2010SignatureFacet implements SignatureFacet {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void preSign(Document document,
|
public void preSign(
|
||||||
XMLSignatureFactory signatureFactory,
|
Document document
|
||||||
String signatureId,
|
, XMLSignatureFactory signatureFactory
|
||||||
List<X509Certificate> signingCertificateChain,
|
, List<Reference> references
|
||||||
List<Reference> references,
|
, List<XMLObject> objects
|
||||||
List<XMLObject> objects
|
|
||||||
) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,8 +66,6 @@ public interface SignatureFacet {
|
||||||
void preSign(
|
void preSign(
|
||||||
Document document
|
Document document
|
||||||
, XMLSignatureFactory signatureFactory
|
, XMLSignatureFactory signatureFactory
|
||||||
, String signatureId
|
|
||||||
, List<X509Certificate> signingCertificateChain
|
|
||||||
, List<Reference> references
|
, List<Reference> references
|
||||||
, List<XMLObject> objects
|
, List<XMLObject> objects
|
||||||
) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, IOException, URISyntaxException, XmlException;
|
) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, IOException, URISyntaxException, XmlException;
|
||||||
|
|
|
@ -53,9 +53,11 @@ import org.apache.poi.poifs.crypt.CryptoFunctions;
|
||||||
import org.apache.poi.poifs.crypt.HashAlgorithm;
|
import org.apache.poi.poifs.crypt.HashAlgorithm;
|
||||||
import org.apache.poi.poifs.crypt.dsig.SignatureInfo;
|
import org.apache.poi.poifs.crypt.dsig.SignatureInfo;
|
||||||
import org.apache.poi.poifs.crypt.dsig.SignatureInfoConfig;
|
import org.apache.poi.poifs.crypt.dsig.SignatureInfoConfig;
|
||||||
import org.apache.poi.poifs.crypt.dsig.services.XmlSignatureService;
|
import org.apache.poi.poifs.crypt.dsig.services.SignaturePolicyService;
|
||||||
import org.apache.poi.util.POILogFactory;
|
import org.apache.poi.util.POILogFactory;
|
||||||
import org.apache.poi.util.POILogger;
|
import org.apache.poi.util.POILogger;
|
||||||
|
import org.apache.xmlbeans.XmlCursor;
|
||||||
|
import org.apache.xmlbeans.XmlObject;
|
||||||
import org.apache.xmlbeans.XmlString;
|
import org.apache.xmlbeans.XmlString;
|
||||||
import org.etsi.uri.x01903.v13.AnyType;
|
import org.etsi.uri.x01903.v13.AnyType;
|
||||||
import org.etsi.uri.x01903.v13.CertIDListType;
|
import org.etsi.uri.x01903.v13.CertIDListType;
|
||||||
|
@ -134,8 +136,6 @@ public class XAdESSignatureFacet implements SignatureFacet {
|
||||||
@Override
|
@Override
|
||||||
public void preSign(Document document,
|
public void preSign(Document document,
|
||||||
XMLSignatureFactory signatureFactory,
|
XMLSignatureFactory signatureFactory,
|
||||||
String signatureId,
|
|
||||||
List<X509Certificate> signingCertificateChain,
|
|
||||||
List<Reference> references, List<XMLObject> objects)
|
List<Reference> references, List<XMLObject> objects)
|
||||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||||
LOG.log(POILogger.DEBUG, "preSign");
|
LOG.log(POILogger.DEBUG, "preSign");
|
||||||
|
@ -143,13 +143,13 @@ public class XAdESSignatureFacet implements SignatureFacet {
|
||||||
// QualifyingProperties
|
// QualifyingProperties
|
||||||
QualifyingPropertiesDocument qualDoc = QualifyingPropertiesDocument.Factory.newInstance();
|
QualifyingPropertiesDocument qualDoc = QualifyingPropertiesDocument.Factory.newInstance();
|
||||||
QualifyingPropertiesType qualifyingProperties = qualDoc.addNewQualifyingProperties();
|
QualifyingPropertiesType qualifyingProperties = qualDoc.addNewQualifyingProperties();
|
||||||
qualifyingProperties.setTarget("#" + signatureId);
|
qualifyingProperties.setTarget("#" + signatureConfig.getPackageSignatureId());
|
||||||
|
|
||||||
// SignedProperties
|
// SignedProperties
|
||||||
SignedPropertiesType signedProperties = qualifyingProperties.addNewSignedProperties();
|
SignedPropertiesType signedProperties = qualifyingProperties.addNewSignedProperties();
|
||||||
String signedPropertiesId = this.idSignedProperties;
|
String signedPropertiesId = this.idSignedProperties;
|
||||||
if (this.idSignedProperties == null) {
|
if (this.idSignedProperties == null) {
|
||||||
signedPropertiesId = signatureId + "-xades";
|
signedPropertiesId = signatureConfig.getPackageSignatureId() + "-xades";
|
||||||
}
|
}
|
||||||
signedProperties.setId(signedPropertiesId);
|
signedProperties.setId(signedPropertiesId);
|
||||||
|
|
||||||
|
@ -164,13 +164,13 @@ public class XAdESSignatureFacet implements SignatureFacet {
|
||||||
signedSignatureProperties.setSigningTime(xmlGregorianCalendar);
|
signedSignatureProperties.setSigningTime(xmlGregorianCalendar);
|
||||||
|
|
||||||
// SigningCertificate
|
// SigningCertificate
|
||||||
if (null == signingCertificateChain
|
if (signatureConfig.getSigningCertificateChain() == null
|
||||||
|| signingCertificateChain.isEmpty()) {
|
|| signatureConfig.getSigningCertificateChain().isEmpty()) {
|
||||||
throw new RuntimeException("no signing certificate chain available");
|
throw new RuntimeException("no signing certificate chain available");
|
||||||
}
|
}
|
||||||
CertIDListType signingCertificates = signedSignatureProperties.addNewSigningCertificate();
|
CertIDListType signingCertificates = signedSignatureProperties.addNewSigningCertificate();
|
||||||
CertIDType certId = signingCertificates.addNewCert();
|
CertIDType certId = signingCertificates.addNewCert();
|
||||||
X509Certificate signingCertificate = signingCertificateChain.get(0);
|
X509Certificate signingCertificate = signatureConfig.getSigningCertificateChain().get(0);
|
||||||
setCertID(certId, signingCertificate, this.signatureConfig.getDigestAlgo(), this.issuerNameNoReverseOrder);
|
setCertID(certId, signingCertificate, this.signatureConfig.getDigestAlgo(), this.issuerNameNoReverseOrder);
|
||||||
|
|
||||||
// ClaimedRole
|
// ClaimedRole
|
||||||
|
@ -181,7 +181,7 @@ public class XAdESSignatureFacet implements SignatureFacet {
|
||||||
AnyType claimedRole = claimedRolesList.addNewClaimedRole();
|
AnyType claimedRole = claimedRolesList.addNewClaimedRole();
|
||||||
XmlString roleString = XmlString.Factory.newInstance();
|
XmlString roleString = XmlString.Factory.newInstance();
|
||||||
roleString.setStringValue(this.role);
|
roleString.setStringValue(this.role);
|
||||||
SignatureInfo.insertXChild(claimedRole, roleString);
|
insertXChild(claimedRole, roleString);
|
||||||
}
|
}
|
||||||
|
|
||||||
// XAdES-EPES
|
// XAdES-EPES
|
||||||
|
@ -208,7 +208,7 @@ public class XAdESSignatureFacet implements SignatureFacet {
|
||||||
AnyType sigPolicyQualifier = sigPolicyQualifiers.addNewSigPolicyQualifier();
|
AnyType sigPolicyQualifier = sigPolicyQualifiers.addNewSigPolicyQualifier();
|
||||||
XmlString spUriElement = XmlString.Factory.newInstance();
|
XmlString spUriElement = XmlString.Factory.newInstance();
|
||||||
spUriElement.setStringValue(signaturePolicyDownloadUrl);
|
spUriElement.setStringValue(signaturePolicyDownloadUrl);
|
||||||
SignatureInfo.insertXChild(sigPolicyQualifier, spUriElement);
|
insertXChild(sigPolicyQualifier, spUriElement);
|
||||||
}
|
}
|
||||||
} else if (this.signaturePolicyImplied) {
|
} else if (this.signaturePolicyImplied) {
|
||||||
SignaturePolicyIdentifierType signaturePolicyIdentifier =
|
SignaturePolicyIdentifierType signaturePolicyIdentifier =
|
||||||
|
@ -238,7 +238,7 @@ public class XAdESSignatureFacet implements SignatureFacet {
|
||||||
// add XAdES ds:Object
|
// add XAdES ds:Object
|
||||||
List<XMLStructure> xadesObjectContent = new ArrayList<XMLStructure>();
|
List<XMLStructure> xadesObjectContent = new ArrayList<XMLStructure>();
|
||||||
Element qualDocEl = (Element)document.importNode(qualifyingProperties.getDomNode(), true);
|
Element qualDocEl = (Element)document.importNode(qualifyingProperties.getDomNode(), true);
|
||||||
XmlSignatureService.registerIdAttribute(qualDocEl.getElementsByTagName("SignedProperties"));
|
SignatureInfo.registerIdAttribute(qualDocEl.getElementsByTagName("SignedProperties"));
|
||||||
qualDocEl.setAttributeNS(XmlNS, "xmlns:xd", "http://uri.etsi.org/01903/v1.3.2#");
|
qualDocEl.setAttributeNS(XmlNS, "xmlns:xd", "http://uri.etsi.org/01903/v1.3.2#");
|
||||||
setPrefix(qualDocEl, "http://uri.etsi.org/01903/v1.3.2#", "xd");
|
setPrefix(qualDocEl, "http://uri.etsi.org/01903/v1.3.2#", "xd");
|
||||||
xadesObjectContent.add(new DOMStructure(qualDocEl));
|
xadesObjectContent.add(new DOMStructure(qualDocEl));
|
||||||
|
@ -376,4 +376,14 @@ public class XAdESSignatureFacet implements SignatureFacet {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static void insertXChild(XmlObject root, XmlObject child) {
|
||||||
|
XmlCursor rootCursor = root.newCursor();
|
||||||
|
rootCursor.toEndToken();
|
||||||
|
XmlCursor childCursor = child.newCursor();
|
||||||
|
childCursor.toNextToken();
|
||||||
|
childCursor.moveXml(rootCursor);
|
||||||
|
childCursor.dispose();
|
||||||
|
rootCursor.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -25,6 +25,7 @@
|
||||||
package org.apache.poi.poifs.crypt.dsig.facets;
|
package org.apache.poi.poifs.crypt.dsig.facets;
|
||||||
|
|
||||||
import static org.apache.poi.poifs.crypt.dsig.SignatureInfo.XmlDSigNS;
|
import static org.apache.poi.poifs.crypt.dsig.SignatureInfo.XmlDSigNS;
|
||||||
|
import static org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet.insertXChild;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
@ -50,7 +51,6 @@ import javax.xml.crypto.dsig.XMLObject;
|
||||||
import javax.xml.crypto.dsig.XMLSignatureFactory;
|
import javax.xml.crypto.dsig.XMLSignatureFactory;
|
||||||
|
|
||||||
import org.apache.poi.poifs.crypt.HashAlgorithm;
|
import org.apache.poi.poifs.crypt.HashAlgorithm;
|
||||||
import org.apache.poi.poifs.crypt.dsig.SignatureInfo;
|
|
||||||
import org.apache.poi.poifs.crypt.dsig.services.RevocationData;
|
import org.apache.poi.poifs.crypt.dsig.services.RevocationData;
|
||||||
import org.apache.poi.poifs.crypt.dsig.services.RevocationDataService;
|
import org.apache.poi.poifs.crypt.dsig.services.RevocationDataService;
|
||||||
import org.apache.poi.poifs.crypt.dsig.services.TimeStampService;
|
import org.apache.poi.poifs.crypt.dsig.services.TimeStampService;
|
||||||
|
@ -221,7 +221,7 @@ public class XAdESXLSignatureFacet implements SignatureFacet {
|
||||||
// xadesv141::TimeStampValidationData
|
// xadesv141::TimeStampValidationData
|
||||||
if (tsaRevocationDataXadesT.hasRevocationDataEntries()) {
|
if (tsaRevocationDataXadesT.hasRevocationDataEntries()) {
|
||||||
ValidationDataType validationData = createValidationData(tsaRevocationDataXadesT);
|
ValidationDataType validationData = createValidationData(tsaRevocationDataXadesT);
|
||||||
SignatureInfo.insertXChild(unsignedSigProps, validationData);
|
insertXChild(unsignedSigProps, validationData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (null == this.revocationDataService) {
|
if (null == this.revocationDataService) {
|
||||||
|
@ -334,7 +334,7 @@ public class XAdESXLSignatureFacet implements SignatureFacet {
|
||||||
this.c14nAlgoId, this.timeStampService);
|
this.c14nAlgoId, this.timeStampService);
|
||||||
if (tsaRevocationDataXadesX1.hasRevocationDataEntries()) {
|
if (tsaRevocationDataXadesX1.hasRevocationDataEntries()) {
|
||||||
ValidationDataType timeStampXadesX1ValidationData = createValidationData(tsaRevocationDataXadesX1);
|
ValidationDataType timeStampXadesX1ValidationData = createValidationData(tsaRevocationDataXadesX1);
|
||||||
SignatureInfo.insertXChild(unsignedSigProps, timeStampXadesX1ValidationData);
|
insertXChild(unsignedSigProps, timeStampXadesX1ValidationData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// marshal XAdES-X
|
// marshal XAdES-X
|
||||||
|
@ -381,8 +381,6 @@ public class XAdESXLSignatureFacet implements SignatureFacet {
|
||||||
@Override
|
@Override
|
||||||
public void preSign(Document document,
|
public void preSign(Document document,
|
||||||
XMLSignatureFactory signatureFactory,
|
XMLSignatureFactory signatureFactory,
|
||||||
String signatureId,
|
|
||||||
List<X509Certificate> signingCertificateChain,
|
|
||||||
List<Reference> references, List<XMLObject> objects)
|
List<Reference> references, List<XMLObject> objects)
|
||||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||||
// nothing to do here
|
// nothing to do here
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
Copyright (C) 2008-2014 FedICT.
|
Copyright (C) 2008-2014 FedICT.
|
||||||
================================================================= */
|
================================================================= */
|
||||||
|
|
||||||
package org.apache.poi.poifs.crypt.dsig.facets;
|
package org.apache.poi.poifs.crypt.dsig.services;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface for the signature policy service.
|
* Interface for the signature policy service.
|
|
@ -1,68 +0,0 @@
|
||||||
/* ====================================================================
|
|
||||||
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.
|
|
||||||
==================================================================== */
|
|
||||||
|
|
||||||
/* ====================================================================
|
|
||||||
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
|
|
||||||
Copyright (C) 2008-2014 FedICT.
|
|
||||||
================================================================= */
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.crypt.dsig.services;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import javax.xml.crypto.MarshalException;
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
|
||||||
|
|
||||||
import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;
|
|
||||||
import org.apache.xmlbeans.XmlException;
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for signature service component.
|
|
||||||
*
|
|
||||||
* @author Frank Cornelis
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public interface SignatureService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pre-sign callback method. Depending on the configuration some parameters
|
|
||||||
* are passed. The returned value will be signed by the eID Applet.
|
|
||||||
*
|
|
||||||
* @param digestInfos
|
|
||||||
* the optional list of digest infos.
|
|
||||||
* @return the digest to be signed.
|
|
||||||
* @throws NoSuchAlgorithmException
|
|
||||||
*/
|
|
||||||
DigestInfo preSign(Document document, List<DigestInfo> digestInfos)
|
|
||||||
throws NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Post-sign callback method. Received the signature value. Depending on the
|
|
||||||
* configuration the signing certificate chain is also obtained.
|
|
||||||
*
|
|
||||||
* @param signatureValue
|
|
||||||
* @param signingCertificateChain
|
|
||||||
* the optional chain of signing certificates.
|
|
||||||
*/
|
|
||||||
void postSign(Document document, byte[] signatureValue)
|
|
||||||
throws IOException, MarshalException, ParserConfigurationException, XmlException;
|
|
||||||
}
|
|
|
@ -1,481 +0,0 @@
|
||||||
/* ====================================================================
|
|
||||||
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.
|
|
||||||
==================================================================== */
|
|
||||||
|
|
||||||
/* ====================================================================
|
|
||||||
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
|
|
||||||
Copyright (C) 2008-2014 FedICT.
|
|
||||||
================================================================= */
|
|
||||||
|
|
||||||
package org.apache.poi.poifs.crypt.dsig.services;
|
|
||||||
|
|
||||||
import static org.apache.poi.poifs.crypt.dsig.SignatureInfo.XmlDSigNS;
|
|
||||||
import static org.apache.poi.poifs.crypt.dsig.SignatureInfo.XmlNS;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import javax.xml.crypto.MarshalException;
|
|
||||||
import javax.xml.crypto.URIDereferencer;
|
|
||||||
import javax.xml.crypto.XMLStructure;
|
|
||||||
import javax.xml.crypto.dsig.CanonicalizationMethod;
|
|
||||||
import javax.xml.crypto.dsig.DigestMethod;
|
|
||||||
import javax.xml.crypto.dsig.Manifest;
|
|
||||||
import javax.xml.crypto.dsig.Reference;
|
|
||||||
import javax.xml.crypto.dsig.SignatureMethod;
|
|
||||||
import javax.xml.crypto.dsig.SignedInfo;
|
|
||||||
import javax.xml.crypto.dsig.XMLObject;
|
|
||||||
import javax.xml.crypto.dsig.XMLSignContext;
|
|
||||||
import javax.xml.crypto.dsig.XMLSignatureFactory;
|
|
||||||
import javax.xml.crypto.dsig.dom.DOMSignContext;
|
|
||||||
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
|
|
||||||
import javax.xml.parsers.ParserConfigurationException;
|
|
||||||
import javax.xml.transform.TransformerException;
|
|
||||||
import javax.xml.transform.TransformerFactoryConfigurationError;
|
|
||||||
|
|
||||||
import org.apache.jcp.xml.dsig.internal.dom.DOMReference;
|
|
||||||
import org.apache.jcp.xml.dsig.internal.dom.DOMSignedInfo;
|
|
||||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
|
||||||
import org.apache.poi.openxml4j.opc.OPCPackage;
|
|
||||||
import org.apache.poi.openxml4j.opc.PackageNamespaces;
|
|
||||||
import org.apache.poi.openxml4j.opc.PackagePart;
|
|
||||||
import org.apache.poi.openxml4j.opc.PackagePartName;
|
|
||||||
import org.apache.poi.openxml4j.opc.PackageRelationship;
|
|
||||||
import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
|
|
||||||
import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
|
|
||||||
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.SignatureInfo;
|
|
||||||
import org.apache.poi.poifs.crypt.dsig.SignatureInfoConfig;
|
|
||||||
import org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet;
|
|
||||||
import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;
|
|
||||||
import org.apache.poi.util.POILogFactory;
|
|
||||||
import org.apache.poi.util.POILogger;
|
|
||||||
import org.apache.xml.security.signature.XMLSignature;
|
|
||||||
import org.apache.xml.security.utils.Base64;
|
|
||||||
import org.apache.xmlbeans.XmlException;
|
|
||||||
import org.apache.xmlbeans.XmlOptions;
|
|
||||||
import org.w3.x2000.x09.xmldsig.SignatureDocument;
|
|
||||||
import org.w3c.dom.Document;
|
|
||||||
import org.w3c.dom.Element;
|
|
||||||
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;
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract base class for an XML Signature Service implementation.
|
|
||||||
*/
|
|
||||||
public class XmlSignatureService implements SignatureService {
|
|
||||||
private static final POILogger LOG = POILogFactory.getLogger(XmlSignatureService.class);
|
|
||||||
|
|
||||||
protected SignatureInfoConfig signatureConfig;
|
|
||||||
|
|
||||||
private String signatureNamespacePrefix;
|
|
||||||
private String signatureId = "idPackageSignature";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main constructor.
|
|
||||||
*/
|
|
||||||
public XmlSignatureService(SignatureInfoConfig signatureConfig) {
|
|
||||||
this.signatureNamespacePrefix = null;
|
|
||||||
this.signatureConfig = signatureConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SignatureInfoConfig getSignatureConfig() {
|
|
||||||
return signatureConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the signature Id attribute value used to create the XML signature. A
|
|
||||||
* <code>null</code> value will trigger an automatically generated signature
|
|
||||||
* Id.
|
|
||||||
*
|
|
||||||
* @param signatureId
|
|
||||||
*/
|
|
||||||
protected void setSignatureId(String signatureId) {
|
|
||||||
this.signatureId = signatureId;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the XML Signature namespace prefix to be used for signature
|
|
||||||
* creation. A <code>null</code> value will omit the prefixing.
|
|
||||||
*
|
|
||||||
* @param signatureNamespacePrefix
|
|
||||||
*/
|
|
||||||
protected void setSignatureNamespacePrefix(String signatureNamespacePrefix) {
|
|
||||||
this.signatureNamespacePrefix = signatureNamespacePrefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gives back the human-readable description of what the citizen will be
|
|
||||||
* signing. The default value is "XML Document". Override this method to
|
|
||||||
* provide the citizen with another description.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
protected String getSignatureDescription() {
|
|
||||||
return "Office OpenXML Document";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gives back the output stream to which to write the signed XML document.
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
// protected abstract OutputStream getSignedDocumentOutputStream();
|
|
||||||
@Override
|
|
||||||
public DigestInfo preSign(Document document, List<DigestInfo> digestInfos)
|
|
||||||
throws NoSuchAlgorithmException {
|
|
||||||
SignatureInfo.initXmlProvider();
|
|
||||||
|
|
||||||
LOG.log(POILogger.DEBUG, "preSign");
|
|
||||||
HashAlgorithm hashAlgo = this.signatureConfig.getDigestAlgo();
|
|
||||||
|
|
||||||
byte[] digestValue;
|
|
||||||
try {
|
|
||||||
digestValue = getXmlSignatureDigestValue(document, digestInfos);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("XML signature error: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
String description = getSignatureDescription();
|
|
||||||
return new DigestInfo(digestValue, hashAlgo, description);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void postSign(Document document, byte[] signatureValue)
|
|
||||||
throws IOException, MarshalException, ParserConfigurationException, XmlException {
|
|
||||||
LOG.log(POILogger.DEBUG, "postSign");
|
|
||||||
SignatureInfo.initXmlProvider();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check ds:Signature node.
|
|
||||||
*/
|
|
||||||
if (!signatureId.equals(document.getDocumentElement().getAttribute("Id"))) {
|
|
||||||
throw new RuntimeException("ds:Signature not found for @Id: " + signatureId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Insert signature value into the ds:SignatureValue element
|
|
||||||
*/
|
|
||||||
NodeList sigValNl = document.getElementsByTagNameNS(XmlDSigNS, "SignatureValue");
|
|
||||||
if (sigValNl.getLength() != 1) {
|
|
||||||
throw new RuntimeException("preSign has to be called before postSign");
|
|
||||||
}
|
|
||||||
sigValNl.item(0).setTextContent(Base64.encode(signatureValue));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Allow signature facets to inject their own stuff.
|
|
||||||
*/
|
|
||||||
for (SignatureFacet signatureFacet : this.signatureConfig.getSignatureFacets()) {
|
|
||||||
signatureFacet.postSign(document, this.signatureConfig.getSigningCertificateChain());
|
|
||||||
}
|
|
||||||
|
|
||||||
registerIds(document);
|
|
||||||
writeDocument(document);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private byte[] getXmlSignatureDigestValue(Document document, List<DigestInfo> digestInfos)
|
|
||||||
throws ParserConfigurationException, NoSuchAlgorithmException,
|
|
||||||
InvalidAlgorithmParameterException, MarshalException,
|
|
||||||
javax.xml.crypto.dsig.XMLSignatureException,
|
|
||||||
TransformerFactoryConfigurationError, TransformerException,
|
|
||||||
IOException, SAXException, NoSuchProviderException, XmlException, URISyntaxException {
|
|
||||||
|
|
||||||
// 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 ...
|
|
||||||
final EventTarget et = (EventTarget)document;
|
|
||||||
EventListener myModificationListener = new EventListener() {
|
|
||||||
@Override
|
|
||||||
public void handleEvent(Event e) {
|
|
||||||
if (e instanceof MutationEvent) {
|
|
||||||
MutationEvent mutEvt = (MutationEvent)e;
|
|
||||||
if (mutEvt.getTarget() instanceof Element) {
|
|
||||||
Element el = (Element)mutEvt.getTarget();
|
|
||||||
if ("idPackageObject".equals(el.getAttribute("Id"))) {
|
|
||||||
et.removeEventListener("DOMSubtreeModified", this, false);
|
|
||||||
el.setAttributeNS(XmlNS, "xmlns:mdssi", PackageNamespaces.DIGITAL_SIGNATURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
et.addEventListener("DOMSubtreeModified", myModificationListener, false);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Signature context construction.
|
|
||||||
*/
|
|
||||||
XMLSignContext xmlSignContext = new DOMSignContext(this.signatureConfig.getKey(), document);
|
|
||||||
URIDereferencer uriDereferencer = this.signatureConfig.getUriDereferencer();
|
|
||||||
if (null != uriDereferencer) {
|
|
||||||
xmlSignContext.setURIDereferencer(uriDereferencer);
|
|
||||||
}
|
|
||||||
|
|
||||||
xmlSignContext.putNamespacePrefix(
|
|
||||||
"http://schemas.openxmlformats.org/package/2006/digital-signature",
|
|
||||||
"mdssi");
|
|
||||||
|
|
||||||
if (this.signatureNamespacePrefix != null) {
|
|
||||||
/*
|
|
||||||
* OOo doesn't like ds namespaces so per default prefixing is off.
|
|
||||||
*/
|
|
||||||
xmlSignContext.putNamespacePrefix(XmlDSigNS, this.signatureNamespacePrefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
XMLSignatureFactory signatureFactory = SignatureInfo.getSignatureFactory();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Add ds:References that come from signing client local files.
|
|
||||||
*/
|
|
||||||
List<Reference> references = new ArrayList<Reference>();
|
|
||||||
addDigestInfosAsReferences(digestInfos, signatureFactory, references);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Invoke the signature facets.
|
|
||||||
*/
|
|
||||||
String localSignatureId = this.signatureId;
|
|
||||||
if (localSignatureId == null) {
|
|
||||||
localSignatureId = "xmldsig-" + UUID.randomUUID().toString();
|
|
||||||
}
|
|
||||||
List<XMLObject> objects = new ArrayList<XMLObject>();
|
|
||||||
for (SignatureFacet signatureFacet : this.signatureConfig.getSignatureFacets()) {
|
|
||||||
LOG.log(POILogger.DEBUG, "invoking signature facet: " + signatureFacet.getClass().getSimpleName());
|
|
||||||
signatureFacet.preSign(document, signatureFactory, localSignatureId, this.signatureConfig.getSigningCertificateChain(), references, objects);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ds:SignedInfo
|
|
||||||
*/
|
|
||||||
SignatureMethod signatureMethod = signatureFactory.newSignatureMethod(getSignatureMethod(this.signatureConfig.getDigestAlgo()), null);
|
|
||||||
CanonicalizationMethod canonicalizationMethod = signatureFactory
|
|
||||||
.newCanonicalizationMethod(getCanonicalizationMethod(),
|
|
||||||
(C14NMethodParameterSpec) null);
|
|
||||||
SignedInfo signedInfo = signatureFactory.newSignedInfo(
|
|
||||||
canonicalizationMethod, signatureMethod, references);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JSR105 ds:Signature creation
|
|
||||||
*/
|
|
||||||
String signatureValueId = localSignatureId + "-signature-value";
|
|
||||||
javax.xml.crypto.dsig.XMLSignature xmlSignature = signatureFactory
|
|
||||||
.newXMLSignature(signedInfo, null, objects, localSignatureId,
|
|
||||||
signatureValueId);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ds:Signature Marshalling.
|
|
||||||
*/
|
|
||||||
xmlSignContext.setDefaultNamespacePrefix(this.signatureNamespacePrefix);
|
|
||||||
// xmlSignContext.putNamespacePrefix(PackageNamespaces.DIGITAL_SIGNATURE, "mdssi");
|
|
||||||
xmlSignature.sign(xmlSignContext);
|
|
||||||
|
|
||||||
registerIds(document);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Completion of undigested ds:References in the ds:Manifests.
|
|
||||||
*/
|
|
||||||
for (XMLObject object : objects) {
|
|
||||||
LOG.log(POILogger.DEBUG, "object java type: " + object.getClass().getName());
|
|
||||||
List<XMLStructure> objectContentList = object.getContent();
|
|
||||||
for (XMLStructure objectContent : objectContentList) {
|
|
||||||
LOG.log(POILogger.DEBUG, "object content java type: " + objectContent.getClass().getName());
|
|
||||||
if (!(objectContent instanceof Manifest)) continue;
|
|
||||||
Manifest manifest = (Manifest) objectContent;
|
|
||||||
List<Reference> manifestReferences = manifest.getReferences();
|
|
||||||
for (Reference manifestReference : manifestReferences) {
|
|
||||||
if (manifestReference.getDigestValue() != null) continue;
|
|
||||||
|
|
||||||
DOMReference manifestDOMReference = (DOMReference)manifestReference;
|
|
||||||
manifestDOMReference.digest(xmlSignContext);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Completion of undigested ds:References.
|
|
||||||
*/
|
|
||||||
List<Reference> signedInfoReferences = signedInfo.getReferences();
|
|
||||||
for (Reference signedInfoReference : signedInfoReferences) {
|
|
||||||
DOMReference domReference = (DOMReference)signedInfoReference;
|
|
||||||
|
|
||||||
// ds:Reference with external digest value
|
|
||||||
if (domReference.getDigestValue() != null) continue;
|
|
||||||
|
|
||||||
domReference.digest(xmlSignContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculation of XML signature digest value.
|
|
||||||
*/
|
|
||||||
DOMSignedInfo domSignedInfo = (DOMSignedInfo)signedInfo;
|
|
||||||
ByteArrayOutputStream dataStream = new ByteArrayOutputStream();
|
|
||||||
domSignedInfo.canonicalize(xmlSignContext, dataStream);
|
|
||||||
byte[] octets = dataStream.toByteArray();
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO: we could be using DigestOutputStream here to optimize memory
|
|
||||||
* usage.
|
|
||||||
*/
|
|
||||||
|
|
||||||
MessageDigest jcaMessageDigest = CryptoFunctions.getMessageDigest(this.signatureConfig.getDigestAlgo());
|
|
||||||
byte[] digestValue = jcaMessageDigest.digest(octets);
|
|
||||||
return digestValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the resulting document needs to be tweaked before it can be digested -
|
|
||||||
* this applies to the verification and signing step
|
|
||||||
*
|
|
||||||
* @param doc
|
|
||||||
*/
|
|
||||||
public static void registerIds(Document doc) {
|
|
||||||
NodeList nl = doc.getElementsByTagNameNS(XmlDSigNS, "Object");
|
|
||||||
registerIdAttribute(nl);
|
|
||||||
nl = doc.getElementsByTagNameNS("http://uri.etsi.org/01903/v1.3.2#", "SignedProperties");
|
|
||||||
registerIdAttribute(nl);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void registerIdAttribute(NodeList nl) {
|
|
||||||
for (int i=0; i<nl.getLength(); i++) {
|
|
||||||
Element el = (Element)nl.item(i);
|
|
||||||
if (el.hasAttribute("Id")) {
|
|
||||||
el.setIdAttribute("Id", true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addDigestInfosAsReferences(List<DigestInfo> digestInfos, XMLSignatureFactory signatureFactory, List<Reference> references)
|
|
||||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, MalformedURLException {
|
|
||||||
for (DigestInfo digestInfo : safe(digestInfos)) {
|
|
||||||
byte[] documentDigestValue = digestInfo.digestValue;
|
|
||||||
|
|
||||||
DigestMethod digestMethod = signatureFactory.newDigestMethod(
|
|
||||||
digestInfo.hashAlgo.xmlSignUri, null);
|
|
||||||
|
|
||||||
String uri = new File(digestInfo.description).getName();
|
|
||||||
|
|
||||||
Reference reference = signatureFactory.newReference(uri,
|
|
||||||
digestMethod, null, null, null, documentDigestValue);
|
|
||||||
references.add(reference);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getSignatureMethod(HashAlgorithm hashAlgo) {
|
|
||||||
if (null == hashAlgo) {
|
|
||||||
throw new RuntimeException("digest algo is null");
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (hashAlgo) {
|
|
||||||
case sha1: return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1;
|
|
||||||
case sha256: return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256;
|
|
||||||
case sha384: return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA384;
|
|
||||||
case sha512: return XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512;
|
|
||||||
case ripemd160: return XMLSignature.ALGO_ID_MAC_HMAC_RIPEMD160;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeException("unsupported sign algo: " + hashAlgo);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected String getCanonicalizationMethod() {
|
|
||||||
return CanonicalizationMethod.INCLUSIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void writeDocument(Document document) throws IOException, XmlException {
|
|
||||||
XmlOptions xo = new XmlOptions();
|
|
||||||
Map<String,String> namespaceMap = new HashMap<String,String>();
|
|
||||||
for (SignatureFacet sf : this.signatureConfig.getSignatureFacets()) {
|
|
||||||
Map<String,String> sfm = sf.getNamespacePrefixMapping();
|
|
||||||
if (sfm != null) {
|
|
||||||
namespaceMap.putAll(sfm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
xo.setSaveSuggestedPrefixes(namespaceMap);
|
|
||||||
xo.setUseDefaultNamespace();
|
|
||||||
|
|
||||||
LOG.log(POILogger.DEBUG, "output signed Office OpenXML document");
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copy the original OOXML content to the signed OOXML package. During
|
|
||||||
* copying some files need to changed.
|
|
||||||
*/
|
|
||||||
OPCPackage pkg = this.signatureConfig.getOpcPackage();
|
|
||||||
|
|
||||||
PackagePartName sigPartName, sigsPartName;
|
|
||||||
try {
|
|
||||||
// <Override PartName="/_xmlsignatures/sig1.xml" ContentType="application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"/>
|
|
||||||
sigPartName = PackagingURIHelper.createPartName("/_xmlsignatures/sig1.xml");
|
|
||||||
// <Default Extension="sigs" ContentType="application/vnd.openxmlformats-package.digital-signature-origin"/>
|
|
||||||
sigsPartName = PackagingURIHelper.createPartName("/_xmlsignatures/origin.sigs");
|
|
||||||
} catch (InvalidFormatException e) {
|
|
||||||
throw new IOException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
String sigContentType = "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml";
|
|
||||||
PackagePart sigPart = pkg.getPart(sigPartName);
|
|
||||||
if (sigPart == null) {
|
|
||||||
sigPart = pkg.createPart(sigPartName, sigContentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
OutputStream os = sigPart.getOutputStream();
|
|
||||||
SignatureDocument sigDoc = SignatureDocument.Factory.parse(document);
|
|
||||||
sigDoc.save(os, xo);
|
|
||||||
os.close();
|
|
||||||
|
|
||||||
String sigsContentType = "application/vnd.openxmlformats-package.digital-signature-origin";
|
|
||||||
PackagePart sigsPart = pkg.getPart(sigsPartName);
|
|
||||||
if (sigsPart == null) {
|
|
||||||
// touch empty marker file
|
|
||||||
sigsPart = pkg.createPart(sigsPartName, sigsContentType);
|
|
||||||
}
|
|
||||||
|
|
||||||
PackageRelationshipCollection relCol = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
|
|
||||||
for (PackageRelationship pr : relCol) {
|
|
||||||
pkg.removeRelationship(pr.getId());
|
|
||||||
}
|
|
||||||
pkg.addRelationship(sigsPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
|
|
||||||
|
|
||||||
sigsPart.addRelationship(sigPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public static <T> List<T> safe(List<T> other) {
|
|
||||||
return other == null ? Collections.EMPTY_LIST : other;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -67,7 +67,6 @@ import org.apache.poi.poifs.crypt.dsig.services.RevocationDataService;
|
||||||
import org.apache.poi.poifs.crypt.dsig.services.TSPTimeStampService;
|
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.TimeStampService;
|
||||||
import org.apache.poi.poifs.crypt.dsig.services.TimeStampServiceValidator;
|
import org.apache.poi.poifs.crypt.dsig.services.TimeStampServiceValidator;
|
||||||
import org.apache.poi.poifs.crypt.dsig.services.XmlSignatureService;
|
|
||||||
import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;
|
import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;
|
||||||
import org.apache.poi.util.DocumentHelper;
|
import org.apache.poi.util.DocumentHelper;
|
||||||
import org.apache.poi.util.IOUtils;
|
import org.apache.poi.util.IOUtils;
|
||||||
|
@ -120,7 +119,10 @@ public class TestSignatureInfo {
|
||||||
|
|
||||||
for (String testFile : testFiles) {
|
for (String testFile : testFiles) {
|
||||||
OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ);
|
OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ);
|
||||||
SignatureInfo si = new SignatureInfo(pkg);
|
SignatureInfoConfig sic = new SignatureInfoConfig();
|
||||||
|
sic.setOpcPackage(pkg);
|
||||||
|
SignatureInfo si = new SignatureInfo();
|
||||||
|
si.setSignatureConfig(sic);
|
||||||
List<X509Certificate> result = si.getSigners();
|
List<X509Certificate> result = si.getSigners();
|
||||||
pkg.revert();
|
pkg.revert();
|
||||||
pkg.close();
|
pkg.close();
|
||||||
|
@ -146,7 +148,10 @@ public class TestSignatureInfo {
|
||||||
|
|
||||||
for (String testFile : testFiles) {
|
for (String testFile : testFiles) {
|
||||||
OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ);
|
OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ);
|
||||||
SignatureInfo si = new SignatureInfo(pkg);
|
SignatureInfoConfig sic = new SignatureInfoConfig();
|
||||||
|
sic.setOpcPackage(pkg);
|
||||||
|
SignatureInfo si = new SignatureInfo();
|
||||||
|
si.setSignatureConfig(sic);
|
||||||
List<X509Certificate> result = si.getSigners();
|
List<X509Certificate> result = si.getSigners();
|
||||||
|
|
||||||
assertNotNull(result);
|
assertNotNull(result);
|
||||||
|
@ -164,7 +169,10 @@ public class TestSignatureInfo {
|
||||||
public void getMultiSigners() throws Exception {
|
public void getMultiSigners() throws Exception {
|
||||||
String testFile = "hello-world-signed-twice.docx";
|
String testFile = "hello-world-signed-twice.docx";
|
||||||
OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ);
|
OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ);
|
||||||
SignatureInfo si = new SignatureInfo(pkg);
|
SignatureInfoConfig sic = new SignatureInfoConfig();
|
||||||
|
sic.setOpcPackage(pkg);
|
||||||
|
SignatureInfo si = new SignatureInfo();
|
||||||
|
si.setSignatureConfig(sic);
|
||||||
List<X509Certificate> result = si.getSigners();
|
List<X509Certificate> result = si.getSigners();
|
||||||
|
|
||||||
assertNotNull(result);
|
assertNotNull(result);
|
||||||
|
@ -189,12 +197,18 @@ public class TestSignatureInfo {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSignSpreadsheetWithSignatureInfo() throws Exception {
|
public void testSignSpreadsheetWithSignatureInfo() throws Exception {
|
||||||
|
initKeyPair("Test", "CN=Test");
|
||||||
String testFile = "hello-world-unsigned.xlsx";
|
String testFile = "hello-world-unsigned.xlsx";
|
||||||
OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
|
OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
|
||||||
SignatureInfo si = new SignatureInfo(pkg);
|
SignatureInfoConfig sic = new SignatureInfoConfig();
|
||||||
initKeyPair("Test", "CN=Test");
|
sic.setOpcPackage(pkg);
|
||||||
|
sic.setKey(keyPair.getPrivate());
|
||||||
|
sic.setSigningCertificateChain(Collections.singletonList(x509));
|
||||||
|
sic.addDefaultFacets();
|
||||||
|
SignatureInfo si = new SignatureInfo();
|
||||||
|
si.setSignatureConfig(sic);
|
||||||
// hash > sha1 doesn't work in excel viewer ...
|
// hash > sha1 doesn't work in excel viewer ...
|
||||||
si.confirmSignature(keyPair.getPrivate(), x509, HashAlgorithm.sha1);
|
si.confirmSignature();
|
||||||
List<X509Certificate> signer = si.getSigners();
|
List<X509Certificate> signer = si.getSigners();
|
||||||
assertEquals(1, signer.size());
|
assertEquals(1, signer.size());
|
||||||
pkg.close();
|
pkg.close();
|
||||||
|
@ -223,7 +237,7 @@ public class TestSignatureInfo {
|
||||||
certificateChain.add(x509);
|
certificateChain.add(x509);
|
||||||
signatureConfig.setSigningCertificateChain(certificateChain);
|
signatureConfig.setSigningCertificateChain(certificateChain);
|
||||||
|
|
||||||
signatureConfig.addSignatureFacet(new EnvelopedSignatureFacet());
|
signatureConfig.addSignatureFacet(new EnvelopedSignatureFacet(signatureConfig));
|
||||||
signatureConfig.addSignatureFacet(new KeyInfoSignatureFacet(true, false, false));
|
signatureConfig.addSignatureFacet(new KeyInfoSignatureFacet(true, false, false));
|
||||||
signatureConfig.addSignatureFacet(new XAdESSignatureFacet(signatureConfig));
|
signatureConfig.addSignatureFacet(new XAdESSignatureFacet(signatureConfig));
|
||||||
|
|
||||||
|
@ -274,12 +288,13 @@ public class TestSignatureInfo {
|
||||||
|
|
||||||
XAdESXLSignatureFacet xadesXLSignatureFacet = new XAdESXLSignatureFacet(
|
XAdESXLSignatureFacet xadesXLSignatureFacet = new XAdESXLSignatureFacet(
|
||||||
timeStampService, revocationDataService);
|
timeStampService, revocationDataService);
|
||||||
XmlSignatureService testedInstance = new XmlSignatureService(signatureConfig);
|
SignatureInfo si = new SignatureInfo();
|
||||||
|
si.setSignatureConfig(signatureConfig);
|
||||||
|
|
||||||
Document document = DocumentHelper.createDocument();
|
Document document = DocumentHelper.createDocument();
|
||||||
|
|
||||||
// operate
|
// operate
|
||||||
DigestInfo digestInfo = testedInstance.preSign(document, null);
|
DigestInfo digestInfo = si.preSign(document, null);
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
assertNotNull(digestInfo);
|
assertNotNull(digestInfo);
|
||||||
|
@ -297,10 +312,10 @@ public class TestSignatureInfo {
|
||||||
assertNotNull(certDigest.getDigestValue());
|
assertNotNull(certDigest.getDigestValue());
|
||||||
|
|
||||||
// Sign the received XML signature digest value.
|
// Sign the received XML signature digest value.
|
||||||
byte[] signatureValue = SignatureInfo.signDigest(keyPair.getPrivate(), HashAlgorithm.sha1, digestInfo.digestValue);
|
byte[] signatureValue = si.signDigest(digestInfo.digestValue);
|
||||||
|
|
||||||
// Operate: postSign
|
// Operate: postSign
|
||||||
testedInstance.postSign(document, signatureValue);
|
si.postSign(document, signatureValue);
|
||||||
|
|
||||||
DOMValidateContext domValidateContext = new DOMValidateContext(
|
DOMValidateContext domValidateContext = new DOMValidateContext(
|
||||||
KeySelector.singletonKeySelector(keyPair.getPublic()),
|
KeySelector.singletonKeySelector(keyPair.getPublic()),
|
||||||
|
@ -341,12 +356,13 @@ public class TestSignatureInfo {
|
||||||
signatureConfig.setOpcPackage(pkgCopy);
|
signatureConfig.setOpcPackage(pkgCopy);
|
||||||
signatureConfig.addDefaultFacets();
|
signatureConfig.addDefaultFacets();
|
||||||
|
|
||||||
XmlSignatureService signatureService = new XmlSignatureService(signatureConfig);
|
SignatureInfo si = new SignatureInfo();
|
||||||
|
si.setSignatureConfig(signatureConfig);
|
||||||
|
|
||||||
Document document = DocumentHelper.createDocument();
|
Document document = DocumentHelper.createDocument();
|
||||||
|
|
||||||
// operate
|
// operate
|
||||||
DigestInfo digestInfo = signatureService.preSign(document, null);
|
DigestInfo digestInfo = si.preSign(document, null);
|
||||||
|
|
||||||
// verify
|
// verify
|
||||||
assertNotNull(digestInfo);
|
assertNotNull(digestInfo);
|
||||||
|
@ -357,13 +373,13 @@ public class TestSignatureInfo {
|
||||||
assertNotNull(digestInfo.digestValue);
|
assertNotNull(digestInfo.digestValue);
|
||||||
|
|
||||||
// setup: key material, signature value
|
// setup: key material, signature value
|
||||||
byte[] signatureValue = SignatureInfo.signDigest(keyPair.getPrivate(), HashAlgorithm.sha1, digestInfo.digestValue);
|
byte[] signatureValue = si.signDigest(digestInfo.digestValue);
|
||||||
|
|
||||||
// operate: postSign
|
// operate: postSign
|
||||||
signatureService.postSign(document, signatureValue);
|
si.postSign(document, signatureValue);
|
||||||
|
|
||||||
// verify: signature
|
// verify: signature
|
||||||
SignatureInfo si = new SignatureInfo(pkgCopy);
|
si.getSignatureConfig().setOpcPackage(pkgCopy);
|
||||||
List<X509Certificate> signers = si.getSigners();
|
List<X509Certificate> signers = si.getSigners();
|
||||||
assertEquals(signerCount, signers.size());
|
assertEquals(signerCount, signers.size());
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue