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.

TestSignatureInfo.java 28KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692
  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;
  22. import static org.junit.Assert.assertEquals;
  23. import static org.junit.Assert.assertFalse;
  24. import static org.junit.Assert.assertNotNull;
  25. import static org.junit.Assert.assertTrue;
  26. import java.io.ByteArrayInputStream;
  27. import java.io.ByteArrayOutputStream;
  28. import java.io.File;
  29. import java.io.FileInputStream;
  30. import java.io.FileOutputStream;
  31. import java.io.IOException;
  32. import java.io.InputStream;
  33. import java.io.OutputStream;
  34. import java.net.ConnectException;
  35. import java.net.HttpURLConnection;
  36. import java.net.MalformedURLException;
  37. import java.net.URL;
  38. import java.security.Key;
  39. import java.security.KeyPair;
  40. import java.security.KeyStore;
  41. import java.security.PrivateKey;
  42. import java.security.cert.Certificate;
  43. import java.security.cert.X509CRL;
  44. import java.security.cert.X509Certificate;
  45. import java.util.ArrayList;
  46. import java.util.Calendar;
  47. import java.util.Collections;
  48. import java.util.Date;
  49. import java.util.Iterator;
  50. import java.util.List;
  51. import org.apache.poi.POIDataSamples;
  52. import org.apache.poi.POITestCase;
  53. import org.apache.poi.openxml4j.opc.OPCPackage;
  54. import org.apache.poi.openxml4j.opc.PackageAccess;
  55. import org.apache.poi.poifs.crypt.dsig.DigestInfo;
  56. import org.apache.poi.poifs.crypt.dsig.SignatureConfig;
  57. import org.apache.poi.poifs.crypt.dsig.SignatureInfo;
  58. import org.apache.poi.poifs.crypt.dsig.SignatureInfo.SignaturePart;
  59. import org.apache.poi.poifs.crypt.dsig.facets.EnvelopedSignatureFacet;
  60. import org.apache.poi.poifs.crypt.dsig.facets.KeyInfoSignatureFacet;
  61. import org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet;
  62. import org.apache.poi.poifs.crypt.dsig.facets.XAdESXLSignatureFacet;
  63. import org.apache.poi.poifs.crypt.dsig.services.RevocationData;
  64. import org.apache.poi.poifs.crypt.dsig.services.RevocationDataService;
  65. import org.apache.poi.poifs.crypt.dsig.services.TimeStampService;
  66. import org.apache.poi.poifs.crypt.dsig.services.TimeStampServiceValidator;
  67. import org.apache.poi.ss.usermodel.WorkbookFactory;
  68. import org.apache.poi.util.DocumentHelper;
  69. import org.apache.poi.util.IOUtils;
  70. import org.apache.poi.util.LocaleUtil;
  71. import org.apache.poi.util.POILogFactory;
  72. import org.apache.poi.util.POILogger;
  73. import org.apache.poi.xssf.streaming.SXSSFWorkbook;
  74. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  75. import org.apache.xmlbeans.XmlObject;
  76. import org.bouncycastle.asn1.x509.KeyUsage;
  77. import org.bouncycastle.cert.ocsp.OCSPResp;
  78. import org.etsi.uri.x01903.v13.DigestAlgAndValueType;
  79. import org.etsi.uri.x01903.v13.QualifyingPropertiesType;
  80. import org.junit.Assume;
  81. import org.junit.BeforeClass;
  82. import org.junit.Test;
  83. import org.w3.x2000.x09.xmldsig.ReferenceType;
  84. import org.w3.x2000.x09.xmldsig.SignatureDocument;
  85. import org.w3c.dom.Document;
  86. public class TestSignatureInfo {
  87. private static final POILogger LOG = POILogFactory.getLogger(TestSignatureInfo.class);
  88. private static final POIDataSamples testdata = POIDataSamples.getXmlDSignInstance();
  89. private static Calendar cal;
  90. private KeyPair keyPair = null;
  91. private X509Certificate x509 = null;
  92. @BeforeClass
  93. public static void initBouncy() throws IOException {
  94. CryptoFunctions.registerBouncyCastle();
  95. /*** TODO : set cal to now ... only set to fixed date for debugging ... */
  96. cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
  97. // cal.set(2014, 7, 6, 21, 42, 12);
  98. // cal.clear(Calendar.MILLISECOND);
  99. // don't run this test when we are using older Xerces as it triggers an XML Parser backwards compatibility issue
  100. // in the xmlsec jar file
  101. String additionalJar = System.getProperty("additionaljar");
  102. //System.out.println("Having: " + additionalJar);
  103. Assume.assumeTrue("Not running TestSignatureInfo because we are testing with additionaljar set to " + additionalJar,
  104. additionalJar == null || additionalJar.trim().length() == 0);
  105. }
  106. @Test
  107. public void office2007prettyPrintedRels() throws Exception {
  108. OPCPackage pkg = OPCPackage.open(testdata.getFile("office2007prettyPrintedRels.docx"), PackageAccess.READ);
  109. try {
  110. SignatureConfig sic = new SignatureConfig();
  111. sic.setOpcPackage(pkg);
  112. SignatureInfo si = new SignatureInfo();
  113. si.setSignatureConfig(sic);
  114. boolean isValid = si.verifySignature();
  115. assertTrue(isValid);
  116. } finally {
  117. pkg.close();
  118. }
  119. }
  120. @Test
  121. public void getSignerUnsigned() throws Exception {
  122. String testFiles[] = {
  123. "hello-world-unsigned.docx",
  124. "hello-world-unsigned.pptx",
  125. "hello-world-unsigned.xlsx",
  126. "hello-world-office-2010-technical-preview-unsigned.docx"
  127. };
  128. for (String testFile : testFiles) {
  129. OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ);
  130. SignatureConfig sic = new SignatureConfig();
  131. sic.setOpcPackage(pkg);
  132. SignatureInfo si = new SignatureInfo();
  133. si.setSignatureConfig(sic);
  134. List<X509Certificate> result = new ArrayList<X509Certificate>();
  135. for (SignaturePart sp : si.getSignatureParts()) {
  136. if (sp.validate()) {
  137. result.add(sp.getSigner());
  138. }
  139. }
  140. pkg.revert();
  141. pkg.close();
  142. assertNotNull(result);
  143. assertTrue(result.isEmpty());
  144. }
  145. }
  146. @Test
  147. public void getSigner() throws Exception {
  148. String testFiles[] = {
  149. "hyperlink-example-signed.docx",
  150. "hello-world-signed.docx",
  151. "hello-world-signed.pptx",
  152. "hello-world-signed.xlsx",
  153. "hello-world-office-2010-technical-preview.docx",
  154. "ms-office-2010-signed.docx",
  155. "ms-office-2010-signed.pptx",
  156. "ms-office-2010-signed.xlsx",
  157. "Office2010-SP1-XAdES-X-L.docx",
  158. "signed.docx",
  159. };
  160. for (String testFile : testFiles) {
  161. OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ);
  162. try {
  163. SignatureConfig sic = new SignatureConfig();
  164. sic.setOpcPackage(pkg);
  165. SignatureInfo si = new SignatureInfo();
  166. si.setSignatureConfig(sic);
  167. List<X509Certificate> result = new ArrayList<X509Certificate>();
  168. for (SignaturePart sp : si.getSignatureParts()) {
  169. if (sp.validate()) {
  170. result.add(sp.getSigner());
  171. }
  172. }
  173. assertNotNull(result);
  174. assertEquals("test-file: "+testFile, 1, result.size());
  175. X509Certificate signer = result.get(0);
  176. LOG.log(POILogger.DEBUG, "signer: " + signer.getSubjectX500Principal());
  177. boolean b = si.verifySignature();
  178. assertTrue("test-file: "+testFile, b);
  179. pkg.revert();
  180. } finally {
  181. pkg.close();
  182. }
  183. }
  184. }
  185. @Test
  186. public void getMultiSigners() throws Exception {
  187. String testFile = "hello-world-signed-twice.docx";
  188. OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ);
  189. try {
  190. SignatureConfig sic = new SignatureConfig();
  191. sic.setOpcPackage(pkg);
  192. SignatureInfo si = new SignatureInfo();
  193. si.setSignatureConfig(sic);
  194. List<X509Certificate> result = new ArrayList<X509Certificate>();
  195. for (SignaturePart sp : si.getSignatureParts()) {
  196. if (sp.validate()) {
  197. result.add(sp.getSigner());
  198. }
  199. }
  200. assertNotNull(result);
  201. assertEquals("test-file: "+testFile, 2, result.size());
  202. X509Certificate signer1 = result.get(0);
  203. X509Certificate signer2 = result.get(1);
  204. LOG.log(POILogger.DEBUG, "signer 1: " + signer1.getSubjectX500Principal());
  205. LOG.log(POILogger.DEBUG, "signer 2: " + signer2.getSubjectX500Principal());
  206. boolean b = si.verifySignature();
  207. assertTrue("test-file: "+testFile, b);
  208. pkg.revert();
  209. } finally {
  210. pkg.close();
  211. }
  212. }
  213. @Test
  214. public void testSignSpreadsheet() throws Exception {
  215. String testFile = "hello-world-unsigned.xlsx";
  216. OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
  217. sign(pkg, "Test", "CN=Test", 1);
  218. pkg.close();
  219. }
  220. @Test
  221. public void testManipulation() throws Exception {
  222. // sign & validate
  223. String testFile = "hello-world-unsigned.xlsx";
  224. @SuppressWarnings("resource")
  225. OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
  226. sign(pkg, "Test", "CN=Test", 1);
  227. // manipulate
  228. XSSFWorkbook wb = new XSSFWorkbook(pkg);
  229. wb.setSheetName(0, "manipulated");
  230. // ... I don't know, why commit is protected ...
  231. POITestCase.callMethod(XSSFWorkbook.class, wb, Void.class, "commit", new Class[0], new Object[0]);
  232. // todo: test a manipulation on a package part, which is not signed
  233. // ... maybe in combination with #56164
  234. // validate
  235. SignatureConfig sic = new SignatureConfig();
  236. sic.setOpcPackage(pkg);
  237. SignatureInfo si = new SignatureInfo();
  238. si.setSignatureConfig(sic);
  239. boolean b = si.verifySignature();
  240. assertFalse("signature should be broken", b);
  241. wb.close();
  242. }
  243. @Test
  244. public void testSignSpreadsheetWithSignatureInfo() throws Exception {
  245. initKeyPair("Test", "CN=Test");
  246. String testFile = "hello-world-unsigned.xlsx";
  247. OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
  248. SignatureConfig sic = new SignatureConfig();
  249. sic.setOpcPackage(pkg);
  250. sic.setKey(keyPair.getPrivate());
  251. sic.setSigningCertificateChain(Collections.singletonList(x509));
  252. SignatureInfo si = new SignatureInfo();
  253. si.setSignatureConfig(sic);
  254. // hash > sha1 doesn't work in excel viewer ...
  255. si.confirmSignature();
  256. List<X509Certificate> result = new ArrayList<X509Certificate>();
  257. for (SignaturePart sp : si.getSignatureParts()) {
  258. if (sp.validate()) {
  259. result.add(sp.getSigner());
  260. }
  261. }
  262. assertEquals(1, result.size());
  263. pkg.close();
  264. }
  265. @Test
  266. public void testSignEnvelopingDocument() throws Exception {
  267. String testFile = "hello-world-unsigned.xlsx";
  268. OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
  269. initKeyPair("Test", "CN=Test");
  270. final X509CRL crl = PkiTestUtils.generateCrl(x509, keyPair.getPrivate());
  271. // setup
  272. SignatureConfig signatureConfig = new SignatureConfig();
  273. signatureConfig.setOpcPackage(pkg);
  274. signatureConfig.setKey(keyPair.getPrivate());
  275. /*
  276. * We need at least 2 certificates for the XAdES-C complete certificate
  277. * refs construction.
  278. */
  279. List<X509Certificate> certificateChain = new ArrayList<X509Certificate>();
  280. certificateChain.add(x509);
  281. certificateChain.add(x509);
  282. signatureConfig.setSigningCertificateChain(certificateChain);
  283. signatureConfig.addSignatureFacet(new EnvelopedSignatureFacet());
  284. signatureConfig.addSignatureFacet(new KeyInfoSignatureFacet());
  285. signatureConfig.addSignatureFacet(new XAdESSignatureFacet());
  286. signatureConfig.addSignatureFacet(new XAdESXLSignatureFacet());
  287. // check for internet, no error means it works
  288. boolean mockTsp = (getAccessError("http://timestamp.comodoca.com/rfc3161", true, 10000) != null);
  289. // http://timestamping.edelweb.fr/service/tsp
  290. // http://tsa.belgium.be/connect
  291. // http://timestamp.comodoca.com/authenticode
  292. // http://timestamp.comodoca.com/rfc3161
  293. // http://services.globaltrustfinder.com/adss/tsa
  294. signatureConfig.setTspUrl("http://timestamp.comodoca.com/rfc3161");
  295. signatureConfig.setTspRequestPolicy(null); // comodoca request fails, if default policy is set ...
  296. signatureConfig.setTspOldProtocol(false);
  297. //set proxy info if any
  298. String proxy = System.getProperty("http_proxy");
  299. if (proxy != null && proxy.trim().length() > 0) {
  300. signatureConfig.setProxyUrl(proxy);
  301. }
  302. if (mockTsp) {
  303. TimeStampService tspService = new TimeStampService(){
  304. @Override
  305. public byte[] timeStamp(byte[] data, RevocationData revocationData) throws Exception {
  306. revocationData.addCRL(crl);
  307. return "time-stamp-token".getBytes(LocaleUtil.CHARSET_1252);
  308. }
  309. @Override
  310. public void setSignatureConfig(SignatureConfig config) {
  311. // empty on purpose
  312. }
  313. };
  314. signatureConfig.setTspService(tspService);
  315. } else {
  316. TimeStampServiceValidator tspValidator = new TimeStampServiceValidator() {
  317. @Override
  318. public void validate(List<X509Certificate> validateChain,
  319. RevocationData revocationData) throws Exception {
  320. for (X509Certificate certificate : validateChain) {
  321. LOG.log(POILogger.DEBUG, "certificate: " + certificate.getSubjectX500Principal());
  322. LOG.log(POILogger.DEBUG, "validity: " + certificate.getNotBefore() + " - " + certificate.getNotAfter());
  323. }
  324. }
  325. };
  326. signatureConfig.setTspValidator(tspValidator);
  327. signatureConfig.setTspOldProtocol(signatureConfig.getTspUrl().contains("edelweb"));
  328. }
  329. final RevocationData revocationData = new RevocationData();
  330. revocationData.addCRL(crl);
  331. OCSPResp ocspResp = PkiTestUtils.createOcspResp(x509, false,
  332. x509, x509, keyPair.getPrivate(), "SHA1withRSA", cal.getTimeInMillis());
  333. revocationData.addOCSP(ocspResp.getEncoded());
  334. RevocationDataService revocationDataService = new RevocationDataService(){
  335. @Override
  336. public RevocationData getRevocationData(List<X509Certificate> revocationChain) {
  337. return revocationData;
  338. }
  339. };
  340. signatureConfig.setRevocationDataService(revocationDataService);
  341. // operate
  342. SignatureInfo si = new SignatureInfo();
  343. si.setSignatureConfig(signatureConfig);
  344. try {
  345. si.confirmSignature();
  346. } catch (RuntimeException e) {
  347. pkg.close();
  348. // only allow a ConnectException because of timeout, we see this in Jenkins from time to time...
  349. if(e.getCause() == null) {
  350. throw e;
  351. }
  352. if(!(e.getCause() instanceof ConnectException)) {
  353. throw e;
  354. }
  355. assertTrue("Only allowing ConnectException with 'timed out' as message here, but had: " + e, e.getCause().getMessage().contains("timed out"));
  356. }
  357. // verify
  358. Iterator<SignaturePart> spIter = si.getSignatureParts().iterator();
  359. assertTrue(spIter.hasNext());
  360. SignaturePart sp = spIter.next();
  361. boolean valid = sp.validate();
  362. assertTrue(valid);
  363. SignatureDocument sigDoc = sp.getSignatureDocument();
  364. String declareNS =
  365. "declare namespace xades='http://uri.etsi.org/01903/v1.3.2#'; "
  366. + "declare namespace ds='http://www.w3.org/2000/09/xmldsig#'; ";
  367. String digestValXQuery = declareNS +
  368. "$this/ds:Signature/ds:SignedInfo/ds:Reference";
  369. for (ReferenceType rt : (ReferenceType[])sigDoc.selectPath(digestValXQuery)) {
  370. assertNotNull(rt.getDigestValue());
  371. assertEquals(signatureConfig.getDigestMethodUri(), rt.getDigestMethod().getAlgorithm());
  372. }
  373. String certDigestXQuery = declareNS +
  374. "$this//xades:SigningCertificate/xades:Cert/xades:CertDigest";
  375. XmlObject xoList[] = sigDoc.selectPath(certDigestXQuery);
  376. assertEquals(xoList.length, 1);
  377. DigestAlgAndValueType certDigest = (DigestAlgAndValueType)xoList[0];
  378. assertNotNull(certDigest.getDigestValue());
  379. String qualPropXQuery = declareNS +
  380. "$this/ds:Signature/ds:Object/xades:QualifyingProperties";
  381. xoList = sigDoc.selectPath(qualPropXQuery);
  382. assertEquals(xoList.length, 1);
  383. QualifyingPropertiesType qualProp = (QualifyingPropertiesType)xoList[0];
  384. boolean qualPropXsdOk = qualProp.validate();
  385. assertTrue(qualPropXsdOk);
  386. pkg.close();
  387. }
  388. public static String getAccessError(String destinationUrl, boolean fireRequest, int timeout) {
  389. URL url;
  390. try {
  391. url = new URL(destinationUrl);
  392. } catch (MalformedURLException e) {
  393. throw new IllegalArgumentException("Invalid destination URL", e);
  394. }
  395. HttpURLConnection conn = null;
  396. try {
  397. conn = (HttpURLConnection) url.openConnection();
  398. // set specified timeout if non-zero
  399. if(timeout != 0) {
  400. conn.setConnectTimeout(timeout);
  401. conn.setReadTimeout(timeout);
  402. }
  403. conn.setDoOutput(false);
  404. conn.setDoInput(true);
  405. /* if connecting is not possible this will throw a connection refused exception */
  406. conn.connect();
  407. if (fireRequest) {
  408. InputStream is = null;
  409. try {
  410. is = conn.getInputStream();
  411. } finally {
  412. IOUtils.closeQuietly(is);
  413. }
  414. }
  415. /* if connecting is possible we return true here */
  416. return null;
  417. } catch (IOException e) {
  418. /* exception is thrown -> server not available */
  419. return e.getClass().getName() + ": " + e.getMessage();
  420. } finally {
  421. if (conn != null) {
  422. conn.disconnect();
  423. }
  424. }
  425. }
  426. @Test
  427. public void testCertChain() throws Exception {
  428. KeyStore keystore = KeyStore.getInstance("PKCS12");
  429. String password = "test";
  430. InputStream is = testdata.openResourceAsStream("chaintest.pfx");
  431. keystore.load(is, password.toCharArray());
  432. is.close();
  433. Key key = keystore.getKey("poitest", password.toCharArray());
  434. Certificate chainList[] = keystore.getCertificateChain("poitest");
  435. List<X509Certificate> certChain = new ArrayList<X509Certificate>();
  436. for (Certificate c : chainList) {
  437. certChain.add((X509Certificate)c);
  438. }
  439. x509 = certChain.get(0);
  440. keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key);
  441. String testFile = "hello-world-unsigned.xlsx";
  442. OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
  443. SignatureConfig signatureConfig = new SignatureConfig();
  444. signatureConfig.setKey(keyPair.getPrivate());
  445. signatureConfig.setSigningCertificateChain(certChain);
  446. Calendar oldCal = LocaleUtil.getLocaleCalendar(2007, 7, 1);
  447. signatureConfig.setExecutionTime(oldCal.getTime());
  448. signatureConfig.setDigestAlgo(HashAlgorithm.sha1);
  449. signatureConfig.setOpcPackage(pkg);
  450. SignatureInfo si = new SignatureInfo();
  451. si.setSignatureConfig(signatureConfig);
  452. si.confirmSignature();
  453. for (SignaturePart sp : si.getSignatureParts()){
  454. assertTrue("Could not validate", sp.validate());
  455. X509Certificate signer = sp.getSigner();
  456. assertNotNull("signer undefined?!", signer);
  457. List<X509Certificate> certChainRes = sp.getCertChain();
  458. assertEquals(3, certChainRes.size());
  459. }
  460. pkg.close();
  461. }
  462. @Test
  463. public void testNonSha1() throws Exception {
  464. String testFile = "hello-world-unsigned.xlsx";
  465. initKeyPair("Test", "CN=Test");
  466. SignatureConfig signatureConfig = new SignatureConfig();
  467. signatureConfig.setKey(keyPair.getPrivate());
  468. signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
  469. HashAlgorithm testAlgo[] = { HashAlgorithm.sha224, HashAlgorithm.sha256
  470. , HashAlgorithm.sha384, HashAlgorithm.sha512, HashAlgorithm.ripemd160 };
  471. for (HashAlgorithm ha : testAlgo) {
  472. OPCPackage pkg = null;
  473. try {
  474. signatureConfig.setDigestAlgo(ha);
  475. pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);
  476. signatureConfig.setOpcPackage(pkg);
  477. SignatureInfo si = new SignatureInfo();
  478. si.setSignatureConfig(signatureConfig);
  479. si.confirmSignature();
  480. boolean b = si.verifySignature();
  481. assertTrue("Signature not correctly calculated for " + ha, b);
  482. } finally {
  483. if (pkg != null) pkg.close();
  484. }
  485. }
  486. }
  487. @Test
  488. public void bug58630() throws Exception {
  489. // test deletion of sheet 0 and signing
  490. File tpl = copy(testdata.getFile("bug58630.xlsx"));
  491. SXSSFWorkbook wb1 = new SXSSFWorkbook((XSSFWorkbook)WorkbookFactory.create(tpl), 10);
  492. wb1.setCompressTempFiles(true);
  493. wb1.removeSheetAt(0);
  494. ByteArrayOutputStream os = new ByteArrayOutputStream();
  495. wb1.write(os);
  496. wb1.close();
  497. OPCPackage pkg = OPCPackage.open(new ByteArrayInputStream(os.toByteArray()));
  498. initKeyPair("Test", "CN=Test");
  499. SignatureConfig signatureConfig = new SignatureConfig();
  500. signatureConfig.setKey(keyPair.getPrivate());
  501. signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
  502. signatureConfig.setOpcPackage(pkg);
  503. SignatureInfo si = new SignatureInfo();
  504. si.setSignatureConfig(signatureConfig);
  505. si.confirmSignature();
  506. assertTrue("invalid signature", si.verifySignature());
  507. pkg.close();
  508. }
  509. private void sign(OPCPackage pkgCopy, String alias, String signerDn, int signerCount) throws Exception {
  510. initKeyPair(alias, signerDn);
  511. SignatureConfig signatureConfig = new SignatureConfig();
  512. signatureConfig.setKey(keyPair.getPrivate());
  513. signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
  514. signatureConfig.setExecutionTime(cal.getTime());
  515. signatureConfig.setDigestAlgo(HashAlgorithm.sha1);
  516. signatureConfig.setOpcPackage(pkgCopy);
  517. SignatureInfo si = new SignatureInfo();
  518. si.setSignatureConfig(signatureConfig);
  519. Document document = DocumentHelper.createDocument();
  520. // operate
  521. DigestInfo digestInfo = si.preSign(document, null);
  522. // verify
  523. assertNotNull(digestInfo);
  524. LOG.log(POILogger.DEBUG, "digest algo: " + digestInfo.hashAlgo);
  525. LOG.log(POILogger.DEBUG, "digest description: " + digestInfo.description);
  526. assertEquals("Office OpenXML Document", digestInfo.description);
  527. assertNotNull(digestInfo.hashAlgo);
  528. assertNotNull(digestInfo.digestValue);
  529. // setup: key material, signature value
  530. byte[] signatureValue = si.signDigest(digestInfo.digestValue);
  531. // operate: postSign
  532. si.postSign(document, signatureValue);
  533. // verify: signature
  534. si.getSignatureConfig().setOpcPackage(pkgCopy);
  535. List<X509Certificate> result = new ArrayList<X509Certificate>();
  536. for (SignaturePart sp : si.getSignatureParts()) {
  537. if (sp.validate()) {
  538. result.add(sp.getSigner());
  539. }
  540. }
  541. assertEquals(signerCount, result.size());
  542. }
  543. private void initKeyPair(String alias, String subjectDN) throws Exception {
  544. final char password[] = "test".toCharArray();
  545. File file = new File("build/test.pfx");
  546. KeyStore keystore = KeyStore.getInstance("PKCS12");
  547. if (file.exists()) {
  548. FileInputStream fis = new FileInputStream(file);
  549. keystore.load(fis, password);
  550. fis.close();
  551. } else {
  552. keystore.load(null, password);
  553. }
  554. if (keystore.isKeyEntry(alias)) {
  555. Key key = keystore.getKey(alias, password);
  556. x509 = (X509Certificate)keystore.getCertificate(alias);
  557. keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key);
  558. } else {
  559. keyPair = PkiTestUtils.generateKeyPair();
  560. Date notBefore = cal.getTime();
  561. Calendar cal2 = (Calendar)cal.clone();
  562. cal2.add(Calendar.YEAR, 1);
  563. Date notAfter = cal2.getTime();
  564. KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature);
  565. x509 = PkiTestUtils.generateCertificate(keyPair.getPublic(), subjectDN
  566. , notBefore, notAfter, null, keyPair.getPrivate(), true, 0, null, null, keyUsage);
  567. keystore.setKeyEntry(alias, keyPair.getPrivate(), password, new Certificate[]{x509});
  568. FileOutputStream fos = new FileOutputStream(file);
  569. keystore.store(fos, password);
  570. fos.close();
  571. }
  572. }
  573. private static File copy(File input) throws IOException {
  574. String extension = input.getName().replaceAll(".*?(\\.[^.]+)?$", "$1");
  575. if (extension == null || "".equals(extension)) {
  576. extension = ".zip";
  577. }
  578. // ensure that we create the "build" directory as it might not be existing
  579. // in the Sonar Maven runs where we are at a different source directory
  580. File buildDir = new File("build");
  581. if(!buildDir.exists()) {
  582. assertTrue("Failed to create " + buildDir.getAbsolutePath(),
  583. buildDir.mkdirs());
  584. }
  585. File tmpFile = new File(buildDir, "sigtest"+extension);
  586. OutputStream fos = new FileOutputStream(tmpFile);
  587. try {
  588. InputStream fis = new FileInputStream(input);
  589. try {
  590. IOUtils.copy(fis, fos);
  591. } finally {
  592. fis.close();
  593. }
  594. } finally {
  595. fos.close();
  596. }
  597. return tmpFile;
  598. }
  599. }