diff --git a/.classpath b/.classpath index a95bc314e..2445269b1 100644 --- a/.classpath +++ b/.classpath @@ -24,8 +24,6 @@ - - diff --git a/build.xml b/build.xml index 0e374e51f..a83ccaea6 100644 --- a/build.xml +++ b/build.xml @@ -151,10 +151,6 @@ under the License. - - - - @@ -267,8 +263,6 @@ under the License. - - @@ -443,14 +437,6 @@ under the License. - - - - - - - - diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TSPTimeStampService.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TSPTimeStampService.java index d0ba961eb..100c0f7f3 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TSPTimeStampService.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TSPTimeStampService.java @@ -24,6 +24,9 @@ package org.apache.poi.poifs.crypt.dsig.services; +import static org.apache.poi.poifs.crypt.dsig.HorribleProxy.createProxy; +import static org.apache.poi.poifs.crypt.dsig.HorribleProxy.newProxy; + import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.OutputStream; @@ -48,6 +51,7 @@ import javax.xml.bind.DatatypeConverter; import org.apache.commons.codec.binary.Hex; import org.apache.poi.poifs.crypt.CryptoFunctions; +import org.apache.poi.poifs.crypt.HashAlgorithm; import org.apache.poi.poifs.crypt.dsig.HorribleProxies.ASN1InputStreamIf; import org.apache.poi.poifs.crypt.dsig.HorribleProxies.ASN1OctetStringIf; import org.apache.poi.poifs.crypt.dsig.HorribleProxies.AuthorityKeyIdentifierIf; @@ -64,7 +68,6 @@ import org.apache.poi.poifs.crypt.dsig.HorribleProxies.TimeStampRequestIf; import org.apache.poi.poifs.crypt.dsig.HorribleProxies.TimeStampResponseIf; import org.apache.poi.poifs.crypt.dsig.HorribleProxies.TimeStampTokenIf; import org.apache.poi.poifs.crypt.dsig.HorribleProxies.X509CertificateHolderIf; -import org.apache.poi.poifs.crypt.dsig.HorribleProxy; import org.apache.poi.util.IOUtils; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; @@ -83,7 +86,7 @@ public class TSPTimeStampService implements TimeStampService { CryptoFunctions.registerBouncyCastle(); } - public static final String DEFAULT_USER_AGENT = "eID Applet Service TSP Client"; + public static final String DEFAULT_USER_AGENT = "POI XmlSign Service TSP Client"; private final String tspServiceUrl; @@ -101,7 +104,7 @@ public class TSPTimeStampService implements TimeStampService { private int proxyPort; - private String digestAlgo; + private HashAlgorithm digestAlgo; private String digestAlgoOid; @@ -143,9 +146,8 @@ public class TSPTimeStampService implements TimeStampService { } else { this.userAgent = DEFAULT_USER_AGENT; } - - this.digestAlgo = "SHA-1"; - this.digestAlgoOid = "1.3.14.3.2.26"; + + setDigestAlgo(HashAlgorithm.sha1); } /** @@ -183,16 +185,21 @@ public class TSPTimeStampService implements TimeStampService { * * @param digestAlgo */ - public void setDigestAlgo(String digestAlgo) { - if ("SHA-1".equals(digestAlgo)) { - this.digestAlgoOid = "1.3.14.3.2.26"; - } else if ("SHA-256".equals(digestAlgo)) { - this.digestAlgoOid = "2.16.840.1.101.3.4.2.1"; - } else if ("SHA-384".equals(digestAlgo)) { - this.digestAlgoOid = "2.16.840.1.101.3.4.2.2"; - } else if ("SHA-512".equals(digestAlgo)) { - this.digestAlgoOid = "2.16.840.1.101.3.4.2.3"; - } else { + public void setDigestAlgo(HashAlgorithm digestAlgo) { + switch (digestAlgo) { + case sha1: + digestAlgoOid = "1.3.14.3.2.26"; + break; + case sha256: + digestAlgoOid = "2.16.840.1.101.3.4.2.1"; + break; + case sha384: + digestAlgoOid = "2.16.840.1.101.3.4.2.2"; + break; + case sha512: + digestAlgoOid = "2.16.840.1.101.3.4.2.3"; + break; + default: throw new IllegalArgumentException("unsupported digest algo: " + digestAlgo); } @@ -222,13 +229,12 @@ public class TSPTimeStampService implements TimeStampService { public byte[] timeStamp(byte[] data, RevocationData revocationData) throws Exception { // digest the message - MessageDigest messageDigest = MessageDigest - .getInstance(this.digestAlgo); + MessageDigest messageDigest = CryptoFunctions.getMessageDigest(this.digestAlgo); byte[] digest = messageDigest.digest(data); // generate the TSP request BigInteger nonce = new BigInteger(128, new SecureRandom()); - TimeStampRequestGeneratorIf requestGenerator = HorribleProxy.newProxy(TimeStampRequestGeneratorIf.class); + TimeStampRequestGeneratorIf requestGenerator = newProxy(TimeStampRequestGeneratorIf.class); requestGenerator.setCertReq(true); if (null != this.requestPolicy) { requestGenerator.setReqPolicy(this.requestPolicy); @@ -250,7 +256,8 @@ public class TSPTimeStampService implements TimeStampService { huc.setDoOutput(true); // also sets method to POST. huc.setRequestProperty("User-Agent", this.userAgent); - huc.setRequestProperty("Content-Type", "application/timestamp-query;charset=ISO-8859-1"); + // "application/timestamp-query;charset=ISO-8859-1" + huc.setRequestProperty("Content-Type", "application/timestamp-request"); OutputStream hucOut = huc.getOutputStream(); hucOut.write(encodedRequest); @@ -274,7 +281,8 @@ public class TSPTimeStampService implements TimeStampService { IOUtils.copy(huc.getInputStream(), bos); LOG.log(POILogger.DEBUG, "response content: ", bos.toString()); - if (!contentType.startsWith("application/timestamp-reply")) { + // "application/timestamp-reply" + if (!contentType.startsWith("application/timestamp-response")) { throw new RuntimeException("invalid Content-Type: " + contentType); } @@ -283,7 +291,7 @@ public class TSPTimeStampService implements TimeStampService { } // TSP response parsing and validation - TimeStampResponseIf timeStampResponse = HorribleProxy.newProxy(TimeStampResponseIf.class, bos.toByteArray()); + TimeStampResponseIf timeStampResponse = newProxy(TimeStampResponseIf.class, bos.toByteArray()); timeStampResponse.validate(request); if (0 != timeStampResponse.getStatus()) { @@ -346,10 +354,10 @@ public class TSPTimeStampService implements TimeStampService { } while (null != certificate); // verify TSP signer signature - X509CertificateHolderIf holder = HorribleProxy.newProxy(X509CertificateHolderIf.class, tspCertificateChain.get(0).getEncoded()); - DefaultDigestAlgorithmIdentifierFinderIf finder = HorribleProxy.newProxy(DefaultDigestAlgorithmIdentifierFinderIf.class); - BcDigestCalculatorProviderIf calculator = HorribleProxy.newProxy(BcDigestCalculatorProviderIf.class); - BcRSASignerInfoVerifierBuilderIf verifierBuilder = HorribleProxy.newProxy(BcRSASignerInfoVerifierBuilderIf.class, finder, calculator); + X509CertificateHolderIf holder = newProxy(X509CertificateHolderIf.class, tspCertificateChain.get(0).getEncoded()); + DefaultDigestAlgorithmIdentifierFinderIf finder = newProxy(DefaultDigestAlgorithmIdentifierFinderIf.class); + BcDigestCalculatorProviderIf calculator = newProxy(BcDigestCalculatorProviderIf.class); + BcRSASignerInfoVerifierBuilderIf verifierBuilder = newProxy(BcRSASignerInfoVerifierBuilderIf.class, finder, calculator); SignerInformationVerifierIf verifier = verifierBuilder.build(holder); timeStampToken.validate(verifier); @@ -369,10 +377,10 @@ public class TSPTimeStampService implements TimeStampService { byte[] extvalue = cert.getExtensionValue("2.5.29.14"); if (extvalue == null) return null; - ASN1InputStreamIf keyCntStream = HorribleProxy.newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(extvalue)); - ASN1OctetStringIf cntStr = HorribleProxy.createProxy(ASN1OctetStringIf.class, "getInstance", keyCntStream.readObject$Object()); - ASN1InputStreamIf keyIdStream = HorribleProxy.newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(cntStr.getOctets())); - SubjectKeyIdentifierIf keyId = HorribleProxy.createProxy(SubjectKeyIdentifierIf.class, "getInstance", keyIdStream.readObject$Object()); + ASN1InputStreamIf keyCntStream = newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(extvalue)); + ASN1OctetStringIf cntStr = createProxy(ASN1OctetStringIf.class, "getInstance", keyCntStream.readObject$Object()); + ASN1InputStreamIf keyIdStream = newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(cntStr.getOctets())); + SubjectKeyIdentifierIf keyId = createProxy(SubjectKeyIdentifierIf.class, "getInstance", keyIdStream.readObject$Object()); return keyId.getKeyIdentifier(); } @@ -382,10 +390,10 @@ public class TSPTimeStampService implements TimeStampService { byte[] extvalue = cert.getExtensionValue("2.5.29.35"); if (extvalue == null) return null; - ASN1InputStreamIf keyCntStream = HorribleProxy.newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(extvalue)); + ASN1InputStreamIf keyCntStream = newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(extvalue)); DEROctetStringIf cntStr = keyCntStream.readObject$DERString(); - ASN1InputStreamIf keyIdStream = HorribleProxy.newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(cntStr.getOctets())); - AuthorityKeyIdentifierIf keyId = HorribleProxy.newProxy(AuthorityKeyIdentifierIf.class, keyIdStream.readObject$Sequence()); + ASN1InputStreamIf keyIdStream = newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(cntStr.getOctets())); + AuthorityKeyIdentifierIf keyId = newProxy(AuthorityKeyIdentifierIf.class, keyIdStream.readObject$Sequence()); return keyId.getKeyIdentifier(); } diff --git a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java index 05dad0b1e..b329474e5 100644 --- a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java +++ b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java @@ -26,12 +26,6 @@ package org.apache.poi.poifs.crypt; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; import java.io.File; import java.io.FileInputStream; @@ -73,7 +67,9 @@ 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.TSPTimeStampService; import org.apache.poi.poifs.crypt.dsig.services.TimeStampService; +import org.apache.poi.poifs.crypt.dsig.services.TimeStampServiceValidator; import org.apache.poi.poifs.crypt.dsig.services.XmlSignatureService; import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo; import org.apache.poi.util.IOUtils; @@ -84,8 +80,6 @@ import org.etsi.uri.x01903.v13.DigestAlgAndValueType; import org.etsi.uri.x01903.v13.QualifyingPropertiesType; import org.junit.BeforeClass; import org.junit.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; import org.w3.x2000.x09.xmldsig.SignatureDocument; public class TestSignatureInfo { @@ -210,23 +204,38 @@ public class TestSignatureInfo { String testFile = "hello-world-unsigned.xlsx"; OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE); + initKeyPair("Test", "CN=Test"); + // setup EnvelopedSignatureFacet envelopedSignatureFacet = new EnvelopedSignatureFacet(); KeyInfoSignatureFacet keyInfoSignatureFacet = new KeyInfoSignatureFacet(true, false, false); SignaturePolicyService signaturePolicyService = null; XAdESSignatureFacet xadesSignatureFacet = new XAdESSignatureFacet(null, null, signaturePolicyService); + final X509CRL crl = PkiTestUtils.generateCrl(x509, keyPair.getPrivate()); - - TimeStampService mockTimeStampService = mock(TimeStampService.class); - RevocationDataService mockRevocationDataService = mock(RevocationDataService.class); +// TimeStampService timeStampService = new TimeStampService(){ +// public byte[] timeStamp(byte[] data, RevocationData revocationData) throws Exception { +// revocationData.addCRL(crl); +// return "time-stamp-token".getBytes(); +// } +// }; - XAdESXLSignatureFacet xadesXLSignatureFacet = new XAdESXLSignatureFacet( - mockTimeStampService, mockRevocationDataService); - XmlSignatureService testedInstance = new XmlSignatureService(HashAlgorithm.sha1, pkg); - testedInstance.addSignatureFacet(envelopedSignatureFacet, keyInfoSignatureFacet, - xadesSignatureFacet, xadesXLSignatureFacet); + // http://timestamping.edelweb.fr/service/tsp + // http://tsa.belgium.be/connect + String tspServiceUrl = "http://timestamping.edelweb.fr/service/tsp"; + TimeStampServiceValidator tspValidator = new TimeStampServiceValidator() { + @Override + public void validate(List certificateChain, + RevocationData revocationData) throws Exception { + for (X509Certificate certificate : certificateChain) { + LOG.log(POILogger.DEBUG, "certificate: " + certificate.getSubjectX500Principal()); + LOG.log(POILogger.DEBUG, "validity: " + certificate.getNotBefore() + " - " + certificate.getNotAfter()); + } + } + }; + + TimeStampService timeStampService = new TSPTimeStampService(tspServiceUrl, tspValidator); - initKeyPair("Test", "CN=Test"); List certificateChain = new ArrayList(); /* * We need at least 2 certificates for the XAdES-C complete certificate @@ -235,25 +244,25 @@ public class TestSignatureInfo { certificateChain.add(x509); certificateChain.add(x509); - RevocationData revocationData = new RevocationData(); - final X509CRL crl = PkiTestUtils.generateCrl(x509, keyPair.getPrivate()); + final RevocationData revocationData = new RevocationData(); revocationData.addCRL(crl); OCSPRespIf ocspResp = PkiTestUtils.createOcspResp(x509, false, x509, x509, keyPair.getPrivate(), "SHA1withRSA", cal.getTimeInMillis()); revocationData.addOCSP(ocspResp.getEncoded()); + + RevocationDataService revocationDataService = new RevocationDataService(){ + public RevocationData getRevocationData(List certificateChain) { + return revocationData; + } + }; + + XAdESXLSignatureFacet xadesXLSignatureFacet = new XAdESXLSignatureFacet( + timeStampService, revocationDataService); + XmlSignatureService testedInstance = new XmlSignatureService(HashAlgorithm.sha1, pkg); + testedInstance.addSignatureFacet(envelopedSignatureFacet, keyInfoSignatureFacet, + xadesSignatureFacet, xadesXLSignatureFacet); - when(mockTimeStampService.timeStamp(any(byte[].class), any(RevocationData.class))) - .thenAnswer(new Answer(){ - public byte[] answer(InvocationOnMock invocation) throws Throwable { - Object[] arguments = invocation.getArguments(); - RevocationData revocationData = (RevocationData) arguments[1]; - revocationData.addCRL(crl); - return "time-stamp-token".getBytes(); - } - }); - when(mockRevocationDataService.getRevocationData(eq(certificateChain))) - .thenReturn(revocationData); // operate DigestInfo digestInfo = testedInstance.preSign(null, certificateChain, null, null, null); @@ -279,10 +288,6 @@ public class TestSignatureInfo { // Operate: postSign testedInstance.postSign(signatureValue, certificateChain); - // verify - verify(mockTimeStampService, times(2)).timeStamp(any(byte[].class), any(RevocationData.class)); - verify(mockRevocationDataService).getRevocationData(certificateChain); - DOMValidateContext domValidateContext = new DOMValidateContext( KeySelector.singletonKeySelector(keyPair.getPublic()), testedInstance.getSignatureDocument().getDomNode()); @@ -389,4 +394,5 @@ public class TestSignatureInfo { fos.close(); return tmpFile; } + }