git-svn-id: https://svn.apache.org/repos/asf/poi/branches/xml_signature@1626107 13f79535-47bb-0310-9956-ffa450edef68pull/11/head
@@ -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()); | |||