From ed2a75890f5a9f313b0797b398e618a376358604 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Fri, 15 Aug 2014 22:20:30 +0000 Subject: [PATCH] migrated enveloped document test (1 of 2) git-svn-id: https://svn.apache.org/repos/asf/poi/branches/xml_signature@1618288 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 16 +++++- .../poi/poifs/crypt/dsig/SignatureInfo.java | 32 ++++++----- .../dsig/facets/XAdESXLSignatureFacet.java | 15 ++--- .../dsig/services/XmlSignatureService.java | 13 +++-- .../poi/poifs/crypt/TestSignatureInfo.java | 56 ++++++++++++++++++- 5 files changed, 100 insertions(+), 32 deletions(-) diff --git a/build.xml b/build.xml index 4423a7833d..18e817232d 100644 --- a/build.xml +++ b/build.xml @@ -145,8 +145,12 @@ under the License. + + + + - + @@ -159,7 +163,7 @@ under the License. - + @@ -431,6 +435,14 @@ under the License. + + + + + + + + diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java index 142d56bc0a..96ac896a63 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/SignatureInfo.java @@ -126,26 +126,28 @@ public class SignatureInfo { DigestInfo digestInfo = signatureService.preSign(null, x509Chain, null, null, null); // setup: key material, signature value - - Cipher cipher = CryptoFunctions.getCipher(key, CipherAlgorithm.rsa - , ChainingMode.ecb, null, Cipher.ENCRYPT_MODE, "PKCS1Padding"); - - byte[] signatureValue; - try { - ByteArrayOutputStream digestInfoValueBuf = new ByteArrayOutputStream(); - digestInfoValueBuf.write(getHashMagic(hashAlgo)); - digestInfoValueBuf.write(digestInfo.digestValue); - byte[] digestInfoValue = digestInfoValueBuf.toByteArray(); - signatureValue = cipher.doFinal(digestInfoValue); - } catch (Exception e) { - throw new EncryptedDocumentException(e); - } - + byte[] signatureValue = signDigest(key, hashAlgo, digestInfo.digestValue); // operate: postSign signatureService.postSign(signatureValue, Collections.singletonList(x509)); } + public static byte[] signDigest(Key key, HashAlgorithm hashAlgo, byte digest[]) { + Cipher cipher = CryptoFunctions.getCipher(key, CipherAlgorithm.rsa + , ChainingMode.ecb, null, Cipher.ENCRYPT_MODE, "PKCS1Padding"); + + try { + ByteArrayOutputStream digestInfoValueBuf = new ByteArrayOutputStream(); + digestInfoValueBuf.write(getHashMagic(hashAlgo)); + digestInfoValueBuf.write(digest); + byte[] digestInfoValue = digestInfoValueBuf.toByteArray(); + byte[] signatureValue = cipher.doFinal(digestInfoValue); + return signatureValue; + } catch (Exception e) { + throw new EncryptedDocumentException(e); + } + } + public XmlSignatureService createSignatureService(HashAlgorithm hashAlgo, OPCPackage pkg) { XmlSignatureService signatureService = new XmlSignatureService(hashAlgo, pkg); signatureService.initFacets(new Date()); diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java index ade2494f06..2b590fdd23 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/facets/XAdESXLSignatureFacet.java @@ -49,8 +49,6 @@ import javax.xml.crypto.dsig.XMLSignatureFactory; import javax.xml.namespace.QName; import org.apache.poi.poifs.crypt.HashAlgorithm; -import org.apache.poi.poifs.crypt.dsig.HorribleProxy; -import org.apache.poi.poifs.crypt.dsig.SignatureInfo; 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.BasicOCSPRespIf; @@ -62,6 +60,8 @@ import org.apache.poi.poifs.crypt.dsig.HorribleProxies.OCSPRespIf; import org.apache.poi.poifs.crypt.dsig.HorribleProxies.RespIDIf; import org.apache.poi.poifs.crypt.dsig.HorribleProxies.ResponderIDIf; import org.apache.poi.poifs.crypt.dsig.HorribleProxies.X509NameIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxy; +import org.apache.poi.poifs.crypt.dsig.SignatureInfo; 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; @@ -373,25 +373,22 @@ public class XAdESXLSignatureFacet implements SignatureFacet { } public static byte[] getC14nValue(List nodeList, String c14nAlgoId) { - byte[] c14nValue = null; + ByteArrayOutputStream c14nValue = new ByteArrayOutputStream(); try { for (Node node : nodeList) { /* * Re-initialize the c14n else the namespaces will get cached * and will be missing from the c14n resulting nodes. */ - CanonicalizerIf c14n = HorribleProxy.createProxy(CanonicalizerIf.class, "newInstance", c14nAlgoId); - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - bos.write(c14nValue); - bos.write(c14n.canonicalizeSubtree(node)); - c14nValue = bos.toByteArray(); + CanonicalizerIf c14n = HorribleProxy.createProxy(CanonicalizerIf.class, "getInstance", c14nAlgoId); + c14nValue.write(c14n.canonicalizeSubtree(node)); } } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException("c14n error: " + e.getMessage(), e); } - return c14nValue; + return c14nValue.toByteArray(); } public void preSign(XMLSignatureFactory signatureFactory, diff --git a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/XmlSignatureService.java b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/XmlSignatureService.java index bea7653430..91ed1646b5 100644 --- a/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/XmlSignatureService.java +++ b/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/XmlSignatureService.java @@ -112,7 +112,7 @@ public class XmlSignatureService implements SignatureService { protected final List signatureFacets; private String signatureNamespacePrefix; - private String signatureId; + private String signatureId = "idPackageSignature"; private final HashAlgorithm hashAlgo; private final OPCPackage opcPackage; private SignatureDocument sigDoc; @@ -124,7 +124,6 @@ public class XmlSignatureService implements SignatureService { public XmlSignatureService(HashAlgorithm digestAlgo, OPCPackage opcPackage) { this.signatureFacets = new LinkedList(); this.signatureNamespacePrefix = null; - this.signatureId = null; this.hashAlgo = digestAlgo; this.opcPackage = opcPackage; this.sigDoc = null; @@ -142,7 +141,6 @@ public class XmlSignatureService implements SignatureService { * Work-around for Office 2010. */ this.xadesSignatureFacet.setIssuerNameNoReverseOrder(true); - setSignatureId("idPackageSignature"); addSignatureFacet(this.xadesSignatureFacet); addSignatureFacet(new Office2010SignatureFacet()); } @@ -398,8 +396,9 @@ public class XmlSignatureService implements SignatureService { registerIds(doc); Element el = doc.getElementById("idPackageObject"); - assert (el != null); - el.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:mdssi", PackageNamespaces.DIGITAL_SIGNATURE); + if (el != null) { + el.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:mdssi", PackageNamespaces.DIGITAL_SIGNATURE); + } /* @@ -549,6 +548,10 @@ public class XmlSignatureService implements SignatureService { public String getFilesDigestAlgorithm() { return null; } + + public SignatureDocument getSignatureDocument() { + return sigDoc; + } protected String getCanonicalizationMethod() { return CanonicalizationMethod.INCLUSIVE; 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 191066014f..73f6b23819 100644 --- a/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java +++ b/src/ooxml/testcases/org/apache/poi/poifs/crypt/TestSignatureInfo.java @@ -29,6 +29,8 @@ 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.ByteArrayOutputStream; @@ -54,6 +56,10 @@ import java.util.List; import java.util.TimeZone; import javax.crypto.Cipher; +import javax.xml.crypto.KeySelector; +import javax.xml.crypto.dsig.XMLSignature; +import javax.xml.crypto.dsig.XMLSignatureFactory; +import javax.xml.crypto.dsig.dom.DOMValidateContext; import org.apache.poi.POIDataSamples; import org.apache.poi.openxml4j.opc.OPCPackage; @@ -75,10 +81,14 @@ import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo; import org.apache.poi.util.IOUtils; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; +import org.apache.xmlbeans.XmlObject; +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 { private static final POILogger LOG = POILogFactory.getLogger(TestSignatureInfo.class); @@ -244,8 +254,52 @@ public class TestSignatureInfo { // verify assertNotNull(digestInfo); - assertEquals("SHA-1", digestInfo.hashAlgo); + assertEquals(HashAlgorithm.sha1, digestInfo.hashAlgo); assertNotNull(digestInfo.digestValue); + + SignatureDocument sigDoc = testedInstance.getSignatureDocument(); + String certDigestXQuery = + "declare namespace xades='http://uri.etsi.org/01903/v1.3.2#'; " + + "declare namespace ds='http://www.w3.org/2000/09/xmldsig#'; " + + "$this/ds:Signature/ds:Object/xades:QualifyingProperties/xades:SignedProperties/xades:SignedSignatureProperties/xades:SigningCertificate/xades:Cert/xades:CertDigest"; + XmlObject xoList[] = sigDoc.selectPath(certDigestXQuery); + assertEquals(xoList.length, 1); + DigestAlgAndValueType certDigest = (DigestAlgAndValueType)xoList[0]; + assertNotNull(certDigest.getDigestValue()); + + // Sign the received XML signature digest value. + byte[] signatureValue = SignatureInfo.signDigest(keyPair.getPrivate(), HashAlgorithm.sha1, digestInfo.digestValue); + + // 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()); + XMLSignatureFactory xmlSignatureFactory = SignatureInfo.getSignatureFactory(); + XMLSignature xmlSignature = xmlSignatureFactory + .unmarshalXMLSignature(domValidateContext); + boolean validity = xmlSignature.validate(domValidateContext); + assertTrue(validity); + + xoList = sigDoc.selectPath(certDigestXQuery); + assertEquals(xoList.length, 1); + certDigest = (DigestAlgAndValueType)xoList[0]; + assertNotNull(certDigest.getDigestValue()); + + String qualPropXQuery = + "declare namespace xades='http://uri.etsi.org/01903/v1.3.2#'; " + + "declare namespace ds='http://www.w3.org/2000/09/xmldsig#'; " + + "$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); } private OPCPackage sign(OPCPackage pkgCopy, String alias, String signerDn, int signerCount) throws Exception {