--<?xml version="1.0" encoding="UTF-8"?>
--<classpath>
-- <classpathentry kind="src" path="src/java"/>
-- <classpathentry kind="src" path="src/testcases"/>
-- <classpathentry kind="src" path="src/resources/main"/>
-- <classpathentry kind="src" path="src/ooxml/java"/>
-- <classpathentry kind="src" path="src/ooxml/testcases"/>
-- <classpathentry kind="src" path="src/resources/ooxml"/>
-- <classpathentry kind="src" path="src/scratchpad/src"/>
-- <classpathentry kind="src" path="src/scratchpad/testcases"/>
-- <classpathentry kind="src" path="src/resources/scratchpad"/>
-- <classpathentry kind="src" path="src/contrib/poi-ruby/java"/>
-- <classpathentry kind="src" path="src/examples/src"/>
-- <classpathentry kind="src" path="src/excelant/java"/>
-- <classpathentry kind="src" path="src/excelant/testcases"/>
-- <classpathentry kind="src" path="src/excelant/resources"/>
-- <classpathentry kind="lib" path="lib/ant-1.9.4.jar"/>
-- <classpathentry kind="lib" path="lib/ant-launcher-1.9.4.jar"/>
-- <classpathentry kind="lib" path="lib/commons-codec-1.9.jar"/>
-- <classpathentry kind="lib" path="lib/commons-logging-1.1.3.jar"/>
-- <classpathentry kind="lib" path="lib/log4j-1.2.17.jar"/>
- <classpathentry kind="lib" path="ooxml-lib/dom4j-1.6.1.jar"/>
-- <classpathentry kind="lib" path="ooxml-lib/xmlbeans-2.6.0.jar"/>
-- <classpathentry kind="lib" path="lib/hamcrest-core-1.3.jar"/>
-- <classpathentry kind="lib" path="lib/junit-4.11.jar"/>
-- <classpathentry kind="lib" path="ooxml-lib/ooxml-schemas-1.1.jar" sourcepath="ooxml-lib/ooxml-schemas-src-1.1.jar"/>
- <classpathentry kind="lib" path="ooxml-lib/ooxml-encryption-1.2.jar" sourcepath="ooxml-lib/ooxml-encryption-src-1.2.jar"/>
- <classpathentry kind="lib" path="ooxml-lib/ooxml-encryption-1.1.jar" sourcepath="ooxml-lib/ooxml-encryption-src-1.1.jar"/>
-- <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-- <classpathentry kind="output" path="build/eclipse"/>
--</classpath>
++<?xml version="1.0" encoding="UTF-8"?>\r
++<classpath>\r
++ <classpathentry kind="src" path="src/java"/>\r
++ <classpathentry kind="src" path="src/testcases"/>\r
++ <classpathentry kind="src" path="src/resources/main"/>\r
++ <classpathentry kind="src" path="src/ooxml/java"/>\r
++ <classpathentry kind="src" path="src/ooxml/testcases"/>\r
++ <classpathentry kind="src" path="src/resources/ooxml"/>\r
++ <classpathentry kind="src" path="src/scratchpad/src"/>\r
++ <classpathentry kind="src" path="src/scratchpad/testcases"/>\r
++ <classpathentry kind="src" path="src/resources/scratchpad"/>\r
++ <classpathentry kind="src" path="src/contrib/poi-ruby/java"/>\r
++ <classpathentry kind="src" path="src/examples/src"/>\r
++ <classpathentry kind="src" path="src/excelant/java"/>\r
++ <classpathentry kind="src" path="src/excelant/testcases"/>\r
++ <classpathentry kind="src" path="src/excelant/resources"/>\r
++ <classpathentry kind="lib" path="lib/ant-1.9.4.jar"/>\r
++ <classpathentry kind="lib" path="lib/ant-launcher-1.9.4.jar"/>\r
++ <classpathentry kind="lib" path="lib/commons-codec-1.9.jar"/>\r
++ <classpathentry kind="lib" path="lib/commons-logging-1.1.3.jar"/>\r
++ <classpathentry kind="lib" path="lib/log4j-1.2.17.jar"/>\r
++ <classpathentry kind="lib" path="ooxml-lib/xmlbeans-2.6.0.jar"/>\r
++ <classpathentry kind="lib" path="lib/hamcrest-core-1.3.jar"/>\r
++ <classpathentry kind="lib" path="lib/junit-4.11.jar"/>\r
++ <classpathentry kind="lib" path="ooxml-lib/ooxml-schemas-1.1.jar" sourcepath="ooxml-lib/ooxml-schemas-src-1.1.jar"/>\r
++ <classpathentry kind="lib" path="ooxml-lib/ooxml-encryption-1.2.jar" sourcepath="ooxml-lib/ooxml-encryption-src-1.2.jar"/>\r
++ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>\r
++ <classpathentry kind="lib" path="lib/mockito-core-1.9.5.jar" sourcepath="E:/tmp/mavenRepo/org/mockito/mockito-core/1.9.5/mockito-core-1.9.5-sources.jar"/>\r
++ <classpathentry kind="output" path="build/eclipse"/>\r
++</classpath>\r
--- /dev/null
--- /dev/null
++eclipse.preferences.version=1\r
++extraProperties=\r
++lastAnalysisDate=1394235544000\r
++projectKey=org.apache.poi\:poi-main\r
++serverUrl=http\://nemo.sonarqube.org\r
++version=2\r
<exclude name="poi-*${version.id}-sources-*.jar"/>
</fileset>
<auxClasspath path="ooxml-lib/ooxml-schemas-1.1.jar" />
- <auxClasspath path="ooxml-lib/ooxml-encryption-1.1.jar" />
+ <auxClasspath path="ooxml-lib/ooxml-encryption-1.2.jar" />
<auxClasspath path="ooxml-lib/xmlbeans-2.6.0.jar" />
- <auxClasspath path="ooxml-lib/dom4j-1.6.1.jar" />
<auxClasspath path="lib/commons-codec-1.9.jar" />
<auxClasspath path="lib/commons-logging-1.1.3.jar" />
<auxClasspath path="lib/junit-4.11.jar" />
--- /dev/null
+package org.apache.poi.poifs.crypt.dsig;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+import java.math.BigInteger;\r
+import java.security.PrivateKey;\r
+import java.security.PublicKey;\r
+import java.security.cert.Certificate;\r
+import java.security.cert.X509CRL;\r
+import java.security.cert.X509Certificate;\r
+import java.util.Collection;\r
+import java.util.Date;\r
+\r
+import javax.security.auth.x500.X500Principal;\r
+import javax.xml.crypto.MarshalException;\r
+import javax.xml.crypto.XMLCryptoContext;\r
+import javax.xml.crypto.dom.DOMCryptoContext;\r
+import javax.xml.crypto.dsig.XMLSignContext;\r
+import javax.xml.crypto.dsig.XMLSignatureException;\r
+\r
+import org.apache.poi.poifs.crypt.dsig.HorribleProxy.ProxyIf;\r
+import org.w3c.dom.Node;\r
+\r
+public interface HorribleProxies {\r
+ public static final String xmlSecBase = "org.jcp.xml.dsig.internal.dom";\r
+ // public static final String xmlSecBase = "org.apache.jcp.xml.dsig.internal.dom";\r
+ \r
+ public interface ASN1InputStreamIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.ASN1InputStream";\r
+ \r
+ ASN1OctetStringIf readObject$ASNString() throws IOException;\r
+ DEROctetStringIf readObject$DERString() throws IOException;\r
+ DERIntegerIf readObject$Integer() throws IOException;\r
+ ASN1SequenceIf readObject$Sequence() throws IOException;\r
+ Object readObject$Object() throws IOException;\r
+ }\r
+\r
+ public interface ASN1ObjectIdentifierIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.ASN1ObjectIdentifier";\r
+ }\r
+ \r
+ public interface ASN1OctetStringIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.ASN1OctetString";\r
+ byte[] getOctets();\r
+ }\r
+ \r
+ public interface ASN1SequenceIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.ASN1Sequence";\r
+ }\r
+ \r
+ public interface AuthorityInformationAccessIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.AuthorityInformationAccess";\r
+ }\r
+ \r
+ public interface AuthorityKeyIdentifierIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.AuthorityKeyIdentifier";\r
+ byte[] getKeyIdentifier();\r
+ }\r
+ \r
+ public interface BasicConstraintsIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.BasicConstraints";\r
+ }\r
+ \r
+ public interface BasicOCSPRespIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.cert.ocsp.BasicOCSPResp";\r
+ Date getProducedAt();\r
+ RespIDIf getResponderId();\r
+ }\r
+ \r
+ public interface BcDigestCalculatorProviderIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.operator.bc.BcDigestCalculatorProvider";\r
+ }\r
+\r
+ public interface BcRSASignerInfoVerifierBuilderIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.cms.bc.BcRSASignerInfoVerifierBuilder";\r
+ SignerInformationVerifierIf build(X509CertificateHolderIf holder); \r
+ }\r
+ \r
+ public interface CanonicalizerIf extends ProxyIf {\r
+ String delegateClass = "com.sun.org.apache.xml.internal.security.c14n.Canonicalizer";\r
+ byte[] canonicalizeSubtree(Node node) throws Exception;\r
+ }\r
+ \r
+ public interface CRLNumberIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.CRLNumber";\r
+ }\r
+ \r
+ public interface DefaultDigestAlgorithmIdentifierFinderIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder";\r
+ }\r
+ \r
+ public interface DistributionPointNameIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.DistributionPointName";\r
+ }\r
+ \r
+ public interface DistributionPointIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.DistributionPoint";\r
+ }\r
+ \r
+ public interface DERIA5StringIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.DERIA5String";\r
+ }\r
+ \r
+ public interface DERIntegerIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.DERInteger";\r
+ BigInteger getPositiveValue();\r
+ }\r
+ \r
+ public interface DEROctetStringIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.DEROctetString";\r
+ byte[] getOctets();\r
+ }\r
+ \r
+ public interface DERTaggedObjectIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.DERTaggedObject";\r
+ int getTagNo();\r
+ ASN1OctetStringIf getObject$String();\r
+ Object getObject$Object();\r
+ }\r
+\r
+ public interface DERSequenceIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.DERSequence";\r
+ }\r
+ \r
+ public interface DOMKeyInfoIf extends ProxyIf {\r
+ String delegateClass = xmlSecBase+".DOMKeyInfo";\r
+ void marshal(Node parent, Node nextSibling, String dsPrefix, DOMCryptoContext context) throws MarshalException;\r
+ }\r
+ \r
+ public interface DOMReferenceIf extends ProxyIf {\r
+ String delegateClass = xmlSecBase+".DOMReference";\r
+ void digest(XMLSignContext paramXMLSignContext) throws XMLSignatureException;\r
+ byte[] getDigestValue();\r
+ }\r
+ \r
+ public interface DOMSignedInfoIf extends ProxyIf {\r
+ String delegateClass = xmlSecBase+".DOMSignedInfo";\r
+ void canonicalize(XMLCryptoContext paramXMLCryptoContext, ByteArrayOutputStream paramByteArrayOutputStream);\r
+ }\r
+ \r
+ public interface XMLSignatureIf extends ProxyIf {\r
+ String delegateClass = "com.sun.org.apache.xml.internal.security.signature.XMLSignature";\r
+ String ALGO_ID_SIGNATURE_RSA_SHA1();\r
+ String ALGO_ID_SIGNATURE_RSA_SHA256();\r
+ String ALGO_ID_SIGNATURE_RSA_SHA384();\r
+ String ALGO_ID_SIGNATURE_RSA_SHA512();\r
+ String ALGO_ID_MAC_HMAC_RIPEMD160();\r
+ }\r
+ \r
+ public interface DOMXMLSignatureIf extends ProxyIf {\r
+ String delegateClass = xmlSecBase+".DOMXMLSignature";\r
+ void marshal(Node node, String prefix, DOMCryptoContext context) throws MarshalException;\r
+ }\r
+ \r
+ public interface GeneralNameIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.GeneralName";\r
+ \r
+ int uniformResourceIdentifier();\r
+ \r
+ }\r
+ \r
+ public interface GeneralNamesIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.GeneralNames";\r
+ }\r
+ \r
+ public interface InitIf extends ProxyIf {\r
+ String delegateClass = "com.sun.org.apache.xml.internal.security.Init";\r
+ void init();\r
+ }\r
+\r
+ public interface KeyUsageIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.KeyUsage";\r
+ int digitalSignature();\r
+ }\r
+ \r
+ public interface OCSPRespIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.cert.ocsp.OCSPResp";\r
+ BasicOCSPRespIf getResponseObject();\r
++ byte[] getEncoded() throws IOException;\r
+ }\r
+ \r
+ public interface PKIFailureInfoIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.cmp.PKIFailureInfo";\r
+ int intValue();\r
+ }\r
+\r
+ public interface RespIDIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.cert.ocsp.RespID";\r
+ ResponderIDIf toASN1Object();\r
+ }\r
+ \r
+ public interface ResponderIDIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.ocsp.ResponderID";\r
+ DERTaggedObjectIf toASN1Object();\r
+ }\r
+\r
+ public interface SignerIdIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.cms.SignerId";\r
+ BigInteger getSerialNumber();\r
+ X500Principal getIssuer();\r
+ }\r
+\r
+ public interface SignerInformationVerifierIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.cms.SignerInformationVerifier";\r
+ }\r
+ \r
+ public interface StoreIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.util.Store";\r
+ Collection<Certificate> getMatches(Object selector) throws Exception;\r
+ }\r
+ \r
+ public interface SubjectKeyIdentifierIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.SubjectKeyIdentifier";\r
+ byte[] getKeyIdentifier();\r
+ }\r
+ \r
+ public interface SubjectPublicKeyInfoIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.SubjectPublicKeyInfo";\r
+ }\r
+ \r
+ public interface TimeStampRequestGeneratorIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.tsp.TimeStampRequestGenerator";\r
+ void setCertReq(boolean certReq);\r
+ void setReqPolicy(String reqPolicy);\r
+ TimeStampRequestIf generate(String igestAlgorithmOID, byte[] digest, BigInteger nonce);\r
+ }\r
+ \r
+ public interface TimeStampRequestIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.tsp.TimeStampRequest";\r
+ byte[] getEncoded() throws IOException;\r
+ }\r
+ \r
+ public interface TimeStampResponseIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.tsp.TimeStampResponse";\r
+ void validate(TimeStampRequestIf request) throws Exception;\r
+ int getStatus();\r
+ String getStatusString();\r
+ PKIFailureInfoIf getFailInfo();\r
+ TimeStampTokenIf getTimeStampToken();\r
+ }\r
+ \r
+ public interface TimeStampTokenIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.tsp.TimeStampToken";\r
+ SignerIdIf getSID();\r
+ StoreIf getCertificates();\r
+ StoreIf getCRLs();\r
+ TimeStampTokenInfoIf getTimeStampInfo();\r
+ byte[] getEncoded() throws IOException;\r
+ void validate(SignerInformationVerifierIf verifier) throws Exception;\r
+ }\r
+ \r
+ public interface TimeStampTokenInfoIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.tsp.TimeStampTokenInfo";\r
+ Date getGenTime();\r
+ }\r
+ \r
+ public interface X509CertificateHolderIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.cert.X509CertificateHolder";\r
+ }\r
+\r
+ public interface X509NameIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.X509Name";\r
+ String toString$delegate();\r
+ }\r
+\r
+ public interface X509PrincipalIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.jce.X509Principal";\r
+ String getName();\r
+ }\r
+ \r
+ public interface X509V3CertificateGeneratorIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.x509.X509V3CertificateGenerator";\r
+ \r
+ void reset();\r
+ void setPublicKey(PublicKey key);\r
+ void setSignatureAlgorithm(String signatureAlgorithm);\r
+ void setNotBefore(Date date);\r
+ void setNotAfter(Date date);\r
+ void setIssuerDN(X509PrincipalIf issuerDN);\r
+ void setSubjectDN(X509PrincipalIf issuerDN);\r
+ void setSerialNumber(BigInteger serialNumber);\r
+ \r
+ void addExtension(ASN1ObjectIdentifierIf oid, boolean critical, SubjectKeyIdentifierIf value);\r
+ void addExtension(ASN1ObjectIdentifierIf oid, boolean critical, AuthorityKeyIdentifierIf value);\r
+ void addExtension(ASN1ObjectIdentifierIf oid, boolean critical, BasicConstraintsIf value);\r
+ void addExtension(ASN1ObjectIdentifierIf oid, boolean critical, DERSequenceIf value);\r
+ void addExtension(ASN1ObjectIdentifierIf oid, boolean critical, AuthorityInformationAccessIf value);\r
+ void addExtension(ASN1ObjectIdentifierIf oid, boolean critical, KeyUsageIf value);\r
+ \r
+ X509Certificate generate(PrivateKey issuerPrivateKey);\r
+ }\r
+\r
+ public interface OCSPReqIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.cert.ocsp.OCSPReq";\r
+\r
+ ReqIf[] getRequestList();\r
+ }\r
+ \r
+ public interface OCSPReqGeneratorIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.ocsp.OCSPReqGenerator";\r
+ \r
+ void addRequest(CertificateIDIf certId);\r
+ OCSPReqIf generate();\r
+ }\r
+\r
+ public interface BasicOCSPRespGeneratorIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.ocsp.BasicOCSPRespGenerator";\r
+\r
+ void addResponse(CertificateIDIf certificateID, CertificateStatusIf certificateStatus);\r
+ BasicOCSPRespIf generate(String signatureAlgorithm, PrivateKey ocspResponderPrivateKey,\r
+ X509Certificate chain[], Date date, String provider);\r
+ }\r
+ \r
+ public interface CertificateIDIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.cert.ocsp.CertificateID";\r
+ \r
+ String HASH_SHA1();\r
+ }\r
+ \r
+ public interface X509ExtensionsIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.X509Extensions";\r
+ \r
+ ASN1ObjectIdentifierIf AuthorityKeyIdentifier();\r
+ ASN1ObjectIdentifierIf SubjectKeyIdentifier();\r
+ ASN1ObjectIdentifierIf BasicConstraints();\r
+ ASN1ObjectIdentifierIf CRLDistributionPoints();\r
+ ASN1ObjectIdentifierIf AuthorityInfoAccess();\r
+ ASN1ObjectIdentifierIf KeyUsage();\r
+ ASN1ObjectIdentifierIf CRLNumber();\r
+ }\r
+ \r
+ public interface X509ObjectIdentifiersIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.X509ObjectIdentifiers";\r
+ \r
+ ASN1ObjectIdentifierIf ocspAccessMethod();\r
+ }\r
+ \r
+ public interface X509V2CRLGeneratorIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.X509V2CRLGenerator";\r
+ \r
+ void setIssuerDN(X500Principal issuerDN);\r
+ void setThisUpdate(Date date);\r
+ void setNextUpdate(Date date);\r
+ void setSignatureAlgorithm(String algorithm);\r
+ \r
+ void addExtension(ASN1ObjectIdentifierIf oid, boolean critical, CRLNumberIf value);\r
+ X509CRL generate(PrivateKey privateKey);\r
+ }\r
+ \r
+ public interface ReqIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.cert.ocsp.Req";\r
+ \r
+ CertificateIDIf getCertID();\r
+ }\r
+ \r
+ public interface CertificateStatusIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.cert.ocsp.CertificateStatus";\r
+ \r
+ CertificateStatusIf GOOD();\r
+ }\r
+ \r
+ public interface RevokedStatusIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.cert.ocsp.RevokedStatus";\r
+ }\r
+ \r
+ public interface CRLReasonIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.asn1.x509.CRLReason";\r
+ int unspecified();\r
+ }\r
+\r
+ public interface OCSPRespGeneratorIf extends ProxyIf {\r
+ String delegateClass = "org.bouncycastle.ocsp.OCSPRespGenerator";\r
+ int SUCCESSFUL();\r
+ OCSPRespIf generate(int status, BasicOCSPRespIf basicOCSPResp);\r
+ }\r
+}\r
--- /dev/null
- Document doc = SAXHelper.readSAXDocumentW3C(signaturePart.getInputStream());\r
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+\r
+/* ====================================================================\r
+ This product contains an ASLv2 licensed version of the OOXML signer\r
+ package from the eID Applet project\r
+ http://code.google.com/p/eid-applet/source/browse/trunk/README.txt \r
+ Copyright (C) 2008-2014 FedICT.\r
+ ================================================================= */ \r
+\r
+package org.apache.poi.poifs.crypt.dsig;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.IOException;\r
+import java.security.Key;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.Provider;\r
+import java.security.Security;\r
+import java.security.cert.X509Certificate;\r
+import java.util.Collections;\r
+import java.util.Date;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+\r
+import javax.crypto.Cipher;\r
+import javax.xml.crypto.dsig.XMLSignature;\r
+import javax.xml.crypto.dsig.XMLSignatureFactory;\r
+import javax.xml.crypto.dsig.dom.DOMValidateContext;\r
+import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;\r
+\r
+import org.apache.poi.EncryptedDocumentException;\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.opc.OPCPackage;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.PackageRelationship;\r
+import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;\r
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;\r
+import org.apache.poi.poifs.crypt.ChainingMode;\r
+import org.apache.poi.poifs.crypt.CipherAlgorithm;\r
+import org.apache.poi.poifs.crypt.CryptoFunctions;\r
+import org.apache.poi.poifs.crypt.HashAlgorithm;\r
+import org.apache.poi.poifs.crypt.dsig.HorribleProxies.InitIf;\r
+import org.apache.poi.poifs.crypt.dsig.services.RelationshipTransformService;\r
+import org.apache.poi.poifs.crypt.dsig.services.XmlSignatureService;\r
+import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;\r
+import org.apache.poi.util.POILogFactory;\r
+import org.apache.poi.util.POILogger;\r
+import org.apache.poi.util.SAXHelper;\r
+import org.apache.xmlbeans.XmlCursor;\r
+import org.apache.xmlbeans.XmlObject;\r
+import org.w3c.dom.Document;\r
+import org.w3c.dom.Element;\r
+\r
+public class SignatureInfo {\r
+ \r
+ public static final byte[] SHA1_DIGEST_INFO_PREFIX = new byte[]\r
+ { 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14 };\r
+\r
+ public static final byte[] SHA224_DIGEST_INFO_PREFIX = new byte[] \r
+ { 0x30, 0x2b, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86\r
+ , 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x04, 0x1c };\r
+\r
+ public static final byte[] SHA256_DIGEST_INFO_PREFIX = new byte[]\r
+ { 0x30, 0x2f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86\r
+ , 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x04, 0x20 };\r
+\r
+ public static final byte[] SHA384_DIGEST_INFO_PREFIX = new byte[]\r
+ { 0x30, 0x3f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86\r
+ , 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x04, 0x30 };\r
+\r
+ public static final byte[] SHA512_DIGEST_INFO_PREFIX = new byte[]\r
+ { 0x30, 0x4f, 0x30, 0x0b, 0x06, 0x09, 0x60, (byte) 0x86\r
+ , 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x04, 0x40 };\r
+\r
+ public static final byte[] RIPEMD128_DIGEST_INFO_PREFIX = new byte[]\r
+ { 0x30, 0x1b, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x02, 0x04, 0x10 };\r
+\r
+ public static final byte[] RIPEMD160_DIGEST_INFO_PREFIX = new byte[]\r
+ { 0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b, 0x24, 0x03, 0x02, 0x01, 0x04, 0x14 };\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
+ \r
+ private static final POILogger LOG = POILogFactory.getLogger(SignatureInfo.class);\r
+ private static boolean isInitialized = false;\r
+ \r
+ private final OPCPackage pkg;\r
+ \r
+ public SignatureInfo(OPCPackage pkg) {\r
+ this.pkg = pkg;\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 LinkedList<X509Certificate>();\r
+ return getSignersAndValidate(signers, true);\r
+ }\r
+\r
+ public void confirmSignature(Key key, X509Certificate x509)\r
+ throws NoSuchAlgorithmException, IOException {\r
+ confirmSignature(key, x509, HashAlgorithm.sha1);\r
+ }\r
+ \r
+ public void confirmSignature(Key key, X509Certificate x509, HashAlgorithm hashAlgo)\r
+ throws NoSuchAlgorithmException, IOException {\r
+ XmlSignatureService signatureService = createSignatureService(hashAlgo, pkg);\r
+ \r
+ // operate\r
+ List<X509Certificate> x509Chain = Collections.singletonList(x509);\r
+ DigestInfo digestInfo = signatureService.preSign(null, x509Chain, null, null, null);\r
+\r
+ // setup: key material, signature value\r
+\r
+ Cipher cipher = CryptoFunctions.getCipher(key, CipherAlgorithm.rsa\r
+ , ChainingMode.ecb, null, Cipher.ENCRYPT_MODE, "PKCS1Padding");\r
+ \r
+ byte[] signatureValue;\r
+ try {\r
+ ByteArrayOutputStream digestInfoValueBuf = new ByteArrayOutputStream();\r
+ digestInfoValueBuf.write(getHashMagic(hashAlgo));\r
+ digestInfoValueBuf.write(digestInfo.digestValue);\r
+ byte[] digestInfoValue = digestInfoValueBuf.toByteArray();\r
+ signatureValue = cipher.doFinal(digestInfoValue);\r
+ } catch (Exception e) {\r
+ throw new EncryptedDocumentException(e);\r
+ }\r
+\r
+ \r
+ // operate: postSign\r
+ signatureService.postSign(signatureValue, Collections.singletonList(x509));\r
+ }\r
+\r
+ public XmlSignatureService createSignatureService(HashAlgorithm hashAlgo, OPCPackage pkg) {\r
+ XmlSignatureService signatureService = new XmlSignatureService(hashAlgo, pkg);\r
+ signatureService.initFacets(new Date());\r
+ return signatureService;\r
+ }\r
+ \r
+ public List<X509Certificate> getSigners() {\r
+ initXmlProvider();\r
+ List<X509Certificate> signers = new LinkedList<X509Certificate>();\r
+ getSignersAndValidate(signers, false);\r
+ return signers;\r
+ }\r
+ \r
+ protected boolean getSignersAndValidate(List<X509Certificate> signers, boolean onlyFirst) {\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 = SAXHelper.readSAXDocument(signaturePart.getInputStream());\r
+ // dummy call to createSignatureService to tweak document afterwards\r
+ createSignatureService(HashAlgorithm.sha1, pkg).registerIds(doc);\r
+ \r
+ DOMValidateContext domValidateContext = new DOMValidateContext(keySelector, doc);\r
+ domValidateContext.setProperty("org.jcp.xml.dsig.validateManifests", Boolean.TRUE);\r
+ OOXMLURIDereferencer dereferencer = new OOXMLURIDereferencer(pkg);\r
+ domValidateContext.setURIDereferencer(dereferencer);\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 LinkedList<PackagePart>();\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
+ }\r
+ \r
+ if (onlyFirst && !packageParts.isEmpty()) break;\r
+ }\r
+\r
+ return packageParts;\r
+ }\r
+ \r
+ public static XMLSignatureFactory getSignatureFactory() {\r
+ Provider p = Security.getProvider("XMLDSig");\r
+ assert(p != null);\r
+ return XMLSignatureFactory.getInstance("DOM", p);\r
+ }\r
+\r
+ public static KeyInfoFactory getKeyInfoFactory() {\r
+ Provider p = Security.getProvider("XMLDSig");\r
+ assert(p != null);\r
+ return KeyInfoFactory.getInstance("DOM", p);\r
+ }\r
+\r
+ public static void insertXChild(XmlObject root, XmlObject child) {\r
+ XmlCursor rootCursor = root.newCursor();\r
+ insertXChild(rootCursor, child);\r
+ rootCursor.dispose();\r
+ }\r
+\r
+ public static void insertXChild(XmlCursor rootCursor, XmlObject child) {\r
+ rootCursor.toEndToken();\r
+ XmlCursor childCursor = child.newCursor();\r
+ childCursor.toNextToken();\r
+ childCursor.moveXml(rootCursor);\r
+ childCursor.dispose();\r
+ }\r
+\r
+ public static void setPrefix(XmlObject xobj, String ns, String prefix) {\r
+ for (XmlCursor cur = xobj.newCursor(); cur.hasNextToken(); cur.toNextToken()) {\r
+ if (cur.isStart()) {\r
+ Element el = (Element)cur.getDomNode();\r
+ if (ns.equals(el.getNamespaceURI())) el.setPrefix(prefix);\r
+ }\r
+ }\r
+ }\r
+ \r
+ protected static byte[] getHashMagic(HashAlgorithm hashAlgo) {\r
+ switch (hashAlgo) {\r
+ case sha1: return SHA1_DIGEST_INFO_PREFIX;\r
+ // sha224: return SHA224_DIGEST_INFO_PREFIX;\r
+ case sha256: return SHA256_DIGEST_INFO_PREFIX;\r
+ case sha384: return SHA384_DIGEST_INFO_PREFIX;\r
+ case sha512: return SHA512_DIGEST_INFO_PREFIX;\r
+ case ripemd128: return RIPEMD128_DIGEST_INFO_PREFIX;\r
+ case ripemd160: return RIPEMD160_DIGEST_INFO_PREFIX;\r
+ // case ripemd256: return RIPEMD256_DIGEST_INFO_PREFIX;\r
+ default: throw new EncryptedDocumentException("Hash algorithm "+hashAlgo+" not supported for signing.");\r
+ }\r
+ }\r
+ \r
+ public static synchronized void initXmlProvider() {\r
+ if (isInitialized) return;\r
+ isInitialized = true;\r
+ \r
+ try {\r
+ InitIf init = HorribleProxy.newProxy(InitIf.class);\r
+ init.init();\r
+\r
+ RelationshipTransformService.registerDsigProvider();\r
+ \r
+ Provider bcProv = Security.getProvider("BC");\r
+ if (bcProv == null) {\r
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();\r
+ Class<?> c = cl.loadClass("org.bouncycastle.jce.provider.BouncyCastleProvider");\r
+ bcProv = (Provider)c.newInstance();\r
+ Security.addProvider(bcProv);\r
+ }\r
+ } catch (Exception e) {\r
+ throw new RuntimeException("Xml & BouncyCastle-Provider initialization failed", e);\r
+ }\r
+ }\r
+}\r
--- /dev/null
--- /dev/null
++package org.apache.poi.poifs.crypt.dsig.facets;\r
++\r
++import java.security.InvalidAlgorithmParameterException;\r
++import java.security.NoSuchAlgorithmException;\r
++import java.security.cert.X509Certificate;\r
++import java.util.LinkedList;\r
++import java.util.List;\r
++import java.util.Map;\r
++\r
++import javax.xml.crypto.dsig.CanonicalizationMethod;\r
++import javax.xml.crypto.dsig.DigestMethod;\r
++import javax.xml.crypto.dsig.Reference;\r
++import javax.xml.crypto.dsig.Transform;\r
++import javax.xml.crypto.dsig.XMLObject;\r
++import javax.xml.crypto.dsig.XMLSignatureFactory;\r
++import javax.xml.crypto.dsig.spec.TransformParameterSpec;\r
++\r
++import org.apache.poi.poifs.crypt.HashAlgorithm;\r
++import org.w3.x2000.x09.xmldsig.SignatureType;\r
++\r
++/**\r
++ * Signature Facet implementation to create enveloped signatures.\r
++ * \r
++ * @author Frank Cornelis\r
++ * \r
++ */\r
++public class EnvelopedSignatureFacet implements SignatureFacet {\r
++\r
++ private final HashAlgorithm hashAlgo;\r
++\r
++ /**\r
++ * Default constructor. Digest algorithm will be SHA-1.\r
++ */\r
++ public EnvelopedSignatureFacet() {\r
++ this(HashAlgorithm.sha1);\r
++ }\r
++\r
++ /**\r
++ * Main constructor.\r
++ * \r
++ * @param hashAlgo\r
++ * the digest algorithm to be used within the ds:Reference\r
++ * element. Possible values: "SHA-1", "SHA-256, or "SHA-512".\r
++ */\r
++ public EnvelopedSignatureFacet(HashAlgorithm hashAlgo) {\r
++ this.hashAlgo = hashAlgo;\r
++ }\r
++\r
++ @Override\r
++ public void postSign(SignatureType signatureElement\r
++ , List<X509Certificate> signingCertificateChain) {\r
++ // empty\r
++ }\r
++\r
++ @Override\r
++ public void preSign(XMLSignatureFactory signatureFactory,\r
++ String signatureId,\r
++ List<X509Certificate> signingCertificateChain,\r
++ List<Reference> references, List<XMLObject> objects)\r
++ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {\r
++ DigestMethod digestMethod = signatureFactory.newDigestMethod(\r
++ this.hashAlgo.xmlSignUri, null);\r
++\r
++ List<Transform> transforms = new LinkedList<Transform>();\r
++ Transform envelopedTransform = signatureFactory\r
++ .newTransform(CanonicalizationMethod.ENVELOPED,\r
++ (TransformParameterSpec) null);\r
++ transforms.add(envelopedTransform);\r
++ Transform exclusiveTransform = signatureFactory\r
++ .newTransform(CanonicalizationMethod.EXCLUSIVE,\r
++ (TransformParameterSpec) null);\r
++ transforms.add(exclusiveTransform);\r
++\r
++ Reference reference = signatureFactory.newReference("", digestMethod,\r
++ transforms, null, null);\r
++\r
++ references.add(reference);\r
++ }\r
++\r
++ @Override\r
++ public Map<String,String> getNamespacePrefixMapping() {\r
++ return null;\r
++ }\r
++}\r
--- /dev/null
- protected void addSignatureFacet(SignatureFacet signatureFacet) {\r
- this.signatureFacets.add(signatureFacet);\r
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+\r
+/* ====================================================================\r
+ This product contains an ASLv2 licensed version of the OOXML signer\r
+ package from the eID Applet project\r
+ http://code.google.com/p/eid-applet/source/browse/trunk/README.txt \r
+ Copyright (C) 2008-2014 FedICT.\r
+ ================================================================= */ \r
+\r
+package org.apache.poi.poifs.crypt.dsig.services;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.File;\r
+import java.io.IOException;\r
+import java.io.OutputStream;\r
+import java.net.MalformedURLException;\r
+import java.security.InvalidAlgorithmParameterException;\r
+import java.security.Key;\r
+import java.security.MessageDigest;\r
+import java.security.NoSuchAlgorithmException;\r
+import java.security.NoSuchProviderException;\r
+import java.security.cert.X509Certificate;\r
+import java.util.Date;\r
+import java.util.HashMap;\r
+import java.util.LinkedList;\r
+import java.util.List;\r
+import java.util.Map;\r
+import java.util.UUID;\r
+\r
+import javax.xml.crypto.MarshalException;\r
+import javax.xml.crypto.URIDereferencer;\r
+import javax.xml.crypto.XMLStructure;\r
+import javax.xml.crypto.dom.DOMCryptoContext;\r
+import javax.xml.crypto.dsig.CanonicalizationMethod;\r
+import javax.xml.crypto.dsig.DigestMethod;\r
+import javax.xml.crypto.dsig.Manifest;\r
+import javax.xml.crypto.dsig.Reference;\r
+import javax.xml.crypto.dsig.SignatureMethod;\r
+import javax.xml.crypto.dsig.SignedInfo;\r
+import javax.xml.crypto.dsig.XMLObject;\r
+import javax.xml.crypto.dsig.XMLSignContext;\r
+import javax.xml.crypto.dsig.XMLSignatureFactory;\r
+import javax.xml.crypto.dsig.dom.DOMSignContext;\r
+import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;\r
+import javax.xml.parsers.DocumentBuilderFactory;\r
+import javax.xml.parsers.ParserConfigurationException;\r
+import javax.xml.transform.TransformerException;\r
+import javax.xml.transform.TransformerFactoryConfigurationError;\r
+\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.openxml4j.opc.OPCPackage;\r
+import org.apache.poi.openxml4j.opc.PackageNamespaces;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.PackagePartName;\r
+import org.apache.poi.openxml4j.opc.PackageRelationship;\r
+import org.apache.poi.openxml4j.opc.PackageRelationshipCollection;\r
+import org.apache.poi.openxml4j.opc.PackageRelationshipTypes;\r
+import org.apache.poi.openxml4j.opc.PackagingURIHelper;\r
+import org.apache.poi.openxml4j.opc.TargetMode;\r
+import org.apache.poi.poifs.crypt.CryptoFunctions;\r
+import org.apache.poi.poifs.crypt.HashAlgorithm;\r
+import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DOMReferenceIf;\r
+import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DOMSignedInfoIf;\r
+import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DOMXMLSignatureIf;\r
+import org.apache.poi.poifs.crypt.dsig.HorribleProxies.XMLSignatureIf;\r
+import org.apache.poi.poifs.crypt.dsig.HorribleProxy;\r
+import org.apache.poi.poifs.crypt.dsig.OOXMLURIDereferencer;\r
+import org.apache.poi.poifs.crypt.dsig.SignatureInfo;\r
+import org.apache.poi.poifs.crypt.dsig.facets.KeyInfoSignatureFacet;\r
+import org.apache.poi.poifs.crypt.dsig.facets.OOXMLSignatureFacet;\r
+import org.apache.poi.poifs.crypt.dsig.facets.Office2010SignatureFacet;\r
+import org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet;\r
+import org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet;\r
+import org.apache.poi.poifs.crypt.dsig.spi.AddressDTO;\r
+import org.apache.poi.poifs.crypt.dsig.spi.Constants;\r
+import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;\r
+import org.apache.poi.poifs.crypt.dsig.spi.IdentityDTO;\r
+import org.apache.poi.util.POILogFactory;\r
+import org.apache.poi.util.POILogger;\r
+import org.apache.xmlbeans.XmlException;\r
+import org.apache.xmlbeans.XmlOptions;\r
+import org.w3.x2000.x09.xmldsig.SignatureDocument;\r
+import org.w3.x2000.x09.xmldsig.SignatureType;\r
+import org.w3.x2000.x09.xmldsig.SignatureValueType;\r
+import org.w3c.dom.Document;\r
+import org.w3c.dom.Element;\r
+import org.w3c.dom.NodeList;\r
+import org.xml.sax.SAXException;\r
+\r
+\r
+/**\r
+ * Abstract base class for an XML Signature Service implementation.\r
+ */\r
+public class XmlSignatureService implements SignatureService {\r
+ private static final POILogger LOG = POILogFactory.getLogger(XmlSignatureService.class);\r
+\r
+ protected final List<SignatureFacet> signatureFacets;\r
+\r
+ private String signatureNamespacePrefix;\r
+ private String signatureId;\r
+ private final HashAlgorithm hashAlgo;\r
+ private final OPCPackage opcPackage;\r
+ private SignatureDocument sigDoc;\r
+ private XAdESSignatureFacet xadesSignatureFacet;\r
+ \r
+ /**\r
+ * Main constructor.\r
+ */\r
+ public XmlSignatureService(HashAlgorithm digestAlgo, OPCPackage opcPackage) {\r
+ this.signatureFacets = new LinkedList<SignatureFacet>();\r
+ this.signatureNamespacePrefix = null;\r
+ this.signatureId = null;\r
+ this.hashAlgo = digestAlgo;\r
+ this.opcPackage = opcPackage;\r
+ this.sigDoc = null;\r
+ }\r
+\r
+ public void initFacets(Date clock) {\r
+ if (clock == null) clock = new Date();\r
+ addSignatureFacet(new OOXMLSignatureFacet(this, clock, hashAlgo));\r
+ addSignatureFacet(new KeyInfoSignatureFacet(true, false, false));\r
+\r
+ this.xadesSignatureFacet = new XAdESSignatureFacet(clock, hashAlgo, null);\r
+ this.xadesSignatureFacet.setIdSignedProperties("idSignedProperties");\r
+ this.xadesSignatureFacet.setSignaturePolicyImplied(true);\r
+ /*\r
+ * Work-around for Office 2010.\r
+ */\r
+ this.xadesSignatureFacet.setIssuerNameNoReverseOrder(true);\r
+ setSignatureId("idPackageSignature");\r
+ addSignatureFacet(this.xadesSignatureFacet);\r
+ addSignatureFacet(new Office2010SignatureFacet());\r
+ }\r
+ \r
+ \r
+ /**\r
+ * Sets the signature Id attribute value used to create the XML signature. A\r
+ * <code>null</code> value will trigger an automatically generated signature\r
+ * Id.\r
+ * \r
+ * @param signatureId\r
+ */\r
+ protected void setSignatureId(String signatureId) {\r
+ this.signatureId = signatureId;\r
+ }\r
+\r
+ /**\r
+ * Sets the XML Signature namespace prefix to be used for signature\r
+ * creation. A <code>null</code> value will omit the prefixing.\r
+ * \r
+ * @param signatureNamespacePrefix\r
+ */\r
+ protected void setSignatureNamespacePrefix(String signatureNamespacePrefix) {\r
+ this.signatureNamespacePrefix = signatureNamespacePrefix;\r
+ }\r
+\r
+ /**\r
+ * Adds a signature facet to this XML signature service.\r
+ * \r
+ * @param signatureFacet\r
+ */\r
++ public void addSignatureFacet(SignatureFacet... signatureFacets) {\r
++ for (SignatureFacet sf : signatureFacets) {\r
++ this.signatureFacets.add(sf);\r
++ }\r
+ }\r
+\r
+ /**\r
+ * Gives back the signature digest algorithm. Allowed values are SHA-1,\r
+ * SHA-256, SHA-384, SHA-512, RIPEND160. The default algorithm is SHA-1.\r
+ * Override this method to select another signature digest algorithm.\r
+ * \r
+ * @return\r
+ */\r
+ protected HashAlgorithm getSignatureDigestAlgorithm() {\r
+ return null != this.hashAlgo ? this.hashAlgo : HashAlgorithm.sha1;\r
+ }\r
+\r
+ /**\r
+ * Override this method to change the URI dereferener used by the signing\r
+ * engine.\r
+ * \r
+ * @return\r
+ */\r
+ protected URIDereferencer getURIDereferencer() {\r
+ OPCPackage ooxmlDocument = getOfficeOpenXMLDocument();\r
+ return new OOXMLURIDereferencer(ooxmlDocument);\r
+ }\r
+\r
+ /**\r
+ * Gives back the human-readable description of what the citizen will be\r
+ * signing. The default value is "XML Document". Override this method to\r
+ * provide the citizen with another description.\r
+ * \r
+ * @return\r
+ */\r
+ protected String getSignatureDescription() {\r
+ return "Office OpenXML Document";\r
+ }\r
+\r
+ /**\r
+ * Gives back the URL of the OOXML to be signed.\r
+ * \r
+ * @return\r
+ */\r
+ public OPCPackage getOfficeOpenXMLDocument() {\r
+ return opcPackage;\r
+ }\r
+ \r
+\r
+ \r
+ /**\r
+ * Gives back the output stream to which to write the signed XML document.\r
+ * \r
+ * @return\r
+ */\r
+ // protected abstract OutputStream getSignedDocumentOutputStream();\r
+\r
+ public DigestInfo preSign(List<DigestInfo> digestInfos,\r
+ List<X509Certificate> signingCertificateChain,\r
+ IdentityDTO identity, AddressDTO address, byte[] photo)\r
+ throws NoSuchAlgorithmException {\r
+ SignatureInfo.initXmlProvider();\r
+ \r
+ LOG.log(POILogger.DEBUG, "preSign");\r
+ HashAlgorithm hashAlgo = getSignatureDigestAlgorithm();\r
+\r
+ byte[] digestValue;\r
+ try {\r
+ digestValue = getXmlSignatureDigestValue(hashAlgo, digestInfos, signingCertificateChain);\r
+ } catch (Exception e) {\r
+ throw new RuntimeException("XML signature error: " + e.getMessage(), e);\r
+ }\r
+\r
+ String description = getSignatureDescription();\r
+ return new DigestInfo(digestValue, hashAlgo, description);\r
+ }\r
+\r
+ public void postSign(byte[] signatureValue, List<X509Certificate> signingCertificateChain)\r
+ throws IOException {\r
+ LOG.log(POILogger.DEBUG, "postSign");\r
+ SignatureInfo.initXmlProvider();\r
+\r
+ /*\r
+ * Retrieve the intermediate XML signature document from the temporary \r
+ * data storage.\r
+ */\r
+ SignatureType sigType = sigDoc.getSignature();\r
+\r
+ /*\r
+ * Check ds:Signature node.\r
+ */\r
+ if (!signatureId.equals(sigType.getId())) {\r
+ throw new RuntimeException("ds:Signature not found for @Id: " + signatureId);\r
+ }\r
+\r
+ /*\r
+ * Insert signature value into the ds:SignatureValue element\r
+ */\r
+ SignatureValueType sigVal = sigType.getSignatureValue();\r
+ sigVal.setByteArrayValue(signatureValue);\r
+\r
+ /*\r
+ * Allow signature facets to inject their own stuff.\r
+ */\r
+ for (SignatureFacet signatureFacet : this.signatureFacets) {\r
+ signatureFacet.postSign(sigType, signingCertificateChain);\r
+ }\r
+\r
+ writeDocument();\r
+ }\r
+\r
+ @SuppressWarnings("unchecked")\r
+ private byte[] getXmlSignatureDigestValue(HashAlgorithm hashAlgo,\r
+ List<DigestInfo> digestInfos,\r
+ List<X509Certificate> signingCertificateChain)\r
+ throws ParserConfigurationException, NoSuchAlgorithmException,\r
+ InvalidAlgorithmParameterException, MarshalException,\r
+ javax.xml.crypto.dsig.XMLSignatureException,\r
+ TransformerFactoryConfigurationError, TransformerException,\r
+ IOException, SAXException, NoSuchProviderException, XmlException {\r
+ /*\r
+ * DOM Document construction.\r
+ */\r
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();\r
+ dbf.setNamespaceAware(true);\r
+ Document doc = dbf.newDocumentBuilder().newDocument();\r
+\r
+ /*\r
+ * Signature context construction.\r
+ */\r
+ Key key = new Key() {\r
+ private static final long serialVersionUID = 1L;\r
+\r
+ public String getAlgorithm() {\r
+ return null;\r
+ }\r
+\r
+ public byte[] getEncoded() {\r
+ return null;\r
+ }\r
+\r
+ public String getFormat() {\r
+ return null;\r
+ }\r
+ };\r
+ \r
+ // As of JDK 7, can't use sigDoc here directly, because the\r
+ // setAttributeID will be called and it's not implemented in xmlbeans\r
+ XMLSignContext xmlSignContext = new DOMSignContext(key, doc);\r
+ URIDereferencer uriDereferencer = getURIDereferencer();\r
+ if (null != uriDereferencer) {\r
+ xmlSignContext.setURIDereferencer(uriDereferencer);\r
+ }\r
+\r
+ xmlSignContext.putNamespacePrefix(\r
+ "http://schemas.openxmlformats.org/package/2006/digital-signature",\r
+ "mdssi");\r
+ \r
+ if (this.signatureNamespacePrefix != null) {\r
+ /*\r
+ * OOo doesn't like ds namespaces so per default prefixing is off.\r
+ */\r
+ xmlSignContext.putNamespacePrefix(\r
+ javax.xml.crypto.dsig.XMLSignature.XMLNS,\r
+ this.signatureNamespacePrefix);\r
+ }\r
+\r
+ XMLSignatureFactory signatureFactory = XMLSignatureFactory.getInstance("DOM", "XMLDSig");\r
+\r
+ /*\r
+ * Add ds:References that come from signing client local files.\r
+ */\r
+ List<Reference> references = new LinkedList<Reference>();\r
+ addDigestInfosAsReferences(digestInfos, signatureFactory, references);\r
+\r
+ /*\r
+ * Invoke the signature facets.\r
+ */\r
+ String localSignatureId;\r
+ if (null == this.signatureId) {\r
+ localSignatureId = "xmldsig-" + UUID.randomUUID().toString();\r
+ } else {\r
+ localSignatureId = this.signatureId;\r
+ }\r
+ List<XMLObject> objects = new LinkedList<XMLObject>();\r
+ for (SignatureFacet signatureFacet : this.signatureFacets) {\r
+ LOG.log(POILogger.DEBUG, "invoking signature facet: "\r
+ + signatureFacet.getClass().getSimpleName());\r
+ signatureFacet.preSign(signatureFactory, localSignatureId, signingCertificateChain, references, objects);\r
+ }\r
+\r
+ /*\r
+ * ds:SignedInfo\r
+ */\r
+ SignatureMethod signatureMethod = signatureFactory.newSignatureMethod(\r
+ getSignatureMethod(hashAlgo), null);\r
+ CanonicalizationMethod canonicalizationMethod = signatureFactory\r
+ .newCanonicalizationMethod(getCanonicalizationMethod(),\r
+ (C14NMethodParameterSpec) null);\r
+ SignedInfo signedInfo = signatureFactory.newSignedInfo(\r
+ canonicalizationMethod, signatureMethod, references);\r
+\r
+ /*\r
+ * JSR105 ds:Signature creation\r
+ */\r
+ String signatureValueId = localSignatureId + "-signature-value";\r
+ javax.xml.crypto.dsig.XMLSignature xmlSignature = signatureFactory\r
+ .newXMLSignature(signedInfo, null, objects, localSignatureId,\r
+ signatureValueId);\r
+\r
+ /*\r
+ * ds:Signature Marshalling.\r
+ */\r
+ DOMXMLSignatureIf domXmlSignature;\r
+ try {\r
+ domXmlSignature = HorribleProxy.newProxy(DOMXMLSignatureIf.class, xmlSignature);\r
+ } catch (Exception e) {\r
+ throw new RuntimeException("DomXmlSignature instance error: " + e.getMessage(), e);\r
+ }\r
+ \r
+ domXmlSignature.marshal(doc, this.signatureNamespacePrefix, (DOMCryptoContext) xmlSignContext);\r
+\r
+ registerIds(doc);\r
+ Element el = doc.getElementById("idPackageObject");\r
+ assert (el != null);\r
+ el.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:mdssi", PackageNamespaces.DIGITAL_SIGNATURE);\r
+\r
+ \r
+ /*\r
+ * Completion of undigested ds:References in the ds:Manifests.\r
+ */\r
+ for (XMLObject object : objects) {\r
+ LOG.log(POILogger.DEBUG, "object java type: " + object.getClass().getName());\r
+ List<XMLStructure> objectContentList = object.getContent();\r
+ for (XMLStructure objectContent : objectContentList) {\r
+ LOG.log(POILogger.DEBUG, "object content java type: " + objectContent.getClass().getName());\r
+ if (!(objectContent instanceof Manifest)) continue;\r
+ Manifest manifest = (Manifest) objectContent;\r
+ List<Reference> manifestReferences = manifest.getReferences();\r
+ for (Reference manifestReference : manifestReferences) {\r
+ if (manifestReference.getDigestValue() != null) continue;\r
+\r
+ DOMReferenceIf manifestDOMReference;\r
+ try {\r
+ manifestDOMReference = HorribleProxy.newProxy(DOMReferenceIf.class, manifestReference);\r
+ } catch (Exception e) {\r
+ throw new RuntimeException("DOMReference instance error: " + e.getMessage(), e);\r
+ }\r
+ manifestDOMReference.digest(xmlSignContext);\r
+ }\r
+ }\r
+ }\r
+\r
+ /*\r
+ * Completion of undigested ds:References.\r
+ */\r
+ List<Reference> signedInfoReferences = signedInfo.getReferences();\r
+ for (Reference signedInfoReference : signedInfoReferences) {\r
+ DOMReferenceIf domReference;\r
+ try {\r
+ domReference = HorribleProxy.newProxy(DOMReferenceIf.class, signedInfoReference);\r
+ } catch (Exception e) {\r
+ throw new RuntimeException("DOMReference instance error: " + e.getMessage(), e);\r
+ }\r
+\r
+ // ds:Reference with external digest value\r
+ if (domReference.getDigestValue() != null) continue;\r
+ \r
+ domReference.digest(xmlSignContext);\r
+ }\r
+\r
+ /*\r
+ * Calculation of XML signature digest value.\r
+ */\r
+ DOMSignedInfoIf domSignedInfo;\r
+ try {\r
+ domSignedInfo = HorribleProxy.newProxy(DOMSignedInfoIf.class, signedInfo); \r
+ } catch (Exception e) {\r
+ throw new RuntimeException("DOMSignedInfo instance error: " + e.getMessage(), e);\r
+ }\r
+ \r
+ ByteArrayOutputStream dataStream = new ByteArrayOutputStream();\r
+ domSignedInfo.canonicalize(xmlSignContext, dataStream);\r
+ byte[] octets = dataStream.toByteArray();\r
+\r
+ sigDoc = SignatureDocument.Factory.parse(doc.getDocumentElement());\r
+ \r
+ \r
+ /*\r
+ * TODO: we could be using DigestOutputStream here to optimize memory\r
+ * usage.\r
+ */\r
+\r
+ MessageDigest jcaMessageDigest = CryptoFunctions.getMessageDigest(hashAlgo);\r
+ byte[] digestValue = jcaMessageDigest.digest(octets);\r
+ return digestValue;\r
+ }\r
+\r
+ /**\r
+ * the resulting document needs to be tweaked before it can be digested -\r
+ * this applies to the verification and signing step\r
+ *\r
+ * @param doc\r
+ */\r
+ public void registerIds(Document doc) {\r
+ NodeList nl = doc.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Object");\r
+ registerIdAttribute(nl);\r
+ nl = doc.getElementsByTagNameNS("http://uri.etsi.org/01903/v1.3.2#", "SignedProperties");\r
+ registerIdAttribute(nl);\r
+ }\r
+ \r
+ protected void registerIdAttribute(NodeList nl) {\r
+ for (int i=0; i<nl.getLength(); i++) {\r
+ Element el = (Element)nl.item(i);\r
+ if (el.hasAttribute("Id")) {\r
+ el.setIdAttribute("Id", true);\r
+ }\r
+ }\r
+ }\r
+ \r
+ private void addDigestInfosAsReferences(List<DigestInfo> digestInfos,\r
+ XMLSignatureFactory signatureFactory, List<Reference> references)\r
+ throws NoSuchAlgorithmException,\r
+ InvalidAlgorithmParameterException, MalformedURLException {\r
+ if (digestInfos == null) return;\r
+ for (DigestInfo digestInfo : digestInfos) {\r
+ byte[] documentDigestValue = digestInfo.digestValue;\r
+\r
+ DigestMethod digestMethod = signatureFactory.newDigestMethod(\r
+ digestInfo.hashAlgo.xmlSignUri, null);\r
+\r
+ String uri = new File(digestInfo.description).getName();\r
+\r
+ Reference reference = signatureFactory.newReference(uri,\r
+ digestMethod, null, null, null, documentDigestValue);\r
+ references.add(reference);\r
+ }\r
+ }\r
+\r
+ private String getSignatureMethod(HashAlgorithm hashAlgo) {\r
+ if (null == hashAlgo) {\r
+ throw new RuntimeException("digest algo is null");\r
+ }\r
+\r
+ XMLSignatureIf XmlSignature;\r
+ try {\r
+ XmlSignature = HorribleProxy.newProxy(XMLSignatureIf.class);\r
+ } catch (Exception e) {\r
+ throw new RuntimeException("JDK doesn't support XmlSignature", e);\r
+ }\r
+ \r
+ switch (hashAlgo) {\r
+ case sha1: return XmlSignature.ALGO_ID_SIGNATURE_RSA_SHA1();\r
+ case sha256: return XmlSignature.ALGO_ID_SIGNATURE_RSA_SHA256();\r
+ case sha384: return XmlSignature.ALGO_ID_SIGNATURE_RSA_SHA384();\r
+ case sha512: return XmlSignature.ALGO_ID_SIGNATURE_RSA_SHA512();\r
+ case ripemd160: return XmlSignature.ALGO_ID_MAC_HMAC_RIPEMD160();\r
+ default: break;\r
+ }\r
+\r
+ throw new RuntimeException("unsupported sign algo: " + hashAlgo);\r
+ }\r
+\r
+ /**\r
+ * Gives back the used XAdES signature facet.\r
+ * \r
+ * @return\r
+ */\r
+ protected XAdESSignatureFacet getXAdESSignatureFacet() {\r
+ return this.xadesSignatureFacet;\r
+ }\r
+\r
+ public String getFilesDigestAlgorithm() {\r
+ return null;\r
+ }\r
+\r
+ protected String getCanonicalizationMethod() {\r
+ return CanonicalizationMethod.INCLUSIVE;\r
+ }\r
+\r
+ protected void writeDocument() throws IOException {\r
+ XmlOptions xo = new XmlOptions();\r
+ Map<String,String> namespaceMap = new HashMap<String,String>();\r
+ for (SignatureFacet sf : this.signatureFacets) {\r
+ Map<String,String> sfm = sf.getNamespacePrefixMapping();\r
+ if (sfm != null) {\r
+ namespaceMap.putAll(sfm);\r
+ }\r
+ }\r
+ xo.setSaveSuggestedPrefixes(namespaceMap);\r
+ xo.setUseDefaultNamespace();\r
+\r
+ LOG.log(POILogger.DEBUG, "output signed Office OpenXML document");\r
+\r
+ /*\r
+ * Copy the original OOXML content to the signed OOXML package. During\r
+ * copying some files need to changed.\r
+ */\r
+ OPCPackage pkg = this.getOfficeOpenXMLDocument();\r
+\r
+ PackagePartName sigPartName, sigsPartName;\r
+ try {\r
+ // <Override PartName="/_xmlsignatures/sig1.xml" ContentType="application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"/>\r
+ sigPartName = PackagingURIHelper.createPartName("/_xmlsignatures/sig1.xml");\r
+ // <Default Extension="sigs" ContentType="application/vnd.openxmlformats-package.digital-signature-origin"/>\r
+ sigsPartName = PackagingURIHelper.createPartName("/_xmlsignatures/origin.sigs");\r
+ } catch (InvalidFormatException e) {\r
+ throw new IOException(e);\r
+ }\r
+ \r
+ String sigContentType = "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml";\r
+ PackagePart sigPart = pkg.getPart(sigPartName);\r
+ if (sigPart == null) {\r
+ sigPart = pkg.createPart(sigPartName, sigContentType);\r
+ }\r
+ \r
+ OutputStream os = sigPart.getOutputStream();\r
+ sigDoc.save(os, xo);\r
+ os.close();\r
+ \r
+ String sigsContentType = "application/vnd.openxmlformats-package.digital-signature-origin";\r
+ PackagePart sigsPart = pkg.getPart(sigsPartName);\r
+ if (sigsPart == null) {\r
+ // touch empty marker file\r
+ sigsPart = pkg.createPart(sigsPartName, sigsContentType);\r
+ }\r
+ \r
+ PackageRelationshipCollection relCol = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);\r
+ for (PackageRelationship pr : relCol) {\r
+ pkg.removeRelationship(pr.getId());\r
+ }\r
+ pkg.addRelationship(sigsPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN);\r
+ \r
+ sigsPart.addRelationship(sigPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE);\r
+ }\r
+}\r
--- /dev/null
+/* ====================================================================\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to You under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+==================================================================== */\r
+\r
+/* ====================================================================\r
+ This product contains an ASLv2 licensed version of the OOXML signer\r
+ package from the eID Applet project\r
+ http://code.google.com/p/eid-applet/source/browse/trunk/README.txt \r
+ Copyright (C) 2008-2014 FedICT.\r
+ ================================================================= */ \r
+package org.apache.poi.poifs.crypt;\r
+\r
+import static org.junit.Assert.assertEquals;\r
+import static org.junit.Assert.assertNotNull;\r
+import static org.junit.Assert.assertTrue;\r
++import static org.mockito.Matchers.any;\r
++import static org.mockito.Matchers.eq;\r
++import static org.mockito.Mockito.mock;\r
++import static org.mockito.Mockito.when;\r
+\r
+import java.io.ByteArrayOutputStream;\r
+import java.io.File;\r
+import java.io.FileInputStream;\r
+import java.io.FileOutputStream;\r
+import java.io.IOException;\r
+import java.net.MalformedURLException;\r
+import java.net.URL;\r
+import java.net.URLClassLoader;\r
+import java.security.Key;\r
+import java.security.KeyPair;\r
+import java.security.KeyStore;\r
+import java.security.PrivateKey;\r
+import java.security.cert.Certificate;\r
++import java.security.cert.X509CRL;\r
+import java.security.cert.X509Certificate;\r
++import java.util.ArrayList;\r
+import java.util.Calendar;\r
+import java.util.Collections;\r
+import java.util.Date;\r
+import java.util.List;\r
+import java.util.TimeZone;\r
+\r
+import javax.crypto.Cipher;\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.HorribleProxies.KeyUsageIf;\r
++import org.apache.poi.poifs.crypt.dsig.HorribleProxies.OCSPRespIf;\r
+import org.apache.poi.poifs.crypt.dsig.HorribleProxy;\r
+import org.apache.poi.poifs.crypt.dsig.SignatureInfo;\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.SignaturePolicyService;\r
++import org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet;\r
++import org.apache.poi.poifs.crypt.dsig.facets.XAdESXLSignatureFacet;\r
++import org.apache.poi.poifs.crypt.dsig.services.RevocationData;\r
++import org.apache.poi.poifs.crypt.dsig.services.RevocationDataService;\r
++import org.apache.poi.poifs.crypt.dsig.services.TimeStampService;\r
+import org.apache.poi.poifs.crypt.dsig.services.XmlSignatureService;\r
+import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo;\r
+import org.apache.poi.util.IOUtils;\r
+import org.apache.poi.util.POILogFactory;\r
+import org.apache.poi.util.POILogger;\r
+import org.junit.BeforeClass;\r
+import org.junit.Test;\r
++import org.mockito.invocation.InvocationOnMock;\r
++import org.mockito.stubbing.Answer;\r
+\r
+public class TestSignatureInfo {\r
+ private static final POILogger LOG = POILogFactory.getLogger(TestSignatureInfo.class);\r
+ private static final POIDataSamples testdata = POIDataSamples.getXmlDSignInstance();\r
+\r
+ private KeyPair keyPair = null;\r
+ private X509Certificate x509 = null;\r
+ \r
+\r
+ \r
+ @BeforeClass\r
+ public static void initBouncy() throws MalformedURLException {\r
+ File bcJar = testdata.getFile("bcprov-ext-jdk15on-1.49.jar");\r
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();\r
+ URLClassLoader ucl = new URLClassLoader(new URL[]{bcJar.toURI().toURL()}, cl);\r
+ Thread.currentThread().setContextClassLoader(ucl);\r
+ }\r
+ \r
+ @Test\r
+ public void getSignerUnsigned() throws Exception {\r
+ String testFiles[] = { \r
+ "hello-world-unsigned.docx",\r
+ "hello-world-unsigned.pptx",\r
+ "hello-world-unsigned.xlsx",\r
+ "hello-world-office-2010-technical-preview-unsigned.docx"\r
+ };\r
+ \r
+ for (String testFile : testFiles) {\r
+ OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ);\r
+ SignatureInfo si = new SignatureInfo(pkg);\r
+ List<X509Certificate> result = si.getSigners();\r
+ pkg.revert();\r
+ pkg.close();\r
+ assertNotNull(result);\r
+ assertTrue(result.isEmpty());\r
+ }\r
+ }\r
+ \r
+ @Test\r
+ public void getSigner() throws Exception {\r
+ String testFiles[] = { \r
+ "hyperlink-example-signed.docx",\r
+ "hello-world-signed.docx",\r
+ "hello-world-signed.pptx",\r
+ "hello-world-signed.xlsx",\r
+ "hello-world-office-2010-technical-preview.docx",\r
+ "ms-office-2010-signed.docx",\r
+ "ms-office-2010-signed.pptx",\r
+ "ms-office-2010-signed.xlsx",\r
+ "Office2010-SP1-XAdES-X-L.docx",\r
+ "signed.docx",\r
+ };\r
+ \r
+ for (String testFile : testFiles) {\r
+ OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ);\r
+ SignatureInfo si = new SignatureInfo(pkg);\r
+ List<X509Certificate> result = si.getSigners();\r
+\r
+ assertNotNull(result);\r
+ assertEquals("test-file: "+testFile, 1, result.size());\r
+ X509Certificate signer = result.get(0);\r
+ LOG.log(POILogger.DEBUG, "signer: " + signer.getSubjectX500Principal());\r
+\r
+ boolean b = si.verifySignature();\r
+ assertTrue("test-file: "+testFile, b);\r
+ pkg.revert();\r
+ }\r
+ }\r
+\r
+ @Test\r
+ public void getMultiSigners() throws Exception {\r
+ String testFile = "hello-world-signed-twice.docx";\r
+ OPCPackage pkg = OPCPackage.open(testdata.getFile(testFile), PackageAccess.READ);\r
+ SignatureInfo si = new SignatureInfo(pkg);\r
+ List<X509Certificate> result = si.getSigners();\r
+\r
+ assertNotNull(result);\r
+ assertEquals("test-file: "+testFile, 2, result.size());\r
+ X509Certificate signer1 = result.get(0);\r
+ X509Certificate signer2 = result.get(1);\r
+ LOG.log(POILogger.DEBUG, "signer 1: " + signer1.getSubjectX500Principal());\r
+ LOG.log(POILogger.DEBUG, "signer 2: " + signer2.getSubjectX500Principal());\r
+\r
+ boolean b = si.verifySignature();\r
+ assertTrue("test-file: "+testFile, b);\r
+ pkg.revert();\r
+ }\r
+ \r
+ @Test\r
+ public void testSignSpreadsheet() throws Exception {\r
+ String testFile = "hello-world-unsigned.xlsx";\r
+ OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);\r
+ sign(pkg, "Test", "CN=Test", 1);\r
+ pkg.close();\r
+ }\r
+\r
+ @Test\r
+ public void testSignSpreadsheetWithSignatureInfo() throws Exception {\r
+ String testFile = "hello-world-unsigned.xlsx";\r
+ OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);\r
+ SignatureInfo si = new SignatureInfo(pkg);\r
+ initKeyPair("Test", "CN=Test");\r
+ // hash > sha1 doesn't work in excel viewer ...\r
+ si.confirmSignature(keyPair.getPrivate(), x509, HashAlgorithm.sha1);\r
+ List<X509Certificate> signer = si.getSigners();\r
+ assertEquals(1, signer.size());\r
+ pkg.close();\r
+ }\r
+\r
++ @Test\r
++ public void testSignEnvelopingDocument() throws Exception {\r
++ String testFile = "hello-world-unsigned.xlsx";\r
++ OPCPackage pkg = OPCPackage.open(copy(testdata.getFile(testFile)), PackageAccess.READ_WRITE);\r
++\r
++ // setup\r
++ EnvelopedSignatureFacet envelopedSignatureFacet = new EnvelopedSignatureFacet();\r
++ KeyInfoSignatureFacet keyInfoSignatureFacet = new KeyInfoSignatureFacet(true, false, false);\r
++ SignaturePolicyService signaturePolicyService = null;\r
++ XAdESSignatureFacet xadesSignatureFacet = new XAdESSignatureFacet(null, null, signaturePolicyService);\r
++\r
++ \r
++ TimeStampService mockTimeStampService = mock(TimeStampService.class);\r
++ RevocationDataService mockRevocationDataService = mock(RevocationDataService.class);\r
++\r
++ XAdESXLSignatureFacet xadesXLSignatureFacet = new XAdESXLSignatureFacet(\r
++ mockTimeStampService, mockRevocationDataService);\r
++ XmlSignatureService testedInstance = new XmlSignatureService(HashAlgorithm.sha1, pkg);\r
++ testedInstance.addSignatureFacet(envelopedSignatureFacet, keyInfoSignatureFacet,\r
++ xadesSignatureFacet, xadesXLSignatureFacet);\r
++ \r
++ initKeyPair("Test", "CN=Test");\r
++ List<X509Certificate> certificateChain = new ArrayList<X509Certificate>();\r
++ /*\r
++ * We need at least 2 certificates for the XAdES-C complete certificate\r
++ * refs construction.\r
++ */\r
++ certificateChain.add(x509);\r
++ certificateChain.add(x509);\r
++ \r
++ RevocationData revocationData = new RevocationData();\r
++ final X509CRL crl = PkiTestUtils.generateCrl(x509, keyPair.getPrivate());\r
++ revocationData.addCRL(crl);\r
++ OCSPRespIf ocspResp = PkiTestUtils.createOcspResp(x509, false,\r
++ x509, x509, keyPair.getPrivate(), "SHA1withRSA");\r
++ revocationData.addOCSP(ocspResp.getEncoded());\r
++ \r
++ when(mockTimeStampService.timeStamp(any(byte[].class), any(RevocationData.class)))\r
++ .thenAnswer(new Answer<byte[]>(){\r
++ public byte[] answer(InvocationOnMock invocation) throws Throwable {\r
++ Object[] arguments = invocation.getArguments();\r
++ RevocationData revocationData = (RevocationData) arguments[1];\r
++ revocationData.addCRL(crl);\r
++ return "time-stamp-token".getBytes();\r
++ } \r
++ });\r
++ \r
++ when(mockRevocationDataService.getRevocationData(eq(certificateChain)))\r
++ .thenReturn(revocationData);\r
++ \r
++ // operate\r
++ DigestInfo digestInfo = testedInstance.preSign(null, certificateChain, null, null, null);\r
++\r
++ // verify\r
++ assertNotNull(digestInfo);\r
++ assertEquals("SHA-1", digestInfo.hashAlgo);\r
++ assertNotNull(digestInfo.digestValue);\r
++ }\r
+ \r
+ private OPCPackage sign(OPCPackage pkgCopy, String alias, String signerDn, int signerCount) throws Exception {\r
+ /*** TODO : set cal to now ... only set to fixed date for debugging ... */ \r
+ Calendar cal = Calendar.getInstance();\r
+ cal.clear();\r
+ cal.setTimeZone(TimeZone.getTimeZone("UTC"));\r
+ cal.set(2014, 7, 6, 21, 42, 12);\r
+ \r
+ XmlSignatureService signatureService = new XmlSignatureService(HashAlgorithm.sha1, pkgCopy);\r
+ signatureService.initFacets(cal.getTime());\r
+ initKeyPair(alias, signerDn);\r
+\r
+ // operate\r
+ List<X509Certificate> x509Chain = Collections.singletonList(x509);\r
+ DigestInfo digestInfo = signatureService.preSign(null, x509Chain, null, null, null);\r
+\r
+ // verify\r
+ assertNotNull(digestInfo);\r
+ LOG.log(POILogger.DEBUG, "digest algo: " + digestInfo.hashAlgo);\r
+ LOG.log(POILogger.DEBUG, "digest description: " + digestInfo.description);\r
+ assertEquals("Office OpenXML Document", digestInfo.description);\r
+ assertNotNull(digestInfo.hashAlgo);\r
+ assertNotNull(digestInfo.digestValue);\r
+\r
+ // setup: key material, signature value\r
+\r
+ Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");\r
+ cipher.init(Cipher.ENCRYPT_MODE, keyPair.getPrivate());\r
+ ByteArrayOutputStream digestInfoValueBuf = new ByteArrayOutputStream();\r
+ digestInfoValueBuf.write(SignatureInfo.SHA1_DIGEST_INFO_PREFIX);\r
+ digestInfoValueBuf.write(digestInfo.digestValue);\r
+ byte[] digestInfoValue = digestInfoValueBuf.toByteArray();\r
+ byte[] signatureValue = cipher.doFinal(digestInfoValue);\r
+\r
+ // operate: postSign\r
+ signatureService.postSign(signatureValue, Collections.singletonList(x509));\r
+\r
+ // verify: signature\r
+ SignatureInfo si = new SignatureInfo(pkgCopy);\r
+ List<X509Certificate> signers = si.getSigners();\r
+ assertEquals(signerCount, signers.size());\r
+\r
+ return pkgCopy;\r
+ }\r
+\r
+ private void initKeyPair(String alias, String subjectDN) throws Exception {\r
+ final char password[] = "test".toCharArray();\r
+ File file = new File("build/test.pfx");\r
+\r
+ KeyStore keystore = KeyStore.getInstance("PKCS12");\r
+\r
+ if (file.exists()) {\r
+ FileInputStream fis = new FileInputStream(file);\r
+ keystore.load(fis, password);\r
+ fis.close();\r
+ } else {\r
+ keystore.load(null, password);\r
+ }\r
+\r
+ if (keystore.isKeyEntry(alias)) {\r
+ Key key = keystore.getKey(alias, password);\r
+ x509 = (X509Certificate)keystore.getCertificate(alias);\r
+ keyPair = new KeyPair(x509.getPublicKey(), (PrivateKey)key);\r
+ } else {\r
+ keyPair = PkiTestUtils.generateKeyPair();\r
+ Calendar cal = Calendar.getInstance();\r
+ Date notBefore = cal.getTime();\r
+ cal.add(Calendar.YEAR, 1);\r
+ Date notAfter = cal.getTime();\r
+ KeyUsageIf keyUsage = HorribleProxy.newProxy(KeyUsageIf.class);\r
+ keyUsage = HorribleProxy.newProxy(KeyUsageIf.class, keyUsage.digitalSignature());\r
+ \r
+ x509 = PkiTestUtils.generateCertificate(keyPair.getPublic(), subjectDN\r
+ , notBefore, notAfter, null, keyPair.getPrivate(), true, 0, null, null, keyUsage);\r
+\r
+ keystore.setKeyEntry(alias, keyPair.getPrivate(), password, new Certificate[]{x509});\r
+ FileOutputStream fos = new FileOutputStream(file);\r
+ keystore.store(fos, password);\r
+ fos.close();\r
+ }\r
+ }\r
+\r
+ private static File copy(File input) throws IOException {\r
+ String extension = input.getName().replaceAll(".*?(\\.[^.]+)?$", "$1");\r
+ if (extension == null || "".equals(extension)) extension = ".zip";\r
+ File tmpFile = new File("build", "sigtest"+extension);\r
+ FileOutputStream fos = new FileOutputStream(tmpFile);\r
+ FileInputStream fis = new FileInputStream(input);\r
+ IOUtils.copy(fis, fos);\r
+ fis.close();\r
+ fos.close();\r
+ return tmpFile;\r
+ }\r
+}\r