import java.util.ArrayList;\r
import java.util.Collections;\r
import java.util.HashMap;\r
+import java.util.Iterator;\r
import java.util.List;\r
import java.util.Map;\r
+import java.util.NoSuchElementException;\r
\r
import javax.crypto.Cipher;\r
import javax.xml.crypto.MarshalException;\r
\r
public static final byte[] RIPEMD256_DIGEST_INFO_PREFIX = new byte[]\r
{ 0x30, 0x2b, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x03, 0x04, 0x20 };\r
+\r
+ private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class);\r
+ private static boolean isInitialized = false;\r
+ \r
+ private SignatureConfig signatureConfig;\r
+\r
+ public class SignaturePart {\r
+ private final PackagePart signaturePart;\r
+ private X509Certificate signer;\r
+ \r
+ private SignaturePart(PackagePart signaturePart) {\r
+ this.signaturePart = signaturePart;\r
+ }\r
+ \r
+ public PackagePart getPackagePart() {\r
+ return signaturePart;\r
+ }\r
+ \r
+ public X509Certificate getSigner() {\r
+ return signer;\r
+ }\r
+ \r
+ public SignatureDocument getSignatureDocument() throws IOException, XmlException {\r
+ // TODO: check for XXE\r
+ return SignatureDocument.Factory.parse(signaturePart.getInputStream());\r
+ }\r
+ \r
+ public boolean validate() {\r
+ KeyInfoKeySelector keySelector = new KeyInfoKeySelector();\r
+ try {\r
+ Document doc = DocumentHelper.readDocument(signaturePart.getInputStream());\r
+ registerIds(doc);\r
+ \r
+ DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc);\r
+ domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE);\r
+ domValidateContext.setURIDereferencer(signatureConfig.getUriDereferencer());\r
+ \r
+ XMLSignatureFactory xmlSignatureFactory = getSignatureFactory();\r
+ XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext);\r
+ boolean valid = xmlSignature.validate(domValidateContext);\r
+\r
+ if (valid) {\r
+ signer = keySelector.getCertificate();\r
+ }\r
+ \r
+ return valid;\r
+ } catch (Exception e) {\r
+ LOG.log(POILogger.ERROR, "error in marshalling and validating the signature", e);\r
+ return false;\r
+ }\r
+ }\r
+ }\r
\r
protected static class SignCreationListener implements EventListener, SignatureConfigurable {\r
ThreadLocal<EventTarget> target = new ThreadLocal<EventTarget>();\r
}\r
\r
\r
- private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class);\r
- private static boolean isInitialized = false;\r
+ public SignatureInfo() {\r
+ initXmlProvider(); \r
+ }\r
\r
- private SignatureConfig signatureConfig;\r
-\r
public SignatureConfig getSignatureConfig() {\r
return signatureConfig;\r
}\r
}\r
\r
public boolean verifySignature() {\r
- initXmlProvider();\r
// http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html\r
- List<X509Certificate> signers = new ArrayList<X509Certificate>();\r
- return getSignersAndValidate(signers, true);\r
+ for (SignaturePart sp : getSignatureParts()){\r
+ // only validate first part\r
+ return sp.validate();\r
+ }\r
+ return false;\r
}\r
\r
public void confirmSignature()\r
}\r
}\r
\r
- public List<X509Certificate> getSigners() {\r
- initXmlProvider();\r
- List<X509Certificate> signers = new ArrayList<X509Certificate>();\r
- getSignersAndValidate(signers, false);\r
- return signers;\r
- }\r
- \r
- protected boolean getSignersAndValidate(List<X509Certificate> signers, boolean onlyFirst) {\r
- signatureConfig.init(true);\r
- \r
- boolean allValid = true;\r
- List<PackagePart> signatureParts = getSignatureParts(onlyFirst);\r
- if (signatureParts.isEmpty()) {\r
- LOG.log(POILogger.DEBUG, "no signature resources");\r
- allValid = false;\r
- }\r
-\r
- for (PackagePart signaturePart : signatureParts) {\r
- KeyInfoKeySelector keySelector = new KeyInfoKeySelector();\r
-\r
- try {\r
- Document doc = DocumentHelper.readDocument(signaturePart.getInputStream());\r
- registerIds(doc);\r
- \r
- DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc);\r
- domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE);\r
- domValidateContext.setURIDereferencer(signatureConfig.getUriDereferencer());\r
- \r
- XMLSignatureFactory xmlSignatureFactory = getSignatureFactory();\r
- XMLSignature xmlSignature = xmlSignatureFactory.unmarshalXMLSignature(domValidateContext);\r
- boolean validity = xmlSignature.validate(domValidateContext);\r
- allValid &= validity;\r
- if (!validity) continue;\r
- // TODO: check what has been signed.\r
- } catch (Exception e) {\r
- LOG.log(POILogger.ERROR, "error in marshalling and validating the signature", e);\r
- continue;\r
- }\r
-\r
- X509Certificate signer = keySelector.getCertificate();\r
- signers.add(signer);\r
- }\r
- \r
- return allValid;\r
- }\r
-\r
- protected List<PackagePart> getSignatureParts(boolean onlyFirst) {\r
- List<PackagePart> packageParts = new ArrayList<PackagePart>();\r
- OPCPackage pkg = signatureConfig.getOpcPackage();\r
- \r
- PackageRelationshipCollection sigOrigRels = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);\r
- for (PackageRelationship rel : sigOrigRels) {\r
- PackagePart sigPart = pkg.getPart(rel);\r
- LOG.log(POILogger.DEBUG, "Digital Signature Origin part", sigPart);\r
-\r
- try {\r
- PackageRelationshipCollection sigRels = sigPart.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE);\r
- for (PackageRelationship sigRel : sigRels) {\r
- PackagePart sigRelPart = sigPart.getRelatedPart(sigRel); \r
- LOG.log(POILogger.DEBUG, "XML Signature part", sigRelPart);\r
- packageParts.add(sigRelPart);\r
- if (onlyFirst) break;\r
- }\r
- } catch (InvalidFormatException e) {\r
- LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);\r
+ public Iterable<SignaturePart> getSignatureParts() {\r
+ return new Iterable<SignaturePart>() {\r
+ public Iterator<SignaturePart> iterator() {\r
+ return new Iterator<SignaturePart>() {\r
+ OPCPackage pkg = signatureConfig.getOpcPackage();\r
+ Iterator<PackageRelationship> sigOrigRels = \r
+ pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN).iterator();\r
+ Iterator<PackageRelationship> sigRels = null;\r
+ PackagePart sigPart = null;\r
+ \r
+ public boolean hasNext() {\r
+ while (sigRels == null || !sigRels.hasNext()) {\r
+ if (!sigOrigRels.hasNext()) return false;\r
+ sigPart = pkg.getPart(sigOrigRels.next());\r
+ LOG.log(POILogger.DEBUG, "Digital Signature Origin part", sigPart);\r
+ try {\r
+ sigRels = sigPart.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE).iterator();\r
+ } catch (InvalidFormatException e) {\r
+ LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+ \r
+ public SignaturePart next() {\r
+ PackagePart sigRelPart = null;\r
+ do {\r
+ try {\r
+ if (!hasNext()) throw new NoSuchElementException();\r
+ sigRelPart = sigPart.getRelatedPart(sigRels.next()); \r
+ LOG.log(POILogger.DEBUG, "XML Signature part", sigRelPart);\r
+ } catch (InvalidFormatException e) {\r
+ LOG.log(POILogger.WARN, "Reference to signature is invalid.", e);\r
+ }\r
+ } while (sigPart == null);\r
+ return new SignaturePart(sigRelPart);\r
+ }\r
+ \r
+ public void remove() {\r
+ throw new UnsupportedOperationException();\r
+ }\r
+ };\r
}\r
- \r
- if (onlyFirst && !packageParts.isEmpty()) break;\r
- }\r
-\r
- return packageParts;\r
+ };\r
}\r
\r
public static XMLSignatureFactory getSignatureFactory() {\r
import java.util.Calendar;\r
import java.util.Collections;\r
import java.util.Date;\r
+import java.util.Iterator;\r
import java.util.List;\r
import java.util.TimeZone;\r
\r
-import javax.xml.crypto.KeySelector;\r
-import javax.xml.crypto.dsig.XMLSignature;\r
-import javax.xml.crypto.dsig.XMLSignatureFactory;\r
-import javax.xml.crypto.dsig.dom.DOMValidateContext;\r
-\r
import org.apache.poi.POIDataSamples;\r
import org.apache.poi.openxml4j.opc.OPCPackage;\r
import org.apache.poi.openxml4j.opc.PackageAccess;\r
import org.apache.poi.poifs.crypt.dsig.SignatureConfig;\r
import org.apache.poi.poifs.crypt.dsig.SignatureInfo;\r
+import org.apache.poi.poifs.crypt.dsig.SignatureInfo.SignaturePart;\r
import org.apache.poi.poifs.crypt.dsig.facets.EnvelopedSignatureFacet;\r
import org.apache.poi.poifs.crypt.dsig.facets.KeyInfoSignatureFacet;\r
import org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet;\r
import org.etsi.uri.x01903.v13.QualifyingPropertiesType;\r
import org.junit.BeforeClass;\r
import org.junit.Test;\r
+import org.w3.x2000.x09.xmldsig.ReferenceType;\r
import org.w3.x2000.x09.xmldsig.SignatureDocument;\r
import org.w3c.dom.Document;\r
\r
sic.setOpcPackage(pkg);\r
SignatureInfo si = new SignatureInfo();\r
si.setSignatureConfig(sic);\r
- List<X509Certificate> result = si.getSigners();\r
+ List<X509Certificate> result = new ArrayList<X509Certificate>();\r
+ for (SignaturePart sp : si.getSignatureParts()) {\r
+ if (sp.validate()) {\r
+ result.add(sp.getSigner());\r
+ }\r
+ }\r
pkg.revert();\r
pkg.close();\r
assertNotNull(result);\r
sic.setOpcPackage(pkg);\r
SignatureInfo si = new SignatureInfo();\r
si.setSignatureConfig(sic);\r
- List<X509Certificate> result = si.getSigners();\r
+ List<X509Certificate> result = new ArrayList<X509Certificate>();\r
+ for (SignaturePart sp : si.getSignatureParts()) {\r
+ if (sp.validate()) {\r
+ result.add(sp.getSigner());\r
+ }\r
+ }\r
\r
assertNotNull(result);\r
assertEquals("test-file: "+testFile, 1, result.size());\r
sic.setOpcPackage(pkg);\r
SignatureInfo si = new SignatureInfo();\r
si.setSignatureConfig(sic);\r
- List<X509Certificate> result = si.getSigners();\r
+ List<X509Certificate> result = new ArrayList<X509Certificate>();\r
+ for (SignaturePart sp : si.getSignatureParts()) {\r
+ if (sp.validate()) {\r
+ result.add(sp.getSigner());\r
+ }\r
+ }\r
\r
assertNotNull(result);\r
assertEquals("test-file: "+testFile, 2, result.size());\r
si.setSignatureConfig(sic);\r
// hash > sha1 doesn't work in excel viewer ...\r
si.confirmSignature();\r
- List<X509Certificate> signer = si.getSigners();\r
- assertEquals(1, signer.size());\r
+ List<X509Certificate> result = new ArrayList<X509Certificate>();\r
+ for (SignaturePart sp : si.getSignatureParts()) {\r
+ if (sp.validate()) {\r
+ result.add(sp.getSigner());\r
+ }\r
+ }\r
+ assertEquals(1, result.size());\r
pkg.close();\r
}\r
\r
- @SuppressWarnings("unused")\r
@Test\r
public void testSignEnvelopingDocument() throws Exception {\r
String testFile = "hello-world-unsigned.xlsx";\r
};\r
signatureConfig.setRevocationDataService(revocationDataService);\r
\r
+ // operate\r
SignatureInfo si = new SignatureInfo();\r
si.setSignatureConfig(signatureConfig);\r
+ si.confirmSignature();\r
\r
- Document document = DocumentHelper.createDocument();\r
- \r
- // operate\r
- DigestInfo digestInfo = si.preSign(document, null);\r
-\r
// verify\r
- assertNotNull(digestInfo);\r
- assertEquals(HashAlgorithm.sha1, digestInfo.hashAlgo);\r
- assertNotNull(digestInfo.digestValue);\r
+ Iterator<SignaturePart> spIter = si.getSignatureParts().iterator();\r
+ assertTrue(spIter.hasNext());\r
+ SignaturePart sp = spIter.next();\r
+ boolean valid = sp.validate();\r
+ assertTrue(valid);\r
\r
- SignatureDocument sigDoc = SignatureDocument.Factory.parse(document);\r
- String certDigestXQuery =\r
- "declare namespace xades='http://uri.etsi.org/01903/v1.3.2#'; "\r
- + "declare namespace ds='http://www.w3.org/2000/09/xmldsig#'; "\r
- + "$this/ds:Signature/ds:Object/xades:QualifyingProperties/xades:SignedProperties/xades:SignedSignatureProperties/xades:SigningCertificate/xades:Cert/xades:CertDigest";\r
+ SignatureDocument sigDoc = sp.getSignatureDocument();\r
+ String declareNS = \r
+ "declare namespace xades='http://uri.etsi.org/01903/v1.3.2#'; "\r
+ + "declare namespace ds='http://www.w3.org/2000/09/xmldsig#'; ";\r
+ \r
+ String digestValXQuery = declareNS +\r
+ "$this/ds:Signature/ds:SignedInfo/ds:Reference";\r
+ for (ReferenceType rt : (ReferenceType[])sigDoc.selectPath(digestValXQuery)) {\r
+ assertNotNull(rt.getDigestValue());\r
+ assertEquals(HashAlgorithm.sha1.xmlSignUri, rt.getDigestMethod().getAlgorithm());\r
+ }\r
+\r
+ String certDigestXQuery = declareNS +\r
+ "$this//xades:SigningCertificate/xades:Cert/xades:CertDigest";\r
XmlObject xoList[] = sigDoc.selectPath(certDigestXQuery);\r
assertEquals(xoList.length, 1);\r
DigestAlgAndValueType certDigest = (DigestAlgAndValueType)xoList[0];\r
assertNotNull(certDigest.getDigestValue());\r
\r
- // Sign the received XML signature digest value.\r
- byte[] signatureValue = si.signDigest(digestInfo.digestValue);\r
-\r
- // Operate: postSign\r
- si.postSign(document, signatureValue);\r
- \r
- DOMValidateContext domValidateContext = new DOMValidateContext(\r
- KeySelector.singletonKeySelector(keyPair.getPublic()),\r
- document);\r
- XMLSignatureFactory xmlSignatureFactory = SignatureInfo.getSignatureFactory();\r
- XMLSignature xmlSignature = xmlSignatureFactory\r
- .unmarshalXMLSignature(domValidateContext);\r
- boolean validity = xmlSignature.validate(domValidateContext);\r
- assertTrue(validity);\r
-\r
- sigDoc = SignatureDocument.Factory.parse(document);\r
- xoList = sigDoc.selectPath(certDigestXQuery);\r
- assertEquals(xoList.length, 1);\r
- certDigest = (DigestAlgAndValueType)xoList[0];\r
- assertNotNull(certDigest.getDigestValue());\r
- \r
- String qualPropXQuery =\r
- "declare namespace xades='http://uri.etsi.org/01903/v1.3.2#'; "\r
- + "declare namespace ds='http://www.w3.org/2000/09/xmldsig#'; "\r
- + "$this/ds:Signature/ds:Object/xades:QualifyingProperties";\r
+ String qualPropXQuery = declareNS +\r
+ "$this/ds:Signature/ds:Object/xades:QualifyingProperties";\r
xoList = sigDoc.selectPath(qualPropXQuery);\r
assertEquals(xoList.length, 1);\r
QualifyingPropertiesType qualProp = (QualifyingPropertiesType)xoList[0];\r
boolean qualPropXsdOk = qualProp.validate();\r
assertTrue(qualPropXsdOk);\r
- \r
+\r
pkg.close();\r
}\r
\r
\r
// verify: signature\r
si.getSignatureConfig().setOpcPackage(pkgCopy);\r
- List<X509Certificate> signers = si.getSigners();\r
- assertEquals(signerCount, signers.size());\r
+ List<X509Certificate> result = new ArrayList<X509Certificate>();\r
+ for (SignaturePart sp : si.getSignatureParts()) {\r
+ if (sp.validate()) {\r
+ result.add(sp.getSigner());\r
+ }\r
+ }\r
+ assertEquals(signerCount, result.size());\r
\r
return pkgCopy;\r
}\r