You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SignatureInfo.java 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. /* ====================================================================
  2. Licensed to the Apache Software Foundation (ASF) under one or more
  3. contributor license agreements. See the NOTICE file distributed with
  4. this work for additional information regarding copyright ownership.
  5. The ASF licenses this file to You under the Apache License, Version 2.0
  6. (the "License"); you may not use this file except in compliance with
  7. the License. You may obtain a copy of the License at
  8. http://www.apache.org/licenses/LICENSE-2.0
  9. Unless required by applicable law or agreed to in writing, software
  10. distributed under the License is distributed on an "AS IS" BASIS,
  11. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. See the License for the specific language governing permissions and
  13. limitations under the License.
  14. ==================================================================== */
  15. /* ====================================================================
  16. This product contains an ASLv2 licensed version of the OOXML signer
  17. package from the eID Applet project
  18. http://code.google.com/p/eid-applet/source/browse/trunk/README.txt
  19. Copyright (C) 2008-2014 FedICT.
  20. ================================================================= */
  21. package org.apache.poi.poifs.crypt.dsig;
  22. import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
  23. import static org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet.XML_DIGSIG_NS;
  24. import java.io.IOException;
  25. import java.io.OutputStream;
  26. import java.security.GeneralSecurityException;
  27. import java.security.PrivateKey;
  28. import java.util.ArrayList;
  29. import java.util.HashMap;
  30. import java.util.Iterator;
  31. import java.util.List;
  32. import java.util.Map;
  33. import java.util.NoSuchElementException;
  34. import javax.xml.bind.DatatypeConverter;
  35. import javax.xml.crypto.MarshalException;
  36. import javax.xml.crypto.URIDereferencer;
  37. import javax.xml.crypto.XMLStructure;
  38. import javax.xml.crypto.dsig.CanonicalizationMethod;
  39. import javax.xml.crypto.dsig.Manifest;
  40. import javax.xml.crypto.dsig.Reference;
  41. import javax.xml.crypto.dsig.SignatureMethod;
  42. import javax.xml.crypto.dsig.SignedInfo;
  43. import javax.xml.crypto.dsig.TransformException;
  44. import javax.xml.crypto.dsig.XMLObject;
  45. import javax.xml.crypto.dsig.XMLSignatureException;
  46. import javax.xml.crypto.dsig.XMLSignatureFactory;
  47. import javax.xml.crypto.dsig.dom.DOMSignContext;
  48. import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
  49. import org.apache.jcp.xml.dsig.internal.dom.DOMReference;
  50. import org.apache.jcp.xml.dsig.internal.dom.DOMSignedInfo;
  51. import org.apache.jcp.xml.dsig.internal.dom.DOMSubTreeData;
  52. import org.apache.poi.EncryptedDocumentException;
  53. import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
  54. import org.apache.poi.openxml4j.opc.OPCPackage;
  55. import org.apache.poi.openxml4j.opc.PackagePart;
  56. import org.apache.poi.openxml4j.opc.PackagePartName;
  57. import org.apache.poi.openxml4j.opc.PackageRelationship;
  58. import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
  59. import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
  60. import org.apache.poi.openxml4j.opc.PackagingURIHelper;
  61. import org.apache.poi.openxml4j.opc.TargetMode;
  62. import org.apache.poi.poifs.crypt.CryptoFunctions;
  63. import org.apache.poi.poifs.crypt.HashAlgorithm;
  64. import org.apache.poi.poifs.crypt.dsig.SignatureConfig.SignatureConfigurable;
  65. import org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet;
  66. import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService;
  67. import org.apache.poi.ooxml.util.DocumentHelper;
  68. import org.apache.poi.util.POILogFactory;
  69. import org.apache.poi.util.POILogger;
  70. import org.apache.xml.security.Init;
  71. import org.apache.xml.security.utils.Base64;
  72. import org.apache.xml.security.utils.XMLUtils;
  73. import org.apache.xmlbeans.XmlOptions;
  74. import org.w3.x2000.x09.xmldsig.SignatureDocument;
  75. import org.w3c.dom.Document;
  76. import org.w3c.dom.Element;
  77. import org.w3c.dom.NodeList;
  78. import org.w3c.dom.events.EventListener;
  79. import org.w3c.dom.events.EventTarget;
  80. /**
  81. * <p>This class is the default entry point for XML signatures and can be used for
  82. * validating an existing signed office document and signing a office document.</p>
  83. *
  84. * <p><b>Validating a signed office document</b></p>
  85. *
  86. * <pre>
  87. * OPCPackage pkg = OPCPackage.open(..., PackageAccess.READ);
  88. * SignatureConfig sic = new SignatureConfig();
  89. * sic.setOpcPackage(pkg);
  90. * SignatureInfo si = new SignatureInfo();
  91. * si.setSignatureConfig(sic);
  92. * boolean isValid = si.validate();
  93. * ...
  94. * </pre>
  95. *
  96. * <p><b>Signing an office document</b></p>
  97. *
  98. * <pre>
  99. * // loading the keystore - pkcs12 is used here, but of course jks &amp; co are also valid
  100. * // the keystore needs to contain a private key and it's certificate having a
  101. * // 'digitalSignature' key usage
  102. * char password[] = "test".toCharArray();
  103. * File file = new File("test.pfx");
  104. * KeyStore keystore = KeyStore.getInstance("PKCS12");
  105. * FileInputStream fis = new FileInputStream(file);
  106. * keystore.load(fis, password);
  107. * fis.close();
  108. *
  109. * // extracting private key and certificate
  110. * String alias = "xyz"; // alias of the keystore entry
  111. * Key key = keystore.getKey(alias, password);
  112. * X509Certificate x509 = (X509Certificate)keystore.getCertificate(alias);
  113. *
  114. * // filling the SignatureConfig entries (minimum fields, more options are available ...)
  115. * SignatureConfig signatureConfig = new SignatureConfig();
  116. * signatureConfig.setKey(keyPair.getPrivate());
  117. * signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
  118. * OPCPackage pkg = OPCPackage.open(..., PackageAccess.READ_WRITE);
  119. * signatureConfig.setOpcPackage(pkg);
  120. *
  121. * // adding the signature document to the package
  122. * SignatureInfo si = new SignatureInfo();
  123. * si.setSignatureConfig(signatureConfig);
  124. * si.confirmSignature();
  125. * // optionally verify the generated signature
  126. * boolean b = si.verifySignature();
  127. * assert (b);
  128. * // write the changes back to disc
  129. * pkg.close();
  130. * </pre>
  131. *
  132. * <p><b>Implementation notes:</b></p>
  133. *
  134. * <p>Although there's a XML signature implementation in the Oracle JDKs 6 and higher,
  135. * compatibility with IBM JDKs is also in focus (... but maybe not thoroughly tested ...).
  136. * Therefore we are using the Apache Santuario libs (xmlsec) instead of the built-in classes,
  137. * as the compatibility seems to be provided there.</p>
  138. *
  139. * <p>To use SignatureInfo and its sibling classes, you'll need to have the following libs
  140. * in the classpath:</p>
  141. * <ul>
  142. * <li>BouncyCastle bcpkix and bcprov (tested against 1.60)</li>
  143. * <li>Apache Santuario "xmlsec" (tested against 2.1.0)</li>
  144. * <li>and slf4j-api (tested against 1.7.25)</li>
  145. * </ul>
  146. */
  147. public class SignatureInfo implements SignatureConfigurable {
  148. private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class);
  149. private static boolean isInitialized;
  150. private SignatureConfig signatureConfig;
  151. /**
  152. * Constructor initializes xml signature environment, if it hasn't been initialized before
  153. */
  154. public SignatureInfo() {
  155. initXmlProvider();
  156. }
  157. /**
  158. * @return the signature config
  159. */
  160. public SignatureConfig getSignatureConfig() {
  161. return signatureConfig;
  162. }
  163. /**
  164. * @param signatureConfig the signature config, needs to be set before a SignatureInfo object is used
  165. */
  166. @Override
  167. public void setSignatureConfig(SignatureConfig signatureConfig) {
  168. this.signatureConfig = signatureConfig;
  169. }
  170. /**
  171. * @return true, if first signature part is valid
  172. */
  173. public boolean verifySignature() {
  174. // http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html
  175. for (SignaturePart sp : getSignatureParts()){
  176. // only validate first part
  177. return sp.validate();
  178. }
  179. return false;
  180. }
  181. /**
  182. * add the xml signature to the document
  183. *
  184. * @throws XMLSignatureException
  185. * @throws MarshalException
  186. */
  187. public void confirmSignature() throws XMLSignatureException, MarshalException {
  188. final Document document = DocumentHelper.createDocument();
  189. final DOMSignContext xmlSignContext = createXMLSignContext(document);
  190. // operate
  191. final DOMSignedInfo signedInfo = preSign(xmlSignContext);
  192. // setup: key material, signature value
  193. final String signatureValue = signDigest(xmlSignContext, signedInfo);
  194. // operate: postSign
  195. postSign(xmlSignContext, signatureValue);
  196. }
  197. /**
  198. * Convenience method for creating the signature context
  199. *
  200. * @param document the document the signature is based on
  201. *
  202. * @return the initialized signature context
  203. */
  204. public DOMSignContext createXMLSignContext(final Document document) {
  205. return new DOMSignContext(signatureConfig.getKey(), document);
  206. }
  207. /**
  208. * Sign (encrypt) the digest with the private key.
  209. * Currently only rsa is supported.
  210. *
  211. * @param digest the hashed input
  212. * @return the encrypted hash
  213. */
  214. public String signDigest(final DOMSignContext xmlSignContext, final DOMSignedInfo signedInfo) {
  215. final PrivateKey key = signatureConfig.getKey();
  216. final HashAlgorithm algo = signatureConfig.getDigestAlgo();
  217. if (algo.hashSize*4/3 > Base64.BASE64DEFAULTLENGTH && !XMLUtils.ignoreLineBreaks()) {
  218. throw new EncryptedDocumentException("The hash size of the choosen hash algorithm ("+algo+" = "+algo.hashSize+" bytes), "+
  219. "will motivate XmlSec to add linebreaks to the generated digest, which results in an invalid signature (... at least "+
  220. "for Office) - please persuade it otherwise by adding '-Dorg.apache.xml.security.ignoreLineBreaks=true' to the JVM "+
  221. "system properties.");
  222. }
  223. try (final DigestOutputStream dos = getDigestStream(algo, key)) {
  224. dos.init();
  225. final Document document = (Document)xmlSignContext.getParent();
  226. final Element el = getDsigElement(document, "SignedInfo");
  227. final DOMSubTreeData subTree = new DOMSubTreeData(el, true);
  228. signedInfo.getCanonicalizationMethod().transform(subTree, xmlSignContext, dos);
  229. return DatatypeConverter.printBase64Binary(dos.sign());
  230. } catch (GeneralSecurityException|IOException|TransformException e) {
  231. throw new EncryptedDocumentException(e);
  232. }
  233. }
  234. private static DigestOutputStream getDigestStream(final HashAlgorithm algo, final PrivateKey key) {
  235. switch (algo) {
  236. case md2: case md5: case sha1: case sha256: case sha384: case sha512:
  237. return new SignatureOutputStream(algo, key);
  238. default:
  239. return new DigestOutputStream(algo, key);
  240. }
  241. }
  242. /**
  243. * @return a signature part for each signature document.
  244. * the parts can be validated independently.
  245. */
  246. public Iterable<SignaturePart> getSignatureParts() {
  247. signatureConfig.init(true);
  248. return new Iterable<SignaturePart>() {
  249. @Override
  250. public Iterator<SignaturePart> iterator() {
  251. return new Iterator<SignaturePart>() {
  252. OPCPackage pkg = signatureConfig.getOpcPackage();
  253. Iterator<PackageRelationship> sigOrigRels =
  254. pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN).iterator();
  255. Iterator<PackageRelationship> sigRels;
  256. PackagePart sigPart;
  257. @Override
  258. public boolean hasNext() {
  259. while (sigRels == null || !sigRels.hasNext()) {
  260. if (!sigOrigRels.hasNext()) {
  261. return false;
  262. }
  263. sigPart = pkg.getPart(sigOrigRels.next());
  264. LOG.log(POILogger.DEBUG, "Digital Signature Origin part", sigPart);
  265. try {
  266. sigRels = sigPart.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE).iterator();
  267. } catch (InvalidFormatException e) {
  268. LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);
  269. }
  270. }
  271. return true;
  272. }
  273. @Override
  274. public SignaturePart next() {
  275. PackagePart sigRelPart = null;
  276. do {
  277. try {
  278. if (!hasNext()) {
  279. throw new NoSuchElementException();
  280. }
  281. sigRelPart = sigPart.getRelatedPart(sigRels.next());
  282. LOG.log(POILogger.DEBUG, "XML Signature part", sigRelPart);
  283. } catch (InvalidFormatException e) {
  284. LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);
  285. }
  286. } while (sigRelPart == null);
  287. return new SignaturePart(sigRelPart, signatureConfig);
  288. }
  289. @Override
  290. public void remove() {
  291. throw new UnsupportedOperationException();
  292. }
  293. };
  294. }
  295. };
  296. }
  297. /**
  298. * Initialize the xml signing environment and the bouncycastle provider
  299. */
  300. protected static synchronized void initXmlProvider() {
  301. if (isInitialized) {
  302. return;
  303. }
  304. isInitialized = true;
  305. try {
  306. Init.init();
  307. RelationshipTransformService.registerDsigProvider();
  308. CryptoFunctions.registerBouncyCastle();
  309. } catch (Exception e) {
  310. throw new RuntimeException("Xml & BouncyCastle-Provider initialization failed", e);
  311. }
  312. }
  313. /**
  314. * Helper method for adding informations before the signing.
  315. * Normally {@link #confirmSignature()} is sufficient to be used.
  316. */
  317. @SuppressWarnings("unchecked")
  318. public DOMSignedInfo preSign(final DOMSignContext xmlSignContext)
  319. throws XMLSignatureException, MarshalException {
  320. signatureConfig.init(false);
  321. final Document document = (Document)xmlSignContext.getParent();
  322. // it's necessary to explicitly set the mdssi namespace, but the sign() method has no
  323. // normal way to interfere with, so we need to add the namespace under the hand ...
  324. EventTarget target = (EventTarget)document;
  325. EventListener creationListener = signatureConfig.getSignatureMarshalListener();
  326. if (creationListener != null) {
  327. if (creationListener instanceof SignatureMarshalListener) {
  328. ((SignatureMarshalListener)creationListener).setEventTarget(target);
  329. }
  330. SignatureMarshalListener.setListener(target, creationListener, true);
  331. }
  332. /*
  333. * Signature context construction.
  334. */
  335. URIDereferencer uriDereferencer = signatureConfig.getUriDereferencer();
  336. if (null != uriDereferencer) {
  337. xmlSignContext.setURIDereferencer(uriDereferencer);
  338. }
  339. signatureConfig.getNamespacePrefixes().forEach(xmlSignContext::putNamespacePrefix);
  340. xmlSignContext.setDefaultNamespacePrefix("");
  341. // signatureConfig.getNamespacePrefixes().get(XML_DIGSIG_NS));
  342. XMLSignatureFactory signatureFactory = signatureConfig.getSignatureFactory();
  343. /*
  344. * Add ds:References that come from signing client local files.
  345. */
  346. List<Reference> references = new ArrayList<>();
  347. /*
  348. * Invoke the signature facets.
  349. */
  350. List<XMLObject> objects = new ArrayList<>();
  351. for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) {
  352. LOG.log(POILogger.DEBUG, "invoking signature facet: " + signatureFacet.getClass().getSimpleName());
  353. signatureFacet.preSign(document, references, objects);
  354. }
  355. /*
  356. * ds:SignedInfo
  357. */
  358. SignedInfo signedInfo;
  359. try {
  360. SignatureMethod signatureMethod = signatureFactory.newSignatureMethod
  361. (signatureConfig.getSignatureMethodUri(), null);
  362. CanonicalizationMethod canonicalizationMethod = signatureFactory
  363. .newCanonicalizationMethod(signatureConfig.getCanonicalizationMethod(),
  364. (C14NMethodParameterSpec) null);
  365. signedInfo = signatureFactory.newSignedInfo(
  366. canonicalizationMethod, signatureMethod, references);
  367. } catch (GeneralSecurityException e) {
  368. throw new XMLSignatureException(e);
  369. }
  370. /*
  371. * JSR105 ds:Signature creation
  372. */
  373. String signatureValueId = signatureConfig.getPackageSignatureId() + "-signature-value";
  374. javax.xml.crypto.dsig.XMLSignature xmlSignature = signatureFactory
  375. .newXMLSignature(signedInfo, null, objects, signatureConfig.getPackageSignatureId(),
  376. signatureValueId);
  377. /*
  378. * ds:Signature Marshalling.
  379. */
  380. xmlSignature.sign(xmlSignContext);
  381. /*
  382. * Completion of undigested ds:References in the ds:Manifests.
  383. */
  384. for (XMLObject object : objects) {
  385. LOG.log(POILogger.DEBUG, "object java type: " + object.getClass().getName());
  386. List<XMLStructure> objectContentList = object.getContent();
  387. for (XMLStructure objectContent : objectContentList) {
  388. LOG.log(POILogger.DEBUG, "object content java type: " + objectContent.getClass().getName());
  389. if (!(objectContent instanceof Manifest)) {
  390. continue;
  391. }
  392. Manifest manifest = (Manifest) objectContent;
  393. List<Reference> manifestReferences = manifest.getReferences();
  394. for (Reference manifestReference : manifestReferences) {
  395. if (manifestReference.getDigestValue() != null) {
  396. continue;
  397. }
  398. DOMReference manifestDOMReference = (DOMReference)manifestReference;
  399. manifestDOMReference.digest(xmlSignContext);
  400. }
  401. }
  402. }
  403. /*
  404. * Completion of undigested ds:References.
  405. */
  406. List<Reference> signedInfoReferences = signedInfo.getReferences();
  407. for (Reference signedInfoReference : signedInfoReferences) {
  408. DOMReference domReference = (DOMReference)signedInfoReference;
  409. // ds:Reference with external digest value
  410. if (domReference.getDigestValue() != null) {
  411. continue;
  412. }
  413. domReference.digest(xmlSignContext);
  414. }
  415. return (DOMSignedInfo)signedInfo;
  416. }
  417. /**
  418. * Helper method for adding informations after the signing.
  419. * Normally {@link #confirmSignature()} is sufficient to be used.
  420. */
  421. public void postSign(final DOMSignContext xmlSignContext, final String signatureValue)
  422. throws MarshalException {
  423. LOG.log(POILogger.DEBUG, "postSign");
  424. final Document document = (Document)xmlSignContext.getParent();
  425. /*
  426. * Check ds:Signature node.
  427. */
  428. String signatureId = signatureConfig.getPackageSignatureId();
  429. if (!signatureId.equals(document.getDocumentElement().getAttribute("Id"))) {
  430. throw new RuntimeException("ds:Signature not found for @Id: " + signatureId);
  431. }
  432. /*
  433. * Insert signature value into the ds:SignatureValue element
  434. */
  435. final Element signatureNode = getDsigElement(document, "SignatureValue");
  436. if (signatureNode == null) {
  437. throw new RuntimeException("preSign has to be called before postSign");
  438. }
  439. signatureNode.setTextContent(signatureValue);
  440. /*
  441. * Allow signature facets to inject their own stuff.
  442. */
  443. for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) {
  444. signatureFacet.postSign(document);
  445. }
  446. writeDocument(document);
  447. }
  448. /**
  449. * Write XML signature into the OPC package
  450. *
  451. * @param document the xml signature document
  452. * @throws MarshalException
  453. */
  454. protected void writeDocument(Document document) throws MarshalException {
  455. XmlOptions xo = new XmlOptions();
  456. Map<String,String> namespaceMap = new HashMap<>();
  457. signatureConfig.getNamespacePrefixes().forEach((k,v) -> namespaceMap.put(v,k));
  458. xo.setSaveSuggestedPrefixes(namespaceMap);
  459. xo.setUseDefaultNamespace();
  460. LOG.log(POILogger.DEBUG, "output signed Office OpenXML document");
  461. /*
  462. * Copy the original OOXML content to the signed OOXML package. During
  463. * copying some files need to changed.
  464. */
  465. OPCPackage pkg = signatureConfig.getOpcPackage();
  466. try {
  467. // <Default Extension="sigs" ContentType="application/vnd.openxmlformats-package.digital-signature-origin"/>
  468. final DSigRelation originDesc = DSigRelation.ORIGIN_SIGS;
  469. PackagePartName originPartName = PackagingURIHelper.createPartName(originDesc.getFileName(0));
  470. PackagePart originPart = pkg.getPart(originPartName);
  471. if (originPart == null) {
  472. // touch empty marker file
  473. originPart = pkg.createPart(originPartName, originDesc.getContentType());
  474. pkg.addRelationship(originPartName, TargetMode.INTERNAL, originDesc.getRelation());
  475. }
  476. // <Override PartName="/_xmlsignatures/sig1.xml" ContentType="application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"/>
  477. final DSigRelation sigDesc = DSigRelation.SIG;
  478. int nextSigIdx = pkg.getUnusedPartIndex(sigDesc.getDefaultFileName());
  479. if (!signatureConfig.isAllowMultipleSignatures()) {
  480. PackageRelationshipCollection prc = originPart.getRelationshipsByType(sigDesc.getRelation());
  481. for (int i=2; i<nextSigIdx; i++) {
  482. PackagePartName pn = PackagingURIHelper.createPartName(sigDesc.getFileName(i));
  483. for (PackageRelationship rel : prc) {
  484. PackagePart pp = originPart.getRelatedPart(rel);
  485. if (pp.getPartName().equals(pn)) {
  486. originPart.removeRelationship(rel.getId());
  487. prc.removeRelationship(rel.getId());
  488. break;
  489. }
  490. }
  491. pkg.removePart(pkg.getPart(pn));
  492. }
  493. nextSigIdx = 1;
  494. }
  495. PackagePartName sigPartName = PackagingURIHelper.createPartName(sigDesc.getFileName(nextSigIdx));
  496. PackagePart sigPart = pkg.getPart(sigPartName);
  497. if (sigPart == null) {
  498. sigPart = pkg.createPart(sigPartName, sigDesc.getContentType());
  499. originPart.addRelationship(sigPartName, TargetMode.INTERNAL, sigDesc.getRelation());
  500. } else {
  501. sigPart.clear();
  502. }
  503. try (OutputStream os = sigPart.getOutputStream()) {
  504. SignatureDocument sigDoc = SignatureDocument.Factory.parse(document, DEFAULT_XML_OPTIONS);
  505. sigDoc.save(os, xo);
  506. }
  507. } catch (Exception e) {
  508. throw new MarshalException("Unable to write signature document", e);
  509. }
  510. }
  511. private Element getDsigElement(final Document document, final String localName) {
  512. NodeList sigValNl = document.getElementsByTagNameNS(XML_DIGSIG_NS, localName);
  513. if (sigValNl.getLength() == 1) {
  514. return (Element)sigValNl.item(0);
  515. }
  516. LOG.log(POILogger.WARN, "Signature element '"+localName+"' was "+(sigValNl.getLength() == 0 ? "not found" : "multiple times"));
  517. return null;
  518. }
  519. }