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 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  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.security.Provider;
  29. import java.util.ArrayList;
  30. import java.util.Base64;
  31. import java.util.HashMap;
  32. import java.util.Iterator;
  33. import java.util.List;
  34. import java.util.Map;
  35. import java.util.NoSuchElementException;
  36. import java.util.Objects;
  37. import java.util.stream.Stream;
  38. import javax.xml.crypto.MarshalException;
  39. import javax.xml.crypto.URIDereferencer;
  40. import javax.xml.crypto.XMLStructure;
  41. import javax.xml.crypto.dsig.CanonicalizationMethod;
  42. import javax.xml.crypto.dsig.Manifest;
  43. import javax.xml.crypto.dsig.Reference;
  44. import javax.xml.crypto.dsig.SignatureMethod;
  45. import javax.xml.crypto.dsig.SignedInfo;
  46. import javax.xml.crypto.dsig.TransformException;
  47. import javax.xml.crypto.dsig.XMLObject;
  48. import javax.xml.crypto.dsig.XMLSignatureException;
  49. import javax.xml.crypto.dsig.XMLSignatureFactory;
  50. import javax.xml.crypto.dsig.dom.DOMSignContext;
  51. import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
  52. import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
  53. import org.apache.jcp.xml.dsig.internal.dom.DOMReference;
  54. import org.apache.jcp.xml.dsig.internal.dom.DOMSignedInfo;
  55. import org.apache.jcp.xml.dsig.internal.dom.DOMSubTreeData;
  56. import org.apache.poi.EncryptedDocumentException;
  57. import org.apache.poi.ooxml.util.DocumentHelper;
  58. import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
  59. import org.apache.poi.openxml4j.opc.OPCPackage;
  60. import org.apache.poi.openxml4j.opc.PackagePart;
  61. import org.apache.poi.openxml4j.opc.PackagePartName;
  62. import org.apache.poi.openxml4j.opc.PackageRelationship;
  63. import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;
  64. import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
  65. import org.apache.poi.openxml4j.opc.PackagingURIHelper;
  66. import org.apache.poi.openxml4j.opc.TargetMode;
  67. import org.apache.poi.poifs.crypt.CryptoFunctions;
  68. import org.apache.poi.poifs.crypt.HashAlgorithm;
  69. import org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet;
  70. import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService;
  71. import org.apache.poi.util.POILogFactory;
  72. import org.apache.poi.util.POILogger;
  73. import org.apache.xml.security.Init;
  74. import org.apache.xml.security.utils.XMLUtils;
  75. import org.apache.xmlbeans.XmlOptions;
  76. import org.w3.x2000.x09.xmldsig.SignatureDocument;
  77. import org.w3c.dom.Document;
  78. import org.w3c.dom.Element;
  79. import org.w3c.dom.NodeList;
  80. import org.w3c.dom.events.EventListener;
  81. import org.w3c.dom.events.EventTarget;
  82. import org.w3c.dom.events.MutationEvent;
  83. /**
  84. * <p>This class is the default entry point for XML signatures and can be used for
  85. * validating an existing signed office document and signing a office document.</p>
  86. *
  87. * <p><b>Validating a signed office document</b></p>
  88. *
  89. * <pre>
  90. * OPCPackage pkg = OPCPackage.open(..., PackageAccess.READ);
  91. * SignatureConfig sic = new SignatureConfig();
  92. * sic.setOpcPackage(pkg);
  93. * SignatureInfo si = new SignatureInfo();
  94. * si.setSignatureConfig(sic);
  95. * boolean isValid = si.validate();
  96. * ...
  97. * </pre>
  98. *
  99. * <p><b>Signing an office document</b></p>
  100. *
  101. * <pre>
  102. * // loading the keystore - pkcs12 is used here, but of course jks &amp; co are also valid
  103. * // the keystore needs to contain a private key and it's certificate having a
  104. * // 'digitalSignature' key usage
  105. * char password[] = "test".toCharArray();
  106. * File file = new File("test.pfx");
  107. * KeyStore keystore = KeyStore.getInstance("PKCS12");
  108. * FileInputStream fis = new FileInputStream(file);
  109. * keystore.load(fis, password);
  110. * fis.close();
  111. *
  112. * // extracting private key and certificate
  113. * String alias = "xyz"; // alias of the keystore entry
  114. * Key key = keystore.getKey(alias, password);
  115. * X509Certificate x509 = (X509Certificate)keystore.getCertificate(alias);
  116. *
  117. * // filling the SignatureConfig entries (minimum fields, more options are available ...)
  118. * SignatureConfig signatureConfig = new SignatureConfig();
  119. * signatureConfig.setKey(keyPair.getPrivate());
  120. * signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
  121. * OPCPackage pkg = OPCPackage.open(..., PackageAccess.READ_WRITE);
  122. * signatureConfig.setOpcPackage(pkg);
  123. *
  124. * // adding the signature document to the package
  125. * SignatureInfo si = new SignatureInfo();
  126. * si.setSignatureConfig(signatureConfig);
  127. * si.confirmSignature();
  128. * // optionally verify the generated signature
  129. * boolean b = si.verifySignature();
  130. * assert (b);
  131. * // write the changes back to disc
  132. * pkg.close();
  133. * </pre>
  134. *
  135. * <p><b>Implementation notes:</b></p>
  136. *
  137. * <p>Although there's a XML signature implementation in the Oracle JDKs 6 and higher,
  138. * compatibility with IBM JDKs is also in focus (... but maybe not thoroughly tested ...).
  139. * Therefore we are using the Apache Santuario libs (xmlsec) instead of the built-in classes,
  140. * as the compatibility seems to be provided there.</p>
  141. *
  142. * <p>To use SignatureInfo and its sibling classes, you'll need to have the following libs
  143. * in the classpath:</p>
  144. * <ul>
  145. * <li>BouncyCastle bcpkix and bcprov (tested against 1.64)</li>
  146. * <li>Apache Santuario "xmlsec" (tested against 2.1.5)</li>
  147. * <li>and slf4j-api (tested against 1.7.30)</li>
  148. * </ul>
  149. */
  150. public class SignatureInfo {
  151. private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class);
  152. private SignatureConfig signatureConfig;
  153. private OPCPackage opcPackage;
  154. private Provider provider;
  155. private XMLSignatureFactory signatureFactory;
  156. private KeyInfoFactory keyInfoFactory;
  157. private URIDereferencer uriDereferencer;
  158. /**
  159. * @return the signature config
  160. */
  161. public SignatureConfig getSignatureConfig() {
  162. return signatureConfig;
  163. }
  164. /**
  165. * @param signatureConfig the signature config, needs to be set before a SignatureInfo object is used
  166. */
  167. public void setSignatureConfig(SignatureConfig signatureConfig) {
  168. this.signatureConfig = signatureConfig;
  169. }
  170. public void setOpcPackage(OPCPackage opcPackage) {
  171. this.opcPackage = opcPackage;
  172. }
  173. public OPCPackage getOpcPackage() {
  174. return opcPackage;
  175. }
  176. public URIDereferencer getUriDereferencer() {
  177. return uriDereferencer;
  178. }
  179. public void setUriDereferencer(URIDereferencer uriDereferencer) {
  180. this.uriDereferencer = uriDereferencer;
  181. }
  182. /**
  183. * @return true, if first signature part is valid
  184. */
  185. public boolean verifySignature() {
  186. initXmlProvider();
  187. // http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html
  188. // only validate first part
  189. Iterator<SignaturePart> iter = getSignatureParts().iterator();
  190. return iter.hasNext() && iter.next().validate();
  191. }
  192. /**
  193. * add the xml signature to the document
  194. *
  195. * @throws XMLSignatureException if the signature can't be calculated
  196. * @throws MarshalException if the document can't be serialized
  197. */
  198. public void confirmSignature() throws XMLSignatureException, MarshalException {
  199. initXmlProvider();
  200. final Document document = DocumentHelper.createDocument();
  201. final DOMSignContext xmlSignContext = createXMLSignContext(document);
  202. // operate
  203. final DOMSignedInfo signedInfo = preSign(xmlSignContext);
  204. // setup: key material, signature value
  205. final String signatureValue = signDigest(xmlSignContext, signedInfo);
  206. // operate: postSign
  207. postSign(xmlSignContext, signatureValue);
  208. }
  209. /**
  210. * Convenience method for creating the signature context
  211. *
  212. * @param document the document the signature is based on
  213. *
  214. * @return the initialized signature context
  215. */
  216. public DOMSignContext createXMLSignContext(final Document document) {
  217. initXmlProvider();
  218. return new DOMSignContext(signatureConfig.getKey(), document);
  219. }
  220. /**
  221. * Sign (encrypt) the digest with the private key.
  222. * Currently only rsa is supported.
  223. *
  224. * @return the encrypted hash
  225. */
  226. public String signDigest(final DOMSignContext xmlSignContext, final DOMSignedInfo signedInfo) {
  227. initXmlProvider();
  228. final PrivateKey key = signatureConfig.getKey();
  229. final HashAlgorithm algo = signatureConfig.getDigestAlgo();
  230. // taken from org.apache.xml.security.utils.Base64
  231. final int BASE64DEFAULTLENGTH = 76;
  232. if (algo.hashSize*4/3 > BASE64DEFAULTLENGTH && !XMLUtils.ignoreLineBreaks()) {
  233. throw new EncryptedDocumentException("The hash size of the chosen hash algorithm ("+algo+" = "+algo.hashSize+" bytes), "+
  234. "will motivate XmlSec to add linebreaks to the generated digest, which results in an invalid signature (... at least "+
  235. "for Office) - please persuade it otherwise by adding '-Dorg.apache.xml.security.ignoreLineBreaks=true' to the JVM "+
  236. "system properties.");
  237. }
  238. try (final DigestOutputStream dos = getDigestStream(algo, key)) {
  239. dos.init();
  240. final Document document = (Document)xmlSignContext.getParent();
  241. final Element el = getDsigElement(document, "SignedInfo");
  242. final DOMSubTreeData subTree = new DOMSubTreeData(el, true);
  243. signedInfo.getCanonicalizationMethod().transform(subTree, xmlSignContext, dos);
  244. return Base64.getEncoder().encodeToString(dos.sign());
  245. } catch (GeneralSecurityException|IOException|TransformException e) {
  246. throw new EncryptedDocumentException(e);
  247. }
  248. }
  249. private static DigestOutputStream getDigestStream(final HashAlgorithm algo, final PrivateKey key) {
  250. switch (algo) {
  251. case md2: case md5: case sha1: case sha256: case sha384: case sha512:
  252. return new SignatureOutputStream(algo, key);
  253. default:
  254. return new DigestOutputStream(algo, key);
  255. }
  256. }
  257. /**
  258. * @return a signature part for each signature document.
  259. * the parts can be validated independently.
  260. */
  261. public Iterable<SignaturePart> getSignatureParts() {
  262. initXmlProvider();
  263. return SignaturePartIterator::new;
  264. }
  265. private final class SignaturePartIterator implements Iterator<SignaturePart> {
  266. Iterator<PackageRelationship> sigOrigRels;
  267. private Iterator<PackageRelationship> sigRels;
  268. private PackagePart sigPart;
  269. private SignaturePartIterator() {
  270. sigOrigRels = opcPackage.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN).iterator();
  271. }
  272. @Override
  273. public boolean hasNext() {
  274. while (sigRels == null || !sigRels.hasNext()) {
  275. if (!sigOrigRels.hasNext()) {
  276. return false;
  277. }
  278. sigPart = opcPackage.getPart(sigOrigRels.next());
  279. LOG.log(POILogger.DEBUG, "Digital Signature Origin part", sigPart);
  280. try {
  281. sigRels = sigPart.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE).iterator();
  282. } catch (InvalidFormatException e) {
  283. LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);
  284. }
  285. }
  286. return true;
  287. }
  288. @Override
  289. public SignaturePart next() {
  290. PackagePart sigRelPart = null;
  291. do {
  292. try {
  293. if (!hasNext()) {
  294. throw new NoSuchElementException();
  295. }
  296. sigRelPart = sigPart.getRelatedPart(sigRels.next());
  297. LOG.log(POILogger.DEBUG, "XML Signature part", sigRelPart);
  298. } catch (InvalidFormatException e) {
  299. LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);
  300. }
  301. } while (sigRelPart == null);
  302. return new SignaturePart(sigRelPart, SignatureInfo.this);
  303. }
  304. @Override
  305. public void remove() {
  306. throw new UnsupportedOperationException();
  307. }
  308. }
  309. /**
  310. * Helper method for adding informations before the signing.
  311. * Normally {@link #confirmSignature()} is sufficient to be used.
  312. */
  313. @SuppressWarnings("unchecked")
  314. public DOMSignedInfo preSign(final DOMSignContext xmlSignContext)
  315. throws XMLSignatureException, MarshalException {
  316. final Document document = (Document)xmlSignContext.getParent();
  317. registerEventListener(document);
  318. // Signature context construction.
  319. if (uriDereferencer != null) {
  320. xmlSignContext.setURIDereferencer(uriDereferencer);
  321. }
  322. signatureConfig.getNamespacePrefixes().forEach(xmlSignContext::putNamespacePrefix);
  323. xmlSignContext.setDefaultNamespacePrefix("");
  324. /*
  325. * Add ds:References that come from signing client local files.
  326. */
  327. List<Reference> references = new ArrayList<>();
  328. /*
  329. * Invoke the signature facets.
  330. */
  331. List<XMLObject> objects = new ArrayList<>();
  332. for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) {
  333. LOG.log(POILogger.DEBUG, "invoking signature facet: " + signatureFacet.getClass().getSimpleName());
  334. signatureFacet.preSign(this, document, references, objects);
  335. }
  336. /*
  337. * ds:SignedInfo
  338. */
  339. SignedInfo signedInfo;
  340. try {
  341. SignatureMethod signatureMethod = signatureFactory.newSignatureMethod
  342. (signatureConfig.getSignatureMethodUri(), null);
  343. CanonicalizationMethod canonicalizationMethod = signatureFactory
  344. .newCanonicalizationMethod(signatureConfig.getCanonicalizationMethod(),
  345. (C14NMethodParameterSpec) null);
  346. signedInfo = signatureFactory.newSignedInfo(
  347. canonicalizationMethod, signatureMethod, references);
  348. } catch (GeneralSecurityException e) {
  349. throw new XMLSignatureException(e);
  350. }
  351. /*
  352. * JSR105 ds:Signature creation
  353. */
  354. String signatureValueId = signatureConfig.getPackageSignatureId() + "-signature-value";
  355. javax.xml.crypto.dsig.XMLSignature xmlSignature = signatureFactory
  356. .newXMLSignature(signedInfo, null, objects, signatureConfig.getPackageSignatureId(),
  357. signatureValueId);
  358. /*
  359. * ds:Signature Marshalling.
  360. */
  361. xmlSignature.sign(xmlSignContext);
  362. /*
  363. * Completion of undigested ds:References in the ds:Manifests.
  364. */
  365. for (XMLObject object : objects) {
  366. LOG.log(POILogger.DEBUG, "object java type: " + object.getClass().getName());
  367. List<XMLStructure> objectContentList = object.getContent();
  368. for (XMLStructure objectContent : objectContentList) {
  369. LOG.log(POILogger.DEBUG, "object content java type: " + objectContent.getClass().getName());
  370. if (!(objectContent instanceof Manifest)) {
  371. continue;
  372. }
  373. Manifest manifest = (Manifest) objectContent;
  374. List<Reference> manifestReferences = manifest.getReferences();
  375. for (Reference manifestReference : manifestReferences) {
  376. if (manifestReference.getDigestValue() != null) {
  377. continue;
  378. }
  379. DOMReference manifestDOMReference = (DOMReference)manifestReference;
  380. manifestDOMReference.digest(xmlSignContext);
  381. }
  382. }
  383. }
  384. /*
  385. * Completion of undigested ds:References.
  386. */
  387. List<Reference> signedInfoReferences = signedInfo.getReferences();
  388. for (Reference signedInfoReference : signedInfoReferences) {
  389. DOMReference domReference = (DOMReference)signedInfoReference;
  390. // ds:Reference with external digest value
  391. if (domReference.getDigestValue() != null) {
  392. continue;
  393. }
  394. domReference.digest(xmlSignContext);
  395. }
  396. return (DOMSignedInfo)signedInfo;
  397. }
  398. // it's necessary to explicitly set the mdssi namespace, but the sign() method has no
  399. // normal way to interfere with, so we need to add the namespace under the hand ...
  400. protected void registerEventListener(Document document) {
  401. final SignatureMarshalListener sml = signatureConfig.getSignatureMarshalListener();
  402. if (sml == null) {
  403. return;
  404. }
  405. final EventListener[] el = { null };
  406. final EventTarget eventTarget = (EventTarget)document;
  407. final String eventType = "DOMSubtreeModified";
  408. final boolean DONT_USE_CAPTURE = false;
  409. el[0] = (e) -> {
  410. if (e instanceof MutationEvent && e.getTarget() instanceof Document) {
  411. eventTarget.removeEventListener(eventType, el[0], DONT_USE_CAPTURE);
  412. sml.handleElement(this, document, eventTarget, el[0]);
  413. eventTarget.addEventListener(eventType, el[0], DONT_USE_CAPTURE);
  414. }
  415. };
  416. eventTarget.addEventListener(eventType, el[0], DONT_USE_CAPTURE);
  417. }
  418. /**
  419. * Helper method for adding informations after the signing.
  420. * Normally {@link #confirmSignature()} is sufficient to be used.
  421. */
  422. public void postSign(final DOMSignContext xmlSignContext, final String signatureValue)
  423. throws MarshalException {
  424. LOG.log(POILogger.DEBUG, "postSign");
  425. final Document document = (Document)xmlSignContext.getParent();
  426. /*
  427. * Check ds:Signature node.
  428. */
  429. String signatureId = signatureConfig.getPackageSignatureId();
  430. if (!signatureId.equals(document.getDocumentElement().getAttribute("Id"))) {
  431. throw new RuntimeException("ds:Signature not found for @Id: " + signatureId);
  432. }
  433. /*
  434. * Insert signature value into the ds:SignatureValue element
  435. */
  436. final Element signatureNode = getDsigElement(document, "SignatureValue");
  437. if (signatureNode == null) {
  438. throw new RuntimeException("preSign has to be called before postSign");
  439. }
  440. signatureNode.setTextContent(signatureValue);
  441. /*
  442. * Allow signature facets to inject their own stuff.
  443. */
  444. for (SignatureFacet signatureFacet : signatureConfig.getSignatureFacets()) {
  445. signatureFacet.postSign(this, document);
  446. }
  447. writeDocument(document);
  448. }
  449. /**
  450. * Write XML signature into the OPC package
  451. *
  452. * @param document the xml signature document
  453. * @throws MarshalException if the document can't be serialized
  454. */
  455. protected void writeDocument(Document document) throws MarshalException {
  456. XmlOptions xo = new XmlOptions();
  457. Map<String,String> namespaceMap = new HashMap<>();
  458. signatureConfig.getNamespacePrefixes().forEach((k,v) -> namespaceMap.put(v,k));
  459. xo.setSaveSuggestedPrefixes(namespaceMap);
  460. xo.setUseDefaultNamespace();
  461. LOG.log(POILogger.DEBUG, "output signed Office OpenXML document");
  462. /*
  463. * Copy the original OOXML content to the signed OOXML package. During
  464. * copying some files need to changed.
  465. */
  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 = opcPackage.getPart(originPartName);
  471. if (originPart == null) {
  472. // touch empty marker file
  473. originPart = opcPackage.createPart(originPartName, originDesc.getContentType());
  474. opcPackage.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 = opcPackage.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. opcPackage.removePart(opcPackage.getPart(pn));
  492. }
  493. nextSigIdx = 1;
  494. }
  495. PackagePartName sigPartName = PackagingURIHelper.createPartName(sigDesc.getFileName(nextSigIdx));
  496. PackagePart sigPart = opcPackage.getPart(sigPartName);
  497. if (sigPart == null) {
  498. sigPart = opcPackage.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. public void setProvider(Provider provider) {
  520. this.provider = provider;
  521. }
  522. public void setSignatureFactory(XMLSignatureFactory signatureFactory) {
  523. this.signatureFactory = signatureFactory;
  524. }
  525. public XMLSignatureFactory getSignatureFactory() {
  526. return signatureFactory;
  527. }
  528. public void setKeyInfoFactory(KeyInfoFactory keyInfoFactory) {
  529. this.keyInfoFactory = keyInfoFactory;
  530. }
  531. public KeyInfoFactory getKeyInfoFactory() {
  532. return keyInfoFactory;
  533. }
  534. /**
  535. * Initialize the xml signing environment and the bouncycastle provider
  536. */
  537. @SuppressWarnings("deprecation")
  538. protected void initXmlProvider() {
  539. if (opcPackage == null) {
  540. opcPackage = signatureConfig.getOpcPackage();
  541. }
  542. if (provider == null) {
  543. provider = signatureConfig.getProvider();
  544. if (provider == null) {
  545. provider = XmlProviderInitSingleton.getInstance().findProvider();
  546. }
  547. }
  548. if (signatureFactory == null) {
  549. signatureFactory = signatureConfig.getSignatureFactory();
  550. if (signatureFactory == null) {
  551. signatureFactory = XMLSignatureFactory.getInstance("DOM", provider);
  552. }
  553. }
  554. if (keyInfoFactory == null) {
  555. keyInfoFactory = signatureConfig.getKeyInfoFactory();
  556. if (keyInfoFactory == null) {
  557. keyInfoFactory = KeyInfoFactory.getInstance("DOM", provider);
  558. }
  559. }
  560. if (uriDereferencer == null) {
  561. uriDereferencer = signatureConfig.getUriDereferencer();
  562. if (uriDereferencer == null) {
  563. uriDereferencer = new OOXMLURIDereferencer();
  564. }
  565. }
  566. if (uriDereferencer instanceof OOXMLURIDereferencer) {
  567. ((OOXMLURIDereferencer)uriDereferencer).setSignatureInfo(this);
  568. }
  569. }
  570. private static final class XmlProviderInitSingleton {
  571. // Bill Pugh Singleton
  572. private static class SingletonHelper {
  573. private static final XmlProviderInitSingleton INSTANCE = new XmlProviderInitSingleton();
  574. }
  575. public static XmlProviderInitSingleton getInstance(){
  576. return SingletonHelper.INSTANCE;
  577. }
  578. private XmlProviderInitSingleton() {
  579. try {
  580. Init.init();
  581. RelationshipTransformService.registerDsigProvider();
  582. CryptoFunctions.registerBouncyCastle();
  583. } catch (Exception e) {
  584. throw new RuntimeException("Xml & BouncyCastle-Provider initialization failed", e);
  585. }
  586. }
  587. /**
  588. * This method tests the existence of xml signature provider in the following order:
  589. * <ul>
  590. * <li>the class pointed to by the system property "jsr105Provider"</li>
  591. * <li>the Santuario xmlsec provider</li>
  592. * <li>the JDK xmlsec provider</li>
  593. * </ul>
  594. *
  595. * For signing the classes are linked against the Santuario xmlsec, so this might
  596. * only work for validation (not tested).
  597. *
  598. * @return the xml dsig provider
  599. */
  600. public Provider findProvider() {
  601. return
  602. Stream.of(SignatureConfig.getProviderNames())
  603. .map(this::getProvider)
  604. .filter(Objects::nonNull).findFirst()
  605. .orElseThrow(this::providerNotFound);
  606. }
  607. private Provider getProvider(String className) {
  608. try {
  609. return (Provider)Class.forName(className).newInstance();
  610. } catch (Exception e) {
  611. LOG.log(POILogger.DEBUG, "XMLDsig-Provider '"+className+"' can't be found - trying next.");
  612. return null;
  613. }
  614. }
  615. private RuntimeException providerNotFound() {
  616. return new RuntimeException("JRE doesn't support default xml signature provider - set jsr105Provider system property!");
  617. }
  618. }
  619. }