Return-Path: X-Original-To: apmail-poi-commits-archive@minotaur.apache.org Delivered-To: apmail-poi-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id B035D11F7B for ; Sun, 10 Aug 2014 18:25:40 +0000 (UTC) Received: (qmail 53367 invoked by uid 500); 10 Aug 2014 18:25:40 -0000 Delivered-To: apmail-poi-commits-archive@poi.apache.org Received: (qmail 53330 invoked by uid 500); 10 Aug 2014 18:25:40 -0000 Mailing-List: contact commits-help@poi.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@poi.apache.org Delivered-To: mailing list commits@poi.apache.org Received: (qmail 53321 invoked by uid 99); 10 Aug 2014 18:25:40 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 10 Aug 2014 18:25:40 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 10 Aug 2014 18:25:34 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 7986623889E1; Sun, 10 Aug 2014 18:25:14 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1617141 [3/5] - in /poi/branches/xml_signature: ./ src/java/org/apache/poi/poifs/crypt/ src/ooxml/java/org/apache/poi/openxml4j/opc/ src/ooxml/java/org/apache/poi/openxml4j/opc/internal/ src/ooxml/java/org/apache/poi/poifs/crypt/dsig/ src/... Date: Sun, 10 Aug 2014 18:25:12 -0000 To: commits@poi.apache.org From: kiwiwings@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20140810182514.7986623889E1@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Added: poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java URL: http://svn.apache.org/viewvc/poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java?rev=1617141&view=auto ============================================================================== --- poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java (added) +++ poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RelationshipTransformService.java Sun Aug 10 18:25:10 2014 @@ -0,0 +1,249 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +/* ==================================================================== + This product contains an ASLv2 licensed version of the OOXML signer + package from the eID Applet project + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + Copyright (C) 2008-2014 FedICT. + ================================================================= */ + +package org.apache.poi.poifs.crypt.dsig.services; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.Provider; +import java.security.Security; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Comparator; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.xml.crypto.Data; +import javax.xml.crypto.MarshalException; +import javax.xml.crypto.OctetStreamData; +import javax.xml.crypto.XMLCryptoContext; +import javax.xml.crypto.XMLStructure; +import javax.xml.crypto.dom.DOMStructure; +import javax.xml.crypto.dsig.TransformException; +import javax.xml.crypto.dsig.TransformService; +import javax.xml.crypto.dsig.spec.TransformParameterSpec; + +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; +import org.apache.poi.util.XmlSort; +import org.apache.xmlbeans.XmlCursor; +import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlObject; +import org.apache.xmlbeans.XmlOptions; +import org.openxmlformats.schemas.xpackage.x2006.digitalSignature.RelationshipReferenceDocument; +import org.openxmlformats.schemas.xpackage.x2006.relationships.CTRelationship; +import org.openxmlformats.schemas.xpackage.x2006.relationships.CTRelationships; +import org.openxmlformats.schemas.xpackage.x2006.relationships.RelationshipsDocument; +import org.openxmlformats.schemas.xpackage.x2006.relationships.STTargetMode; +import org.w3.x2000.x09.xmldsig.TransformDocument; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +/** + * JSR105 implementation of the RelationshipTransform transformation. + * + *

+ * Specs: http://openiso.org/Ecma/376/Part2/12.2.4#26 + *

+ */ +public class RelationshipTransformService extends TransformService { + + public static final String TRANSFORM_URI = "http://schemas.openxmlformats.org/package/2006/RelationshipTransform"; + + private final List sourceIds; + + private static final POILogger LOG = POILogFactory.getLogger(RelationshipTransformService.class); + + /** + * Relationship Transform parameter specification class. + */ + public static class RelationshipTransformParameterSpec implements TransformParameterSpec { + List sourceIds = new LinkedList(); + public void addRelationshipReference(String relationshipId) { + sourceIds.add(relationshipId); + } + public boolean hasSourceIds() { + return !sourceIds.isEmpty(); + } + } + + + public RelationshipTransformService() { + super(); + LOG.log(POILogger.DEBUG, "constructor"); + this.sourceIds = new LinkedList(); + } + + /** + * Register the provider for this TransformService + * + * @see javax.xml.crypto.dsig.TransformService + */ + public static synchronized void registerDsigProvider() { + // the xml signature classes will try to find a special TransformerService, + // which is ofcourse unknown to JCE before ... + final String dsigProvider = "POIXmlDsigProvider"; + if (Security.getProperty(dsigProvider) == null) { + Provider p = new Provider(dsigProvider, 1.0, dsigProvider){ + static final long serialVersionUID = 1L; + }; + p.put("TransformService." + TRANSFORM_URI, RelationshipTransformService.class.getName()); + p.put("TransformService." + TRANSFORM_URI + " MechanismType", "DOM"); + Security.addProvider(p); + } + } + + + @Override + public void init(TransformParameterSpec params) throws InvalidAlgorithmParameterException { + LOG.log(POILogger.DEBUG, "init(params)"); + if (!(params instanceof RelationshipTransformParameterSpec)) { + throw new InvalidAlgorithmParameterException(); + } + RelationshipTransformParameterSpec relParams = (RelationshipTransformParameterSpec) params; + for (String sourceId : relParams.sourceIds) { + this.sourceIds.add(sourceId); + } + } + + @Override + public void init(XMLStructure parent, XMLCryptoContext context) throws InvalidAlgorithmParameterException { + LOG.log(POILogger.DEBUG, "init(parent,context)"); + LOG.log(POILogger.DEBUG, "parent java type: " + parent.getClass().getName()); + DOMStructure domParent = (DOMStructure) parent; + Node parentNode = domParent.getNode(); + + try { + TransformDocument transDoc = TransformDocument.Factory.parse(parentNode); + XmlObject xoList[] = transDoc.getTransform().selectChildren(RelationshipReferenceDocument.type.getDocumentElementName()); + if (xoList.length == 0) { + LOG.log(POILogger.WARN, "no RelationshipReference/@SourceId parameters present"); + } + for (XmlObject xo : xoList) { + RelationshipReferenceDocument refDoc = + RelationshipReferenceDocument.Factory.parse(xo.getDomNode()); + String sourceId = refDoc.getRelationshipReference().getSourceId(); + LOG.log(POILogger.DEBUG, "sourceId: ", sourceId); + this.sourceIds.add(sourceId); + } + } catch (XmlException e) { + throw new InvalidAlgorithmParameterException(e); + } + } + + @Override + public void marshalParams(XMLStructure parent, XMLCryptoContext context) throws MarshalException { + LOG.log(POILogger.DEBUG, "marshallParams(parent,context)"); + DOMStructure domParent = (DOMStructure) parent; + Element parentNode = (Element)domParent.getNode(); + // parentNode.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:mdssi", DIGITAL_SIGNATURE); + Document doc = parentNode.getOwnerDocument(); + + for (String sourceId : this.sourceIds) { + RelationshipReferenceDocument relRef = RelationshipReferenceDocument.Factory.newInstance(); + relRef.addNewRelationshipReference().setSourceId(sourceId); + Node n = relRef.getRelationshipReference().getDomNode(); + // TODO: is there a more elegant way to do this? + n.setPrefix("mdssi"); + n = doc.importNode(n, true); + parentNode.appendChild(n); + } + } + + public AlgorithmParameterSpec getParameterSpec() { + LOG.log(POILogger.DEBUG, "getParameterSpec"); + return null; + } + + public Data transform(Data data, XMLCryptoContext context) throws TransformException { + LOG.log(POILogger.DEBUG, "transform(data,context)"); + LOG.log(POILogger.DEBUG, "data java type: " + data.getClass().getName()); + OctetStreamData octetStreamData = (OctetStreamData) data; + LOG.log(POILogger.DEBUG, "URI: " + octetStreamData.getURI()); + InputStream octetStream = octetStreamData.getOctetStream(); + + RelationshipsDocument relDoc; + try { + relDoc = RelationshipsDocument.Factory.parse(octetStream); + } catch (Exception e) { + throw new TransformException(e.getMessage(), e); + } + LOG.log(POILogger.DEBUG, "relationships document", relDoc); + + CTRelationships rels = relDoc.getRelationships(); + List relList = rels.getRelationshipList(); + Iterator relIter = rels.getRelationshipList().iterator(); + while (relIter.hasNext()) { + CTRelationship rel = relIter.next(); + /* + * See: ISO/IEC 29500-2:2008(E) - 13.2.4.24 Relationships Transform + * Algorithm. + */ + if (!this.sourceIds.contains(rel.getId())) { + LOG.log(POILogger.DEBUG, "removing element: " + rel.getId()); + relIter.remove(); + } else { + if (!rel.isSetTargetMode()) { + rel.setTargetMode(STTargetMode.INTERNAL); + } + } + } + + // TODO: remove non element nodes ??? + LOG.log(POILogger.DEBUG, "# Relationship elements", relList.size()); + + XmlSort.sort(rels, new Comparator(){ + public int compare(XmlCursor c1, XmlCursor c2) { + String id1 = ((CTRelationship)c1.getObject()).getId(); + String id2 = ((CTRelationship)c2.getObject()).getId(); + return id1.compareTo(id2); + } + }); + + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + XmlOptions xo = new XmlOptions(); + xo.setSaveNoXmlDecl(); + relDoc.save(bos, xo); + return new OctetStreamData(new ByteArrayInputStream(bos.toByteArray())); + } catch (IOException e) { + throw new TransformException(e.getMessage(), e); + } + } + + public Data transform(Data data, XMLCryptoContext context, OutputStream os) throws TransformException { + LOG.log(POILogger.DEBUG, "transform(data,context,os)"); + return null; + } + + public boolean isFeatureSupported(String feature) { + LOG.log(POILogger.DEBUG, "isFeatureSupported(feature)"); + return false; + } +} Added: poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RevocationData.java URL: http://svn.apache.org/viewvc/poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RevocationData.java?rev=1617141&view=auto ============================================================================== --- poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RevocationData.java (added) +++ poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RevocationData.java Sun Aug 10 18:25:10 2014 @@ -0,0 +1,131 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +/* ==================================================================== + This product contains an ASLv2 licensed version of the OOXML signer + package from the eID Applet project + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + Copyright (C) 2008-2014 FedICT. + ================================================================= */ + +package org.apache.poi.poifs.crypt.dsig.services; + +import java.security.cert.CRLException; +import java.security.cert.X509CRL; +import java.util.LinkedList; +import java.util.List; + +/** + * Container class for PKI revocation data. + * + * @author Frank Cornelis + * + */ +public class RevocationData { + + private final List crls; + + private final List ocsps; + + /** + * Default constructor. + */ + public RevocationData() { + this.crls = new LinkedList(); + this.ocsps = new LinkedList(); + } + + /** + * Adds a CRL to this revocation data set. + * + * @param encodedCrl + */ + public void addCRL(byte[] encodedCrl) { + this.crls.add(encodedCrl); + } + + /** + * Adds a CRL to this revocation data set. + * + * @param crl + */ + public void addCRL(X509CRL crl) { + byte[] encodedCrl; + try { + encodedCrl = crl.getEncoded(); + } catch (CRLException e) { + throw new IllegalArgumentException("CRL coding error: " + + e.getMessage(), e); + } + addCRL(encodedCrl); + } + + /** + * Adds an OCSP response to this revocation data set. + * + * @param encodedOcsp + */ + public void addOCSP(byte[] encodedOcsp) { + this.ocsps.add(encodedOcsp); + } + + /** + * Gives back a list of all CRLs. + * + * @return + */ + public List getCRLs() { + return this.crls; + } + + /** + * Gives back a list of all OCSP responses. + * + * @return + */ + public List getOCSPs() { + return this.ocsps; + } + + /** + * Returns true if this revocation data set holds OCSP + * responses. + * + * @return + */ + public boolean hasOCSPs() { + return false == this.ocsps.isEmpty(); + } + + /** + * Returns true if this revocation data set holds CRLs. + * + * @return + */ + public boolean hasCRLs() { + return false == this.crls.isEmpty(); + } + + /** + * Returns true if this revocation data is not empty. + * + * @return + */ + public boolean hasRevocationDataEntries() { + return hasOCSPs() || hasCRLs(); + } +} Added: poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RevocationDataService.java URL: http://svn.apache.org/viewvc/poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RevocationDataService.java?rev=1617141&view=auto ============================================================================== --- poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RevocationDataService.java (added) +++ poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/RevocationDataService.java Sun Aug 10 18:25:10 2014 @@ -0,0 +1,47 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +/* ==================================================================== + This product contains an ASLv2 licensed version of the OOXML signer + package from the eID Applet project + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + Copyright (C) 2008-2014 FedICT. + ================================================================= */ + +package org.apache.poi.poifs.crypt.dsig.services; + +import java.security.cert.X509Certificate; +import java.util.List; + +/** + * Interface for a service that retrieves revocation data about some given + * certificate chain. + * + * @author Frank Cornelis + * + */ +public interface RevocationDataService { + + /** + * Gives back the revocation data corresponding with the given certificate + * chain. + * + * @param certificateChain + * @return + */ + RevocationData getRevocationData(List certificateChain); +} Added: poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/SignatureService.java URL: http://svn.apache.org/viewvc/poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/SignatureService.java?rev=1617141&view=auto ============================================================================== --- poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/SignatureService.java (added) +++ poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/SignatureService.java Sun Aug 10 18:25:10 2014 @@ -0,0 +1,101 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +/* ==================================================================== + This product contains an ASLv2 licensed version of the OOXML signer + package from the eID Applet project + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + Copyright (C) 2008-2014 FedICT. + ================================================================= */ + +package org.apache.poi.poifs.crypt.dsig.services; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import java.util.List; + +import org.apache.poi.poifs.crypt.dsig.CertificateSecurityException; +import org.apache.poi.poifs.crypt.dsig.ExpiredCertificateSecurityException; +import org.apache.poi.poifs.crypt.dsig.RevokedCertificateSecurityException; +import org.apache.poi.poifs.crypt.dsig.TrustCertificateSecurityException; +import org.apache.poi.poifs.crypt.dsig.spi.AddressDTO; +import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo; +import org.apache.poi.poifs.crypt.dsig.spi.IdentityDTO; + +/** + * Interface for signature service component. + * + * @author Frank Cornelis + * + */ +public interface SignatureService { + + /** + * Gives back the digest algorithm to be used for construction of the digest + * infos of the preSign method. Return a digest algorithm here if you want + * to let the client sign some locally stored files. Return + * null if no pre-sign digest infos are required. + * + * @return the digest algorithm to be used when digesting local files. + * @see #preSign(List, List) + */ + String getFilesDigestAlgorithm(); + + /** + * Pre-sign callback method. Depending on the configuration some parameters + * are passed. The returned value will be signed by the eID Applet. + * + *

+ * TODO: service must be able to throw some exception on failure. + *

+ * + * @param digestInfos + * the optional list of digest infos. + * @param signingCertificateChain + * the optional list of certificates. + * @param identity + * the optional identity. + * @param address + * the optional identity address. + * @param photo + * the optional identity photo. + * @param timestamp + * the optional timestamp, defaults to now + * @return the digest to be signed. + * @throws NoSuchAlgorithmException + */ + DigestInfo preSign(List digestInfos, + List signingCertificateChain, + IdentityDTO identity, AddressDTO address, byte[] photo) + throws NoSuchAlgorithmException; + + /** + * Post-sign callback method. Received the signature value. Depending on the + * configuration the signing certificate chain is also obtained. + * + * @param signatureValue + * @param signingCertificateChain + * the optional chain of signing certificates. + */ + void postSign(byte[] signatureValue, + List signingCertificateChain) + throws ExpiredCertificateSecurityException, + RevokedCertificateSecurityException, + TrustCertificateSecurityException, CertificateSecurityException, + SecurityException, IOException; +} Added: poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TSPTimeStampService.java URL: http://svn.apache.org/viewvc/poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TSPTimeStampService.java?rev=1617141&view=auto ============================================================================== --- poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TSPTimeStampService.java (added) +++ poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TSPTimeStampService.java Sun Aug 10 18:25:10 2014 @@ -0,0 +1,392 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +/* ==================================================================== + This product contains an ASLv2 licensed version of the OOXML signer + package from the eID Applet project + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + Copyright (C) 2008-2014 FedICT. + ================================================================= */ + +package org.apache.poi.poifs.crypt.dsig.services; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URL; +import java.nio.charset.Charset; +import java.security.MessageDigest; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import javax.security.auth.x500.X500Principal; +import javax.xml.bind.DatatypeConverter; + +import org.apache.commons.codec.binary.Hex; +import org.apache.poi.poifs.crypt.CryptoFunctions; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.ASN1InputStreamIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.ASN1OctetStringIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.AuthorityKeyIdentifierIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.BcDigestCalculatorProviderIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.BcRSASignerInfoVerifierBuilderIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DEROctetStringIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DefaultDigestAlgorithmIdentifierFinderIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.PKIFailureInfoIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.SignerIdIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.SignerInformationVerifierIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.SubjectKeyIdentifierIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.TimeStampRequestGeneratorIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.TimeStampRequestIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.TimeStampResponseIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.TimeStampTokenIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.X509CertificateHolderIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxy; +import org.apache.poi.util.IOUtils; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; + +/** + * A TSP time-stamp service implementation. + * + * @author Frank Cornelis + * + */ +public class TSPTimeStampService implements TimeStampService { + + private static final POILogger LOG = POILogFactory.getLogger(TSPTimeStampService.class); + + static { + CryptoFunctions.registerBouncyCastle(); + } + + public static final String DEFAULT_USER_AGENT = "eID Applet Service TSP Client"; + + private final String tspServiceUrl; + + private String requestPolicy; + + private final String userAgent; + + private final TimeStampServiceValidator validator; + + private String username; + + private String password; + + private String proxyHost; + + private int proxyPort; + + private String digestAlgo; + + private String digestAlgoOid; + + public TSPTimeStampService(String tspServiceUrl, + TimeStampServiceValidator validator) { + this(tspServiceUrl, validator, null, null); + } + + /** + * Main constructor. + * + * @param tspServiceUrl + * the URL of the TSP service. + * @param validator + * the trust validator used to validate incoming TSP response + * signatures. + * @param requestPolicy + * the optional TSP request policy. + * @param userAgent + * the optional User-Agent TSP request header value. + */ + public TSPTimeStampService(String tspServiceUrl, + TimeStampServiceValidator validator, String requestPolicy, + String userAgent) { + if (null == tspServiceUrl) { + throw new IllegalArgumentException("TSP service URL required"); + } + this.tspServiceUrl = tspServiceUrl; + + if (null == validator) { + throw new IllegalArgumentException("TSP validator required"); + } + this.validator = validator; + + this.requestPolicy = requestPolicy; + + if (null != userAgent) { + this.userAgent = userAgent; + } else { + this.userAgent = DEFAULT_USER_AGENT; + } + + this.digestAlgo = "SHA-1"; + this.digestAlgoOid = "1.3.14.3.2.26"; + } + + /** + * Sets the request policy OID. + * + * @param policyOid + */ + public void setRequestPolicy(String policyOid) { + this.requestPolicy = policyOid; + } + + /** + * Sets the credentials used in case the TSP service requires + * authentication. + * + * @param username + * @param password + */ + public void setAuthenticationCredentials(String username, String password) { + this.username = username; + this.password = password; + } + + /** + * Resets the authentication credentials. + */ + public void resetAuthenticationCredentials() { + this.username = null; + this.password = null; + } + + /** + * Sets the digest algorithm used for time-stamping data. Example value: + * "SHA-1". + * + * @param digestAlgo + */ + public void setDigestAlgo(String digestAlgo) { + if ("SHA-1".equals(digestAlgo)) { + this.digestAlgoOid = "1.3.14.3.2.26"; + } else if ("SHA-256".equals(digestAlgo)) { + this.digestAlgoOid = "2.16.840.1.101.3.4.2.1"; + } else if ("SHA-384".equals(digestAlgo)) { + this.digestAlgoOid = "2.16.840.1.101.3.4.2.2"; + } else if ("SHA-512".equals(digestAlgo)) { + this.digestAlgoOid = "2.16.840.1.101.3.4.2.3"; + } else { + throw new IllegalArgumentException("unsupported digest algo: " + digestAlgo); + } + + this.digestAlgo = digestAlgo; + } + + /** + * Configures the HTTP proxy settings to be used to connect to the TSP + * service. + * + * @param proxyHost + * @param proxyPort + */ + public void setProxy(String proxyHost, int proxyPort) { + this.proxyHost = proxyHost; + this.proxyPort = proxyPort; + } + + /** + * Resets the HTTP proxy settings. + */ + public void resetProxy() { + this.proxyHost = null; + this.proxyPort = 0; + } + + public byte[] timeStamp(byte[] data, RevocationData revocationData) + throws Exception { + // digest the message + MessageDigest messageDigest = MessageDigest + .getInstance(this.digestAlgo); + byte[] digest = messageDigest.digest(data); + + // generate the TSP request + BigInteger nonce = new BigInteger(128, new SecureRandom()); + TimeStampRequestGeneratorIf requestGenerator = HorribleProxy.newProxy(TimeStampRequestGeneratorIf.class); + requestGenerator.setCertReq(true); + if (null != this.requestPolicy) { + requestGenerator.setReqPolicy(this.requestPolicy); + } + TimeStampRequestIf request = requestGenerator.generate(this.digestAlgoOid, digest, nonce); + byte[] encodedRequest = request.getEncoded(); + + // create the HTTP POST request + Proxy proxy = (this.proxyHost != null) + ? new Proxy(Proxy.Type.HTTP, new InetSocketAddress(this.proxyHost, this.proxyPort)) + : Proxy.NO_PROXY; + HttpURLConnection huc = (HttpURLConnection)new URL(this.tspServiceUrl).openConnection(proxy); + + if (null != this.username) { + String userPassword = this.username + ":" + this.password; + String encoding = DatatypeConverter.printBase64Binary(userPassword.getBytes(Charset.forName("iso-8859-1"))); + huc.setRequestProperty("Authorization", "Basic " + encoding); + } + + huc.setDoOutput(true); // also sets method to POST. + huc.setRequestProperty("User-Agent", this.userAgent); + huc.setRequestProperty("Content-Type", "application/timestamp-query;charset=ISO-8859-1"); + + OutputStream hucOut = huc.getOutputStream(); + hucOut.write(encodedRequest); + + // invoke TSP service + huc.connect(); + + int statusCode = huc.getResponseCode(); + if (statusCode != 200) { + LOG.log(POILogger.ERROR, "Error contacting TSP server ", this.tspServiceUrl); + throw new Exception("Error contacting TSP server " + this.tspServiceUrl); + } + + // HTTP input validation + String contentType = huc.getHeaderField("Content-Type"); + if (null == contentType) { + throw new RuntimeException("missing Content-Type header"); + } + + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + IOUtils.copy(huc.getInputStream(), bos); + LOG.log(POILogger.DEBUG, "response content: ", bos.toString()); + + if (!contentType.startsWith("application/timestamp-reply")) { + throw new RuntimeException("invalid Content-Type: " + contentType); + } + + if (bos.size() == 0) { + throw new RuntimeException("Content-Length is zero"); + } + + // TSP response parsing and validation + TimeStampResponseIf timeStampResponse = HorribleProxy.newProxy(TimeStampResponseIf.class, bos.toByteArray()); + timeStampResponse.validate(request); + + if (0 != timeStampResponse.getStatus()) { + LOG.log(POILogger.DEBUG, "status: " + timeStampResponse.getStatus()); + LOG.log(POILogger.DEBUG, "status string: " + timeStampResponse.getStatusString()); + PKIFailureInfoIf failInfo = timeStampResponse.getFailInfo(); + if (null != failInfo) { + LOG.log(POILogger.DEBUG, "fail info int value: " + failInfo.intValue()); + if (/*PKIFailureInfo.unacceptedPolicy*/(1 << 8) == failInfo.intValue()) { + LOG.log(POILogger.DEBUG, "unaccepted policy"); + } + } + throw new RuntimeException("timestamp response status != 0: " + + timeStampResponse.getStatus()); + } + TimeStampTokenIf timeStampToken = timeStampResponse.getTimeStampToken(); + SignerIdIf signerId = timeStampToken.getSID(); + BigInteger signerCertSerialNumber = signerId.getSerialNumber(); + X500Principal signerCertIssuer = signerId.getIssuer(); + LOG.log(POILogger.DEBUG, "signer cert serial number: " + signerCertSerialNumber); + LOG.log(POILogger.DEBUG, "signer cert issuer: " + signerCertIssuer); + + // TSP signer certificates retrieval + Collection certificates = timeStampToken.getCertificates().getMatches(null); + + X509Certificate signerCert = null; + Map certificateMap = new HashMap(); + for (Certificate certificate : certificates) { + X509Certificate x509Certificate = (X509Certificate) certificate; + if (signerCertIssuer.equals(x509Certificate + .getIssuerX500Principal()) + && signerCertSerialNumber.equals(x509Certificate + .getSerialNumber())) { + signerCert = x509Certificate; + } + String ski = Hex.encodeHexString(getSubjectKeyId(x509Certificate)); + certificateMap.put(ski, x509Certificate); + LOG.log(POILogger.DEBUG, "embedded certificate: " + + x509Certificate.getSubjectX500Principal() + "; SKI=" + + ski); + } + + // TSP signer cert path building + if (null == signerCert) { + throw new RuntimeException( + "TSP response token has no signer certificate"); + } + List tspCertificateChain = new LinkedList(); + X509Certificate certificate = signerCert; + do { + LOG.log(POILogger.DEBUG, "adding to certificate chain: " + + certificate.getSubjectX500Principal()); + tspCertificateChain.add(certificate); + if (certificate.getSubjectX500Principal().equals( + certificate.getIssuerX500Principal())) { + break; + } + String aki = Hex.encodeHexString(getAuthorityKeyId(certificate)); + certificate = certificateMap.get(aki); + } while (null != certificate); + + // verify TSP signer signature + X509CertificateHolderIf holder = HorribleProxy.newProxy(X509CertificateHolderIf.class, tspCertificateChain.get(0).getEncoded()); + DefaultDigestAlgorithmIdentifierFinderIf finder = HorribleProxy.newProxy(DefaultDigestAlgorithmIdentifierFinderIf.class); + BcDigestCalculatorProviderIf calculator = HorribleProxy.newProxy(BcDigestCalculatorProviderIf.class); + BcRSASignerInfoVerifierBuilderIf verifierBuilder = HorribleProxy.newProxy(BcRSASignerInfoVerifierBuilderIf.class, finder, calculator); + SignerInformationVerifierIf verifier = verifierBuilder.build(holder); + + timeStampToken.validate(verifier); + + // verify TSP signer certificate + this.validator.validate(tspCertificateChain, revocationData); + + LOG.log(POILogger.DEBUG, "time-stamp token time: " + + timeStampToken.getTimeStampInfo().getGenTime()); + + byte[] timestamp = timeStampToken.getEncoded(); + return timestamp; + } + + private byte[] getSubjectKeyId(X509Certificate cert) throws Exception { + // X509Extensions.SubjectKeyIdentifier.getId() + byte[] extvalue = cert.getExtensionValue("2.5.29.14"); + if (extvalue == null) return null; + + ASN1InputStreamIf keyCntStream = HorribleProxy.newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(extvalue)); + ASN1OctetStringIf cntStr = HorribleProxy.createProxy(ASN1OctetStringIf.class, "getInstance", keyCntStream.readObject$Object()); + ASN1InputStreamIf keyIdStream = HorribleProxy.newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(cntStr.getOctets())); + SubjectKeyIdentifierIf keyId = HorribleProxy.createProxy(SubjectKeyIdentifierIf.class, "getInstance", keyIdStream.readObject$Object()); + + return keyId.getKeyIdentifier(); + } + + private byte[] getAuthorityKeyId(X509Certificate cert) throws Exception { + // X509Extensions.AuthorityKeyIdentifier.getId() + byte[] extvalue = cert.getExtensionValue("2.5.29.35"); + if (extvalue == null) return null; + + ASN1InputStreamIf keyCntStream = HorribleProxy.newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(extvalue)); + DEROctetStringIf cntStr = keyCntStream.readObject$DERString(); + ASN1InputStreamIf keyIdStream = HorribleProxy.newProxy(ASN1InputStreamIf.class, new ByteArrayInputStream(cntStr.getOctets())); + AuthorityKeyIdentifierIf keyId = HorribleProxy.newProxy(AuthorityKeyIdentifierIf.class, keyIdStream.readObject$Sequence()); + + return keyId.getKeyIdentifier(); + } +} \ No newline at end of file Added: poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TimeStampService.java URL: http://svn.apache.org/viewvc/poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TimeStampService.java?rev=1617141&view=auto ============================================================================== --- poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TimeStampService.java (added) +++ poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TimeStampService.java Sun Aug 10 18:25:10 2014 @@ -0,0 +1,52 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +/* ==================================================================== + This product contains an ASLv2 licensed version of the OOXML signer + package from the eID Applet project + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + Copyright (C) 2008-2014 FedICT. + ================================================================= */ + +package org.apache.poi.poifs.crypt.dsig.services; + + +/** + * Interface for a time-stamp service. + * + * @author Frank Cornelis + * + */ +public interface TimeStampService { + + /** + * Gives back the encoded time-stamp token for the given array of data + * bytes. We assume that the time-stamp token itself contains its full + * certificate chain required for proper validation. + * + * @param data + * the data to be time-stamped. + * @param revocationData + * the optional container that needs to be filled up with the + * revocation data used to validate the TSA certificate chain. + * @return the DER encoded time-stamp token. + * @throws Exception + * in case something went wrong. + */ + byte[] timeStamp(byte[] data, RevocationData revocationData) + throws Exception; +} Added: poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TimeStampServiceValidator.java URL: http://svn.apache.org/viewvc/poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TimeStampServiceValidator.java?rev=1617141&view=auto ============================================================================== --- poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TimeStampServiceValidator.java (added) +++ poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/TimeStampServiceValidator.java Sun Aug 10 18:25:10 2014 @@ -0,0 +1,51 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +/* ==================================================================== + This product contains an ASLv2 licensed version of the OOXML signer + package from the eID Applet project + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + Copyright (C) 2008-2014 FedICT. + ================================================================= */ + +package org.apache.poi.poifs.crypt.dsig.services; + +import java.security.cert.X509Certificate; +import java.util.List; + +/** + * Interface for trust validator of a TSP. + * + * @author Frank Cornelis + * + */ +public interface TimeStampServiceValidator { + + /** + * Validates the given certificate chain. + * + * @param certificateChain + * @param revocationData + * the optional data container that should be filled with + * revocation data that was used to validate the given + * certificate chain. + * @throws Exception + * in case the certificate chain is invalid. + */ + void validate(List certificateChain, + RevocationData revocationData) throws Exception; +} Added: poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/XmlSignatureService.java URL: http://svn.apache.org/viewvc/poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/XmlSignatureService.java?rev=1617141&view=auto ============================================================================== --- poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/XmlSignatureService.java (added) +++ poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/services/XmlSignatureService.java Sun Aug 10 18:25:10 2014 @@ -0,0 +1,610 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +/* ==================================================================== + This product contains an ASLv2 licensed version of the OOXML signer + package from the eID Applet project + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + Copyright (C) 2008-2014 FedICT. + ================================================================= */ + +package org.apache.poi.poifs.crypt.dsig.services; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.security.InvalidAlgorithmParameterException; +import java.security.Key; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import javax.xml.crypto.MarshalException; +import javax.xml.crypto.URIDereferencer; +import javax.xml.crypto.XMLStructure; +import javax.xml.crypto.dom.DOMCryptoContext; +import javax.xml.crypto.dsig.CanonicalizationMethod; +import javax.xml.crypto.dsig.DigestMethod; +import javax.xml.crypto.dsig.Manifest; +import javax.xml.crypto.dsig.Reference; +import javax.xml.crypto.dsig.SignatureMethod; +import javax.xml.crypto.dsig.SignedInfo; +import javax.xml.crypto.dsig.XMLObject; +import javax.xml.crypto.dsig.XMLSignContext; +import javax.xml.crypto.dsig.XMLSignatureFactory; +import javax.xml.crypto.dsig.dom.DOMSignContext; +import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactoryConfigurationError; + +import org.apache.poi.openxml4j.exceptions.InvalidFormatException; +import org.apache.poi.openxml4j.opc.OPCPackage; +import org.apache.poi.openxml4j.opc.PackageNamespaces; +import org.apache.poi.openxml4j.opc.PackagePart; +import org.apache.poi.openxml4j.opc.PackagePartName; +import org.apache.poi.openxml4j.opc.PackageRelationship; +import org.apache.poi.openxml4j.opc.PackageRelationshipCollection; +import org.apache.poi.openxml4j.opc.PackageRelationshipTypes; +import org.apache.poi.openxml4j.opc.PackagingURIHelper; +import org.apache.poi.openxml4j.opc.TargetMode; +import org.apache.poi.poifs.crypt.CryptoFunctions; +import org.apache.poi.poifs.crypt.HashAlgorithm; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DOMReferenceIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DOMSignedInfoIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.DOMXMLSignatureIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxies.XMLSignatureIf; +import org.apache.poi.poifs.crypt.dsig.HorribleProxy; +import org.apache.poi.poifs.crypt.dsig.OOXMLURIDereferencer; +import org.apache.poi.poifs.crypt.dsig.SignatureInfo; +import org.apache.poi.poifs.crypt.dsig.facets.KeyInfoSignatureFacet; +import org.apache.poi.poifs.crypt.dsig.facets.OOXMLSignatureFacet; +import org.apache.poi.poifs.crypt.dsig.facets.Office2010SignatureFacet; +import org.apache.poi.poifs.crypt.dsig.facets.SignatureFacet; +import org.apache.poi.poifs.crypt.dsig.facets.XAdESSignatureFacet; +import org.apache.poi.poifs.crypt.dsig.spi.AddressDTO; +import org.apache.poi.poifs.crypt.dsig.spi.Constants; +import org.apache.poi.poifs.crypt.dsig.spi.DigestInfo; +import org.apache.poi.poifs.crypt.dsig.spi.IdentityDTO; +import org.apache.poi.util.POILogFactory; +import org.apache.poi.util.POILogger; +import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlOptions; +import org.w3.x2000.x09.xmldsig.SignatureDocument; +import org.w3.x2000.x09.xmldsig.SignatureType; +import org.w3.x2000.x09.xmldsig.SignatureValueType; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + + +/** + * Abstract base class for an XML Signature Service implementation. + */ +public class XmlSignatureService implements SignatureService { + private static final POILogger LOG = POILogFactory.getLogger(XmlSignatureService.class); + + protected final List signatureFacets; + + private String signatureNamespacePrefix; + private String signatureId; + private final HashAlgorithm hashAlgo; + private final OPCPackage opcPackage; + private SignatureDocument sigDoc; + private XAdESSignatureFacet xadesSignatureFacet; + + /** + * Main constructor. + */ + 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; + } + + public void initFacets(Date clock) { + if (clock == null) clock = new Date(); + addSignatureFacet(new OOXMLSignatureFacet(this, clock, hashAlgo)); + addSignatureFacet(new KeyInfoSignatureFacet(true, false, false)); + + this.xadesSignatureFacet = new XAdESSignatureFacet(clock, hashAlgo, null); + this.xadesSignatureFacet.setIdSignedProperties("idSignedProperties"); + this.xadesSignatureFacet.setSignaturePolicyImplied(true); + /* + * Work-around for Office 2010. + */ + this.xadesSignatureFacet.setIssuerNameNoReverseOrder(true); + setSignatureId("idPackageSignature"); + addSignatureFacet(this.xadesSignatureFacet); + addSignatureFacet(new Office2010SignatureFacet()); + } + + + /** + * Sets the signature Id attribute value used to create the XML signature. A + * null value will trigger an automatically generated signature + * Id. + * + * @param signatureId + */ + protected void setSignatureId(String signatureId) { + this.signatureId = signatureId; + } + + /** + * Sets the XML Signature namespace prefix to be used for signature + * creation. A null value will omit the prefixing. + * + * @param signatureNamespacePrefix + */ + protected void setSignatureNamespacePrefix(String signatureNamespacePrefix) { + this.signatureNamespacePrefix = signatureNamespacePrefix; + } + + /** + * Adds a signature facet to this XML signature service. + * + * @param signatureFacet + */ + protected void addSignatureFacet(SignatureFacet signatureFacet) { + this.signatureFacets.add(signatureFacet); + } + + /** + * Gives back the signature digest algorithm. Allowed values are SHA-1, + * SHA-256, SHA-384, SHA-512, RIPEND160. The default algorithm is SHA-1. + * Override this method to select another signature digest algorithm. + * + * @return + */ + protected HashAlgorithm getSignatureDigestAlgorithm() { + return null != this.hashAlgo ? this.hashAlgo : HashAlgorithm.sha1; + } + + /** + * Override this method to change the URI dereferener used by the signing + * engine. + * + * @return + */ + protected URIDereferencer getURIDereferencer() { + OPCPackage ooxmlDocument = getOfficeOpenXMLDocument(); + return new OOXMLURIDereferencer(ooxmlDocument); + } + + /** + * Gives back the human-readable description of what the citizen will be + * signing. The default value is "XML Document". Override this method to + * provide the citizen with another description. + * + * @return + */ + protected String getSignatureDescription() { + return "Office OpenXML Document"; + } + + /** + * Gives back the URL of the OOXML to be signed. + * + * @return + */ + public OPCPackage getOfficeOpenXMLDocument() { + return opcPackage; + } + + + + /** + * Gives back the output stream to which to write the signed XML document. + * + * @return + */ + // protected abstract OutputStream getSignedDocumentOutputStream(); + + public DigestInfo preSign(List digestInfos, + List signingCertificateChain, + IdentityDTO identity, AddressDTO address, byte[] photo) + throws NoSuchAlgorithmException { + SignatureInfo.initXmlProvider(); + + LOG.log(POILogger.DEBUG, "preSign"); + HashAlgorithm hashAlgo = getSignatureDigestAlgorithm(); + + byte[] digestValue; + try { + digestValue = getXmlSignatureDigestValue(hashAlgo, digestInfos, signingCertificateChain); + } catch (Exception e) { + throw new RuntimeException("XML signature error: " + e.getMessage(), e); + } + + String description = getSignatureDescription(); + return new DigestInfo(digestValue, hashAlgo, description); + } + + public void postSign(byte[] signatureValue, List signingCertificateChain) + throws IOException { + LOG.log(POILogger.DEBUG, "postSign"); + SignatureInfo.initXmlProvider(); + + /* + * Retrieve the intermediate XML signature document from the temporary + * data storage. + */ + SignatureType sigType = sigDoc.getSignature(); + + /* + * Check ds:Signature node. + */ + if (!signatureId.equals(sigType.getId())) { + throw new RuntimeException("ds:Signature not found for @Id: " + signatureId); + } + + /* + * Insert signature value into the ds:SignatureValue element + */ + SignatureValueType sigVal = sigType.getSignatureValue(); + sigVal.setByteArrayValue(signatureValue); + + /* + * Allow signature facets to inject their own stuff. + */ + for (SignatureFacet signatureFacet : this.signatureFacets) { + signatureFacet.postSign(sigType, signingCertificateChain); + } + + writeDocument(); + } + + @SuppressWarnings("unchecked") + private byte[] getXmlSignatureDigestValue(HashAlgorithm hashAlgo, + List digestInfos, + List signingCertificateChain) + throws ParserConfigurationException, NoSuchAlgorithmException, + InvalidAlgorithmParameterException, MarshalException, + javax.xml.crypto.dsig.XMLSignatureException, + TransformerFactoryConfigurationError, TransformerException, + IOException, SAXException, NoSuchProviderException, XmlException { + /* + * DOM Document construction. + */ + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + Document doc = dbf.newDocumentBuilder().newDocument(); + + /* + * Signature context construction. + */ + Key key = new Key() { + private static final long serialVersionUID = 1L; + + public String getAlgorithm() { + return null; + } + + public byte[] getEncoded() { + return null; + } + + public String getFormat() { + return null; + } + }; + + // As of JDK 7, can't use sigDoc here directly, because the + // setAttributeID will be called and it's not implemented in xmlbeans + XMLSignContext xmlSignContext = new DOMSignContext(key, doc); + URIDereferencer uriDereferencer = getURIDereferencer(); + if (null != uriDereferencer) { + xmlSignContext.setURIDereferencer(uriDereferencer); + } + + xmlSignContext.putNamespacePrefix( + "http://schemas.openxmlformats.org/package/2006/digital-signature", + "mdssi"); + + if (this.signatureNamespacePrefix != null) { + /* + * OOo doesn't like ds namespaces so per default prefixing is off. + */ + xmlSignContext.putNamespacePrefix( + javax.xml.crypto.dsig.XMLSignature.XMLNS, + this.signatureNamespacePrefix); + } + + XMLSignatureFactory signatureFactory = XMLSignatureFactory.getInstance("DOM", "XMLDSig"); + + /* + * Add ds:References that come from signing client local files. + */ + List references = new LinkedList(); + addDigestInfosAsReferences(digestInfos, signatureFactory, references); + + /* + * Invoke the signature facets. + */ + String localSignatureId; + if (null == this.signatureId) { + localSignatureId = "xmldsig-" + UUID.randomUUID().toString(); + } else { + localSignatureId = this.signatureId; + } + List objects = new LinkedList(); + for (SignatureFacet signatureFacet : this.signatureFacets) { + LOG.log(POILogger.DEBUG, "invoking signature facet: " + + signatureFacet.getClass().getSimpleName()); + signatureFacet.preSign(signatureFactory, localSignatureId, signingCertificateChain, references, objects); + } + + /* + * ds:SignedInfo + */ + SignatureMethod signatureMethod = signatureFactory.newSignatureMethod( + getSignatureMethod(hashAlgo), null); + CanonicalizationMethod canonicalizationMethod = signatureFactory + .newCanonicalizationMethod(getCanonicalizationMethod(), + (C14NMethodParameterSpec) null); + SignedInfo signedInfo = signatureFactory.newSignedInfo( + canonicalizationMethod, signatureMethod, references); + + /* + * JSR105 ds:Signature creation + */ + String signatureValueId = localSignatureId + "-signature-value"; + javax.xml.crypto.dsig.XMLSignature xmlSignature = signatureFactory + .newXMLSignature(signedInfo, null, objects, localSignatureId, + signatureValueId); + + /* + * ds:Signature Marshalling. + */ + DOMXMLSignatureIf domXmlSignature; + try { + domXmlSignature = HorribleProxy.newProxy(DOMXMLSignatureIf.class, xmlSignature); + } catch (Exception e) { + throw new RuntimeException("DomXmlSignature instance error: " + e.getMessage(), e); + } + + domXmlSignature.marshal(doc, this.signatureNamespacePrefix, (DOMCryptoContext) xmlSignContext); + + registerIds(doc); + Element el = doc.getElementById("idPackageObject"); + assert (el != null); + el.setAttributeNS(Constants.NamespaceSpecNS, "xmlns:mdssi", PackageNamespaces.DIGITAL_SIGNATURE); + + + /* + * Completion of undigested ds:References in the ds:Manifests. + */ + for (XMLObject object : objects) { + LOG.log(POILogger.DEBUG, "object java type: " + object.getClass().getName()); + List objectContentList = object.getContent(); + for (XMLStructure objectContent : objectContentList) { + LOG.log(POILogger.DEBUG, "object content java type: " + objectContent.getClass().getName()); + if (!(objectContent instanceof Manifest)) continue; + Manifest manifest = (Manifest) objectContent; + List manifestReferences = manifest.getReferences(); + for (Reference manifestReference : manifestReferences) { + if (manifestReference.getDigestValue() != null) continue; + + DOMReferenceIf manifestDOMReference; + try { + manifestDOMReference = HorribleProxy.newProxy(DOMReferenceIf.class, manifestReference); + } catch (Exception e) { + throw new RuntimeException("DOMReference instance error: " + e.getMessage(), e); + } + manifestDOMReference.digest(xmlSignContext); + } + } + } + + /* + * Completion of undigested ds:References. + */ + List signedInfoReferences = signedInfo.getReferences(); + for (Reference signedInfoReference : signedInfoReferences) { + DOMReferenceIf domReference; + try { + domReference = HorribleProxy.newProxy(DOMReferenceIf.class, signedInfoReference); + } catch (Exception e) { + throw new RuntimeException("DOMReference instance error: " + e.getMessage(), e); + } + + // ds:Reference with external digest value + if (domReference.getDigestValue() != null) continue; + + domReference.digest(xmlSignContext); + } + + /* + * Calculation of XML signature digest value. + */ + DOMSignedInfoIf domSignedInfo; + try { + domSignedInfo = HorribleProxy.newProxy(DOMSignedInfoIf.class, signedInfo); + } catch (Exception e) { + throw new RuntimeException("DOMSignedInfo instance error: " + e.getMessage(), e); + } + + ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); + domSignedInfo.canonicalize(xmlSignContext, dataStream); + byte[] octets = dataStream.toByteArray(); + + sigDoc = SignatureDocument.Factory.parse(doc.getDocumentElement()); + + + /* + * TODO: we could be using DigestOutputStream here to optimize memory + * usage. + */ + + MessageDigest jcaMessageDigest = CryptoFunctions.getMessageDigest(hashAlgo); + byte[] digestValue = jcaMessageDigest.digest(octets); + return digestValue; + } + + /** + * the resulting document needs to be tweaked before it can be digested - + * this applies to the verification and signing step + * + * @param doc + */ + public void registerIds(Document doc) { + NodeList nl = doc.getElementsByTagNameNS("http://www.w3.org/2000/09/xmldsig#", "Object"); + registerIdAttribute(nl); + nl = doc.getElementsByTagNameNS("http://uri.etsi.org/01903/v1.3.2#", "SignedProperties"); + registerIdAttribute(nl); + } + + protected void registerIdAttribute(NodeList nl) { + for (int i=0; i digestInfos, + XMLSignatureFactory signatureFactory, List references) + throws NoSuchAlgorithmException, + InvalidAlgorithmParameterException, MalformedURLException { + if (digestInfos == null) return; + for (DigestInfo digestInfo : digestInfos) { + byte[] documentDigestValue = digestInfo.digestValue; + + DigestMethod digestMethod = signatureFactory.newDigestMethod( + digestInfo.hashAlgo.xmlSignUri, null); + + String uri = new File(digestInfo.description).getName(); + + Reference reference = signatureFactory.newReference(uri, + digestMethod, null, null, null, documentDigestValue); + references.add(reference); + } + } + + private String getSignatureMethod(HashAlgorithm hashAlgo) { + if (null == hashAlgo) { + throw new RuntimeException("digest algo is null"); + } + + XMLSignatureIf XmlSignature; + try { + XmlSignature = HorribleProxy.newProxy(XMLSignatureIf.class); + } catch (Exception e) { + throw new RuntimeException("JDK doesn't support XmlSignature", e); + } + + switch (hashAlgo) { + case sha1: return XmlSignature.ALGO_ID_SIGNATURE_RSA_SHA1(); + case sha256: return XmlSignature.ALGO_ID_SIGNATURE_RSA_SHA256(); + case sha384: return XmlSignature.ALGO_ID_SIGNATURE_RSA_SHA384(); + case sha512: return XmlSignature.ALGO_ID_SIGNATURE_RSA_SHA512(); + case ripemd160: return XmlSignature.ALGO_ID_MAC_HMAC_RIPEMD160(); + default: break; + } + + throw new RuntimeException("unsupported sign algo: " + hashAlgo); + } + + /** + * Gives back the used XAdES signature facet. + * + * @return + */ + protected XAdESSignatureFacet getXAdESSignatureFacet() { + return this.xadesSignatureFacet; + } + + public String getFilesDigestAlgorithm() { + return null; + } + + protected String getCanonicalizationMethod() { + return CanonicalizationMethod.INCLUSIVE; + } + + protected void writeDocument() throws IOException { + XmlOptions xo = new XmlOptions(); + Map namespaceMap = new HashMap(); + for (SignatureFacet sf : this.signatureFacets) { + Map sfm = sf.getNamespacePrefixMapping(); + if (sfm != null) { + namespaceMap.putAll(sfm); + } + } + xo.setSaveSuggestedPrefixes(namespaceMap); + xo.setUseDefaultNamespace(); + + LOG.log(POILogger.DEBUG, "output signed Office OpenXML document"); + + /* + * Copy the original OOXML content to the signed OOXML package. During + * copying some files need to changed. + */ + OPCPackage pkg = this.getOfficeOpenXMLDocument(); + + PackagePartName sigPartName, sigsPartName; + try { + // + sigPartName = PackagingURIHelper.createPartName("/_xmlsignatures/sig1.xml"); + // + sigsPartName = PackagingURIHelper.createPartName("/_xmlsignatures/origin.sigs"); + } catch (InvalidFormatException e) { + throw new IOException(e); + } + + String sigContentType = "application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml"; + PackagePart sigPart = pkg.getPart(sigPartName); + if (sigPart == null) { + sigPart = pkg.createPart(sigPartName, sigContentType); + } + + OutputStream os = sigPart.getOutputStream(); + sigDoc.save(os, xo); + os.close(); + + String sigsContentType = "application/vnd.openxmlformats-package.digital-signature-origin"; + PackagePart sigsPart = pkg.getPart(sigsPartName); + if (sigsPart == null) { + // touch empty marker file + sigsPart = pkg.createPart(sigsPartName, sigsContentType); + } + + PackageRelationshipCollection relCol = pkg.getRelationshipsByType(PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN); + for (PackageRelationship pr : relCol) { + pkg.removeRelationship(pr.getId()); + } + pkg.addRelationship(sigsPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE_ORIGIN); + + sigsPart.addRelationship(sigPartName, TargetMode.INTERNAL, PackageRelationshipTypes.DIGITAL_SIGNATURE); + } +} Added: poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/AddressDTO.java URL: http://svn.apache.org/viewvc/poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/AddressDTO.java?rev=1617141&view=auto ============================================================================== --- poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/AddressDTO.java (added) +++ poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/AddressDTO.java Sun Aug 10 18:25:10 2014 @@ -0,0 +1,51 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +/* ==================================================================== + This product contains an ASLv2 licensed version of the OOXML signer + package from the eID Applet project + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + Copyright (C) 2008-2014 FedICT. + ================================================================= */ + +package org.apache.poi.poifs.crypt.dsig.spi; + +import java.io.Serializable; +import java.security.Identity; + +/** + * Address Data Transfer Object. + * + * @author Frank Cornelis + * @see Identity + * + */ +public class AddressDTO implements Serializable { + + /* + * We implement serializable to allow this class to be used in distributed + * containers as defined in the Servlet v2.4 specification. + */ + + private static final long serialVersionUID = 1L; + + public String streetAndNumber; + + public String zip; + + public String city; +} \ No newline at end of file Added: poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/Constants.java URL: http://svn.apache.org/viewvc/poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/Constants.java?rev=1617141&view=auto ============================================================================== --- poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/Constants.java (added) +++ poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/Constants.java Sun Aug 10 18:25:10 2014 @@ -0,0 +1,30 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +/* ==================================================================== + This product contains an ASLv2 licensed version of the OOXML signer + package from the eID Applet project + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + Copyright (C) 2008-2014 FedICT. + ================================================================= */ + +package org.apache.poi.poifs.crypt.dsig.spi; + +public interface Constants { + String NamespaceSpecNS = "http://www.w3.org/2000/xmlns/"; + String SignatureSpecNS = "http://www.w3.org/2000/09/xmldsig#"; +} Added: poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/DigestInfo.java URL: http://svn.apache.org/viewvc/poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/DigestInfo.java?rev=1617141&view=auto ============================================================================== --- poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/DigestInfo.java (added) +++ poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/DigestInfo.java Sun Aug 10 18:25:10 2014 @@ -0,0 +1,56 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +/* ==================================================================== + This product contains an ASLv2 licensed version of the OOXML signer + package from the eID Applet project + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + Copyright (C) 2008-2014 FedICT. + ================================================================= */ + +package org.apache.poi.poifs.crypt.dsig.spi; + +import java.io.Serializable; + +import org.apache.poi.poifs.crypt.HashAlgorithm; + +/** + * Digest Information data transfer class. + */ +public class DigestInfo implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * Main constructor. + * + * @param digestValue + * @param hashAlgo + * @param description + */ + public DigestInfo(byte[] digestValue, HashAlgorithm hashAlgo, String description) { + this.digestValue = digestValue; + this.hashAlgo = hashAlgo; + this.description = description; + } + + public final byte[] digestValue; + + public final String description; + + public final HashAlgorithm hashAlgo; +} Added: poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/IdentityDTO.java URL: http://svn.apache.org/viewvc/poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/IdentityDTO.java?rev=1617141&view=auto ============================================================================== --- poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/IdentityDTO.java (added) +++ poi/branches/xml_signature/src/ooxml/java/org/apache/poi/poifs/crypt/dsig/spi/IdentityDTO.java Sun Aug 10 18:25:10 2014 @@ -0,0 +1,75 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +/* ==================================================================== + This product contains an ASLv2 licensed version of the OOXML signer + package from the eID Applet project + http://code.google.com/p/eid-applet/source/browse/trunk/README.txt + Copyright (C) 2008-2014 FedICT. + ================================================================= */ + +package org.apache.poi.poifs.crypt.dsig.spi; + +import java.io.Serializable; +import java.util.GregorianCalendar; + +/** + * Identity Data Transfer Object. + * + * @author Frank Cornelis + * + */ +public class IdentityDTO implements Serializable { + + /* + * We implement serializable to allow this class to be used in distributed + * containers as defined in the Servlet v2.4 specification. + */ + private static final long serialVersionUID = 1L; + + public String cardNumber; + + public String chipNumber; + + public GregorianCalendar cardValidityDateBegin; + + public GregorianCalendar cardValidityDateEnd; + + public String cardDeliveryMunicipality; + + public String nationalNumber; + + public String name; + + public String firstName; + + public String middleName; + + public String nationality; + + public String placeOfBirth; + + public GregorianCalendar dateOfBirth; + + public boolean male; + + public boolean female; + + public String nobleCondition; + + public String duplicate; +} \ No newline at end of file --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscribe@poi.apache.org For additional commands, e-mail: commits-help@poi.apache.org