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;
|
||||
|
||||
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.File;
|
||||
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.PrivateKey;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.Provider;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
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.XMLSignatureException;
|
||||
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.keyinfo.KeyInfoFactory;
|
||||
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.EncryptedDocumentException;
|
||||
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.ChainingMode;
|
||||
import org.apache.poi.poifs.crypt.CipherAlgorithm;
|
||||
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.XmlSignatureService;
|
||||
import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;
|
||||
import org.apache.poi.util.DocumentHelper;
|
||||
import org.apache.poi.util.POILogFactory;
|
||||
import org.apache.poi.util.POILogger;
|
||||
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.XmlObject;
|
||||
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.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.w3c.dom.events.Event;
|
||||
import org.w3c.dom.events.EventListener;
|
||||
import org.w3c.dom.events.EventTarget;
|
||||
import org.w3c.dom.events.MutationEvent;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
public class SignatureInfo {
|
||||
|
||||
|
@ -104,12 +144,16 @@ public class SignatureInfo {
|
|||
private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class);
|
||||
private static boolean isInitialized = false;
|
||||
|
||||
private final OPCPackage pkg;
|
||||
|
||||
public SignatureInfo(OPCPackage pkg) {
|
||||
this.pkg = pkg;
|
||||
private SignatureInfoConfig signatureConfig;
|
||||
|
||||
public SignatureInfoConfig getSignatureConfig() {
|
||||
return signatureConfig;
|
||||
}
|
||||
|
||||
|
||||
public void setSignatureConfig(SignatureInfoConfig signatureConfig) {
|
||||
this.signatureConfig = signatureConfig;
|
||||
}
|
||||
|
||||
public boolean verifySignature() {
|
||||
initXmlProvider();
|
||||
// http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html
|
||||
|
@ -117,40 +161,27 @@ public class SignatureInfo {
|
|||
return getSignersAndValidate(signers, true);
|
||||
}
|
||||
|
||||
public void confirmSignature(PrivateKey key, X509Certificate x509)
|
||||
throws NoSuchAlgorithmException, IOException, MarshalException, ParserConfigurationException, XmlException {
|
||||
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);
|
||||
|
||||
public void confirmSignature()
|
||||
throws NoSuchAlgorithmException, IOException, MarshalException, ParserConfigurationException, XmlException, InvalidAlgorithmParameterException, NoSuchProviderException, XMLSignatureException, TransformerFactoryConfigurationError, TransformerException, SAXException, URISyntaxException {
|
||||
Document document = DocumentHelper.createDocument();
|
||||
|
||||
// operate
|
||||
DigestInfo digestInfo = signatureService.preSign(document, null);
|
||||
DigestInfo digestInfo = preSign(document, null);
|
||||
|
||||
// setup: key material, signature value
|
||||
byte[] signatureValue = signDigest(key, hashAlgo, digestInfo.digestValue);
|
||||
byte[] signatureValue = signDigest(digestInfo.digestValue);
|
||||
|
||||
// operate: postSign
|
||||
signatureService.postSign(document, signatureValue);
|
||||
postSign(document, signatureValue);
|
||||
}
|
||||
|
||||
public static byte[] signDigest(PrivateKey key, HashAlgorithm hashAlgo, byte digest[]) {
|
||||
Cipher cipher = CryptoFunctions.getCipher(key, CipherAlgorithm.rsa
|
||||
public byte[] signDigest(byte digest[]) {
|
||||
Cipher cipher = CryptoFunctions.getCipher(signatureConfig.getKey(), CipherAlgorithm.rsa
|
||||
, ChainingMode.ecb, null, Cipher.ENCRYPT_MODE, "PKCS1Padding");
|
||||
|
||||
try {
|
||||
ByteArrayOutputStream digestInfoValueBuf = new ByteArrayOutputStream();
|
||||
digestInfoValueBuf.write(getHashMagic(hashAlgo));
|
||||
digestInfoValueBuf.write(getHashMagic());
|
||||
digestInfoValueBuf.write(digest);
|
||||
byte[] digestInfoValue = digestInfoValueBuf.toByteArray();
|
||||
byte[] signatureValue = cipher.doFinal(digestInfoValue);
|
||||
|
@ -175,15 +206,12 @@ public class SignatureInfo {
|
|||
allValid = false;
|
||||
}
|
||||
|
||||
SignatureInfoConfig signatureConfig = new SignatureInfoConfig();
|
||||
signatureConfig.setOpcPackage(pkg);
|
||||
|
||||
for (PackagePart signaturePart : signatureParts) {
|
||||
KeyInfoKeySelector keySelector = new KeyInfoKeySelector();
|
||||
|
||||
try {
|
||||
Document doc = DocumentHelper.readDocument(signaturePart.getInputStream());
|
||||
XmlSignatureService.registerIds(doc);
|
||||
registerIds(doc);
|
||||
|
||||
DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc);
|
||||
domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE);
|
||||
|
@ -209,6 +237,7 @@ public class SignatureInfo {
|
|||
|
||||
protected List<PackagePart> getSignatureParts(boolean onlyFirst) {
|
||||
List<PackagePart> packageParts = new ArrayList<PackagePart>();
|
||||
OPCPackage pkg = signatureConfig.getOpcPackage();
|
||||
|
||||
PackageRelationshipCollection sigOrigRels = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);
|
||||
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!");
|
||||
}
|
||||
|
||||
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) {
|
||||
if (ns.equals(el.getNamespaceURI())) el.setPrefix(prefix);
|
||||
NodeList nl = el.getChildNodes();
|
||||
|
@ -293,8 +297,8 @@ public class SignatureInfo {
|
|||
}
|
||||
}
|
||||
|
||||
protected static byte[] getHashMagic(HashAlgorithm hashAlgo) {
|
||||
switch (hashAlgo) {
|
||||
protected byte[] getHashMagic() {
|
||||
switch (signatureConfig.getDigestAlgo()) {
|
||||
case sha1: return SHA1_DIGEST_INFO_PREFIX;
|
||||
// sha224: return SHA224_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 ripemd160: return RIPEMD160_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() {
|
||||
if (isInitialized) return;
|
||||
|
@ -319,4 +336,281 @@ public class SignatureInfo {
|
|||
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.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.xml.crypto.URIDereferencer;
|
||||
import javax.xml.crypto.dsig.CanonicalizationMethod;
|
||||
|
||||
import org.apache.poi.openxml4j.opc.OPCPackage;
|
||||
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.Office2010SignatureFacet;
|
||||
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.services.SignaturePolicyService;
|
||||
import org.apache.poi.poifs.crypt.dsig.spi.AddressDTO;
|
||||
import org.apache.poi.poifs.crypt.dsig.spi.IdentityDTO;
|
||||
|
||||
|
@ -48,7 +50,21 @@ public class SignatureInfoConfig {
|
|||
private AddressDTO address;
|
||||
private byte[] photo;
|
||||
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() {
|
||||
OOXMLURIDereferencer uriDereferencer = new OOXMLURIDereferencer();
|
||||
|
@ -148,8 +164,7 @@ public class SignatureInfoConfig {
|
|||
public SignaturePolicyService getSignaturePolicyService() {
|
||||
return signaturePolicyService;
|
||||
}
|
||||
public void setSignaturePolicyService(
|
||||
SignaturePolicyService signaturePolicyService) {
|
||||
public void setSignaturePolicyService(SignaturePolicyService signaturePolicyService) {
|
||||
this.signaturePolicyService = signaturePolicyService;
|
||||
}
|
||||
public URIDereferencer getUriDereferencer() {
|
||||
|
@ -158,6 +173,30 @@ public class SignatureInfoConfig {
|
|||
public void setUriDereferencer(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.spec.TransformParameterSpec;
|
||||
|
||||
import org.apache.poi.poifs.crypt.HashAlgorithm;
|
||||
import org.apache.poi.poifs.crypt.dsig.SignatureInfoConfig;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
/**
|
||||
|
@ -26,24 +26,10 @@ import org.w3c.dom.Document;
|
|||
*/
|
||||
public class EnvelopedSignatureFacet implements SignatureFacet {
|
||||
|
||||
private final HashAlgorithm hashAlgo;
|
||||
private SignatureInfoConfig signatureConfig;
|
||||
|
||||
/**
|
||||
* Default constructor. Digest algorithm will be SHA-1.
|
||||
*/
|
||||
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;
|
||||
public EnvelopedSignatureFacet(SignatureInfoConfig signatureConfig) {
|
||||
this.signatureConfig = signatureConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -52,14 +38,12 @@ public class EnvelopedSignatureFacet implements SignatureFacet {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void preSign(Document document,
|
||||
XMLSignatureFactory signatureFactory,
|
||||
String signatureId,
|
||||
List<X509Certificate> signingCertificateChain,
|
||||
List<Reference> references, List<XMLObject> objects)
|
||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
DigestMethod digestMethod = signatureFactory.newDigestMethod(
|
||||
this.hashAlgo.xmlSignUri, null);
|
||||
public void preSign(Document document
|
||||
, XMLSignatureFactory signatureFactory
|
||||
, List<Reference> references
|
||||
, List<XMLObject> objects)
|
||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
DigestMethod digestMethod = signatureFactory.newDigestMethod(signatureConfig.getDigestAlgo().xmlSignUri, null);
|
||||
|
||||
List<Transform> transforms = new ArrayList<Transform>();
|
||||
Transform envelopedTransform = signatureFactory
|
||||
|
|
|
@ -173,12 +173,11 @@ public class KeyInfoSignatureFacet implements SignatureFacet {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void preSign(Document document,
|
||||
XMLSignatureFactory signatureFactory,
|
||||
String signatureId,
|
||||
List<X509Certificate> signingCertificateChain,
|
||||
List<Reference> references,
|
||||
List<XMLObject> objects
|
||||
public void preSign(
|
||||
Document document
|
||||
, XMLSignatureFactory signatureFactory
|
||||
, List<Reference> references
|
||||
, List<XMLObject> objects
|
||||
) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
// empty
|
||||
}
|
||||
|
|
|
@ -104,20 +104,20 @@ public class OOXMLSignatureFacet implements SignatureFacet {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void preSign(Document document,
|
||||
XMLSignatureFactory signatureFactory,
|
||||
String signatureId,
|
||||
List<X509Certificate> signingCertificateChain,
|
||||
List<Reference> references, List<XMLObject> objects)
|
||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, IOException, URISyntaxException, XmlException {
|
||||
public void preSign(
|
||||
Document document
|
||||
, XMLSignatureFactory signatureFactory
|
||||
, List<Reference> references
|
||||
, List<XMLObject> objects)
|
||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, IOException, URISyntaxException, XmlException {
|
||||
LOG.log(POILogger.DEBUG, "pre sign");
|
||||
addManifestObject(document, signatureFactory, signatureId, references, objects);
|
||||
addSignatureInfo(document, signatureFactory, signatureId, references, objects);
|
||||
addManifestObject(document, signatureFactory, references, objects);
|
||||
addSignatureInfo(document, signatureFactory, references, objects);
|
||||
}
|
||||
|
||||
private void addManifestObject(Document document,
|
||||
XMLSignatureFactory signatureFactory,
|
||||
String signatureId, List<Reference> references,
|
||||
List<Reference> references,
|
||||
List<XMLObject> objects) throws NoSuchAlgorithmException,
|
||||
InvalidAlgorithmParameterException, IOException, URISyntaxException, XmlException {
|
||||
|
||||
|
@ -129,7 +129,7 @@ public class OOXMLSignatureFacet implements SignatureFacet {
|
|||
List<XMLStructure> objectContent = new ArrayList<XMLStructure>();
|
||||
objectContent.add(manifest);
|
||||
|
||||
addSignatureTime(document, signatureFactory, signatureId, objectContent);
|
||||
addSignatureTime(document, signatureFactory, objectContent);
|
||||
|
||||
XMLObject xo = signatureFactory.newXMLObject(objectContent, objectId, null, null);
|
||||
objects.add(xo);
|
||||
|
@ -225,7 +225,6 @@ public class OOXMLSignatureFacet implements SignatureFacet {
|
|||
|
||||
private void addSignatureTime(Document document,
|
||||
XMLSignatureFactory signatureFactory,
|
||||
String signatureId,
|
||||
List<XMLStructure> objectContent) {
|
||||
/*
|
||||
* SignatureTime
|
||||
|
@ -247,7 +246,7 @@ public class OOXMLSignatureFacet implements SignatureFacet {
|
|||
List<XMLStructure> signatureTimeContent = new ArrayList<XMLStructure>();
|
||||
signatureTimeContent.add(new DOMStructure(n));
|
||||
SignatureProperty signatureTimeSignatureProperty = signatureFactory
|
||||
.newSignatureProperty(signatureTimeContent, "#" + signatureId,
|
||||
.newSignatureProperty(signatureTimeContent, "#" + signatureConfig.getPackageSignatureId(),
|
||||
"idSignatureTime");
|
||||
List<SignatureProperty> signaturePropertyContent = new ArrayList<SignatureProperty>();
|
||||
signaturePropertyContent.add(signatureTimeSignatureProperty);
|
||||
|
@ -258,10 +257,10 @@ public class OOXMLSignatureFacet implements SignatureFacet {
|
|||
}
|
||||
|
||||
private void addSignatureInfo(Document document,
|
||||
XMLSignatureFactory signatureFactory,
|
||||
String signatureId, List<Reference> references,
|
||||
List<XMLObject> objects) throws NoSuchAlgorithmException,
|
||||
InvalidAlgorithmParameterException {
|
||||
XMLSignatureFactory signatureFactory,
|
||||
List<Reference> references,
|
||||
List<XMLObject> objects)
|
||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
List<XMLStructure> objectContent = new ArrayList<XMLStructure>();
|
||||
|
||||
SignatureInfoV1Document sigV1 = SignatureInfoV1Document.Factory.newInstance();
|
||||
|
@ -273,7 +272,7 @@ public class OOXMLSignatureFacet implements SignatureFacet {
|
|||
List<XMLStructure> signatureInfoContent = new ArrayList<XMLStructure>();
|
||||
signatureInfoContent.add(new DOMStructure(n));
|
||||
SignatureProperty signatureInfoSignatureProperty = signatureFactory
|
||||
.newSignatureProperty(signatureInfoContent, "#" + signatureId,
|
||||
.newSignatureProperty(signatureInfoContent, "#" + signatureConfig.getPackageSignatureId(),
|
||||
"idOfficeV1Details");
|
||||
|
||||
List<SignatureProperty> signaturePropertyContent = new ArrayList<SignatureProperty>();
|
||||
|
|
|
@ -54,12 +54,11 @@ import org.w3c.dom.NodeList;
|
|||
public class Office2010SignatureFacet implements SignatureFacet {
|
||||
|
||||
@Override
|
||||
public void preSign(Document document,
|
||||
XMLSignatureFactory signatureFactory,
|
||||
String signatureId,
|
||||
List<X509Certificate> signingCertificateChain,
|
||||
List<Reference> references,
|
||||
List<XMLObject> objects
|
||||
public void preSign(
|
||||
Document document
|
||||
, XMLSignatureFactory signatureFactory
|
||||
, List<Reference> references
|
||||
, List<XMLObject> objects
|
||||
) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
}
|
||||
|
||||
|
|
|
@ -66,8 +66,6 @@ public interface SignatureFacet {
|
|||
void preSign(
|
||||
Document document
|
||||
, XMLSignatureFactory signatureFactory
|
||||
, String signatureId
|
||||
, List<X509Certificate> signingCertificateChain
|
||||
, List<Reference> references
|
||||
, List<XMLObject> objects
|
||||
) 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.dsig.SignatureInfo;
|
||||
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.POILogger;
|
||||
import org.apache.xmlbeans.XmlCursor;
|
||||
import org.apache.xmlbeans.XmlObject;
|
||||
import org.apache.xmlbeans.XmlString;
|
||||
import org.etsi.uri.x01903.v13.AnyType;
|
||||
import org.etsi.uri.x01903.v13.CertIDListType;
|
||||
|
@ -134,8 +136,6 @@ public class XAdESSignatureFacet implements SignatureFacet {
|
|||
@Override
|
||||
public void preSign(Document document,
|
||||
XMLSignatureFactory signatureFactory,
|
||||
String signatureId,
|
||||
List<X509Certificate> signingCertificateChain,
|
||||
List<Reference> references, List<XMLObject> objects)
|
||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
LOG.log(POILogger.DEBUG, "preSign");
|
||||
|
@ -143,13 +143,13 @@ public class XAdESSignatureFacet implements SignatureFacet {
|
|||
// QualifyingProperties
|
||||
QualifyingPropertiesDocument qualDoc = QualifyingPropertiesDocument.Factory.newInstance();
|
||||
QualifyingPropertiesType qualifyingProperties = qualDoc.addNewQualifyingProperties();
|
||||
qualifyingProperties.setTarget("#" + signatureId);
|
||||
qualifyingProperties.setTarget("#" + signatureConfig.getPackageSignatureId());
|
||||
|
||||
// SignedProperties
|
||||
SignedPropertiesType signedProperties = qualifyingProperties.addNewSignedProperties();
|
||||
String signedPropertiesId = this.idSignedProperties;
|
||||
if (this.idSignedProperties == null) {
|
||||
signedPropertiesId = signatureId + "-xades";
|
||||
signedPropertiesId = signatureConfig.getPackageSignatureId() + "-xades";
|
||||
}
|
||||
signedProperties.setId(signedPropertiesId);
|
||||
|
||||
|
@ -164,13 +164,13 @@ public class XAdESSignatureFacet implements SignatureFacet {
|
|||
signedSignatureProperties.setSigningTime(xmlGregorianCalendar);
|
||||
|
||||
// SigningCertificate
|
||||
if (null == signingCertificateChain
|
||||
|| signingCertificateChain.isEmpty()) {
|
||||
if (signatureConfig.getSigningCertificateChain() == null
|
||||
|| signatureConfig.getSigningCertificateChain().isEmpty()) {
|
||||
throw new RuntimeException("no signing certificate chain available");
|
||||
}
|
||||
CertIDListType signingCertificates = signedSignatureProperties.addNewSigningCertificate();
|
||||
CertIDType certId = signingCertificates.addNewCert();
|
||||
X509Certificate signingCertificate = signingCertificateChain.get(0);
|
||||
X509Certificate signingCertificate = signatureConfig.getSigningCertificateChain().get(0);
|
||||
setCertID(certId, signingCertificate, this.signatureConfig.getDigestAlgo(), this.issuerNameNoReverseOrder);
|
||||
|
||||
// ClaimedRole
|
||||
|
@ -181,7 +181,7 @@ public class XAdESSignatureFacet implements SignatureFacet {
|
|||
AnyType claimedRole = claimedRolesList.addNewClaimedRole();
|
||||
XmlString roleString = XmlString.Factory.newInstance();
|
||||
roleString.setStringValue(this.role);
|
||||
SignatureInfo.insertXChild(claimedRole, roleString);
|
||||
insertXChild(claimedRole, roleString);
|
||||
}
|
||||
|
||||
// XAdES-EPES
|
||||
|
@ -208,7 +208,7 @@ public class XAdESSignatureFacet implements SignatureFacet {
|
|||
AnyType sigPolicyQualifier = sigPolicyQualifiers.addNewSigPolicyQualifier();
|
||||
XmlString spUriElement = XmlString.Factory.newInstance();
|
||||
spUriElement.setStringValue(signaturePolicyDownloadUrl);
|
||||
SignatureInfo.insertXChild(sigPolicyQualifier, spUriElement);
|
||||
insertXChild(sigPolicyQualifier, spUriElement);
|
||||
}
|
||||
} else if (this.signaturePolicyImplied) {
|
||||
SignaturePolicyIdentifierType signaturePolicyIdentifier =
|
||||
|
@ -238,7 +238,7 @@ public class XAdESSignatureFacet implements SignatureFacet {
|
|||
// add XAdES ds:Object
|
||||
List<XMLStructure> xadesObjectContent = new ArrayList<XMLStructure>();
|
||||
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#");
|
||||
setPrefix(qualDocEl, "http://uri.etsi.org/01903/v1.3.2#", "xd");
|
||||
xadesObjectContent.add(new DOMStructure(qualDocEl));
|
||||
|
@ -376,4 +376,14 @@ public class XAdESSignatureFacet implements SignatureFacet {
|
|||
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;
|
||||
|
||||
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.ByteArrayOutputStream;
|
||||
|
@ -50,7 +51,6 @@ import javax.xml.crypto.dsig.XMLObject;
|
|||
import javax.xml.crypto.dsig.XMLSignatureFactory;
|
||||
|
||||
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.RevocationDataService;
|
||||
import org.apache.poi.poifs.crypt.dsig.services.TimeStampService;
|
||||
|
@ -221,7 +221,7 @@ public class XAdESXLSignatureFacet implements SignatureFacet {
|
|||
// xadesv141::TimeStampValidationData
|
||||
if (tsaRevocationDataXadesT.hasRevocationDataEntries()) {
|
||||
ValidationDataType validationData = createValidationData(tsaRevocationDataXadesT);
|
||||
SignatureInfo.insertXChild(unsignedSigProps, validationData);
|
||||
insertXChild(unsignedSigProps, validationData);
|
||||
}
|
||||
|
||||
if (null == this.revocationDataService) {
|
||||
|
@ -334,7 +334,7 @@ public class XAdESXLSignatureFacet implements SignatureFacet {
|
|||
this.c14nAlgoId, this.timeStampService);
|
||||
if (tsaRevocationDataXadesX1.hasRevocationDataEntries()) {
|
||||
ValidationDataType timeStampXadesX1ValidationData = createValidationData(tsaRevocationDataXadesX1);
|
||||
SignatureInfo.insertXChild(unsignedSigProps, timeStampXadesX1ValidationData);
|
||||
insertXChild(unsignedSigProps, timeStampXadesX1ValidationData);
|
||||
}
|
||||
|
||||
// marshal XAdES-X
|
||||
|
@ -381,8 +381,6 @@ public class XAdESXLSignatureFacet implements SignatureFacet {
|
|||
@Override
|
||||
public void preSign(Document document,
|
||||
XMLSignatureFactory signatureFactory,
|
||||
String signatureId,
|
||||
List<X509Certificate> signingCertificateChain,
|
||||
List<Reference> references, List<XMLObject> objects)
|
||||
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
|
||||
// nothing to do here
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
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.
|
|
@ -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.TimeStampService;
|
||||
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.util.DocumentHelper;
|
||||
import org.apache.poi.util.IOUtils;
|
||||
|
@ -120,7 +119,10 @@ public class TestSignatureInfo {
|
|||
|
||||
for (String testFile : testFiles) {
|
||||
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();
|
||||
pkg.revert();
|
||||
pkg.close();
|
||||
|
@ -146,7 +148,10 @@ public class TestSignatureInfo {
|
|||
|
||||
for (String testFile : testFiles) {
|
||||
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();
|
||||
|
||||
assertNotNull(result);
|
||||
|
@ -164,7 +169,10 @@ public class TestSignatureInfo {
|
|||
public void getMultiSigners() throws Exception {
|
||||
String testFile = "hello-world-signed-twice.docx";
|
||||
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();
|
||||
|
||||
assertNotNull(result);
|
||||
|
@ -189,12 +197,18 @@ public class TestSignatureInfo {
|
|||
|
||||
@Test
|
||||
public void testSignSpreadsheetWithSignatureInfo() throws Exception {
|
||||
initKeyPair("Test", "CN=Test");
|
||||
String testFile = "hello-world-unsigned.xlsx";
|
||||
OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
|
||||
SignatureInfo si = new SignatureInfo(pkg);
|
||||
initKeyPair("Test", "CN=Test");
|
||||
SignatureInfoConfig sic = new SignatureInfoConfig();
|
||||
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 ...
|
||||
si.confirmSignature(keyPair.getPrivate(), x509, HashAlgorithm.sha1);
|
||||
si.confirmSignature();
|
||||
List<X509Certificate> signer = si.getSigners();
|
||||
assertEquals(1, signer.size());
|
||||
pkg.close();
|
||||
|
@ -223,7 +237,7 @@ public class TestSignatureInfo {
|
|||
certificateChain.add(x509);
|
||||
signatureConfig.setSigningCertificateChain(certificateChain);
|
||||
|
||||
signatureConfig.addSignatureFacet(new EnvelopedSignatureFacet());
|
||||
signatureConfig.addSignatureFacet(new EnvelopedSignatureFacet(signatureConfig));
|
||||
signatureConfig.addSignatureFacet(new KeyInfoSignatureFacet(true, false, false));
|
||||
signatureConfig.addSignatureFacet(new XAdESSignatureFacet(signatureConfig));
|
||||
|
||||
|
@ -274,12 +288,13 @@ public class TestSignatureInfo {
|
|||
|
||||
XAdESXLSignatureFacet xadesXLSignatureFacet = new XAdESXLSignatureFacet(
|
||||
timeStampService, revocationDataService);
|
||||
XmlSignatureService testedInstance = new XmlSignatureService(signatureConfig);
|
||||
SignatureInfo si = new SignatureInfo();
|
||||
si.setSignatureConfig(signatureConfig);
|
||||
|
||||
Document document = DocumentHelper.createDocument();
|
||||
|
||||
// operate
|
||||
DigestInfo digestInfo = testedInstance.preSign(document, null);
|
||||
DigestInfo digestInfo = si.preSign(document, null);
|
||||
|
||||
// verify
|
||||
assertNotNull(digestInfo);
|
||||
|
@ -297,10 +312,10 @@ public class TestSignatureInfo {
|
|||
assertNotNull(certDigest.getDigestValue());
|
||||
|
||||
// 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
|
||||
testedInstance.postSign(document, signatureValue);
|
||||
si.postSign(document, signatureValue);
|
||||
|
||||
DOMValidateContext domValidateContext = new DOMValidateContext(
|
||||
KeySelector.singletonKeySelector(keyPair.getPublic()),
|
||||
|
@ -341,12 +356,13 @@ public class TestSignatureInfo {
|
|||
signatureConfig.setOpcPackage(pkgCopy);
|
||||
signatureConfig.addDefaultFacets();
|
||||
|
||||
XmlSignatureService signatureService = new XmlSignatureService(signatureConfig);
|
||||
SignatureInfo si = new SignatureInfo();
|
||||
si.setSignatureConfig(signatureConfig);
|
||||
|
||||
Document document = DocumentHelper.createDocument();
|
||||
|
||||
// operate
|
||||
DigestInfo digestInfo = signatureService.preSign(document, null);
|
||||
DigestInfo digestInfo = si.preSign(document, null);
|
||||
|
||||
// verify
|
||||
assertNotNull(digestInfo);
|
||||
|
@ -357,13 +373,13 @@ public class TestSignatureInfo {
|
|||
assertNotNull(digestInfo.digestValue);
|
||||
|
||||
// setup: key material, signature value
|
||||
byte[] signatureValue = SignatureInfo.signDigest(keyPair.getPrivate(), HashAlgorithm.sha1, digestInfo.digestValue);
|
||||
byte[] signatureValue = si.signDigest(digestInfo.digestValue);
|
||||
|
||||
// operate: postSign
|
||||
signatureService.postSign(document, signatureValue);
|
||||
si.postSign(document, signatureValue);
|
||||
|
||||
// verify: signature
|
||||
SignatureInfo si = new SignatureInfo(pkgCopy);
|
||||
si.getSignatureConfig().setOpcPackage(pkgCopy);
|
||||
List<X509Certificate> signers = si.getSigners();
|
||||
assertEquals(signerCount, signers.size());
|
||||
|
||||
|
|
Loading…
Reference in New Issue