Browse Source

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
pull/11/head
Andreas Beeker 9 years ago
parent
commit
6639a1938e

+ 357
- 63
src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java View File

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

+ 45
- 6
src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfoConfig.java View File

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

+ 10
- 26
src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/EnvelopedSignatureFacet.java View File

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

+ 5
- 6
src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/KeyInfoSignatureFacet.java View File

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

+ 16
- 17
src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/OOXMLSignatureFacet.java View File

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

+ 5
- 6
src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/Office2010SignatureFacet.java View File

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

+ 0
- 2
src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/SignatureFacet.java View File

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

+ 21
- 11
src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESSignatureFacet.java View File

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

+ 3
- 5
src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java View File

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

src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/SignaturePolicyService.java → src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/SignaturePolicyService.java View File

@@ -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.

+ 0
- 68
src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/SignatureService.java View File

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

+ 0
- 481
src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/XmlSignatureService.java View File

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

+ 33
- 17
src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java View File

@@ -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…
Cancel
Save