| /* ==================================================================== |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| ==================================================================== */ |
| |
| /* ==================================================================== |
| This product contains an ASLv2 licensed version of the OOXML signer |
| package from the eID Applet project |
| http://code.google.com/p/eid-applet/source/browse/trunk/README.txt |
| Copyright (C) 2008-2014 FedICT. |
| ================================================================= */ |
| package org.apache.poi.poifs.crypt; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.net.ConnectException; |
| import java.net.HttpURLConnection; |
| import java.net.MalformedURLException; |
| import java.net.SocketTimeoutException; |
| import java.net.URL; |
| import java.security.Key; |
| import java.security.KeyPair; |
| import java.security.KeyStore; |
| import java.security.PrivateKey; |
| import java.security.cert.Certificate; |
| import java.security.cert.X509CRL; |
| import java.security.cert.X509Certificate; |
| import java.util.ArrayList; |
| import java.util.Calendar; |
| import java.util.Collections; |
| import java.util.Date; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.apache.poi.POIDataSamples; |
| import org.apache.poi.POITestCase; |
| import org.apache.poi.openxml4j.opc.OPCPackage; |
| import org.apache.poi.openxml4j.opc.PackageAccess; |
| import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; |
| import org.apache.poi.poifs.crypt.dsig.DigestInfo; |
| import org.apache.poi.poifs.crypt.dsig.SignatureConfig; |
| import org.apache.poi.poifs.crypt.dsig.SignatureInfo; |
| import org.apache.poi.poifs.crypt.dsig.SignatureInfo.SignaturePart; |
| import org.apache.poi.poifs.crypt.dsig.facets.EnvelopedSignatureFacet; |
| import org.apache.poi.poifs.crypt.dsig.facets.KeyInfoSignatureFacet; |
| import org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet; |
| import org.apache.poi.poifs.crypt.dsig.facets.XAdESXLSignatureFacet; |
| import org.apache.poi.poifs.crypt.dsig.services.RevocationData; |
| import org.apache.poi.poifs.crypt.dsig.services.RevocationDataService; |
| import org.apache.poi.poifs.crypt.dsig.services.TimeStampService; |
| import org.apache.poi.poifs.crypt.dsig.services.TimeStampServiceValidator; |
| import org.apache.poi.ss.usermodel.WorkbookFactory; |
| import org.apache.poi.util.DocumentHelper; |
| import org.apache.poi.util.IOUtils; |
| import org.apache.poi.util.LocaleUtil; |
| import org.apache.poi.util.POILogFactory; |
| import org.apache.poi.util.POILogger; |
| import org.apache.poi.xssf.streaming.SXSSFWorkbook; |
| import org.apache.poi.xssf.usermodel.XSSFWorkbook; |
| import org.apache.xmlbeans.XmlObject; |
| import org.bouncycastle.asn1.x509.KeyUsage; |
| import org.bouncycastle.cert.ocsp.OCSPResp; |
| import org.etsi.uri.x01903.v13.DigestAlgAndValueType; |
| import org.etsi.uri.x01903.v13.QualifyingPropertiesType; |
| import org.junit.Assume; |
| import org.junit.BeforeClass; |
| import org.junit.Test; |
| import org.w3.x2000.x09.xmldsig.ReferenceType; |
| import org.w3.x2000.x09.xmldsig.SignatureDocument; |
| import org.w3c.dom.Document; |
| |
| public class TestSignatureInfo { |
| private static final POILogger LOG = POILogFactory.getLogger(TestSignatureInfo.class); |
| private static final POIDataSamples testdata = POIDataSamples.getXmlDSignInstance(); |
| |
| private static Calendar cal; |
| private KeyPair keyPair = null; |
| private X509Certificate x509 = null; |
| |
| @BeforeClass |
| public static void initBouncy() throws IOException { |
| CryptoFunctions.registerBouncyCastle(); |
| |
| // Set cal to now ... only set to fixed date for debugging ... |
| cal = LocaleUtil.getLocaleCalendar(LocaleUtil.TIMEZONE_UTC); |
| assertNotNull(cal); |
| // cal.set(2014, 7, 6, 21, 42, 12); |
| // cal.clear(Calendar.MILLISECOND); |
| |
| // don't run this test when we are using older Xerces as it triggers an XML Parser backwards compatibility issue |
| // in the xmlsec jar file |
| String additionalJar = System.getProperty("additionaljar"); |
| //System.out.println("Having: " + additionalJar); |
| Assume.assumeTrue("Not running TestSignatureInfo because we are testing with additionaljar set to " + additionalJar, |
| additionalJar == null || additionalJar.trim().length() == 0); |
| } |
| |
| @Test |
| public void office2007prettyPrintedRels() throws Exception { |
| OPCPackage pkg = OPCPackage.open(testdata.getFile("office2007prettyPrintedRels.docx"), PackageAccess.READ); |
| try { |
| SignatureConfig sic = new SignatureConfig(); |
| sic.setOpcPackage(pkg); |
| SignatureInfo si = new SignatureInfo(); |
| si.setSignatureConfig(sic); |
| boolean isValid = si.verifySignature(); |
| assertTrue(isValid); |
| } finally { |
| pkg.close(); |
| } |
| } |
| |
| @Test |
| public void getSignerUnsigned() throws Exception { |
| String testFiles[] = { |
| "hello-world-unsigned.docx", |
| "hello-world-unsigned.pptx", |
| "hello-world-unsigned.xlsx", |
| "hello-world-office-2010-technical-preview-unsigned.docx" |
| }; |
| |
| for (String testFile : testFiles) { |
| OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ); |
| SignatureConfig sic = new SignatureConfig(); |
| sic.setOpcPackage(pkg); |
| SignatureInfo si = new SignatureInfo(); |
| si.setSignatureConfig(sic); |
| List<X509Certificate> result = new ArrayList<X509Certificate>(); |
| for (SignaturePart sp : si.getSignatureParts()) { |
| if (sp.validate()) { |
| result.add(sp.getSigner()); |
| } |
| } |
| pkg.revert(); |
| pkg.close(); |
| assertNotNull(result); |
| assertTrue(result.isEmpty()); |
| } |
| } |
| |
| @Test |
| public void getSigner() throws Exception { |
| String testFiles[] = { |
| "hyperlink-example-signed.docx", |
| "hello-world-signed.docx", |
| "hello-world-signed.pptx", |
| "hello-world-signed.xlsx", |
| "hello-world-office-2010-technical-preview.docx", |
| "ms-office-2010-signed.docx", |
| "ms-office-2010-signed.pptx", |
| "ms-office-2010-signed.xlsx", |
| "Office2010-SP1-XAdES-X-L.docx", |
| "signed.docx", |
| }; |
| |
| for (String testFile : testFiles) { |
| OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ); |
| try { |
| SignatureConfig sic = new SignatureConfig(); |
| sic.setOpcPackage(pkg); |
| SignatureInfo si = new SignatureInfo(); |
| si.setSignatureConfig(sic); |
| List<X509Certificate> result = new ArrayList<X509Certificate>(); |
| for (SignaturePart sp : si.getSignatureParts()) { |
| if (sp.validate()) { |
| result.add(sp.getSigner()); |
| } |
| } |
| |
| assertNotNull(result); |
| assertEquals("test-file: "+testFile, 1, result.size()); |
| X509Certificate signer = result.get(0); |
| LOG.log(POILogger.DEBUG, "signer: " + signer.getSubjectX500Principal()); |
| |
| boolean b = si.verifySignature(); |
| assertTrue("test-file: "+testFile, b); |
| pkg.revert(); |
| } finally { |
| pkg.close(); |
| } |
| } |
| } |
| |
| @Test |
| public void getMultiSigners() throws Exception { |
| String testFile = "hello-world-signed-twice.docx"; |
| OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ); |
| try { |
| SignatureConfig sic = new SignatureConfig(); |
| sic.setOpcPackage(pkg); |
| SignatureInfo si = new SignatureInfo(); |
| si.setSignatureConfig(sic); |
| List<X509Certificate> result = new ArrayList<X509Certificate>(); |
| for (SignaturePart sp : si.getSignatureParts()) { |
| if (sp.validate()) { |
| result.add(sp.getSigner()); |
| } |
| } |
| |
| assertNotNull(result); |
| assertEquals("test-file: "+testFile, 2, result.size()); |
| X509Certificate signer1 = result.get(0); |
| X509Certificate signer2 = result.get(1); |
| LOG.log(POILogger.DEBUG, "signer 1: " + signer1.getSubjectX500Principal()); |
| LOG.log(POILogger.DEBUG, "signer 2: " + signer2.getSubjectX500Principal()); |
| |
| boolean b = si.verifySignature(); |
| assertTrue("test-file: "+testFile, b); |
| pkg.revert(); |
| } finally { |
| pkg.close(); |
| } |
| } |
| |
| @Test |
| public void testSignSpreadsheet() throws Exception { |
| String testFile = "hello-world-unsigned.xlsx"; |
| OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE); |
| sign(pkg, "Test", "CN=Test", 1); |
| pkg.close(); |
| } |
| |
| @Test |
| public void testManipulation() throws Exception { |
| // sign & validate |
| String testFile = "hello-world-unsigned.xlsx"; |
| @SuppressWarnings("resource") |
| OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE); |
| sign(pkg, "Test", "CN=Test", 1); |
| |
| // manipulate |
| XSSFWorkbook wb = new XSSFWorkbook(pkg); |
| wb.setSheetName(0, "manipulated"); |
| // ... I don't know, why commit is protected ... |
| POITestCase.callMethod(XSSFWorkbook.class, wb, Void.class, "commit", new Class[0], new Object[0]); |
| |
| // todo: test a manipulation on a package part, which is not signed |
| // ... maybe in combination with #56164 |
| |
| // validate |
| SignatureConfig sic = new SignatureConfig(); |
| sic.setOpcPackage(pkg); |
| SignatureInfo si = new SignatureInfo(); |
| si.setSignatureConfig(sic); |
| boolean b = si.verifySignature(); |
| assertFalse("signature should be broken", b); |
| |
| wb.close(); |
| } |
| |
| @Test |
| public void testSignSpreadsheetWithSignatureInfo() throws Exception { |
| initKeyPair("Test", "CN=Test"); |
| String testFile = "hello-world-unsigned.xlsx"; |
| OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE); |
| SignatureConfig sic = new SignatureConfig(); |
| sic.setOpcPackage(pkg); |
| sic.setKey(keyPair.getPrivate()); |
| sic.setSigningCertificateChain(Collections.singletonList(x509)); |
| SignatureInfo si = new SignatureInfo(); |
| si.setSignatureConfig(sic); |
| // hash > sha1 doesn't work in excel viewer ... |
| si.confirmSignature(); |
| List<X509Certificate> result = new ArrayList<X509Certificate>(); |
| for (SignaturePart sp : si.getSignatureParts()) { |
| if (sp.validate()) { |
| result.add(sp.getSigner()); |
| } |
| } |
| assertEquals(1, result.size()); |
| pkg.close(); |
| } |
| |
| @Test |
| public void testSignEnvelopingDocument() throws Exception { |
| String testFile = "hello-world-unsigned.xlsx"; |
| OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE); |
| |
| initKeyPair("Test", "CN=Test"); |
| final X509CRL crl = PkiTestUtils.generateCrl(x509, keyPair.getPrivate()); |
| |
| // setup |
| SignatureConfig signatureConfig = new SignatureConfig(); |
| signatureConfig.setOpcPackage(pkg); |
| signatureConfig.setKey(keyPair.getPrivate()); |
| |
| /* |
| * We need at least 2 certificates for the XAdES-C complete certificate |
| * refs construction. |
| */ |
| List<X509Certificate> certificateChain = new ArrayList<X509Certificate>(); |
| certificateChain.add(x509); |
| certificateChain.add(x509); |
| signatureConfig.setSigningCertificateChain(certificateChain); |
| |
| signatureConfig.addSignatureFacet(new EnvelopedSignatureFacet()); |
| signatureConfig.addSignatureFacet(new KeyInfoSignatureFacet()); |
| signatureConfig.addSignatureFacet(new XAdESSignatureFacet()); |
| signatureConfig.addSignatureFacet(new XAdESXLSignatureFacet()); |
| |
| // check for internet, no error means it works |
| boolean mockTsp = (getAccessError("http://timestamp.comodoca.com/rfc3161", true, 10000) != null); |
| |
| // http://timestamping.edelweb.fr/service/tsp |
| // http://tsa.belgium.be/connect |
| // http://timestamp.comodoca.com/authenticode |
| // http://timestamp.comodoca.com/rfc3161 |
| // http://services.globaltrustfinder.com/adss/tsa |
| signatureConfig.setTspUrl("http://timestamp.comodoca.com/rfc3161"); |
| signatureConfig.setTspRequestPolicy(null); // comodoca request fails, if default policy is set ... |
| signatureConfig.setTspOldProtocol(false); |
| |
| //set proxy info if any |
| String proxy = System.getProperty("http_proxy"); |
| if (proxy != null && proxy.trim().length() > 0) { |
| signatureConfig.setProxyUrl(proxy); |
| } |
| |
| if (mockTsp) { |
| TimeStampService tspService = new TimeStampService(){ |
| @Override |
| public byte[] timeStamp(byte[] data, RevocationData revocationData) throws Exception { |
| revocationData.addCRL(crl); |
| return "time-stamp-token".getBytes(LocaleUtil.CHARSET_1252); |
| } |
| @Override |
| public void setSignatureConfig(SignatureConfig config) { |
| // empty on purpose |
| } |
| }; |
| signatureConfig.setTspService(tspService); |
| } else { |
| TimeStampServiceValidator tspValidator = new TimeStampServiceValidator() { |
| @Override |
| public void validate(List<X509Certificate> validateChain, |
| RevocationData revocationData) throws Exception { |
| for (X509Certificate certificate : validateChain) { |
| LOG.log(POILogger.DEBUG, "certificate: " + certificate.getSubjectX500Principal()); |
| LOG.log(POILogger.DEBUG, "validity: " + certificate.getNotBefore() + " - " + certificate.getNotAfter()); |
| } |
| } |
| }; |
| signatureConfig.setTspValidator(tspValidator); |
| signatureConfig.setTspOldProtocol(signatureConfig.getTspUrl().contains("edelweb")); |
| } |
| |
| final RevocationData revocationData = new RevocationData(); |
| revocationData.addCRL(crl); |
| OCSPResp ocspResp = PkiTestUtils.createOcspResp(x509, false, |
| x509, x509, keyPair.getPrivate(), "SHA1withRSA", cal.getTimeInMillis()); |
| revocationData.addOCSP(ocspResp.getEncoded()); |
| |
| RevocationDataService revocationDataService = new RevocationDataService(){ |
| @Override |
| public RevocationData getRevocationData(List<X509Certificate> revocationChain) { |
| return revocationData; |
| } |
| }; |
| signatureConfig.setRevocationDataService(revocationDataService); |
| |
| // operate |
| SignatureInfo si = new SignatureInfo(); |
| si.setSignatureConfig(signatureConfig); |
| try { |
| si.confirmSignature(); |
| } catch (RuntimeException e) { |
| pkg.close(); |
| // only allow a ConnectException because of timeout, we see this in Jenkins from time to time... |
| if(e.getCause() == null) { |
| throw e; |
| } |
| if((e.getCause() instanceof ConnectException) || (e.getCause() instanceof SocketTimeoutException)) { |
| Assume.assumeFalse("Only allowing ConnectException with 'timed out' as message here, but had: " + e, |
| e.getCause().getMessage().contains("timed out")); |
| } else if (e.getCause() instanceof IOException) { |
| Assume.assumeFalse("Only allowing IOException with 'Error contacting TSP server' as message here, but had: " + e, |
| e.getCause().getMessage().contains("Error contacting TSP server")); |
| } else if (e.getCause() instanceof RuntimeException) { |
| Assume.assumeFalse("Only allowing RuntimeException with 'This site is cur' as message here, but had: " + e, |
| e.getCause().getMessage().contains("This site is cur")); |
| } |
| throw e; |
| } |
| |
| // verify |
| Iterator<SignaturePart> spIter = si.getSignatureParts().iterator(); |
| assertTrue("Had: " + si.getSignatureConfig().getOpcPackage(). |
| getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN), |
| spIter.hasNext()); |
| SignaturePart sp = spIter.next(); |
| boolean valid = sp.validate(); |
| assertTrue(valid); |
| |
| SignatureDocument sigDoc = sp.getSignatureDocument(); |
| String declareNS = |
| "declare namespace xades='http://uri.etsi.org/01903/v1.3.2#'; " |
| + "declare namespace ds='http://www.w3.org/2000/09/xmldsig#'; "; |
| |
| String digestValXQuery = declareNS + |
| "$this/ds:Signature/ds:SignedInfo/ds:Reference"; |
| for (ReferenceType rt : (ReferenceType[])sigDoc.selectPath(digestValXQuery)) { |
| assertNotNull(rt.getDigestValue()); |
| assertEquals(signatureConfig.getDigestMethodUri(), rt.getDigestMethod().getAlgorithm()); |
| } |
| |
| String certDigestXQuery = declareNS + |
| "$this//xades:SigningCertificate/xades:Cert/xades:CertDigest"; |
| XmlObject xoList[] = sigDoc.selectPath(certDigestXQuery); |
| assertEquals(xoList.length, 1); |
| DigestAlgAndValueType certDigest = (DigestAlgAndValueType)xoList[0]; |
| assertNotNull(certDigest.getDigestValue()); |
| |
| String qualPropXQuery = declareNS + |
| "$this/ds:Signature/ds:Object/xades:QualifyingProperties"; |
| xoList = sigDoc.selectPath(qualPropXQuery); |
| assertEquals(xoList.length, 1); |
| QualifyingPropertiesType qualProp = (QualifyingPropertiesType)xoList[0]; |
| boolean qualPropXsdOk = qualProp.validate(); |
| assertTrue(qualPropXsdOk); |
| |
| pkg.close(); |
| } |
| |
| public static String getAccessError(String destinationUrl, boolean fireRequest, int timeout) { |
| URL url; |
| try { |
| url = new URL(destinationUrl); |
| } catch (MalformedURLException e) { |
| throw new IllegalArgumentException("Invalid destination URL", e); |
| } |
| |
| HttpURLConnection conn = null; |
| try { |
| conn = (HttpURLConnection) url.openConnection(); |
| |
| // set specified timeout if non-zero |
| if(timeout != 0) { |
| conn.setConnectTimeout(timeout); |
| conn.setReadTimeout(timeout); |
| } |
| |
| conn.setDoOutput(false); |
| conn.setDoInput(true); |
| |
| /* if connecting is not possible this will throw a connection refused exception */ |
| conn.connect(); |
| |
| if (fireRequest) { |
| InputStream is = null; |
| try { |
| is = conn.getInputStream(); |
| } finally { |
| IOUtils.closeQuietly(is); |
| } |
| |
| } |
| /* if connecting is possible we return true here */ |
| return null; |
| |
| } catch (IOException e) { |
| /* exception is thrown -> server not available */ |
| return e.getClass().getName() + ": " + e.getMessage(); |
| } finally { |
| if (conn != null) { |
| conn.disconnect(); |
| } |
| } |
| } |
| |
| @Test |
| public void testCertChain() throws Exception { |
| KeyStore keystore = KeyStore.getInstance("PKCS12"); |
| String password = "test"; |
| InputStream is = testdata.openResourceAsStream("chaintest.pfx"); |
| keystore.load(is, password.toCharArray()); |
| is.close(); |
| |
| Key key = keystore.getKey("poitest", password.toCharArray()); |
| Certificate chainList[] = keystore.getCertificateChain("poitest"); |
| List<X509Certificate> certChain = new ArrayList<X509Certificate>(); |
| for (Certificate c : chainList) { |
| certChain.add((X509Certificate)c); |
| } |
| x509 = certChain.get(0); |
| keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key); |
| |
| String testFile = "hello-world-unsigned.xlsx"; |
| OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE); |
| |
| SignatureConfig signatureConfig = new SignatureConfig(); |
| signatureConfig.setKey(keyPair.getPrivate()); |
| signatureConfig.setSigningCertificateChain(certChain); |
| Calendar oldCal = LocaleUtil.getLocaleCalendar(2007, 7, 1); |
| signatureConfig.setExecutionTime(oldCal.getTime()); |
| signatureConfig.setDigestAlgo(HashAlgorithm.sha1); |
| signatureConfig.setOpcPackage(pkg); |
| |
| SignatureInfo si = new SignatureInfo(); |
| si.setSignatureConfig(signatureConfig); |
| |
| si.confirmSignature(); |
| |
| for (SignaturePart sp : si.getSignatureParts()){ |
| assertTrue("Could not validate", sp.validate()); |
| X509Certificate signer = sp.getSigner(); |
| assertNotNull("signer undefined?!", signer); |
| List<X509Certificate> certChainRes = sp.getCertChain(); |
| assertEquals(3, certChainRes.size()); |
| } |
| |
| pkg.close(); |
| } |
| |
| @Test |
| public void testNonSha1() throws Exception { |
| String testFile = "hello-world-unsigned.xlsx"; |
| initKeyPair("Test", "CN=Test"); |
| |
| SignatureConfig signatureConfig = new SignatureConfig(); |
| signatureConfig.setKey(keyPair.getPrivate()); |
| signatureConfig.setSigningCertificateChain(Collections.singletonList(x509)); |
| |
| HashAlgorithm testAlgo[] = { HashAlgorithm.sha224, HashAlgorithm.sha256 |
| , HashAlgorithm.sha384, HashAlgorithm.sha512, HashAlgorithm.ripemd160 }; |
| |
| for (HashAlgorithm ha : testAlgo) { |
| OPCPackage pkg = null; |
| try { |
| signatureConfig.setDigestAlgo(ha); |
| pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE); |
| signatureConfig.setOpcPackage(pkg); |
| |
| SignatureInfo si = new SignatureInfo(); |
| si.setSignatureConfig(signatureConfig); |
| |
| si.confirmSignature(); |
| boolean b = si.verifySignature(); |
| assertTrue("Signature not correctly calculated for " + ha, b); |
| } finally { |
| if (pkg != null) { |
| pkg.close(); |
| } |
| } |
| } |
| } |
| |
| @Test |
| public void bug58630() throws Exception { |
| // test deletion of sheet 0 and signing |
| File tpl = copy(testdata.getFile("bug58630.xlsx")); |
| SXSSFWorkbook wb1 = new SXSSFWorkbook((XSSFWorkbook)WorkbookFactory.create(tpl), 10); |
| wb1.setCompressTempFiles(true); |
| wb1.removeSheetAt(0); |
| ByteArrayOutputStream os = new ByteArrayOutputStream(); |
| wb1.write(os); |
| wb1.close(); |
| OPCPackage pkg = OPCPackage.open(new ByteArrayInputStream(os.toByteArray())); |
| |
| initKeyPair("Test", "CN=Test"); |
| SignatureConfig signatureConfig = new SignatureConfig(); |
| signatureConfig.setKey(keyPair.getPrivate()); |
| signatureConfig.setSigningCertificateChain(Collections.singletonList(x509)); |
| signatureConfig.setOpcPackage(pkg); |
| |
| SignatureInfo si = new SignatureInfo(); |
| si.setSignatureConfig(signatureConfig); |
| si.confirmSignature(); |
| assertTrue("invalid signature", si.verifySignature()); |
| |
| pkg.close(); |
| } |
| |
| @Test |
| public void testMultiSign() throws Exception { |
| initKeyPair("KeyA", "CN=KeyA"); |
| //KeyPair keyPairA = keyPair; |
| //X509Certificate x509A = x509; |
| initKeyPair("KeyB", "CN=KeyB"); |
| //KeyPair keyPairB = keyPair; |
| //X509Certificate x509B = x509; |
| |
| File tpl = copy(testdata.getFile("bug58630.xlsx")); |
| OPCPackage pkg = OPCPackage.open(tpl); |
| try { |
| //SignatureConfig signatureConfig = new SignatureConfig(); |
| assertNotNull(pkg); |
| } finally { |
| pkg.close(); |
| } |
| } |
| |
| private void sign(OPCPackage pkgCopy, String alias, String signerDn, int signerCount) throws Exception { |
| initKeyPair(alias, signerDn); |
| |
| SignatureConfig signatureConfig = new SignatureConfig(); |
| signatureConfig.setKey(keyPair.getPrivate()); |
| signatureConfig.setSigningCertificateChain(Collections.singletonList(x509)); |
| signatureConfig.setExecutionTime(cal.getTime()); |
| signatureConfig.setDigestAlgo(HashAlgorithm.sha1); |
| signatureConfig.setOpcPackage(pkgCopy); |
| |
| SignatureInfo si = new SignatureInfo(); |
| si.setSignatureConfig(signatureConfig); |
| |
| Document document = DocumentHelper.createDocument(); |
| |
| // operate |
| DigestInfo digestInfo = si.preSign(document, null); |
| |
| // verify |
| assertNotNull(digestInfo); |
| LOG.log(POILogger.DEBUG, "digest algo: " + digestInfo.hashAlgo); |
| LOG.log(POILogger.DEBUG, "digest description: " + digestInfo.description); |
| assertEquals("Office OpenXML Document", digestInfo.description); |
| assertNotNull(digestInfo.hashAlgo); |
| assertNotNull(digestInfo.digestValue); |
| |
| // setup: key material, signature value |
| byte[] signatureValue = si.signDigest(digestInfo.digestValue); |
| |
| // operate: postSign |
| si.postSign(document, signatureValue); |
| |
| // verify: signature |
| si.getSignatureConfig().setOpcPackage(pkgCopy); |
| List<X509Certificate> result = new ArrayList<X509Certificate>(); |
| for (SignaturePart sp : si.getSignatureParts()) { |
| if (sp.validate()) { |
| result.add(sp.getSigner()); |
| } |
| } |
| assertEquals(signerCount, result.size()); |
| } |
| |
| private void initKeyPair(String alias, String subjectDN) throws Exception { |
| final char password[] = "test".toCharArray(); |
| File file = new File("build/test.pfx"); |
| |
| KeyStore keystore = KeyStore.getInstance("PKCS12"); |
| |
| if (file.exists()) { |
| FileInputStream fis = new FileInputStream(file); |
| keystore.load(fis, password); |
| fis.close(); |
| } else { |
| keystore.load(null, password); |
| } |
| |
| if (keystore.isKeyEntry(alias)) { |
| Key key = keystore.getKey(alias, password); |
| x509 = (X509Certificate)keystore.getCertificate(alias); |
| keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key); |
| } else { |
| keyPair = PkiTestUtils.generateKeyPair(); |
| Date notBefore = cal.getTime(); |
| Calendar cal2 = (Calendar)cal.clone(); |
| cal2.add(Calendar.YEAR, 1); |
| Date notAfter = cal2.getTime(); |
| KeyUsage keyUsage = new KeyUsage(KeyUsage.digitalSignature); |
| |
| x509 = PkiTestUtils.generateCertificate(keyPair.getPublic(), subjectDN |
| , notBefore, notAfter, null, keyPair.getPrivate(), true, 0, null, null, keyUsage); |
| |
| keystore.setKeyEntry(alias, keyPair.getPrivate(), password, new Certificate[]{x509}); |
| FileOutputStream fos = new FileOutputStream(file); |
| keystore.store(fos, password); |
| fos.close(); |
| } |
| } |
| |
| private static File copy(File input) throws IOException { |
| String extension = input.getName().replaceAll(".*?(\\.[^.]+)?$", "$1"); |
| if (extension == null || "".equals(extension)) { |
| extension = ".zip"; |
| } |
| |
| // ensure that we create the "build" directory as it might not be existing |
| // in the Sonar Maven runs where we are at a different source directory |
| File buildDir = new File("build"); |
| if(!buildDir.exists()) { |
| assertTrue("Failed to create " + buildDir.getAbsolutePath(), |
| buildDir.mkdirs()); |
| } |
| File tmpFile = new File(buildDir, "sigtest"+extension); |
| |
| OutputStream fos = new FileOutputStream(tmpFile); |
| try { |
| InputStream fis = new FileInputStream(input); |
| try { |
| IOUtils.copy(fis, fos); |
| } finally { |
| fis.close(); |
| } |
| } finally { |
| fos.close(); |
| } |
| |
| return tmpFile; |
| } |
| |
| } |