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

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253
  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.junit.jupiter.api.Assertions.assertEquals;
  23. import static org.junit.jupiter.api.Assertions.assertFalse;
  24. import static org.junit.jupiter.api.Assertions.assertNotNull;
  25. import static org.junit.jupiter.api.Assertions.assertTrue;
  26. import static org.junit.jupiter.api.Assumptions.assumeFalse;
  27. import static org.junit.jupiter.api.Assumptions.assumeTrue;
  28. import java.io.BufferedReader;
  29. import java.io.ByteArrayInputStream;
  30. import java.io.ByteArrayOutputStream;
  31. import java.io.File;
  32. import java.io.FileInputStream;
  33. import java.io.FileOutputStream;
  34. import java.io.IOException;
  35. import java.io.InputStream;
  36. import java.io.InputStreamReader;
  37. import java.io.OutputStream;
  38. import java.math.BigInteger;
  39. import java.net.ConnectException;
  40. import java.net.HttpURLConnection;
  41. import java.net.MalformedURLException;
  42. import java.net.SocketTimeoutException;
  43. import java.net.URL;
  44. import java.nio.charset.StandardCharsets;
  45. import java.security.Key;
  46. import java.security.KeyPair;
  47. import java.security.KeyPairGenerator;
  48. import java.security.KeyStore;
  49. import java.security.PrivateKey;
  50. import java.security.PublicKey;
  51. import java.security.SecureRandom;
  52. import java.security.cert.CRLException;
  53. import java.security.cert.Certificate;
  54. import java.security.cert.CertificateEncodingException;
  55. import java.security.cert.CertificateException;
  56. import java.security.cert.X509CRL;
  57. import java.security.cert.X509Certificate;
  58. import java.security.interfaces.RSAPublicKey;
  59. import java.security.spec.RSAKeyGenParameterSpec;
  60. import java.util.ArrayList;
  61. import java.util.Arrays;
  62. import java.util.Calendar;
  63. import java.util.Collections;
  64. import java.util.Date;
  65. import java.util.Iterator;
  66. import java.util.List;
  67. import java.util.function.BiFunction;
  68. import java.util.function.Supplier;
  69. import javax.xml.crypto.MarshalException;
  70. import javax.xml.crypto.dsig.CanonicalizationMethod;
  71. import javax.xml.crypto.dsig.XMLSignatureException;
  72. import javax.xml.crypto.dsig.dom.DOMSignContext;
  73. import org.apache.jcp.xml.dsig.internal.dom.DOMSignedInfo;
  74. import org.apache.logging.log4j.LogManager;
  75. import org.apache.logging.log4j.Logger;
  76. import org.apache.poi.EncryptedDocumentException;
  77. import org.apache.poi.POIDataSamples;
  78. import org.apache.poi.POITestCase;
  79. import org.apache.poi.ooxml.POIXMLDocument;
  80. import org.apache.poi.ooxml.util.DocumentHelper;
  81. import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
  82. import org.apache.poi.openxml4j.opc.OPCPackage;
  83. import org.apache.poi.openxml4j.opc.PackageAccess;
  84. import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;
  85. import org.apache.poi.poifs.crypt.CryptoFunctions;
  86. import org.apache.poi.poifs.crypt.HashAlgorithm;
  87. import org.apache.poi.poifs.crypt.dsig.facets.EnvelopedSignatureFacet;
  88. import org.apache.poi.poifs.crypt.dsig.facets.KeyInfoSignatureFacet;
  89. import org.apache.poi.poifs.crypt.dsig.facets.OOXMLSignatureFacet;
  90. import org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet;
  91. import org.apache.poi.poifs.crypt.dsig.facets.XAdESXLSignatureFacet;
  92. import org.apache.poi.poifs.crypt.dsig.services.RevocationData;
  93. import org.apache.poi.poifs.crypt.dsig.services.RevocationDataService;
  94. import org.apache.poi.poifs.crypt.dsig.services.TimeStampService;
  95. import org.apache.poi.poifs.crypt.dsig.services.TimeStampServiceValidator;
  96. import org.apache.poi.poifs.storage.RawDataUtil;
  97. import org.apache.poi.ss.usermodel.WorkbookFactory;
  98. import org.apache.poi.util.IOUtils;
  99. import org.apache.poi.util.LocaleUtil;
  100. import org.apache.poi.util.TempFile;
  101. import org.apache.poi.xssf.streaming.SXSSFWorkbook;
  102. import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
  103. import org.apache.poi.xssf.usermodel.XSSFSheet;
  104. import org.apache.poi.xssf.usermodel.XSSFSignatureLine;
  105. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  106. import org.apache.poi.xwpf.usermodel.XWPFDocument;
  107. import org.apache.poi.xwpf.usermodel.XWPFSignatureLine;
  108. import org.apache.xmlbeans.SystemProperties;
  109. import org.apache.xmlbeans.XmlException;
  110. import org.apache.xmlbeans.XmlObject;
  111. import org.bouncycastle.asn1.DEROctetString;
  112. import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
  113. import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
  114. import org.bouncycastle.asn1.x500.X500Name;
  115. import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
  116. import org.bouncycastle.asn1.x509.BasicConstraints;
  117. import org.bouncycastle.asn1.x509.CRLNumber;
  118. import org.bouncycastle.asn1.x509.Extension;
  119. import org.bouncycastle.asn1.x509.Extensions;
  120. import org.bouncycastle.asn1.x509.KeyUsage;
  121. import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
  122. import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
  123. import org.bouncycastle.cert.X509CRLHolder;
  124. import org.bouncycastle.cert.X509CertificateHolder;
  125. import org.bouncycastle.cert.X509ExtensionUtils;
  126. import org.bouncycastle.cert.X509v2CRLBuilder;
  127. import org.bouncycastle.cert.X509v3CertificateBuilder;
  128. import org.bouncycastle.cert.jcajce.JcaX509CRLConverter;
  129. import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
  130. import org.bouncycastle.cert.ocsp.BasicOCSPResp;
  131. import org.bouncycastle.cert.ocsp.BasicOCSPRespBuilder;
  132. import org.bouncycastle.cert.ocsp.CertificateID;
  133. import org.bouncycastle.cert.ocsp.CertificateStatus;
  134. import org.bouncycastle.cert.ocsp.OCSPReq;
  135. import org.bouncycastle.cert.ocsp.OCSPReqBuilder;
  136. import org.bouncycastle.cert.ocsp.OCSPResp;
  137. import org.bouncycastle.cert.ocsp.OCSPRespBuilder;
  138. import org.bouncycastle.cert.ocsp.Req;
  139. import org.bouncycastle.crypto.params.RSAKeyParameters;
  140. import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
  141. import org.bouncycastle.openssl.PEMParser;
  142. import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
  143. import org.bouncycastle.operator.ContentSigner;
  144. import org.bouncycastle.operator.DigestCalculator;
  145. import org.bouncycastle.operator.OperatorCreationException;
  146. import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
  147. import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
  148. import org.etsi.uri.x01903.v13.DigestAlgAndValueType;
  149. import org.etsi.uri.x01903.v13.QualifyingPropertiesType;
  150. import org.junit.jupiter.api.AfterAll;
  151. import org.junit.jupiter.api.BeforeAll;
  152. import org.junit.jupiter.api.Disabled;
  153. import org.junit.jupiter.api.Test;
  154. import org.w3.x2000.x09.xmldsig.ReferenceType;
  155. import org.w3.x2000.x09.xmldsig.SignatureDocument;
  156. import org.w3c.dom.Document;
  157. class TestSignatureInfo {
  158. private static final Logger LOG = LogManager.getLogger(TestSignatureInfo.class);
  159. private static final POIDataSamples testdata = POIDataSamples.getXmlDSignInstance();
  160. private static Calendar cal;
  161. private KeyPair keyPair;
  162. private X509Certificate x509;
  163. @BeforeAll
  164. public static void setUpClass() {
  165. POITestCase.setImageIOCacheDir();
  166. }
  167. @AfterAll
  168. public static void removeUserLocale() {
  169. LocaleUtil.resetUserLocale();
  170. }
  171. @BeforeAll
  172. public static void initBouncy() {
  173. CryptoFunctions.registerBouncyCastle();
  174. // Set cal to now ... only set to fixed date for debugging ...
  175. LocaleUtil.resetUserLocale();
  176. LocaleUtil.resetUserTimeZone();
  177. cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
  178. assertNotNull(cal);
  179. // don't run this test when we are using older Xerces as it triggers an XML Parser backwards compatibility issue
  180. // in the xmlsec jar file
  181. String additionalJar = System.getProperty("additionaljar");
  182. //System.out.println("Having: " + additionalJar);
  183. assumeTrue(additionalJar == null || additionalJar.trim().length() == 0,
  184. "Not running TestSignatureInfo because we are testing with additionaljar set to " + additionalJar);
  185. System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true");
  186. // Set line.separator for bug61182
  187. // System.setProperty("line.separator", "\n");
  188. }
  189. @Disabled("This test is very sensitive, it breaks with every little change to the produced XML")
  190. @Test
  191. void bug61182() throws Exception {
  192. final String pfxInput =
  193. "H4sIAAAAAAAAAFXTfzzTeRwH8P2uGRmG6hKSmJh9a2HsuPy60VnHCEU6v86sieZH2Jr2qFl+s+ZHJ5tfUcfKb4uho/OjiFq1qTv5ceFyp0PqEK"+
  194. "fH4+66++Pz+Dwer9fj8f7r9cRzEd4QMBTPRWxDIM14ZN47NfAWsJgL34Bx4at4Lvwdngvd9b8KqgbjQpGbMXzzgRGovytVFTBEzIXU47kQCd4U"+
  195. "ofJPvHl8JwyTjRS55hbKoor3UJLDE1i/PcPKCBAIDATjQlKiK67XjVYdcnkZgD2txroiAUb8W9dtn57DvTsbM+3wIsdocXDEN7TdPKgaSl+tU1"+
  196. "xq9oqiB5yMaZCPho8uUEbFU9U6u3N7lEMLTJGeA0RfX+5FMRrpXPFrbrlJ8uNUCE2H247P28Ckyfqlsy32yeKg/HTbH5JpqUDNw2B32+SaiRw7"+
  197. "ofRMePUpaAoK7KYgmd5ZIc0rLLYjJBfOWCb28xlrGhbpJvdToFdqt5PXVjEz5YOJ6g7W0fskuKW9/iZP0yLEVpR9XkkHmb6tfpcE8YwCdWNCan"+
  198. "LvAsco25JdF1j2/FLAMVU79HdOex07main90dy40511OZtTGZ+TdVd3lKZ7D3clEg9hLESHwSNnZ6239X4yLM4xYSElQ/hqSbwdmiozYG9PhF2"+
  199. "Zf0XaZnxzTK0Iot+rJ3kYoxWTLE8DR9leV62Ywbtlg4mapYOxb3lT7fQ1x4EQ44flh2oFWSPLR8LMbsc6jzJsV6OZ3TrODjHEdw9W+8OD32vd8"+
  200. "XQ6iCaIHcrSOn6qS0TKLr786234eeSAhvAQbEsVn7vrvc/487Be/O2e/+5Y5zRq2zAtz6pfcNyraJNDqMW1inNkgJ3t3VESbZ3pNzyl3KHILs0"+
  201. "51dY6msDYSlWhw40TglXxj9rw95O6gFWIuN012W/vhS50jpKXcao4gc1aLaXtJXxirbRkpZ/0e7a0pD6TDa7+GxEdEEML3VGo9udD5YUKhU3y7"+
  202. "SzWAgN6WIEIglq7LilvCjqIVLIfg8CvVGL9f5iSsCDf5hef4vMxbyvcjINuy06gZu+iPYOWNxjfrwKGYzoqqotK2aywgYVrPMh0JovfkDuN95n"+
  203. "MdVlYHbN1Mnn4TxAwuv+u3AkBlDZvRUUCwoDMUGxeMNPhTaAgWl60xhhBgCBaEMgAACReMAav7n3x598IDYJ9GxGXRAwaPOT/kfO/1AgPqLQkp"+
  204. "MiIVaHthnUS4v2y32e2BjdMPyIImUTBW3cV3R5tjVQm0MOm+D2C5+bBW9vHLjLR4lun4toQiY3Ls/v4bES/OJ4EmpZk5xhL9i5ClofYZNEsxFn"+
  205. "An/q821Tg+Cq9Er4XYGQe8ogjjLJ2b7dUsJ3auFQFNUJF7Ke7yUL2EeYYxl6vz5l4q5u8704mRbFts1E1eWMp6WIy91GPrsVlRGvtuNERfrjfE"+
  206. "YtzUI3Flcv65zJUbUBEzUnTS0fEYso2XyToAl8kb251mUY2o2lJzv5dp/1htmcjeeP2MjxC+3S45ljx7jd52Pv9XAat+ryiauFOF7YgztkoWWD"+
  207. "h62tplPH1bzDV+d0NLdaE5AfVJ09HuUYTFS+iggtvT5Euyk+unj4N2XvzW91n+GNjtgWfKOHmkinUPvYRh70Jv+wlPJrVaT8mL7GxJLqDC9jbv"+
  208. "Gznoiae6es+wQejnk3XjU366MrK/zXxngBYj9J6NnXc9mMiTFLX8WqQ8iTelTAFs2NJzPoDzrBUz4JFIEOa6Dja6dULc68g1jFDTeEHZyra7RZ"+
  209. "2ElqGDEqcNRo3SNX6feMy9EF1GOyZK0Sa87KwjKw8aM68dpsIYjfLcTXaZ6atg0BKfMnl6axeUGEaIFSP7rzj9wjzumRbG3jgUVp2lX5AK/tsO"+
  210. "7R4TQX/9/H6RiN34c9KldmPZZGANXzzTajZS9mR2OSvlJ+F4AgSko4htrMAKFTBu51/5SWNsO1vlRaaG48ZRJ+8PzuHQMdvS36gNpRPi7jhF1S"+
  211. "H3B2ycI4y0VURv6SrqJNUY/X645ZFJQ+eBO+ptG7o8axf1dcqh2beiQk+GRTeZ37LVeUlaeo9vl1/+8tyBfyT2v5lFC5E19WdKIyCuZe7r99Px"+
  212. "D/Od4Qj0TA92+DQnbCQTCMy/wwse9O4gsEebkkpPIP5GBV3Q0YBsj75XE0uSFQ1tCZSW8bNa9MUJZ/nPBfExohHlgGAAA=";
  213. // Unix
  214. final String unixSignExp =
  215. "QkqTFQZjXagjRAoOWKpAGa8AR0rKqkSfBtfSWqtjBmTgyjarn+t2POHkpySIpheHAbg+90GKSH88ACMtPHbG7q" +
  216. "FL4gtgAD9Kjew6j16j0IRBwy145UlPrSLFMfF7YF7UlU1k1LBkIlRJ6Fv4MAJl6XspuzZOZIUmHZrWrdxycUQ=";
  217. // Windows
  218. final String winSignExp =
  219. "GmAlL7+bT1r3FsMHJOp3pKg8betblYieZTjhMIrPZPRBbSzjO7KsYRGNtr0aOE3qr8xzyYJN6/8QdF5X7pUEUc" +
  220. "2m8ctrm7s5o2vZTkAqk9ENJGDjBPXX7TnuVOiVeL1cJdtjHC2QpjtRwkFR+B54G6b1OXLOFuQpP3vqR3+/XXE=";
  221. // Mac
  222. final String macSignExp =
  223. "NZedY/LNTYU4nAUEUhIOg5+fKdgVtzRXKmdD3v+47E7Mb84oeiUGv9cCEE91DU3StF/JFIhjOJqavOzKnCsNcz" +
  224. "NJ4j/inggUl1OJUsicqIGQnA7E8vzWnN1kf5lINgJLv+0PyrrX9sQZbItzxUpgqyOFYcD0trid+31nRt4wtaA=";
  225. Calendar cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
  226. cal.clear();
  227. cal.setTimeZone(LocaleUtil.TIMEZONE_UTC);
  228. cal.set(2017, Calendar.JULY, 1);
  229. SignatureConfig signatureConfig = prepareConfig(pfxInput);
  230. signatureConfig.setExecutionTime(cal.getTime());
  231. SignatureInfo si = new SignatureInfo();
  232. si.setSignatureConfig(signatureConfig);
  233. ByteArrayOutputStream bos = new ByteArrayOutputStream(100000);
  234. try (XSSFWorkbook wb1 = new XSSFWorkbook()) {
  235. wb1.createSheet().createRow(1).createCell(1).setCellValue("Test");
  236. wb1.write(bos);
  237. }
  238. try (OPCPackage pkg1 = OPCPackage.open(new ByteArrayInputStream(bos.toByteArray()))) {
  239. si.setOpcPackage(pkg1);
  240. si.confirmSignature();
  241. assertTrue(si.verifySignature());
  242. bos.reset();
  243. pkg1.save(bos);
  244. }
  245. try (XSSFWorkbook wb2 = new XSSFWorkbook(new ByteArrayInputStream(bos.toByteArray()))) {
  246. assertEquals("Test", wb2.getSheetAt(0).getRow(1).getCell(1).getStringCellValue());
  247. OPCPackage pkg2 = wb2.getPackage();
  248. si.setOpcPackage(pkg2);
  249. assertTrue(si.verifySignature());
  250. // xmlbeans adds line-breaks depending on the system setting, so we get different
  251. // test results on Unix/Mac/Windows
  252. // if the xml documents eventually change, this test needs to be run with the
  253. // separator set to the various system configurations
  254. String sep = SystemProperties.getProperty("line.separator");
  255. String signExp;
  256. assumeTrue(sep == null || "\n".equals(sep) || "\r\n".equals(sep) || "\r".equals(sep), "Hashes only known for Windows/Unix/Mac");
  257. signExp = (sep == null || "\n".equals(sep)) ? unixSignExp : ("\r\n".equals(sep)) ? winSignExp : macSignExp;
  258. String signAct = si.getSignatureParts().iterator().next().
  259. getSignatureDocument().getSignature().getSignatureValue().getStringValue();
  260. assertEquals(signExp, signAct);
  261. }
  262. }
  263. @Test
  264. void office2007prettyPrintedRels() throws Exception {
  265. try (OPCPackage pkg = OPCPackage.open(testdata.getFile("office2007prettyPrintedRels.docx"), PackageAccess.READ)) {
  266. SignatureConfig sic = new SignatureConfig();
  267. SignatureInfo si = new SignatureInfo();
  268. si.setOpcPackage(pkg);
  269. si.setSignatureConfig(sic);
  270. boolean isValid = si.verifySignature();
  271. assertTrue(isValid);
  272. }
  273. }
  274. @Test
  275. void getSignerUnsigned() throws Exception {
  276. String[] testFiles = {
  277. "hello-world-unsigned.docx",
  278. "hello-world-unsigned.pptx",
  279. "hello-world-unsigned.xlsx",
  280. "hello-world-office-2010-technical-preview-unsigned.docx"
  281. };
  282. for (String testFile : testFiles) {
  283. List<X509Certificate> result = new ArrayList<>();
  284. try (OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ)) {
  285. SignatureConfig sic = new SignatureConfig();
  286. SignatureInfo si = new SignatureInfo();
  287. si.setOpcPackage(pkg);
  288. si.setSignatureConfig(sic);
  289. for (SignaturePart sp : si.getSignatureParts()) {
  290. if (sp.validate()) {
  291. result.add(sp.getSigner());
  292. }
  293. }
  294. pkg.revert();
  295. }
  296. assertNotNull(result);
  297. assertTrue(result.isEmpty());
  298. }
  299. }
  300. @Test
  301. void getSigner() throws Exception {
  302. String[] testFiles = {
  303. "hyperlink-example-signed.docx",
  304. "hello-world-signed.docx",
  305. "hello-world-signed.pptx",
  306. "hello-world-signed.xlsx",
  307. "hello-world-office-2010-technical-preview.docx",
  308. "ms-office-2010-signed.docx",
  309. "ms-office-2010-signed.pptx",
  310. "ms-office-2010-signed.xlsx",
  311. "Office2010-SP1-XAdES-X-L.docx",
  312. "signed.docx"
  313. };
  314. for (String testFile : testFiles) {
  315. try (OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ)) {
  316. SignatureConfig sic = new SignatureConfig();
  317. SignatureInfo si = new SignatureInfo();
  318. si.setOpcPackage(pkg);
  319. si.setSignatureConfig(sic);
  320. List<X509Certificate> result = new ArrayList<>();
  321. for (SignaturePart sp : si.getSignatureParts()) {
  322. if (sp.validate()) {
  323. result.add(sp.getSigner());
  324. }
  325. }
  326. assertNotNull(result);
  327. assertEquals(1, result.size(), "test-file: " + testFile);
  328. X509Certificate signer = result.get(0);
  329. LOG.atDebug().log("signer: {}", signer.getSubjectX500Principal());
  330. boolean b = si.verifySignature();
  331. assertTrue(b, "test-file: " + testFile);
  332. pkg.revert();
  333. }
  334. }
  335. }
  336. @Test
  337. void getMultiSigners() throws Exception {
  338. String testFile = "hello-world-signed-twice.docx";
  339. try (OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ)) {
  340. SignatureConfig sic = new SignatureConfig();
  341. SignatureInfo si = new SignatureInfo();
  342. si.setOpcPackage(pkg);
  343. si.setSignatureConfig(sic);
  344. List<X509Certificate> result = new ArrayList<>();
  345. for (SignaturePart sp : si.getSignatureParts()) {
  346. if (sp.validate()) {
  347. result.add(sp.getSigner());
  348. }
  349. }
  350. assertNotNull(result);
  351. assertEquals(2, result.size(), "test-file: " + testFile);
  352. X509Certificate signer1 = result.get(0);
  353. X509Certificate signer2 = result.get(1);
  354. LOG.atDebug().log("signer 1: {}", signer1.getSubjectX500Principal());
  355. LOG.atDebug().log("signer 2: {}", signer2.getSubjectX500Principal());
  356. boolean b = si.verifySignature();
  357. assertTrue(b, "test-file: " + testFile);
  358. pkg.revert();
  359. }
  360. }
  361. @Test
  362. void testSignSpreadsheet() throws Exception {
  363. String testFile = "hello-world-unsigned.xlsx";
  364. try (OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE)) {
  365. sign(pkg);
  366. }
  367. }
  368. private static class CommitableWorkbook extends XSSFWorkbook {
  369. CommitableWorkbook(OPCPackage pkg) throws IOException {
  370. super(pkg);
  371. }
  372. public void commit() throws IOException {
  373. super.commit();
  374. }
  375. }
  376. @Test
  377. void testManipulation() throws Exception {
  378. // sign & validate
  379. String testFile = "hello-world-unsigned.xlsx";
  380. try (OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE)) {
  381. sign(pkg);
  382. // manipulate
  383. try (CommitableWorkbook wb = new CommitableWorkbook(pkg)) {
  384. wb.setSheetName(0, "manipulated");
  385. // ... I don't know, why commit is protected ...
  386. wb.commit();
  387. // todo: test a manipulation on a package part, which is not signed
  388. // ... maybe in combination with #56164
  389. // validate
  390. SignatureConfig sic = new SignatureConfig();
  391. SignatureInfo si = new SignatureInfo();
  392. si.setOpcPackage(pkg);
  393. si.setSignatureConfig(sic);
  394. boolean b = si.verifySignature();
  395. assertFalse(b, "signature should be broken");
  396. }
  397. }
  398. }
  399. @Test
  400. void testSignSpreadsheetWithSignatureInfo() throws Exception {
  401. initKeyPair();
  402. String testFile = "hello-world-unsigned.xlsx";
  403. try (OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE)) {
  404. SignatureConfig sic = new SignatureConfig();
  405. sic.setKey(keyPair.getPrivate());
  406. sic.setSigningCertificateChain(Collections.singletonList(x509));
  407. SignatureInfo si = new SignatureInfo();
  408. si.setOpcPackage(pkg);
  409. si.setSignatureConfig(sic);
  410. // hash > sha1 doesn't work in excel viewer ...
  411. si.confirmSignature();
  412. List<X509Certificate> result = new ArrayList<>();
  413. for (SignaturePart sp : si.getSignatureParts()) {
  414. if (sp.validate()) {
  415. result.add(sp.getSigner());
  416. }
  417. }
  418. assertEquals(1, result.size());
  419. }
  420. }
  421. @Test
  422. void testSignEnvelopingDocument() throws Exception {
  423. String testFile = "hello-world-unsigned.xlsx";
  424. File sigCopy = testdata.getFile(testFile);
  425. ByteArrayOutputStream bos = new ByteArrayOutputStream(50000);
  426. final String execTimestr;
  427. try (OPCPackage pkg = OPCPackage.open(copy(sigCopy), PackageAccess.READ_WRITE)) {
  428. initKeyPair();
  429. final X509CRL crl = generateCrl(x509, keyPair.getPrivate());
  430. // setup
  431. SignatureConfig signatureConfig = new SignatureConfig();
  432. signatureConfig.setKey(keyPair.getPrivate());
  433. /*
  434. * We need at least 2 certificates for the XAdES-C complete certificate
  435. * refs construction.
  436. */
  437. List<X509Certificate> certificateChain = new ArrayList<>();
  438. certificateChain.add(x509);
  439. certificateChain.add(x509);
  440. signatureConfig.setSigningCertificateChain(certificateChain);
  441. signatureConfig.addSignatureFacet(new OOXMLSignatureFacet());
  442. signatureConfig.addSignatureFacet(new EnvelopedSignatureFacet());
  443. signatureConfig.addSignatureFacet(new KeyInfoSignatureFacet());
  444. signatureConfig.addSignatureFacet(new XAdESSignatureFacet());
  445. signatureConfig.addSignatureFacet(new XAdESXLSignatureFacet());
  446. // check for internet, no error means it works
  447. boolean mockTsp = (getAccessError("http://timestamp.comodoca.com/rfc3161", true, 10000) != null);
  448. // http://timestamping.edelweb.fr/service/tsp
  449. // http://tsa.belgium.be/connect
  450. // http://timestamp.comodoca.com/authenticode
  451. // http://timestamp.comodoca.com/rfc3161
  452. // http://services.globaltrustfinder.com/adss/tsa
  453. signatureConfig.setTspUrl("http://timestamp.comodoca.com/rfc3161");
  454. signatureConfig.setTspRequestPolicy(null); // comodoca request fails, if default policy is set ...
  455. signatureConfig.setTspOldProtocol(false);
  456. signatureConfig.setXadesDigestAlgo(HashAlgorithm.sha512);
  457. signatureConfig.setXadesRole("Xades Reviewer");
  458. signatureConfig.setSignatureDescription("test xades signature");
  459. execTimestr = signatureConfig.formatExecutionTime();
  460. //set proxy info if any
  461. String proxy = System.getProperty("http_proxy");
  462. if (proxy != null && proxy.trim().length() > 0) {
  463. signatureConfig.setProxyUrl(proxy);
  464. }
  465. if (mockTsp) {
  466. TimeStampService tspService = (signatureInfo, data, revocationData) -> {
  467. revocationData.addCRL(crl);
  468. return "time-stamp-token".getBytes(LocaleUtil.CHARSET_1252);
  469. };
  470. signatureConfig.setTspService(tspService);
  471. } else {
  472. TimeStampServiceValidator tspValidator = (validateChain, revocationData) -> {
  473. for (X509Certificate certificate : validateChain) {
  474. LOG.atDebug().log("certificate: {}", certificate.getSubjectX500Principal());
  475. LOG.atDebug().log("validity: {} - {}", certificate.getNotBefore(), certificate.getNotAfter());
  476. }
  477. };
  478. signatureConfig.setTspValidator(tspValidator);
  479. signatureConfig.setTspOldProtocol(signatureConfig.getTspUrl().contains("edelweb"));
  480. }
  481. final RevocationData revocationData = new RevocationData();
  482. revocationData.addCRL(crl);
  483. OCSPResp ocspResp = createOcspResp(x509, x509, x509, keyPair.getPrivate(), cal.getTimeInMillis());
  484. revocationData.addOCSP(ocspResp.getEncoded());
  485. RevocationDataService revocationDataService = revocationChain -> revocationData;
  486. signatureConfig.setRevocationDataService(revocationDataService);
  487. // operate
  488. SignatureInfo si = new SignatureInfo();
  489. si.setOpcPackage(pkg);
  490. si.setSignatureConfig(signatureConfig);
  491. try {
  492. si.confirmSignature();
  493. } catch (RuntimeException e) {
  494. // only allow a ConnectException because of timeout, we see this in Jenkins from time to time...
  495. if (e.getCause() == null) {
  496. throw e;
  497. }
  498. if ((e.getCause() instanceof ConnectException) || (e.getCause() instanceof SocketTimeoutException)) {
  499. assumeFalse(e.getCause().getMessage().contains("timed out"),
  500. "Only allowing ConnectException with 'timed out' as message here, but had: " + e);
  501. } else if (e.getCause() instanceof IOException) {
  502. assumeFalse(e.getCause().getMessage().contains("Error contacting TSP server"),
  503. "Only allowing IOException with 'Error contacting TSP server' as message here, but had: " + e);
  504. } else if (e.getCause() instanceof RuntimeException) {
  505. assumeFalse(e.getCause().getMessage().contains("This site is cur"),
  506. "Only allowing RuntimeException with 'This site is cur' as message here, but had: " + e);
  507. }
  508. throw e;
  509. }
  510. // verify
  511. Iterator<SignaturePart> spIter = si.getSignatureParts().iterator();
  512. assertTrue(spIter.hasNext(), "Had: " + pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN));
  513. SignaturePart sp = spIter.next();
  514. boolean valid = sp.validate();
  515. assertTrue(valid);
  516. SignatureDocument sigDoc = sp.getSignatureDocument();
  517. String declareNS =
  518. "declare namespace xades='http://uri.etsi.org/01903/v1.3.2#'; "
  519. + "declare namespace ds='http://www.w3.org/2000/09/xmldsig#'; ";
  520. String digestValXQuery = declareNS +
  521. "$this/ds:Signature/ds:SignedInfo/ds:Reference";
  522. for (ReferenceType rt : (ReferenceType[]) sigDoc.selectPath(digestValXQuery)) {
  523. assertNotNull(rt.getDigestValue());
  524. assertEquals(signatureConfig.getDigestMethodUri(), rt.getDigestMethod().getAlgorithm());
  525. }
  526. String certDigestXQuery = declareNS +
  527. "$this//xades:SigningCertificate/xades:Cert/xades:CertDigest";
  528. XmlObject[] xoList = sigDoc.selectPath(certDigestXQuery);
  529. assertEquals(xoList.length, 1);
  530. DigestAlgAndValueType certDigest = (DigestAlgAndValueType) xoList[0];
  531. assertNotNull(certDigest.getDigestValue());
  532. String qualPropXQuery = declareNS +
  533. "$this/ds:Signature/ds:Object/xades:QualifyingProperties";
  534. xoList = sigDoc.selectPath(qualPropXQuery);
  535. assertEquals(xoList.length, 1);
  536. QualifyingPropertiesType qualProp = (QualifyingPropertiesType) xoList[0];
  537. boolean qualPropXsdOk = qualProp.validate();
  538. assertTrue(qualPropXsdOk);
  539. pkg.save(bos);
  540. }
  541. try (OPCPackage pkg = OPCPackage.open(new ByteArrayInputStream(bos.toByteArray()))) {
  542. SignatureConfig signatureConfig = new SignatureConfig();
  543. signatureConfig.setUpdateConfigOnValidate(true);
  544. SignatureInfo si = new SignatureInfo();
  545. si.setOpcPackage(pkg);
  546. si.setSignatureConfig(signatureConfig);
  547. assertTrue(si.verifySignature());
  548. assertEquals(HashAlgorithm.sha512, signatureConfig.getXadesDigestAlgo());
  549. assertEquals("Xades Reviewer", signatureConfig.getXadesRole());
  550. assertEquals("test xades signature", signatureConfig.getSignatureDescription());
  551. assertEquals(execTimestr, signatureConfig.formatExecutionTime());
  552. }
  553. }
  554. public static String getAccessError(String destinationUrl, boolean fireRequest, int timeout) {
  555. URL url;
  556. try {
  557. url = new URL(destinationUrl);
  558. } catch (MalformedURLException e) {
  559. throw new IllegalArgumentException("Invalid destination URL", e);
  560. }
  561. HttpURLConnection conn = null;
  562. try {
  563. conn = (HttpURLConnection) url.openConnection();
  564. // set specified timeout if non-zero
  565. if(timeout != 0) {
  566. conn.setConnectTimeout(timeout);
  567. conn.setReadTimeout(timeout);
  568. }
  569. conn.setDoOutput(false);
  570. conn.setDoInput(true);
  571. /* if connecting is not possible this will throw a connection refused exception */
  572. conn.connect();
  573. if (fireRequest) {
  574. conn.getInputStream().close();
  575. }
  576. /* if connecting is possible we return true here */
  577. return null;
  578. } catch (IOException e) {
  579. /* exception is thrown -> server not available */
  580. return e.getClass().getName() + ": " + e.getMessage();
  581. } finally {
  582. if (conn != null) {
  583. conn.disconnect();
  584. }
  585. }
  586. }
  587. @Test
  588. void testCertChain() throws Exception {
  589. final boolean isIBM = System.getProperty("java.vendor").contains("IBM");
  590. KeyStore keystore = KeyStore.getInstance("PKCS12");
  591. String password = "test";
  592. try (InputStream is = testdata.openResourceAsStream("chaintest.pfx")) {
  593. keystore.load(is, password.toCharArray());
  594. }
  595. Key key = keystore.getKey("poitest", password.toCharArray());
  596. Certificate[] chainList = keystore.getCertificateChain("poitest");
  597. List<X509Certificate> certChain = new ArrayList<>();
  598. for (Certificate c : chainList) {
  599. certChain.add((X509Certificate)c);
  600. }
  601. x509 = certChain.get(0);
  602. keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key);
  603. String testFile = "hello-world-unsigned.xlsx";
  604. try (OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE)) {
  605. SignatureConfig signatureConfig = new SignatureConfig();
  606. signatureConfig.setKey(keyPair.getPrivate());
  607. signatureConfig.setSigningCertificateChain(certChain);
  608. Calendar oldCal = LocaleUtil.getLocaleCalendar(2007, 7, 1);
  609. signatureConfig.setExecutionTime(oldCal.getTime());
  610. signatureConfig.setDigestAlgo(HashAlgorithm.sha1);
  611. SignatureInfo si = new SignatureInfo();
  612. si.setOpcPackage(pkg);
  613. si.setSignatureConfig(signatureConfig);
  614. si.confirmSignature();
  615. for (SignaturePart sp : si.getSignatureParts()) {
  616. assertTrue(sp.validate(), "Could not validate");
  617. X509Certificate signer = sp.getSigner();
  618. assertNotNull(signer, "signer undefined?!");
  619. List<X509Certificate> certChainRes = sp.getCertChain();
  620. // IBM JDK is still buggy, even after fix for APAR IJ21985
  621. int exp = isIBM ? 1 : 3;
  622. assertEquals(exp, certChainRes.size());
  623. }
  624. }
  625. }
  626. @Test
  627. void testNonSha1() throws Exception {
  628. String testFile = "hello-world-unsigned.xlsx";
  629. initKeyPair();
  630. SignatureConfig signatureConfig = new SignatureConfig();
  631. signatureConfig.setKey(keyPair.getPrivate());
  632. signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
  633. HashAlgorithm[] testAlgo = {HashAlgorithm.sha224, HashAlgorithm.sha256
  634. , HashAlgorithm.sha384, HashAlgorithm.sha512, HashAlgorithm.ripemd160};
  635. for (HashAlgorithm ha : testAlgo) {
  636. signatureConfig.setDigestAlgo(ha);
  637. try (OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE)) {
  638. SignatureInfo si = new SignatureInfo();
  639. si.setOpcPackage(pkg);
  640. si.setSignatureConfig(signatureConfig);
  641. si.confirmSignature();
  642. boolean b = si.verifySignature();
  643. assertTrue(b, "Signature not correctly calculated for " + ha);
  644. } catch (EncryptedDocumentException e) {
  645. assumeTrue(e.getMessage().startsWith("Export Restrictions"));
  646. }
  647. }
  648. }
  649. @Test
  650. void bug58630() throws Exception {
  651. // test deletion of sheet 0 and signing
  652. File tpl = copy(testdata.getFile("bug58630.xlsx"));
  653. try (SXSSFWorkbook wb1 = new SXSSFWorkbook((XSSFWorkbook)WorkbookFactory.create(tpl), 10)) {
  654. wb1.setCompressTempFiles(true);
  655. wb1.removeSheetAt(0);
  656. ByteArrayOutputStream os = new ByteArrayOutputStream();
  657. wb1.write(os);
  658. try (OPCPackage pkg = OPCPackage.open(new ByteArrayInputStream(os.toByteArray()))) {
  659. initKeyPair();
  660. SignatureConfig signatureConfig = new SignatureConfig();
  661. signatureConfig.setKey(keyPair.getPrivate());
  662. signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
  663. SignatureInfo si = new SignatureInfo();
  664. si.setOpcPackage(pkg);
  665. si.setSignatureConfig(signatureConfig);
  666. si.confirmSignature();
  667. assertTrue(si.verifySignature(), "invalid signature");
  668. }
  669. }
  670. }
  671. @Test
  672. void testMultiSign() throws Exception {
  673. cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC);
  674. cal.clear();
  675. cal.setTimeZone(LocaleUtil.TIMEZONE_UTC);
  676. cal.set(2018, Calendar.DECEMBER, 14);
  677. // test signing with separate opened packages
  678. File tpl = copy(testdata.getFile("hello-world-unsigned.xlsx"));
  679. try (OPCPackage pkg = OPCPackage.open(tpl)) {
  680. signPkg63011(pkg, "bug63011_key1.pem", true);
  681. }
  682. try (OPCPackage pkg = OPCPackage.open(tpl)) {
  683. signPkg63011(pkg, "bug63011_key2.pem", true);
  684. }
  685. verifyPkg63011(tpl, true);
  686. // test signing with single opened package
  687. tpl = copy(testdata.getFile("hello-world-unsigned.xlsx"));
  688. try (OPCPackage pkg = OPCPackage.open(tpl)) {
  689. signPkg63011(pkg, "bug63011_key1.pem", true);
  690. signPkg63011(pkg, "bug63011_key2.pem", true);
  691. }
  692. verifyPkg63011(tpl, true);
  693. try (OPCPackage pkg = OPCPackage.open(tpl)) {
  694. signPkg63011(pkg, "bug63011_key1.pem", true);
  695. signPkg63011(pkg, "bug63011_key2.pem", false);
  696. }
  697. verifyPkg63011(tpl, false);
  698. }
  699. private void verifyPkg63011(File tpl, boolean multi) throws InvalidFormatException, IOException {
  700. try (OPCPackage pkg = OPCPackage.open(tpl, PackageAccess.READ)) {
  701. SignatureConfig sic = new SignatureConfig();
  702. SignatureInfo si = new SignatureInfo();
  703. si.setOpcPackage(pkg);
  704. si.setSignatureConfig(sic);
  705. List<X509Certificate> result = new ArrayList<>();
  706. for (SignaturePart sp : si.getSignatureParts()) {
  707. if (sp.validate()) {
  708. result.add(sp.getSigner());
  709. }
  710. }
  711. assertNotNull(result);
  712. if (multi) {
  713. assertEquals(2, result.size());
  714. assertEquals("CN=Muj Klic", result.get(0).getSubjectDN().toString());
  715. assertEquals("CN=My Second key", result.get(1).getSubjectDN().toString());
  716. } else {
  717. assertEquals(1, result.size());
  718. assertEquals("CN=My Second key", result.get(0).getSubjectDN().toString());
  719. }
  720. assertTrue(si.verifySignature());
  721. pkg.revert();
  722. }
  723. }
  724. private void signPkg63011(OPCPackage pkg, String pemFile, boolean multi)
  725. throws IOException, CertificateException, XMLSignatureException, MarshalException {
  726. assertNotNull(pkg);
  727. initKeyFromPEM(testdata.getFile(pemFile));
  728. SignatureConfig config = new SignatureConfig();
  729. config.setKey(keyPair.getPrivate());
  730. config.setSigningCertificateChain(Collections.singletonList(x509));
  731. config.setExecutionTime(cal.getTime());
  732. config.setAllowMultipleSignatures(multi);
  733. SignatureInfo si = new SignatureInfo();
  734. si.setOpcPackage(pkg);
  735. si.setSignatureConfig(config);
  736. si.confirmSignature();
  737. }
  738. @Test
  739. void testRetrieveCertificate() throws InvalidFormatException, IOException {
  740. SignatureConfig sic = new SignatureConfig();
  741. final File file = testdata.getFile("PPT2016withComment.pptx");
  742. try (final OPCPackage pkg = OPCPackage.open(file, PackageAccess.READ)) {
  743. sic.setUpdateConfigOnValidate(true);
  744. SignatureInfo si = new SignatureInfo();
  745. si.setOpcPackage(pkg);
  746. si.setSignatureConfig(sic);
  747. assertTrue(si.verifySignature());
  748. }
  749. final List<X509Certificate> certs = sic.getSigningCertificateChain();
  750. assertEquals(1, certs.size());
  751. assertEquals("CN=Test", certs.get(0).getSubjectDN().getName());
  752. assertEquals("SuperDuper-Reviewer", sic.getXadesRole());
  753. assertEquals("Purpose for signing", sic.getSignatureDescription());
  754. assertEquals("2018-06-10T09:00:54Z", sic.formatExecutionTime());
  755. assertEquals(CanonicalizationMethod.INCLUSIVE, sic.getCanonicalizationMethod());
  756. }
  757. private interface XmlDocumentPackageInit {
  758. POIXMLDocument init(SignatureLine line, OPCPackage pkg) throws IOException, XmlException;
  759. }
  760. @Test
  761. void testSignatureImage() throws Exception {
  762. initKeyPair();
  763. List<Supplier<SignatureLine>> lines = Arrays.asList(XSSFSignatureLine::new, XWPFSignatureLine::new);
  764. for (Supplier<SignatureLine> sup : lines) {
  765. SignatureLine line = sup.get();
  766. line.setSuggestedSigner("Jack Sparrow");
  767. line.setSuggestedSigner2("Captain");
  768. line.setSuggestedSignerEmail("jack.bl@ck.perl");
  769. line.setInvalidStamp("Bungling!");
  770. line.setPlainSignature(testdata.readFile("jack-sign.emf"));
  771. String[] ext = { "" };
  772. BiFunction<SignatureLine,String[],POIXMLDocument> init =
  773. (line instanceof XSSFSignatureLine)
  774. ? this::initSignatureImageXSSF
  775. : this::initSignatureImageXWPF;
  776. File signDoc;
  777. try (POIXMLDocument xmlDoc = init.apply(line,ext)) {
  778. signDoc = TempFile.createTempFile("visual-signature", ext[0]);
  779. try (FileOutputStream fos = new FileOutputStream(signDoc)) {
  780. xmlDoc.write(fos);
  781. }
  782. }
  783. try (OPCPackage pkg = OPCPackage.open(signDoc, PackageAccess.READ_WRITE)) {
  784. SignatureConfig sic = new SignatureConfig();
  785. sic.setKey(keyPair.getPrivate());
  786. sic.setSigningCertificateChain(Collections.singletonList(x509));
  787. line.updateSignatureConfig(sic);
  788. sic.setDigestAlgo(HashAlgorithm.sha1);
  789. SignatureInfo si = new SignatureInfo();
  790. si.setOpcPackage(pkg);
  791. si.setSignatureConfig(sic);
  792. // hash > sha1 doesn't work in excel viewer ...
  793. si.confirmSignature();
  794. } catch (java.util.ServiceConfigurationError e) {
  795. assumeFalse(true, "running on module-path / JPMS and batik is \"kaputt\" WRT JPMS");
  796. }
  797. XmlDocumentPackageInit reinit =
  798. (line instanceof XSSFSignatureLine)
  799. ? this::initSignatureImageXSSF
  800. : this::initSignatureImageXWPF;
  801. try (OPCPackage pkg = OPCPackage.open(signDoc, PackageAccess.READ)) {
  802. SignatureLine line2 = sup.get();
  803. try (POIXMLDocument doc = reinit.init(line2, pkg)) {
  804. assertNotNull(doc);
  805. line2.parse();
  806. assertEquals(line.getSuggestedSigner(), line2.getSuggestedSigner());
  807. assertEquals(line.getSuggestedSigner2(), line2.getSuggestedSigner2());
  808. assertEquals(line.getSuggestedSignerEmail(), line2.getSuggestedSignerEmail());
  809. }
  810. pkg.revert();
  811. }
  812. }
  813. }
  814. private XWPFDocument initSignatureImageXWPF(SignatureLine line, String[] ext) {
  815. XWPFDocument doc = new XWPFDocument();
  816. ((XWPFSignatureLine)line).add(doc.createParagraph());
  817. ext[0] = ".docx";
  818. return doc;
  819. }
  820. private XWPFDocument initSignatureImageXWPF(SignatureLine line, OPCPackage pkg) throws IOException, XmlException {
  821. XWPFDocument doc = new XWPFDocument(pkg);
  822. ((XWPFSignatureLine)line).parse(doc);
  823. return doc;
  824. }
  825. private XSSFWorkbook initSignatureImageXSSF(SignatureLine line, String[] ext) {
  826. XSSFWorkbook xls = new XSSFWorkbook();
  827. XSSFSheet sheet = xls.createSheet();
  828. XSSFClientAnchor anchor = new XSSFClientAnchor(0,0,0,0,3,3,8,13);
  829. ((XSSFSignatureLine)line).add(sheet, anchor);
  830. ext[0] = ".xlsx";
  831. return xls;
  832. }
  833. private XSSFWorkbook initSignatureImageXSSF(SignatureLine line, OPCPackage pkg) throws IOException, XmlException {
  834. XSSFWorkbook xls = new XSSFWorkbook(pkg);
  835. ((XSSFSignatureLine)line).parse(xls.getSheetAt(0));
  836. return xls;
  837. }
  838. private SignatureConfig prepareConfig(String pfxInput) throws Exception {
  839. initKeyPair(pfxInput);
  840. SignatureConfig signatureConfig = new SignatureConfig();
  841. signatureConfig.setKey(keyPair.getPrivate());
  842. signatureConfig.setSigningCertificateChain(Collections.singletonList(x509));
  843. signatureConfig.setExecutionTime(cal.getTime());
  844. signatureConfig.setDigestAlgo(HashAlgorithm.sha1);
  845. return signatureConfig;
  846. }
  847. private void sign(OPCPackage pkgCopy) throws Exception {
  848. int signerCount = 1;
  849. SignatureConfig signatureConfig = prepareConfig(null);
  850. SignatureInfo si = new SignatureInfo();
  851. si.setOpcPackage(pkgCopy);
  852. si.setSignatureConfig(signatureConfig);
  853. final Document document = DocumentHelper.createDocument();
  854. final DOMSignContext xmlSignContext = si.createXMLSignContext(document);
  855. // operate
  856. final DOMSignedInfo signedInfo = si.preSign(xmlSignContext);
  857. // verify
  858. assertNotNull(signedInfo);
  859. assertEquals("Office OpenXML Document", signatureConfig.getSignatureDescription());
  860. // setup: key material, signature value
  861. final String signatureValue = si.signDigest(xmlSignContext, signedInfo);
  862. // operate: postSign
  863. si.postSign(xmlSignContext, signatureValue);
  864. // verify: signature
  865. si.setOpcPackage(pkgCopy);
  866. List<X509Certificate> result = new ArrayList<>();
  867. for (SignaturePart sp : si.getSignatureParts()) {
  868. if (sp.validate()) {
  869. result.add(sp.getSigner());
  870. }
  871. }
  872. assertEquals(signerCount, result.size());
  873. }
  874. private void initKeyPair() throws Exception {
  875. initKeyPair(null);
  876. }
  877. private void initKeyPair(String pfxInput) throws Exception {
  878. final String alias = "Test";
  879. final char[] password = "test".toCharArray();
  880. File file = new File("build/test.pfx");
  881. assertTrue(file.getParentFile().exists() || file.getParentFile().mkdir());
  882. KeyStore keystore = KeyStore.getInstance("PKCS12");
  883. if (pfxInput != null) {
  884. try (InputStream fis = new ByteArrayInputStream(RawDataUtil.decompress(pfxInput))) {
  885. keystore.load(fis, password);
  886. }
  887. } else if (file.exists()) {
  888. try (InputStream fis = new FileInputStream(file)) {
  889. keystore.load(fis, password);
  890. }
  891. } else {
  892. keystore.load(null, password);
  893. }
  894. if (keystore.isKeyEntry(alias)) {
  895. Key key = keystore.getKey(alias, password);
  896. x509 = (X509Certificate)keystore.getCertificate(alias);
  897. keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key);
  898. } else {
  899. keyPair = generateKeyPair();
  900. Date notBefore = cal.getTime();
  901. Calendar cal2 = (Calendar)cal.clone();
  902. cal2.add(Calendar.YEAR, 1);
  903. Date notAfter = cal2.getTime();
  904. KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature);
  905. x509 = generateCertificate(keyPair.getPublic(), notBefore, notAfter, keyPair.getPrivate(), keyUsage);
  906. keystore.setKeyEntry(alias, keyPair.getPrivate(), password, new Certificate[]{x509});
  907. if (pfxInput == null) {
  908. try (FileOutputStream fos = new FileOutputStream(file)) {
  909. keystore.store(fos, password);
  910. }
  911. }
  912. }
  913. }
  914. private void initKeyFromPEM(File pemFile) throws IOException, CertificateException {
  915. // see https://stackoverflow.com/questions/11787571/how-to-read-pem-file-to-get-private-and-public-key
  916. PrivateKey key = null;
  917. x509 = null;
  918. try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(pemFile), StandardCharsets.ISO_8859_1))) {
  919. PEMParser parser = new PEMParser(br);
  920. for (Object obj; (obj = parser.readObject()) != null; ) {
  921. if (obj instanceof PrivateKeyInfo) {
  922. key = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey((PrivateKeyInfo)obj);
  923. } else if (obj instanceof X509CertificateHolder) {
  924. x509 = new JcaX509CertificateConverter().setProvider("BC").getCertificate((X509CertificateHolder)obj);
  925. }
  926. }
  927. }
  928. if (key != null && x509 != null) {
  929. keyPair = new KeyPair(x509.getPublicKey(), key);
  930. }
  931. }
  932. private static File copy(File input) throws IOException {
  933. String extension = input.getName().replaceAll(".*?(\\.[^.]+)?$", "$1");
  934. if (extension.isEmpty()) {
  935. extension = ".zip";
  936. }
  937. // ensure that we create the "build" directory as it might not be existing
  938. // in the Sonar Maven runs where we are at a different source directory
  939. File buildDir = new File("build");
  940. if(!buildDir.exists()) {
  941. assertTrue(buildDir.mkdirs(), "Failed to create " + buildDir.getAbsolutePath());
  942. }
  943. File tmpFile = new File(buildDir, "sigtest"+extension);
  944. try (OutputStream fos = new FileOutputStream(tmpFile)) {
  945. try (InputStream fis = new FileInputStream(input)) {
  946. IOUtils.copy(fis, fos);
  947. }
  948. }
  949. return tmpFile;
  950. }
  951. private static KeyPair generateKeyPair() throws Exception {
  952. KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
  953. SecureRandom random = new SecureRandom();
  954. keyPairGenerator.initialize(new RSAKeyGenParameterSpec(1024,
  955. RSAKeyGenParameterSpec.F4), random);
  956. return keyPairGenerator.generateKeyPair();
  957. }
  958. private static X509Certificate generateCertificate(PublicKey subjectPublicKey,
  959. Date notBefore, Date notAfter,
  960. PrivateKey issuerPrivateKey,
  961. KeyUsage keyUsage)
  962. throws IOException, OperatorCreationException, CertificateException {
  963. final String signatureAlgorithm = "SHA1withRSA";
  964. final String subjectDn = "CN=Test";
  965. X500Name issuerName = new X500Name(subjectDn);
  966. RSAPublicKey rsaPubKey = (RSAPublicKey)subjectPublicKey;
  967. RSAKeyParameters rsaSpec = new RSAKeyParameters(false, rsaPubKey.getModulus(), rsaPubKey.getPublicExponent());
  968. SubjectPublicKeyInfo subjectPublicKeyInfo =
  969. SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(rsaSpec);
  970. DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
  971. .setProvider("BC").build().get(CertificateID.HASH_SHA1);
  972. X509v3CertificateBuilder certificateGenerator = new X509v3CertificateBuilder(
  973. issuerName
  974. , new BigInteger(128, new SecureRandom())
  975. , notBefore
  976. , notAfter
  977. , new X500Name(subjectDn)
  978. , subjectPublicKeyInfo
  979. );
  980. X509ExtensionUtils exUtils = new X509ExtensionUtils(digestCalc);
  981. SubjectKeyIdentifier subKeyId = exUtils.createSubjectKeyIdentifier(subjectPublicKeyInfo);
  982. AuthorityKeyIdentifier autKeyId = exUtils.createAuthorityKeyIdentifier(subjectPublicKeyInfo);
  983. certificateGenerator.addExtension(Extension.subjectKeyIdentifier, false, subKeyId);
  984. certificateGenerator.addExtension(Extension.authorityKeyIdentifier, false, autKeyId);
  985. BasicConstraints bc = new BasicConstraints(0);
  986. certificateGenerator.addExtension(Extension.basicConstraints, false, bc);
  987. if (null != keyUsage) {
  988. certificateGenerator.addExtension(Extension.keyUsage, true, keyUsage);
  989. }
  990. JcaContentSignerBuilder signerBuilder = new JcaContentSignerBuilder(signatureAlgorithm);
  991. signerBuilder.setProvider("BC");
  992. X509CertificateHolder certHolder =
  993. certificateGenerator.build(signerBuilder.build(issuerPrivateKey));
  994. /*
  995. * Next certificate factory trick is needed to make sure that the
  996. * certificate delivered to the caller is provided by the default
  997. * security provider instead of BouncyCastle. If we don't do this trick
  998. * we might run into trouble when trying to use the CertPath validator.
  999. */
  1000. // CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
  1001. // certificate = (X509Certificate) certificateFactory
  1002. // .generateCertificate(new ByteArrayInputStream(certificate
  1003. // .getEncoded()));
  1004. return new JcaX509CertificateConverter().getCertificate(certHolder);
  1005. }
  1006. private static X509CRL generateCrl(X509Certificate issuer, PrivateKey issuerPrivateKey)
  1007. throws CertificateEncodingException, IOException, CRLException, OperatorCreationException {
  1008. X509CertificateHolder holder = new X509CertificateHolder(issuer.getEncoded());
  1009. X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(holder.getIssuer(), new Date());
  1010. crlBuilder.setNextUpdate(new Date(new Date().getTime() + 100000));
  1011. JcaContentSignerBuilder contentBuilder = new JcaContentSignerBuilder("SHA1withRSA").setProvider("BC");
  1012. CRLNumber crlNumber = new CRLNumber(new BigInteger("1234"));
  1013. crlBuilder.addExtension(Extension.cRLNumber, false, crlNumber);
  1014. X509CRLHolder x509Crl = crlBuilder.build(contentBuilder.build(issuerPrivateKey));
  1015. return new JcaX509CRLConverter().setProvider("BC").getCRL(x509Crl);
  1016. }
  1017. private static OCSPResp createOcspResp(X509Certificate certificate,
  1018. X509Certificate issuerCertificate,
  1019. X509Certificate ocspResponderCertificate,
  1020. PrivateKey ocspResponderPrivateKey,
  1021. long nonceTimeinMillis)
  1022. throws Exception {
  1023. DigestCalculator digestCalc = new JcaDigestCalculatorProviderBuilder()
  1024. .setProvider("BC").build().get(CertificateID.HASH_SHA1);
  1025. X509CertificateHolder issuerHolder = new X509CertificateHolder(issuerCertificate.getEncoded());
  1026. CertificateID certId = new CertificateID(digestCalc, issuerHolder, certificate.getSerialNumber());
  1027. // request
  1028. //create a nonce to avoid replay attack
  1029. BigInteger nonce = BigInteger.valueOf(nonceTimeinMillis);
  1030. DEROctetString nonceDer = new DEROctetString(nonce.toByteArray());
  1031. Extension ext = new Extension(OCSPObjectIdentifiers.id_pkix_ocsp_nonce, true, nonceDer);
  1032. Extensions exts = new Extensions(ext);
  1033. OCSPReqBuilder ocspReqBuilder = new OCSPReqBuilder();
  1034. ocspReqBuilder.addRequest(certId);
  1035. ocspReqBuilder.setRequestExtensions(exts);
  1036. OCSPReq ocspReq = ocspReqBuilder.build();
  1037. SubjectPublicKeyInfo keyInfo = new SubjectPublicKeyInfo
  1038. (CertificateID.HASH_SHA1, ocspResponderCertificate.getPublicKey().getEncoded());
  1039. BasicOCSPRespBuilder basicOCSPRespBuilder = new BasicOCSPRespBuilder(keyInfo, digestCalc);
  1040. basicOCSPRespBuilder.setResponseExtensions(exts);
  1041. // request processing
  1042. Req[] requestList = ocspReq.getRequestList();
  1043. for (Req ocspRequest : requestList) {
  1044. CertificateID certificateID = ocspRequest.getCertID();
  1045. CertificateStatus certificateStatus = CertificateStatus.GOOD;
  1046. basicOCSPRespBuilder.addResponse(certificateID, certificateStatus);
  1047. }
  1048. // basic response generation
  1049. X509CertificateHolder[] chain = null;
  1050. if (!ocspResponderCertificate.equals(issuerCertificate)) {
  1051. // TODO: HorribleProxy can't convert array input params yet
  1052. chain = new X509CertificateHolder[] {
  1053. new X509CertificateHolder(ocspResponderCertificate.getEncoded()),
  1054. issuerHolder
  1055. };
  1056. }
  1057. ContentSigner contentSigner = new JcaContentSignerBuilder("SHA1withRSA")
  1058. .setProvider("BC").build(ocspResponderPrivateKey);
  1059. BasicOCSPResp basicOCSPResp = basicOCSPRespBuilder.build(contentSigner, chain, new Date(nonceTimeinMillis));
  1060. OCSPRespBuilder ocspRespBuilder = new OCSPRespBuilder();
  1061. return ocspRespBuilder.build(OCSPRespBuilder.SUCCESSFUL, basicOCSPResp);
  1062. }
  1063. }