From 32d17447a258188b2d534bcb0bf65a659ba7b7d0 Mon Sep 17 00:00:00 2001
From: mcentner
+ * A transform to Base64-decode the xmlObject's content is inserted at the top
+ * of to the optional
+ * A transform to select the xmlObject's content is inserted at the top of to
+ * the optional
+ * The to-be parsed content is surrounded by
+ * This IdValueFactory creates signatureContext
.
+ *
+ * @param signatureContext
+ * the signature context
+ *
+ * @return a DigestMethod for the given signatureContext
+ *
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidAlgorithmParameterException
+ */
+ public DigestMethod createDigestMethod(SignatureContext signatureContext)
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException;
+
+ /**
+ * Creates a new SignatureMethod for the given signatureContext
.
+ *
+ * @param signatureContext
+ * the signature context
+ *
+ * @return a SignatureMethod for the given signatureContext
+ *
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidAlgorithmParameterException
+ */
+ public SignatureMethod createSignatureMethod(SignatureContext signatureContext)
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException;
+
+ /**
+ * Creates a new CanonicalizationMethod for the given
+ * signatureContext
.
+ *
+ * @param signatureContext
+ * the signature context
+ *
+ * @return a CanonicalizationMethod for the given
+ * signatureContext
+ *
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidAlgorithmParameterException
+ */
+ public CanonicalizationMethod createCanonicalizationMethod(
+ SignatureContext signatureContext) throws NoSuchAlgorithmException,
+ InvalidAlgorithmParameterException;
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/AlgorithmMethodFactoryImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/AlgorithmMethodFactoryImpl.java
new file mode 100644
index 00000000..6b963465
--- /dev/null
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/AlgorithmMethodFactoryImpl.java
@@ -0,0 +1,125 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed 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.
+*/
+package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import iaik.xml.crypto.XmldsigMore;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.cert.X509Certificate;
+
+import javax.xml.crypto.dsig.CanonicalizationMethod;
+import javax.xml.crypto.dsig.DigestMethod;
+import javax.xml.crypto.dsig.SignatureMethod;
+import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
+import javax.xml.crypto.dsig.spec.DigestMethodParameterSpec;
+import javax.xml.crypto.dsig.spec.SignatureMethodParameterSpec;
+
+/**
+ * An implementation of the AlgorithmMethod factory that uses the signing
+ * certificate to choose appropriate algorithms.
+ *
+ * @author mcentner
+ */
+public class AlgorithmMethodFactoryImpl implements AlgorithmMethodFactory {
+
+ /**
+ * The signature algorithm URI.
+ */
+ private String signatureAlgorithmURI;
+
+ /**
+ * The algorithm parameters for the signature algorithm.
+ */
+ private SignatureMethodParameterSpec signatureMethodParameterSpec;
+
+ /**
+ * Creates a new AlgrithmMethodFactory with the given
+ * signingCertificate
.
+ *
+ * @param siginingCertificate
+ *
+ * @throws NoSuchAlgorithmException
+ * if the public key algorithm of the given
+ * signingCertificate
is not supported
+ */
+ public AlgorithmMethodFactoryImpl(X509Certificate siginingCertificate)
+ throws NoSuchAlgorithmException {
+
+ String algorithm = siginingCertificate.getPublicKey().getAlgorithm();
+
+ if ("DSA".equals(algorithm)) {
+ signatureAlgorithmURI = SignatureMethod.DSA_SHA1;
+ } else if ("RSA".equals(algorithm)) {
+ signatureAlgorithmURI = SignatureMethod.RSA_SHA1;
+ } else if (("EC".equals(algorithm)) || ("ECDSA".equals(algorithm))) {
+ signatureAlgorithmURI = XmldsigMore.SIGNATURE_ECDSA_SHA1;
+ } else {
+ throw new NoSuchAlgorithmException("Public key algorithm '" + algorithm
+ + "' not supported.");
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @seeat.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactory#
+ * createCanonicalizationMethod
+ * (at.gv.egiz.bku.slcommands.impl.xsect.SignatureContext)
+ */
+ @Override
+ public CanonicalizationMethod createCanonicalizationMethod(
+ SignatureContext signatureContext) throws NoSuchAlgorithmException,
+ InvalidAlgorithmParameterException {
+
+ return signatureContext.getSignatureFactory().newCanonicalizationMethod(
+ CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null);
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactory#createDigestMethod
+ * (at.gv.egiz.bku.slcommands.impl.xsect.SignatureContext)
+ */
+ @Override
+ public DigestMethod createDigestMethod(SignatureContext signatureContext)
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
+
+ return signatureContext.getSignatureFactory().newDigestMethod(
+ DigestMethod.SHA1, (DigestMethodParameterSpec) null);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @seeat.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactory#
+ * createSignatureMethod
+ * (at.gv.egiz.bku.slcommands.impl.xsect.SignatureContext)
+ */
+ @Override
+ public SignatureMethod createSignatureMethod(SignatureContext signatureContext)
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
+
+ return signatureContext.getSignatureFactory().newSignatureMethod(
+ signatureAlgorithmURI, signatureMethodParameterSpec);
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/ByteArrayDereferencer.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/ByteArrayDereferencer.java
new file mode 100644
index 00000000..a6473a05
--- /dev/null
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/ByteArrayDereferencer.java
@@ -0,0 +1,65 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed 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.
+*/
+package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import java.io.ByteArrayInputStream;
+
+import javax.xml.crypto.Data;
+import javax.xml.crypto.OctetStreamData;
+import javax.xml.crypto.URIDereferencer;
+import javax.xml.crypto.URIReference;
+import javax.xml.crypto.URIReferenceException;
+import javax.xml.crypto.XMLCryptoContext;
+
+/**
+ * An URIDereferencer implementation that dereferences the given
+ * byte array.
+ *
+ * @author mcentner
+ */
+public class ByteArrayDereferencer implements URIDereferencer {
+
+ /**
+ * The dereferenced data.
+ */
+ protected byte[] dereferencedData;
+
+ /**
+ * Creates a new instance of this ByteArrayDereferencer with
+ * the given dereferencedData
.
+ *
+ * @param dereferencedData the octets to be returned by {@link #dereference(URIReference, XMLCryptoContext)}
+ *
+ * @throws NullPointerException if dereferencedData
is null
+ */
+ public ByteArrayDereferencer(byte[] dereferencedData) {
+ if (dereferencedData == null) {
+ throw new NullPointerException("Parameter 'dereferencedData' must not be null.");
+ }
+ this.dereferencedData = dereferencedData;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.xml.crypto.URIDereferencer#dereference(javax.xml.crypto.URIReference, javax.xml.crypto.XMLCryptoContext)
+ */
+ @Override
+ public Data dereference(URIReference uriReference, XMLCryptoContext context)
+ throws URIReferenceException {
+ return new OctetStreamData(new ByteArrayInputStream(dereferencedData));
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java
new file mode 100644
index 00000000..d25f2526
--- /dev/null
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java
@@ -0,0 +1,1006 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed 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.
+*/
+package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import iaik.xml.crypto.dom.DOMCryptoContext;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.SequenceInputStream;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.crypto.MarshalException;
+import javax.xml.crypto.dom.DOMStructure;
+import javax.xml.crypto.dsig.CanonicalizationMethod;
+import javax.xml.crypto.dsig.DigestMethod;
+import javax.xml.crypto.dsig.Reference;
+import javax.xml.crypto.dsig.Transform;
+import javax.xml.crypto.dsig.XMLObject;
+import javax.xml.crypto.dsig.spec.TransformParameterSpec;
+import javax.xml.crypto.dsig.spec.XPathFilter2ParameterSpec;
+import javax.xml.crypto.dsig.spec.XPathType;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.w3c.dom.DOMConfiguration;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.Text;
+import org.w3c.dom.bootstrap.DOMImplementationRegistry;
+import org.w3c.dom.ls.DOMImplementationLS;
+import org.w3c.dom.ls.LSException;
+import org.w3c.dom.ls.LSInput;
+import org.w3c.dom.ls.LSOutput;
+import org.w3c.dom.ls.LSParser;
+import org.w3c.dom.ls.LSSerializer;
+
+import at.buergerkarte.namespaces.securitylayer._1.Base64XMLLocRefOptRefContentType;
+import at.buergerkarte.namespaces.securitylayer._1.DataObjectInfoType;
+import at.buergerkarte.namespaces.securitylayer._1.MetaInfoType;
+import at.buergerkarte.namespaces.securitylayer._1.TransformsInfoType;
+import at.gv.egiz.bku.binding.HttpUtil;
+import at.gv.egiz.bku.slexceptions.SLCommandException;
+import at.gv.egiz.bku.slexceptions.SLRequestException;
+import at.gv.egiz.bku.slexceptions.SLRuntimeException;
+import at.gv.egiz.bku.utils.urldereferencer.StreamData;
+import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer;
+import at.gv.egiz.dom.DOMUtils;
+import at.gv.egiz.slbinding.impl.XMLContentType;
+
+/**
+ * This class represents a DataObject
of an XML-Signature
+ * created by the security layer command CreateXMLSignature
.
+ *
+ * @author mcentner
+ */
+public class DataObject {
+
+ /**
+ * Logging facility.
+ */
+ private static Log log = LogFactory.getLog(DataObject.class);
+
+ /**
+ * DOM Implementation.
+ */
+ private static final String DOM_LS_3_0 = "LS 3.0";
+
+ /**
+ * The array of the default preferred MIME type order.
+ */
+ private static final String[] DEFAULT_PREFFERED_MIME_TYPES =
+ new String[] {
+ "application/xhtml+xml",
+ "text/plain"
+ };
+
+ /**
+ * The DOM implementation used.
+ */
+ private DOMImplementationLS domImplLS;
+
+ /**
+ * The signature context.
+ */
+ private SignatureContext ctx;
+
+ /**
+ * The Reference for this DataObject.
+ */
+ private XSECTReference reference;
+
+ /**
+ * The XMLObject for this DataObject.
+ */
+ private XMLObject xmlObject;
+
+ /**
+ * The MIME-Type of the digest input.
+ */
+ private String mimeType;
+
+ /**
+ * An optional description of the digest input.
+ */
+ private String description;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param document the document of the target signature
+ */
+ public DataObject(SignatureContext signatureContext) {
+ this.ctx = signatureContext;
+
+ DOMImplementationRegistry registry;
+ try {
+ registry = DOMImplementationRegistry.newInstance();
+ } catch (Exception e) {
+ log.error("Failed to get DOMImplementationRegistry.", e);
+ throw new SLRuntimeException("Failed to get DOMImplementationRegistry.");
+ }
+
+ domImplLS = (DOMImplementationLS) registry.getDOMImplementation(DOM_LS_3_0);
+ if (domImplLS == null) {
+ log.error("Failed to get DOMImplementation " + DOM_LS_3_0);
+ throw new SLRuntimeException("Failed to get DOMImplementation " + DOM_LS_3_0);
+ }
+
+ }
+
+ /**
+ * @return the reference
+ */
+ public Reference getReference() {
+ return reference;
+ }
+
+ /**
+ * @return the xmlObject
+ */
+ public XMLObject getXmlObject() {
+ return xmlObject;
+ }
+
+ /**
+ * @return the mimeType
+ */
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ /**
+ * @return the description
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Configures this DataObject with the information provided within the given
+ * sl:DataObjectInfo
.
+ *
+ * @param dataObjectInfo
+ * the sl:DataObjectInfo
+ *
+ * @throws SLCommandException
+ * if configuring this DataObject with the information provided in
+ * the sl:DataObjectInfo
fails.
+ * @throws SLRequestException
+ * if the information provided in the sl:DataObjectInfo
+ * does not conform to the security layer specification.
+ * @throws NullPointerException
+ * if dataObjectInfo
is null
+ */
+ public void setDataObjectInfo(DataObjectInfoType dataObjectInfo) throws SLCommandException, SLRequestException {
+
+ Base64XMLLocRefOptRefContentType dataObject = dataObjectInfo.getDataObject();
+ String structure = dataObjectInfo.getStructure();
+
+ // select and unmarshal an appropriate transformation path if provided
+ // and set the final data meta information
+ XSECTTransforms transforms = createTransformsAndSetFinalDataMetaInfo(dataObjectInfo.getTransformsInfo());
+
+ if ("enveloping".equals(structure)) {
+
+ // configure this DataObject as an enveloped DataObject
+ setEnvelopedDataObject(dataObject, transforms);
+
+ } else if ("detached".equals(structure)) {
+
+ // configure this DataObject as an detached DataObject
+ setDetachedDataObject(dataObject, transforms);
+
+ }
+ // other values are not allowed by the schema and are therefore ignored
+
+ }
+
+ /**
+ * Configures this DataObject as an enveloped DataObject with the information
+ * provided within the given sl:DataObject
.
+ *
+ * @param dataObject
+ * the sl:DataObject
+ * @param transforms
+ * an optional Transforms
element (may be
+ * null
)
+ *
+ * @throws SLCommandException
+ * if configuring this DataObject with the information provided in
+ * the sl:DataObject
fails.
+ * @throws SLRequestException
+ * if the information provided in the sl:DataObject
+ * does not conform to the security layer specification.
+ * @throws NullPointerException
+ * if dataObject
is null
+ */
+ private void setEnvelopedDataObject(
+ Base64XMLLocRefOptRefContentType dataObject, XSECTTransforms transforms)
+ throws SLCommandException, SLRequestException {
+
+ String reference = dataObject.getReference();
+ if (reference == null) {
+ //
+ // case A
+ //
+ // The Reference attribute is not used; the content of sl:DataObject represents the data object.
+ // If the data object is XML-coded (the sl:XMLContent element is used in sl:DataObject), then it
+ // must be incorporated in the signature structure as parsed XML.
+ //
+
+ if (dataObject.getBase64Content() != null) {
+
+ log.debug("Adding DataObject (Base64Content) without a reference URI.");
+
+ // create XMLObject
+ XMLObject xmlObject = createXMLObject(new ByteArrayInputStream(dataObject.getBase64Content()));
+
+ setXMLObjectAndReferenceBase64(xmlObject, transforms);
+
+ } else if (dataObject.getXMLContent() != null) {
+
+ log.debug("Adding DataObject (XMLContent) without a reference URI.");
+
+ // create XMLObject
+ DocumentFragment content = parseDataObject((XMLContentType) dataObject.getXMLContent());
+ XMLObject xmlObject = createXMLObject(content);
+
+ setXMLObjectAndReferenceXML(xmlObject, transforms);
+
+ } else if (dataObject.getLocRefContent() != null) {
+
+ log.debug("Adding DataObject (LocRefContent) without a reference URI.");
+
+ setEnvelopedDataObject(dataObject.getLocRefContent(), transforms);
+
+ } else {
+
+ // not allowed
+ log.info("XML structure of the command request contains an " +
+ "invalid combination of optional elements or attributes. " +
+ "DataObject of structure='enveloped' without a reference must contain content.");
+ throw new SLRequestException(3003);
+
+ }
+
+ } else {
+
+ if (dataObject.getBase64Content() == null &&
+ dataObject.getXMLContent() == null &&
+ dataObject.getLocRefContent() == null) {
+
+ //
+ // case B
+ //
+ // The Reference attribute contains a URI that must be resolved by the
+ // Citizen Card Environment to obtain the data object.
+ // The content of sl:DataObject remains empty
+ //
+
+ log.debug("Adding DataObject from reference URI '" + reference + "'.");
+
+ setEnvelopedDataObject(reference, transforms);
+
+ } else {
+
+ // not allowed
+ log.info("XML structure of the command request contains an " +
+ "invalid combination of optional elements or attributes. " +
+ "DataObject of structure='enveloped' with reference must not contain content.");
+ throw new SLRequestException(3003);
+
+ }
+
+
+ }
+
+ }
+
+ /**
+ * Configures this DataObject as an enveloped DataObject with the content to
+ * be dereferenced from the given reference
.
+ *
+ * @param reference
+ * the reference
URI
+ * @param transforms
+ * an optional Transforms
element (may be
+ * null
)
+ *
+ * @throws SLCommandException
+ * if dereferencing the given reference
fails, or if
+ * configuring this DataObject with the data dereferenced from the
+ * given reference
fails.
+ * @throws NullPointerException
+ * if reference
is null
+ */
+ private void setEnvelopedDataObject(String reference, XSECTTransforms transforms) throws SLCommandException {
+
+ if (reference == null) {
+ throw new NullPointerException("Argument 'reference' must not be null.");
+ }
+
+ // dereference URL
+ URLDereferencer dereferencer = URLDereferencer.getInstance();
+
+ StreamData streamData;
+ try {
+ streamData = dereferencer.dereference(reference, ctx.getDereferencerContext());
+ } catch (IOException e) {
+ log.info("Failed to dereference XMLObject from '" + reference + "'.", e);
+ throw new SLCommandException(4110);
+ }
+
+ Node childNode;
+
+ String contentType = streamData.getContentType();
+ if (contentType.startsWith("text/xml")) {
+
+ // If content type is text/xml parse content.
+ String charset = HttpUtil.getCharset(contentType, true);
+
+ Document doc = parseDataObject(streamData.getStream(), charset);
+
+ childNode = doc.getDocumentElement();
+
+ if (childNode == null) {
+ log.info("Failed to parse XMLObject from '" + reference + "'.");
+ throw new SLCommandException(4111);
+ }
+
+ XMLObject xmlObject = createXMLObject(childNode);
+
+ setXMLObjectAndReferenceXML(xmlObject, transforms);
+
+ } else {
+
+ // Include content Base64 encoded.
+ XMLObject xmlObject = createXMLObject(streamData.getStream());
+
+ setXMLObjectAndReferenceBase64(xmlObject, transforms);
+
+ }
+
+ }
+
+ /**
+ * Configures this DataObject as an detached DataObject with the information
+ * provided in the given sl:DataObject
and optionally
+ * transforms
.
+ *
+ * @param dataObject
+ * the sl:DataObject
+ * @param transforms
+ * an optional Transforms object, may be null
+ *
+ * @throws SLCommandException
+ * if configuring this DataObject with the information provided in
+ * the sl:DataObject
fails.
+ * @throws SLRequestException
+ * if the information provided in the sl:DataObject
+ * does not conform to the security layer specification.
+ * @throws NullPointerException
+ * if dataObject
is null
+ */
+ private void setDetachedDataObject(
+ Base64XMLLocRefOptRefContentType dataObject, XSECTTransforms transforms)
+ throws SLCommandException, SLRequestException {
+
+ String referenceURI = dataObject.getReference();
+
+ if (referenceURI == null) {
+
+ // not allowed
+ log.info("XML structure of the command request contains an " +
+ "invalid combination of optional elements or attributes. " +
+ "DataObject of structure='detached' must contain a reference.");
+ throw new SLRequestException(3003);
+
+ } else {
+
+ DigestMethod dm;
+ try {
+ dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx);
+ } catch (NoSuchAlgorithmException e) {
+ log.error("Failed to get DigestMethod.", e);
+ throw new SLCommandException(4006);
+ } catch (InvalidAlgorithmParameterException e) {
+ log.error("Failed to get DigestMethod.", e);
+ throw new SLCommandException(4006);
+ }
+
+ String idValue = ctx.getIdValueFactory().createIdValue("Reference");
+
+ reference = new XSECTReference(referenceURI, dm, transforms, null, idValue);
+
+ // case D:
+ //
+ // The Reference attribute contains a URI that is used by the Citizen Card
+ // Environment to code the reference to the data object as part of the XML
+ // signature (attribute URI in the dsig:Reference) element. The content of
+ // sl:DataObject represents the data object.
+
+ if (dataObject.getLocRefContent() != null) {
+ String locRef = dataObject.getLocRefContent();
+ try {
+ this.reference.setDereferencer(new LocRefDereferencer(ctx.getDereferencerContext(), locRef));
+ } catch (URISyntaxException e) {
+ log.info("Invalid URI '" + locRef + "' in DataObject.", e);
+ throw new SLCommandException(4003);
+ } catch (IllegalArgumentException e) {
+ log.info("LocRef URI of '" + locRef + "' not supported in DataObject. ", e);
+ throw new SLCommandException(4003);
+ }
+ } else if (dataObject.getBase64Content() != null) {
+ byte[] base64Content = dataObject.getBase64Content();
+ this.reference.setDereferencer(new ByteArrayDereferencer(base64Content));
+ } else if (dataObject.getXMLContent() != null) {
+ XMLContentType xmlContent = (XMLContentType) dataObject.getXMLContent();
+ byte[] bytes = xmlContent.getRedirectedStream().toByteArray();
+ this.reference.setDereferencer(new ByteArrayDereferencer(bytes));
+ } else {
+
+ // case C:
+ //
+ // The Reference attribute contains a URI that must be resolved by the
+ // Citizen Card Environment to obtain the data object. The Reference
+ // attribute contains a URI that is used by the Citizen Card Environment
+ // to code the reference to the data object as part of the XML signature
+ // (attribute URI in the dsig:Reference) element. The content of
+ // sl:DataObject remains empty.
+
+ }
+
+ }
+ }
+
+ /**
+ * Returns the preferred sl:TransformInfo
from the given list of
+ * transformInfos
, or null
if none of the given
+ * transformInfos
is preferred over the others.
+ *
+ * @param transformsInfos
+ * a list of sl:TransformInfo
s
+ *
+ * @return the selected sl:TransformInfo
or null
, if
+ * none is preferred over the others
+ */
+ private TransformsInfoType selectPreferredTransformsInfo(Listds:Transforms
from the given
+ * sl:TransformsInfo
.
+ *
+ * @param transformsInfo
+ * the sl:TransformsInfo
+ *
+ * @return a corresponding unmarshalled ds:Transforms
, or
+ * null
if the given sl:TransformsInfo
does
+ * not contain a dsig:Transforms
element
+ *
+ * @throws SLRequestException
+ * if the ds:Transforms
in the given
+ * transformsInfo
are not valid or cannot be parsed.
+ *
+ * @throws MarshalException
+ * if the ds:Transforms
in the given
+ * transformsInfo
cannot be unmarshalled.
+ */
+ private XSECTTransforms createTransforms(TransformsInfoType transformsInfo) throws SLRequestException, MarshalException {
+
+ ByteArrayOutputStream redirectedStream = ((at.gv.egiz.slbinding.impl.TransformsInfoType) transformsInfo).getRedirectedStream();
+ byte[] transformBytes = (redirectedStream != null) ? redirectedStream.toByteArray() : null;
+
+ if (transformBytes != null && transformBytes.length > 0) {
+
+ // debug
+ if (log.isTraceEnabled()) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Trying to parse transforms:\n");
+ sb.append(new String(transformBytes, Charset.forName("UTF-8")));
+ log.trace(sb);
+ }
+
+ DOMImplementationLS domImplLS = DOMUtils.getDOMImplementationLS();
+ LSInput input = domImplLS.createLSInput();
+ input.setByteStream(new ByteArrayInputStream(transformBytes));
+
+ LSParser parser = domImplLS.createLSParser(
+ DOMImplementationLS.MODE_SYNCHRONOUS, null);
+ DOMConfiguration domConfig = parser.getDomConfig();
+ SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler();
+ domConfig.setParameter("error-handler", errorHandler);
+ domConfig.setParameter("validate", Boolean.FALSE);
+
+ Document document;
+ try {
+ document = parser.parse(input);
+ } catch (DOMException e) {
+ log.info("Failed to parse dsig:Transforms.", e);
+ throw new SLRequestException(3002);
+ } catch (LSException e) {
+ log.info("Failed to parse dsig:Transforms.", e);
+ throw new SLRequestException(3002);
+ }
+
+ // adopt ds:Transforms
+ Element documentElement = document.getDocumentElement();
+ Node adoptedTransforms = ctx.getDocument().adoptNode(documentElement);
+
+ DOMCryptoContext context = new DOMCryptoContext();
+
+ // unmarshall ds:Transforms
+ return new XSECTTransforms(context, adoptedTransforms);
+
+ } else {
+ return null;
+ }
+
+ }
+
+ /**
+ * Sets the mimeType
and the description
value
+ * for this DataObject.
+ *
+ * @param metaInfoType the sl:FinalMetaDataInfo
+ *
+ * @throws NullPointerException if metaInfoType
is null
+ */
+ private void setFinalDataMetaInfo(MetaInfoType metaInfoType) {
+
+ this.mimeType = metaInfoType.getMimeType();
+ this.description = metaInfoType.getDescription();
+
+ }
+
+ /**
+ * Selects an appropriate transformation path (if present) from the given list
+ * of sl:TransformInfos
, sets the corresponding final data meta info and
+ * returns the corresponding unmarshalled ds:Transforms
.
+ *
+ * @param transformsInfos the sl:TransformInfos
+ *
+ * @return the unmarshalled ds:Transforms
, or null
if
+ * no transformation path has been selected.
+ *
+ * @throws SLRequestException if the given list ds:TransformsInfo
contains
+ * an invalid ds:Transforms
element, or no suitable transformation path
+ * can be found.
+ */
+ private XSECTTransforms createTransformsAndSetFinalDataMetaInfo(
+ Listcontent
.
+ *
+ * @param content
+ * the to-be Base64 encoded content
+ * @return an XMLObject with the Base64 encoded content
+ */
+ private XMLObject createXMLObject(InputStream content) {
+
+ Text textNode;
+ try {
+ textNode = at.gv.egiz.dom.DOMUtils.createBase64Text(content, ctx.getDocument());
+ } catch (IOException e) {
+ log.error(e);
+ throw new SLRuntimeException(e);
+ }
+
+ DOMStructure structure = new DOMStructure(textNode);
+
+ String idValue = ctx.getIdValueFactory().createIdValue("Object");
+
+ return ctx.getSignatureFactory().newXMLObject(Collections.singletonList(structure), idValue, null, null);
+
+ }
+
+ /**
+ * Create an XMLObject with the given content
node.
+ *
+ * @param content the content node
+ *
+ * @return an XMLObject with the given content
+ */
+ private XMLObject createXMLObject(Node content) {
+
+ String idValue = ctx.getIdValueFactory().createIdValue("Object");
+
+ ListxmlObject
and creates and sets a corresponding
+ * Reference
.
+ * transforms
if given, or to a newly created
+ * Transforms
element if transforms
is
+ * null
.
+ *
+ * @param xmlObject
+ * the XMLObject
+ * @param transforms
+ * an optional Transforms
element (may be
+ * null
)
+ *
+ * @throws SLCommandException
+ * if creating the Reference fails
+ * @throws NullPointerException
+ * if xmlObject
is null
+ */
+ private void setXMLObjectAndReferenceBase64(XMLObject xmlObject, XSECTTransforms transforms) throws SLCommandException {
+
+ // create reference URI
+ //
+ // NOTE: the ds:Object can be referenced directly, as the Base64 transform
+ // operates on the text() of the input nodelist.
+ //
+ String referenceURI = "#" + xmlObject.getId();
+
+ // create Base64 Transform
+ Transform transform;
+ try {
+ transform = ctx.getSignatureFactory().newTransform(Transform.BASE64, (TransformParameterSpec) null);
+ } catch (NoSuchAlgorithmException e) {
+ // algorithm must be present
+ throw new SLRuntimeException(e);
+ } catch (InvalidAlgorithmParameterException e) {
+ // algorithm does not take parameters
+ throw new SLRuntimeException(e);
+ }
+
+ if (transforms == null) {
+ transforms = new XSECTTransforms(Collections.singletonList(transform));
+ } else {
+ transforms.insertTransform(transform);
+ }
+
+ DigestMethod dm;
+ try {
+ dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx);
+ } catch (NoSuchAlgorithmException e) {
+ log.error("Failed to get DigestMethod.", e);
+ throw new SLCommandException(4006);
+ } catch (InvalidAlgorithmParameterException e) {
+ log.error("Failed to get DigestMethod.", e);
+ throw new SLCommandException(4006);
+ }
+ String id = ctx.getIdValueFactory().createIdValue("Reference");
+
+ this.xmlObject = xmlObject;
+ this.reference = new XSECTReference(referenceURI, dm, transforms, null, id);
+
+ }
+
+ /**
+ * Sets the given xmlObject
and creates and sets a corresponding
+ * Reference
.
+ * transforms
if given, or to a newly created
+ * Transforms
element if transforms
is
+ * null
.
+ * Transforms
element (may be
+ * null
)
+ *
+ * @throws SLCommandException
+ * if creating the Reference fails
+ * @throws NullPointerException
+ * if xmlObject
is null
+ */
+ private void setXMLObjectAndReferenceXML(XMLObject xmlObject, XSECTTransforms transforms) throws SLCommandException {
+
+ // create reference URI
+ String referenceURI = "#" + xmlObject.getId();
+
+ // create Transform to select ds:Object's children
+ Transform xpathTransform;
+ Transform c14nTransform;
+ try {
+
+ XPathType xpath = new XPathType("id(\"" + xmlObject.getId() + "\")/node()", XPathType.Filter.INTERSECT);
+ ListxmlContent
and returns a corresponding
+ * document fragment.
+ *
+ * xmlContent
fails
+ *
+ * @throws NullPointerException
+ * if xmlContent
is null
+ */
+ private DocumentFragment parseDataObject(XMLContentType xmlContent) throws SLCommandException {
+
+ ByteArrayOutputStream redirectedStream = xmlContent.getRedirectedStream();
+
+ // Note: We can assume a fixed character encoding of UTF-8 for the
+ // content of the redirect stream as the content has already been parsed
+ // and serialized again to the redirect stream.
+
+ ListinputStream
using the given
+ * encoding
and returns the parsed document.
+ *
+ * @param inputStream
+ * the to-be parsed input
+ *
+ * @param encoding
+ * the encoding to be used for parsing the given
+ * inputStream
+ *
+ * @return the parsed document
+ *
+ * @throws SLCommandException
+ * if parsing the inputStream
fails.
+ *
+ * @throws NullPointerException
+ * if inputStram
is null
+ */
+ private Document parseDataObject(InputStream inputStream, String encoding) throws SLCommandException {
+
+ LSInput input = domImplLS.createLSInput();
+ input.setByteStream(inputStream);
+
+ if (encoding != null) {
+ input.setEncoding(encoding);
+ }
+
+ LSParser parser = domImplLS.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null);
+ DOMConfiguration domConfig = parser.getDomConfig();
+ SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler();
+ domConfig.setParameter("error-handler", errorHandler);
+ domConfig.setParameter("validate", Boolean.FALSE);
+
+ Document doc;
+ try {
+ doc = parser.parse(input);
+ } catch (DOMException e) {
+ log.info("Existing XML document cannot be parsed.", e);
+ throw new SLCommandException(4111);
+ } catch (LSException e) {
+ log.info("Existing XML document cannot be parsed. ", e);
+ throw new SLCommandException(4111);
+ }
+
+ if (errorHandler.hasErrors()) {
+ // log errors
+ if (log.isInfoEnabled()) {
+ Listxsd:Id
-attribute values.
+ *
+ * @author mcentner
+ */
+public interface IdValueFactory {
+
+ /**
+ * Creates a new xsd:Id
-attribute value for an Element of the
+ * given elementName
.
+ *
+ * @param elementName
+ * the local name of the element to create the value for
+ *
+ * @return a xsd:Id
-attribute value
+ */
+ public String createIdValue(String elementName);
+
+}
\ No newline at end of file
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/IdValueFactoryImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/IdValueFactoryImpl.java
new file mode 100644
index 00000000..b9824655
--- /dev/null
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/IdValueFactoryImpl.java
@@ -0,0 +1,127 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed 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.
+*/
+package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * An implementation of the IdValueFactory.
+ * xsd:Id
-attribute values of the form
+ * '<elementName>-<random>-<sequenceNumber>
',
+ * where
+ *
+ *
+ * <elementName>
is the name provided at
+ * {@link #createIdValue(String)},<random>
is a random generated fixed value for an
+ * instance of this IdValueFactory and<sequenceNumber>
is the sequence number of the value
+ * generated for a given elementName
by an instance of this
+ * IdValueFactory.
xsd:Id
-attribute values.
+ *
+ * @author mcentner
+ */
+ private class IdGenerator {
+
+ /**
+ * The salt.
+ */
+ private String salt;
+
+ /**
+ * The element name.
+ */
+ private String elementName;
+
+ /**
+ * The sequence number.
+ */
+ private int i = 0;
+
+ /**
+ * Creates a new instance of this IdGenerator with the given
+ * elementName
and salt
value.
+ *
+ * @param elementName the element name
+ * @param salt the salt valeu
+ */
+ private IdGenerator(String elementName, String salt) {
+ super();
+ this.elementName = elementName;
+ this.salt = salt;
+ }
+
+ /**
+ * @return returns the next xsd:Id
-attribute value.
+ */
+ public String getNextId() {
+ return elementName + "-" + salt + "-" + Integer.toString(++i);
+ }
+
+ }
+
+ /**
+ * A map of element names to xsd:Id
-value generators.
+ */
+ private MapLocRef
+ * references.
+ *
+ * @author mcentner
+ */
+public class LocRefDereferencer implements URIDereferencer {
+
+ /**
+ * Logging facility.
+ */
+ private static Log log = LogFactory.getLog(LocRefDereferencer.class);
+
+ /**
+ * The LocRef
-reference to be dereferenced by
+ * {@link #dereference(URIReference, XMLCryptoContext)}.
+ */
+ protected String locRef;
+
+ /**
+ * The context to be used for dereferencing.
+ */
+ protected URLDereferencerContext dereferencerContext;
+
+ /**
+ * Creates a new instance of this LocRefDereferencer with the given
+ * dereferencerContext
and locRef
reference.
+ *
+ * @param dereferencerContext
+ * the context to be used for dereferencing
+ * @param locRef
+ * the LocRef
-reference (must be an absolute URI)
+ *
+ * @throws URISyntaxException
+ * if LocRef
is not an absolute URI
+ */
+ public LocRefDereferencer(URLDereferencerContext dereferencerContext,
+ String locRef) throws URISyntaxException {
+
+ this.dereferencerContext = dereferencerContext;
+
+ URI locRefUri = new URI(locRef);
+ if (locRefUri.isAbsolute()) {
+ this.locRef = locRef;
+ } else {
+ throw new IllegalArgumentException(
+ "Parameter 'locRef' must be an absolut URI.");
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * javax.xml.crypto.URIDereferencer#dereference(javax.xml.crypto.URIReference,
+ * javax.xml.crypto.XMLCryptoContext)
+ */
+ @Override
+ public Data dereference(URIReference uriReference, XMLCryptoContext context)
+ throws URIReferenceException {
+
+ URLDereferencer dereferencer = URLDereferencer.getInstance();
+ StreamData streamData;
+ try {
+ streamData = dereferencer.dereference(locRef, dereferencerContext);
+ } catch (IOException e) {
+ log.info("Failed to dereference URI'" + locRef + "'. " + e.getMessage(),
+ e);
+ throw new URIReferenceException("Failed to dereference URI '" + locRef
+ + "'. " + e.getMessage(), e);
+ }
+
+ return new OctetStreamData(streamData.getStream(), locRef, streamData
+ .getContentType());
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALPrivateKey.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALPrivateKey.java
new file mode 100644
index 00000000..64c758c9
--- /dev/null
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALPrivateKey.java
@@ -0,0 +1,122 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed 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.
+*/
+package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import java.security.PrivateKey;
+
+import at.gv.egiz.stal.STAL;
+import at.gv.egiz.stal.HashDataInputCallback;
+
+/**
+ * This class implements a private key used by the {@link STALSignature} class.
+ *
+ * @author mcentner
+ */
+public class STALPrivateKey implements PrivateKey {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The STAL implementation.
+ */
+ private STAL stal;
+
+ /**
+ * The callback interface for obtaining the hash input data.
+ */
+ private HashDataInputCallback hashDataInputCallback;
+
+ /**
+ * The keybox identifier.
+ */
+ private String keyboxIdentifier;
+
+ /**
+ * The signature algorithm.
+ */
+ private String algorithm;
+
+ /**
+ * Creates a new instance of this STALPrivateKey
with the given
+ * stal
implementation, signature algorithm
,
+ * keyboxIdentifier
and hashDataInputCallback
+ * interface.
+ *
+ * @param stal
+ * the STAL implementation
+ * @param algorithm
+ * the signature algorithm
+ * @param keyboxIdentifier
+ * the keybox identifier
+ * @param hashDataInputCallback
+ * the interface for obtaining the has input data
+ */
+ public STALPrivateKey(STAL stal,
+ String algorithm, String keyboxIdentifier, HashDataInputCallback hashDataInputCallback) {
+ super();
+ this.keyboxIdentifier = keyboxIdentifier;
+ this.hashDataInputCallback = hashDataInputCallback;
+ this.stal = stal;
+ this.algorithm = algorithm;
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.Key#getAlgorithm()
+ */
+ @Override
+ public String getAlgorithm() {
+ return algorithm;
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.Key#getEncoded()
+ */
+ @Override
+ public byte[] getEncoded() {
+ throw new UnsupportedOperationException("STALPrivateKey does not support the getEncoded() method.");
+ }
+
+ /* (non-Javadoc)
+ * @see java.security.Key#getFormat()
+ */
+ @Override
+ public String getFormat() {
+ return null;
+ }
+
+ /**
+ * @return the STAL implementation
+ */
+ public STAL getStal() {
+ return stal;
+ }
+
+ /**
+ * @return the interface for obtaining the hash data input
+ */
+ public HashDataInputCallback getHashDataInputCallback() {
+ return hashDataInputCallback;
+ }
+
+ /**
+ * @return the keybox identifier
+ */
+ public String getKeyboxIdentifier() {
+ return keyboxIdentifier;
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALProvider.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALProvider.java
new file mode 100644
index 00000000..0ab30530
--- /dev/null
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALProvider.java
@@ -0,0 +1,64 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed 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.
+*/
+package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import iaik.xml.crypto.XmldsigMore;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.Provider;
+import java.security.Signature;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.crypto.dsig.SignatureMethod;
+
+/**
+ * A security provider implementation that provides {@link Signature} implementations
+ * based on STAL.
+ *
+ * @author mcentner
+ */
+public class STALProvider extends Provider {
+
+ private static final long serialVersionUID = 1L;
+
+ private static String IMPL_PACKAGE_NAME = "at.gv.egiz.bku.slcommands.impl.xsect";
+
+ public STALProvider() {
+
+ super("STAL", 1.0, "Security Token Abstraction Layer Provider");
+
+ final MaperrorCode
.
+ *
+ * @param errorCode the error code
+ */
+ public STALSignatureException(int errorCode) {
+ this.errorCode = errorCode;
+ }
+
+ /**
+ * Creates a new instance of this STALSignatureException with
+ * the given error msg
.
+ *
+ * @param msg the error message
+ * @see SignatureException#SignatureException(String)
+ */
+ public STALSignatureException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Creates a new instance of this STALSignatureException with
+ * the given root cause
.
+ *
+ * @param cause the cause
+ * @see SignatureException#SignatureException(Throwable)
+ */
+ public STALSignatureException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Creates a new instance of this STALSignautureException with
+ * the given error message
and root cause
.
+ *
+ * @param message the error message
+ * @param cause the cause
+ * @see SignatureException#SignatureException(String, Throwable)
+ */
+ public STALSignatureException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * @return the error code
+ */
+ public int getErrorCode() {
+ return errorCode;
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java
new file mode 100644
index 00000000..94a4a066
--- /dev/null
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java
@@ -0,0 +1,935 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed 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.
+*/
+package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.crypto.MarshalException;
+import javax.xml.crypto.dom.DOMStructure;
+import javax.xml.crypto.dsig.CanonicalizationMethod;
+import javax.xml.crypto.dsig.DigestMethod;
+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.XMLSignature;
+import javax.xml.crypto.dsig.XMLSignatureException;
+import javax.xml.crypto.dsig.XMLSignatureFactory;
+import javax.xml.crypto.dsig.dom.DOMSignContext;
+import javax.xml.crypto.dsig.keyinfo.KeyInfo;
+import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory;
+import javax.xml.crypto.dsig.keyinfo.X509Data;
+import javax.xml.stream.XMLStreamException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.etsi.uri._01903.v1_1.DataObjectFormatType;
+import org.etsi.uri._01903.v1_1.QualifyingPropertiesType;
+import org.w3c.dom.DOMConfiguration;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentFragment;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ls.DOMImplementationLS;
+import org.w3c.dom.ls.LSException;
+import org.w3c.dom.ls.LSInput;
+import org.w3c.dom.ls.LSOutput;
+import org.w3c.dom.ls.LSParser;
+import org.w3c.dom.ls.LSResourceResolver;
+import org.w3c.dom.ls.LSSerializer;
+
+import at.buergerkarte.namespaces.securitylayer._1.Base64XMLLocRefReqRefContentType;
+import at.buergerkarte.namespaces.securitylayer._1.Base64XMLOptRefContentType;
+import at.buergerkarte.namespaces.securitylayer._1.DataObjectAssociationType;
+import at.buergerkarte.namespaces.securitylayer._1.DataObjectInfoType;
+import at.buergerkarte.namespaces.securitylayer._1.SignatureInfoCreationType;
+import at.gv.egiz.bku.binding.HttpUtil;
+import at.gv.egiz.bku.slexceptions.SLCommandException;
+import at.gv.egiz.bku.slexceptions.SLRequestException;
+import at.gv.egiz.bku.utils.HexDump;
+import at.gv.egiz.bku.utils.urldereferencer.StreamData;
+import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer;
+import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext;
+import at.gv.egiz.dom.DOMUtils;
+import at.gv.egiz.slbinding.impl.XMLContentType;
+import at.gv.egiz.stal.HashDataInputCallback;
+import at.gv.egiz.stal.STAL;
+import at.gv.egiz.xades.QualifyingPropertiesException;
+import at.gv.egiz.xades.QualifyingPropertiesFactory;
+
+/**
+ * This class represents an XML-Signature as to be created by the
+ * security layer command CreateXMLSignatureRequest
.
+ *
+ * @author mcentner
+ */
+public class Signature implements HashDataInputCallback {
+
+ /**
+ * Logging facility.
+ */
+ private static Log log = LogFactory.getLog(Signature.class);
+
+ /**
+ * The DOM implementation used.
+ */
+ private DOMImplementationLS domImplLS;
+
+ /**
+ * The SignatureContext for the XMLSignature.
+ */
+ private SignatureContext ctx;
+
+ /**
+ * The list of {@link DataObject}s for this signature.
+ */
+ private ListId
-attribute values of this signature's
+ * ds:Reference
s to the corresponding {@link DataObject}s.
+ */
+ private Mapxsd:ID
to be registered in the {@link DOMSignContext}.
+ */
+ private ListsignatureInfo
provided.
+ *
+ * @param signatureInfo
+ * the SignatureInfo
+ *
+ * @throws SLCommandException
+ * if processing fails for any reason
+ * @throws IllegalStateException
+ * if the parent
node has already been set
+ * @throws NullPointerException
+ * if signatureInfo
is null
+ */
+ public void setSignatureInfo(SignatureInfoCreationType signatureInfo) throws SLCommandException {
+
+ if (signatureLocation != null) {
+ throw new IllegalStateException("SignatureEnvironment already set.");
+ }
+
+ Base64XMLOptRefContentType signatureEnvironment = signatureInfo.getSignatureEnvironment();
+
+ if (signatureEnvironment == null) {
+
+ // no SignatureEnvironment, so we use an empty document and the document as parent
+ ensureSignatureLocation();
+
+ } else {
+
+ // parse SignatureEnvrionment and use as document
+ Document document = parseSignatureEnvironment(signatureEnvironment, signatureInfo.getSupplement());
+ ctx.setDocument(document);
+
+ signatureLocation = new SignatureLocation(ctx);
+ signatureLocation.setSignatureInfo(signatureInfo);
+
+ }
+
+ }
+
+ /**
+ * Ensures a SignatureLocation for this Signature.
+ */
+ private void ensureSignatureLocation() {
+
+ if (signatureLocation == null) {
+ Document document = DOMUtils.createDocument();
+ ctx.setDocument(document);
+
+ signatureLocation = new SignatureLocation(ctx);
+ signatureLocation.setParent(document);
+ }
+
+ }
+
+ /**
+ * Adds a DataObject with the information given by the
+ * dataObjectInfo
provided to this Signature.
+ *
+ * @param dataObjectInfo
+ * the DataObjectInfo
element
+ *
+ * @throws SLCommandException
+ * if adding the DataObject fails
+ * @throws SLRequestException
+ * if the information provided by the given
+ * dataObjectInfo
does not conform to the security
+ * layer specification
+ * @throws NullPointerException
+ * if dataObjectInfo
is null
+ */
+ public void addDataObject(DataObjectInfoType dataObjectInfo) throws SLCommandException, SLRequestException {
+
+ ensureSignatureLocation();
+
+ DataObject dataObject = new DataObject(ctx);
+ dataObject.setDataObjectInfo(dataObjectInfo);
+
+ dataObjects.add(dataObject);
+
+ dataObjectReferencIds.put(dataObject.getReference().getId(), dataObject);
+
+ }
+
+ /**
+ * Sets the SigningTime
qualifying property of this Signature.
+ *
+ * @param signingTime the signing time to set
+ */
+ public void setSigningTime(Date signingTime) {
+ this.signingTime = signingTime;
+ }
+
+ /**
+ * Sets the SignerCertificate
qualifying property of this Signature.
+ *
+ * @param certificate the signer's certificate
+ */
+ public void setSignerCeritifcate(X509Certificate certificate) {
+ this.signerCertificate = certificate;
+ }
+
+ /**
+ * Builds the XMLSignature data structure of this Signature as configured by
+ * the various setter methods.
+ *
+ * @throws SLCommandException if building this signature fails
+ */
+ public void buildXMLSignature() throws SLCommandException {
+
+ ListsignContext
.
+ * + * Call's {@link #buildXMLSignature()} if it has not been called yet. + *
+ * + * @param signContext + * the signing context + * + * @throws MarshalException + * if marshalling the XMLSignature fails + * @throws XMLSignatureException + * if signing the XMLSignature fails + * @throws SLCommandException + * if building the XMLSignature fails + * @throws NullPointerException + * ifsignContext
is null
+ */
+ public void sign(DOMSignContext signContext) throws MarshalException, XMLSignatureException, SLCommandException {
+
+ if (xmlSignature == null) {
+ buildXMLSignature();
+ }
+
+ for (IdAttribute idAttribute : idAttributes) {
+ signContext.setIdAttributeNS(idAttribute.element, idAttribute.namespaceURI, idAttribute.localName);
+ }
+
+ // DO NOT USE:
+ // signContext.setProperty("iaik.xml.crypto.dsig.sign-over", Boolean.TRUE);
+
+ signContext.setProperty("javax.xml.crypto.dsig.cacheReference", Boolean.TRUE);
+
+ signContext.putNamespacePrefix(XMLSignature.XMLNS, "dsig");
+
+ signContext.setURIDereferencer(new URIDereferncerAdapter(ctx.getDereferencerContext()));
+
+ try {
+ xmlSignature.sign(signContext);
+ } catch (XMLSignatureException e) {
+ Throwable cause = e.getCause();
+ while (cause != null) {
+ if (cause instanceof STALSignatureException) {
+ int errorCode = ((STALSignatureException) cause).getErrorCode();
+ SLCommandException commandException = new SLCommandException(errorCode);
+ log.info("Failed to sign signature.", commandException);
+ throw commandException;
+ } else {
+ cause = cause.getCause();
+ }
+ }
+ throw e;
+ }
+
+ // debug
+ if (log.isTraceEnabled()) {
+ for (DataObject dataObject : dataObjects) {
+ Reference reference = dataObject.getReference();
+ InputStream digestInputStream = reference.getDigestInputStream();
+ if (digestInputStream != null) {
+ String mimeType = dataObject.getMimeType();
+ StringBuilder sb = new StringBuilder();
+ sb.append("DigestInput for Reference with id='");
+ sb.append(reference.getId());
+ sb.append("' (MIME-Type=");
+ sb.append(dataObject.getMimeType());
+ sb.append("):\n");
+ try {
+ if (mimeType != null && (
+ mimeType.startsWith("text") ||
+ "application/xhtml+xml".equals(mimeType))) {
+ byte[] b = new byte[512];
+ for (int l; (l = digestInputStream.read(b)) != -1;) {
+ sb.append(new String(b, 0, l));
+ }
+ } else {
+ sb.append(HexDump.hexDump(digestInputStream));
+ }
+ } catch (IOException e) {
+ log.error(e);
+ }
+ log.trace(sb.toString());
+ } else {
+ log.trace("Reference caching is not enabled.");
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Sign this Signature using the given stal
implementation and
+ * keyboxIdentifier
.
+ * + * This method configures an appropriate {@link DOMSignContext} and calls + * {@link #sign(DOMSignContext)}. If {@link #buildXMLSignature()} has not been + * called yet, it is called by this method. + *
+ * + * @param stal + * the STAL implementation to use + * @param keyboxIdentifier + * the KeyboxIdentifier to use + * + * @throws MarshalException + * if marshalling this Signature fails + * @throws XMLSignatureException + * if signing this Signature fails + * @throws SLCommandException + * if building this Signature fails + * @throws NullPointerException + * ifstal
or keyboxIdentifier
is
+ * null
+ */
+ public void sign(STAL stal, String keyboxIdentifier) throws MarshalException, XMLSignatureException, SLCommandException {
+
+ if (stal == null) {
+ throw new NullPointerException("Argument 'stal' must not be null.");
+ }
+
+ if (keyboxIdentifier == null) {
+ throw new NullPointerException("Argument 'keyboxIdentifier' must not be null.");
+ }
+
+ if (xmlSignature == null) {
+ buildXMLSignature();
+ }
+
+ SignatureMethod signatureMethod = xmlSignature.getSignedInfo().getSignatureMethod();
+ String algorithm = signatureMethod.getAlgorithm();
+
+ PrivateKey privateKey = new STALPrivateKey(stal, algorithm, keyboxIdentifier, this);
+
+ DOMSignContext signContext;
+ if (getNextSibling() == null) {
+ signContext = new DOMSignContext(privateKey, getParent());
+ } else {
+ signContext = new DOMSignContext(privateKey, getParent(), getNextSibling());
+ }
+
+ sign(signContext);
+ }
+
+ @Override
+ public InputStream getHashDataInput(String referenceId) {
+
+ DataObject dataObject = dataObjectReferencIds.get(referenceId);
+ if (dataObject != null) {
+ return dataObject.getReference().getDigestInputStream();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Adds the XAdES QualifyingProperties
as an
+ * ds:Object
and a corresponding ds:Reference
to
+ * it's SignedProperties
element to this Signature.
+ *
+ * @param objects
+ * the list of ds:Objects
to add the created
+ * ds:Object
to
+ * @param references
+ * the list of ds:References
to add the created
+ * ds:Reference
to
+ *
+ * @throws SLCommandException
+ * if creating and adding the XAdES
+ * QualifyingProperties
fails
+ * @throws NullPointerException
+ * if objects
or references
is
+ * null
+ */
+ private void addXAdESObjectAndReference(ListSignatureEnvironment
element
+ * @param supplements
+ * an optional list of Supplements
(may be
+ * null
)
+ *
+ * @return the parsed SignatureEnvironment document
+ *
+ * @throws SLCommandException
+ * if parsing the SignatureEnvironment fails
+ * @throws NullPointerException
+ * if signatureEnvironment
is null
+ */
+ private Document parseSignatureEnvironment(
+ Base64XMLOptRefContentType signatureEnvironment,
+ Listreference
URI.
+ *
+ * @param reference
+ * the reference URL
+ *
+ * @return an LSInput from the given reference
URI
+ *
+ * @throws IOException
+ * if dereferencing the given reference
fails
+ */
+ private LSInput createLSInput(String reference) throws IOException {
+
+ URLDereferencer urlDereferencer = URLDereferencer.getInstance();
+ StreamData streamData = urlDereferencer.dereference(reference, ctx.getDereferencerContext());
+
+ String contentType = streamData.getContentType();
+ String charset = HttpUtil.getCharset(contentType, true);
+ InputStreamReader streamReader;
+ try {
+ streamReader = new InputStreamReader(streamData.getStream(), charset);
+ } catch (UnsupportedEncodingException e) {
+ log.info("Charset " + charset + " not supported. Using default.");
+ streamReader = new InputStreamReader(streamData.getStream());
+ }
+
+ LSInput input = domImplLS.createLSInput();
+ input = domImplLS.createLSInput();
+ input.setCharacterStream(streamReader);
+
+ return input;
+
+ }
+
+ /**
+ * Creates an LSInput from the given content
bytes.
+ *
+ * @param content
+ * the content bytes
+ *
+ * @return an LSInput from the givne content
bytes
+ */
+ private LSInput createLSInput(byte[] content) {
+
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(content);
+ LSInput input = domImplLS.createLSInput();
+ input.setByteStream(inputStream);
+
+ return input;
+
+ }
+
+ /**
+ * Creates an LSInput from the given XML content
.
+ *
+ * @param content
+ * the XML content
+ * @return an LSInput from the given XML content
+ *
+ * @throws XMLStreamException
+ * if reading the XMLStream from the given XML content fails
+ */
+ private LSInput createLSInput(XMLContentType content) throws XMLStreamException {
+
+ ByteArrayOutputStream redirectedStream = content.getRedirectedStream();
+ if (redirectedStream != null) {
+ LSInput input = domImplLS.createLSInput();
+ input.setByteStream(new ByteArrayInputStream(redirectedStream.toByteArray()));
+ return input;
+ } else {
+ return null;
+ }
+
+ }
+
+ /**
+ * Represents an xsd:Id
-attribute value.
+ *
+ * @author mcentner
+ */
+ private class IdAttribute {
+
+ private Element element;
+
+ private String namespaceURI;
+
+ private String localName;
+
+ }
+
+ /**
+ * An implementation of the LSResourceResolver that uses a list of supplements
+ * to resolve resources.
+ *
+ * @author mcentner
+ */
+ private class LSResourceResolverAdapter implements LSResourceResolver {
+
+ ListCreateXMLSignature
.
+ *
+ * @author mcentner
+ */
+public class SignatureContext {
+
+ /**
+ * The document going to contain the XML signature.
+ */
+ private Document document;
+
+ /**
+ * The IdValueFactory used to create xsd:ID
-attribute values.
+ */
+ private IdValueFactory idValueFactory;
+
+ /**
+ * The XMLSignatureFactory to create XML signature objects.
+ */
+ private XMLSignatureFactory signatureFactory;
+
+ /**
+ * The URLDereferencerContext for dereferencing URLs.
+ */
+ private URLDereferencerContext dereferencerContext;
+
+ /**
+ * The DigestMethodFactory to create {@link DigestMethod} objects.
+ */
+ private AlgorithmMethodFactory digestMethodFactory;
+
+ /**
+ * @return the document
+ */
+ public Document getDocument() {
+ return document;
+ }
+
+ /**
+ * @param document the document to set
+ */
+ public void setDocument(Document document) {
+ this.document = document;
+ }
+
+ /**
+ * @return the idValueFactory
+ */
+ public IdValueFactory getIdValueFactory() {
+ return idValueFactory;
+ }
+
+ /**
+ * @param idValueFactory the idValueFactory to set
+ */
+ public void setIdValueFactory(IdValueFactory idValueFactory) {
+ this.idValueFactory = idValueFactory;
+ }
+
+ /**
+ * @return the signatureFactory
+ */
+ public XMLSignatureFactory getSignatureFactory() {
+ return signatureFactory;
+ }
+
+ /**
+ * @param signatureFactory the signatureFactory to set
+ */
+ public void setSignatureFactory(XMLSignatureFactory signatureFactory) {
+ this.signatureFactory = signatureFactory;
+ }
+
+ /**
+ * @return the dereferencerContext
+ */
+ public URLDereferencerContext getDereferencerContext() {
+ return dereferencerContext;
+ }
+
+ /**
+ * @param dereferencerContext the dereferencerContext to set
+ */
+ public void setDereferencerContext(URLDereferencerContext dereferencerContext) {
+ this.dereferencerContext = dereferencerContext;
+ }
+
+ /**
+ * @return the digestMethodFactory
+ */
+ public AlgorithmMethodFactory getAlgorithmMethodFactory() {
+ return digestMethodFactory;
+ }
+
+ /**
+ * @param digestMethodFactory the digestMethodFactory to set
+ */
+ public void setAlgorithmMethodFactory(AlgorithmMethodFactory digestMethodFactory) {
+ this.digestMethodFactory = digestMethodFactory;
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureLocation.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureLocation.java
new file mode 100644
index 00000000..5ec02893
--- /dev/null
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureLocation.java
@@ -0,0 +1,235 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed 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.
+*/
+package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import java.util.Iterator;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import at.buergerkarte.namespaces.securitylayer._1.SignatureInfoCreationType;
+import at.gv.egiz.bku.slexceptions.SLCommandException;
+import at.gv.egiz.slbinding.impl.SignatureLocationType;
+
+/**
+ * This class implements the SignatureLocation
of an XML-Signature
+ * to be created by the security layer command CreateXMLSignature
.
+ *
+ * @author mcentner
+ */
+public class SignatureLocation {
+
+ /**
+ * Logging facility.
+ */
+ private static Log log = LogFactory.getLog(SignatureLocation.class);
+
+ /**
+ * The SignatureContext for the XML signature
+ */
+ private SignatureContext ctx;
+
+ /**
+ * The parent node for the XML signature.
+ */
+ private Node parent;
+
+ /**
+ * The next sibling node for the XML signature.
+ */
+ private Node nextSibling;
+
+ /**
+ * Creates a new SignatureLocation with the given signatureContext
+ *
+ * @param signatureContext the context for the XML signature creation
+ */
+ public SignatureLocation(SignatureContext signatureContext) {
+ this.ctx = signatureContext;
+ }
+
+ /**
+ * @return the parent node for the XML signature
+ */
+ public Node getParent() {
+ return parent;
+ }
+
+ /**
+ * @param parent the parent for the XML signature
+ */
+ public void setParent(Node parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * @return the next sibling node for the XML signature
+ */
+ public Node getNextSibling() {
+ return nextSibling;
+ }
+
+ /**
+ * @param nextSibling the next sibling node for the XML signature
+ */
+ public void setNextSibling(Node nextSibling) {
+ this.nextSibling = nextSibling;
+ }
+
+ /**
+ * Configures this SignatureLocation with the information provided by the
+ * given SignatureInfo
element.
+ *
+ * @param signatureInfo
+ * the SignatureInfo
element
+ *
+ * @throws SLCommandException
+ * if configuring this SignatureLocation with given
+ * signatureInfo
fails
+ */
+ public void setSignatureInfo(SignatureInfoCreationType signatureInfo)
+ throws SLCommandException {
+
+ // evaluate signature location XPath ...
+ SignatureLocationType signatureLocation = (SignatureLocationType) signatureInfo
+ .getSignatureLocation();
+
+ NamespaceContext namespaceContext = new MOAIDWorkaroundNamespaceContext(
+ signatureLocation.getNamespaceContext());
+
+ parent = evaluateSignatureLocation(signatureInfo.getSignatureLocation()
+ .getValue(), namespaceContext, ctx.getDocument().getDocumentElement());
+
+ // ... and index
+ nextSibling = findNextSibling(parent, signatureInfo.getSignatureLocation()
+ .getIndex().intValue());
+
+ }
+
+ /**
+ * Evaluates the given xpath
with the document element as context node
+ * and returns the resulting node.
+ *
+ * @param xpath the XPath expression
+ * @param nsContext the namespace context of the XPath expression
+ * @param contextNode the context node for the XPath evaluation
+ *
+ * @return the result of evaluating the XPath expression
+ *
+ * @throws SLCommandException
+ */
+ private Node evaluateSignatureLocation(String xpath, NamespaceContext nsContext, Node contextNode) throws SLCommandException {
+
+ Node node = null;
+ try {
+ XPathFactory xpathFactory = XPathFactory.newInstance();
+ XPath xPath = xpathFactory.newXPath();
+ xPath.setNamespaceContext(nsContext);
+ XPathExpression xpathExpr = xPath.compile(xpath);
+ node = (Node) xpathExpr.evaluate(contextNode, XPathConstants.NODE);
+ } catch (XPathExpressionException e) {
+ log.info("Failed to evaluate SignatureLocation XPath expression '" + xpath + "' on context node.", e);
+ throw new SLCommandException(4102);
+ }
+
+ if (node == null) {
+ log.info("Failed to evaluate SignatureLocation XPath expression '" + xpath + "'. Result is empty.");
+ throw new SLCommandException(4102);
+ }
+
+ return node;
+
+ }
+
+ /**
+ * Finds the next sibling node of the parent
's n
-th child node
+ * or null
if there is no next sibling.
+ *
+ * @param parent the parent node
+ * @param n the index of the child node
+ *
+ * @return the next sibling node of the node specified by parent
and index n
,
+ * or null
if there is no next sibling node.
+ *
+ * @throws SLCommandException if the n
-th child of parent
does not exist
+ */
+ private Node findNextSibling(Node parent, int n) throws SLCommandException {
+
+ NodeList childNodes = parent.getChildNodes();
+ Node childNode = childNodes.item(n);
+ if (childNode == null) {
+ log.info("SingatureLocation Index '" + n + "' not found in document.");
+ throw new SLCommandException(4102);
+ } else {
+ return childNode.getNextSibling();
+ }
+
+ }
+
+ /**
+ * Workaround for a missing namespace prefix declaration in MOA-ID.
+ *
+ * @author mcentner
+ */
+ private class MOAIDWorkaroundNamespaceContext implements NamespaceContext {
+
+ private NamespaceContext namespaceContext;
+
+ public MOAIDWorkaroundNamespaceContext(NamespaceContext namespaceContext) {
+ super();
+ this.namespaceContext = namespaceContext;
+ }
+
+ @Override
+ public String getNamespaceURI(String prefix) {
+
+ String namespaceURI = namespaceContext.getNamespaceURI(prefix);
+
+ if ((namespaceURI == null || XMLConstants.NULL_NS_URI.equals(namespaceURI)) && "saml".equals(prefix)) {
+ namespaceURI = "urn:oasis:names:tc:SAML:1.0:assertion";
+ log.debug("Namespace prefix '" + prefix + "' resolved to '" + namespaceURI + "' (MOA-ID Workaround).");
+ } else {
+ log.trace("Namespace prefix '" + prefix + "' resolved to '" + namespaceURI + "'.");
+ }
+
+ return namespaceURI;
+ }
+
+ @Override
+ public String getPrefix(String namespaceURI) {
+ return namespaceContext.getPrefix(namespaceURI);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Iterator getPrefixes(String namespaceURI) {
+ return namespaceContext.getPrefixes(namespaceURI);
+ }
+
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SimpleDOMErrorHandler.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SimpleDOMErrorHandler.java
new file mode 100644
index 00000000..0d54adce
--- /dev/null
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SimpleDOMErrorHandler.java
@@ -0,0 +1,98 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed 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.
+*/
+package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.DOMError;
+import org.w3c.dom.DOMErrorHandler;
+
+/**
+ * A simple DOMErrorHandler implementation.
+ *
+ * @author mcentner
+ */
+public class SimpleDOMErrorHandler implements DOMErrorHandler {
+
+ /**
+ * Have there been errors reported?
+ */
+ private boolean errors = false;
+
+ /**
+ * Have there been fatal error reported?
+ */
+ private boolean fatalErrors = false;
+
+ /**
+ * The list of error messages of reported errors.
+ */
+ private Listtrue
if errors have been reported, or false
otherwise
+ */
+ public boolean hasErrors() {
+ return errors;
+ }
+
+ /**
+ * @return true
if fatal errors have been reported, or false
otherwise
+ */
+ public boolean hasFatalErrors() {
+ return fatalErrors;
+ }
+
+ /**
+ * @return a list of error messages that have been reported
+ */
+ public ListurlDereferencerContext
.
+ *
+ * @param urlDereferencerContext the context to be used for dereferencing
+ */
+ public URIDereferncerAdapter(URLDereferencerContext urlDereferencerContext) {
+ super();
+ this.urlDereferencerContext = urlDereferencerContext;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.xml.crypto.URIDereferencer#dereference(javax.xml.crypto.URIReference, javax.xml.crypto.XMLCryptoContext)
+ */
+ @Override
+ public Data dereference(URIReference uriReference, XMLCryptoContext context)
+ throws URIReferenceException {
+
+ String uriString = uriReference.getURI();
+ if (uriString == null) {
+ return null;
+ }
+
+ URI uri;
+ try {
+ uri = new URI(uriString);
+ } catch (URISyntaxException e) {
+ throw new URIReferenceException(e.getMessage(), e);
+ }
+
+ if (uri.isAbsolute()) {
+
+ URLDereferencer dereferencer = URLDereferencer.getInstance();
+ StreamData streamData;
+ try {
+ streamData = dereferencer.dereference(uriString, urlDereferencerContext);
+ } catch (IOException e) {
+ throw new URIReferenceException(e.getMessage(), e);
+ }
+ return new OctetStreamData(streamData.getStream(), uriString, streamData.getContentType());
+
+ } else {
+
+ URIDereferencer uriDereferencer = context.getURIDereferencer();
+ if (uriDereferencer == null || uriDereferencer == this) {
+ uriDereferencer = new URIDereferencerImpl();
+ }
+
+ return uriDereferencer.dereference(uriReference, context);
+
+ }
+
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTReference.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTReference.java
new file mode 100644
index 00000000..6b388f2a
--- /dev/null
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTReference.java
@@ -0,0 +1,112 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed 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.
+*/
+package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import iaik.xml.crypto.dsig.DigestMethodImpl;
+import iaik.xml.crypto.dsig.DigestValueImpl;
+import iaik.xml.crypto.dsig.ReferenceImpl;
+import iaik.xml.crypto.dsig.TransformImpl;
+import iaik.xml.crypto.dsig.TransformsImpl;
+
+import javax.xml.crypto.Data;
+import javax.xml.crypto.URIDereferencer;
+import javax.xml.crypto.URIReferenceException;
+import javax.xml.crypto.XMLCryptoContext;
+import javax.xml.crypto.dsig.DigestMethod;
+import javax.xml.crypto.dsig.TransformException;
+
+import at.gv.egiz.bku.slexceptions.SLCommandException;
+import at.gv.egiz.bku.slexceptions.SLExceptionMessages;
+
+/**
+ * This class extends the XSECT ReferenceImpl to allow for the use
+ * of already marshalled ds:Transforms
elements for initialization.
+ *
+ * @author mcentner
+ */
+public class XSECTReference extends ReferenceImpl {
+
+ /**
+ * The URIDereferencer to be used for dereferencing.
+ */
+ protected URIDereferencer dereferencer;
+
+ /**
+ * Creates a new instance of this XSECTReference with the given
+ * uri
, digest method, transforms
, type
+ * and id
value.
+ *
+ * @param uri
+ * the URI
-attribute value (may be null
)
+ * @param dm
+ * the digest method
+ * @param transforms
+ * a TransformsImpl element (may be null
)
+ * @param type
+ * the Type
-attribute value (may be null
)
+ * @param id
+ * the Id
-attribute value (may be null
)
+ *
+ * @throws NullPointerException
+ * if digestMethod
is null
+ * @throws IllegalArgumentException
+ * if uri
is not RFC 2396 compliant
+ * @throws ClassCastException
+ * if any of the transforms
is not of type
+ * {@link TransformImpl}
+ */
+ public XSECTReference(String uri, DigestMethod dm, TransformsImpl transforms, String type,
+ String id) {
+ super(uri, transforms, type, id);
+ digestMethod_ = (DigestMethodImpl) dm;
+ digestValue_ = new DigestValueImpl();
+ }
+
+ /* (non-Javadoc)
+ * @see iaik.xml.crypto.dsig.ReferenceType#dereference(javax.xml.crypto.XMLCryptoContext)
+ */
+ @Override
+ public Data dereference(XMLCryptoContext context) throws TransformException,
+ URIReferenceException {
+ if (dereferencer != null) {
+ return dereferencer.dereference(this, context);
+ } else {
+ try {
+ return super.dereference(context);
+ } catch (URIReferenceException e) {
+ SLCommandException commandException = new SLCommandException(4003,
+ SLExceptionMessages.EC4003_NOT_RESOLVED, new Object[] { getURI() });
+ throw new URIReferenceException("Failed to dereference data to-be signed.", commandException);
+ }
+ }
+ }
+
+ /**
+ * @return the dereferencer to be used for dereferencing this reference
+ */
+ public URIDereferencer getDereferencer() {
+ return dereferencer;
+ }
+
+ /**
+ * @param dereferencer the dereferencer to be used for dereferencing this reference
+ */
+ public void setDereferencer(URIDereferencer dereferencer) {
+ this.dereferencer = dereferencer;
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTTransforms.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTTransforms.java
new file mode 100644
index 00000000..a98e4236
--- /dev/null
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTTransforms.java
@@ -0,0 +1,124 @@
+/*
+* Copyright 2008 Federal Chancellery Austria and
+* Graz University of Technology
+*
+* Licensed 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.
+*/
+package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import iaik.xml.crypto.dsig.TransformImpl;
+import iaik.xml.crypto.dsig.TransformsImpl;
+
+import java.util.List;
+
+import javax.xml.crypto.MarshalException;
+import javax.xml.crypto.dom.DOMCryptoContext;
+import javax.xml.crypto.dsig.Transform;
+
+import org.w3c.dom.Node;
+
+/**
+ * This class extends the XSECT TransformsImpl to allow for the use of an
+ * unmarshalled ds:Transforms
element for initalization.
+ *
+ * @author mcentner
+ */
+public class XSECTTransforms extends TransformsImpl {
+
+ /**
+ * Creates a new XSECTTransforms with the given list of transforms
.
+ *
+ * @param transforms a list of {@link TransformImpl}s
+ * @see TransformsImpl#TransformsImpl(List)
+ */
+ @SuppressWarnings("unchecked")
+ public XSECTTransforms(List transforms) {
+ super(transforms);
+ }
+
+ /**
+ * Creates a new XSECTTransforms and initializes it from the given
+ * ds:Transforms
node.
+ *
+ * @param context the context used for unmarshalling
+ * @param node the ds:Transforms
node
+ *
+ * @throws MarshalException if unmarshalling the ds:Transforms
fails
+ */
+ public XSECTTransforms(DOMCryptoContext context, Node node)
+ throws MarshalException {
+ super(context, node);
+ }
+
+ /**
+ * Inserts the given transform
at the top of the
+ * transform list.
+ *
+ * @param transform the ds:Transform
to instert
+ */
+ @SuppressWarnings("unchecked")
+ public void insertTransform(Transform transform) {
+ if (transform == null) {
+ throw new NullPointerException("Parameter 'transform' must not be null.");
+ }
+ if (!(transform instanceof TransformImpl)) {
+ throw new ClassCastException("Transform 'transform' must be of type '" + TransformImpl.class.getName() + "'.");
+ }
+ transforms_.add(0, transform);
+ }
+
+ /**
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ private ListSTALPrivateKey
with the given
- * stal
implementation, signature algorithm
,
- * keyboxIdentifier
and hashDataInputCallback
- * interface.
- *
- * @param stal
- * the STAL implementation
- * @param algorithm
- * the signature algorithm
- * @param keyboxIdentifier
- * the keybox identifier
- * @param hashDataInputCallback
- * the interface for obtaining the has input data
- */
- public STALPrivateKey(STAL stal,
- String algorithm, String keyboxIdentifier, HashDataInputCallback hashDataInputCallback) {
- super();
- this.keyboxIdentifier = keyboxIdentifier;
- this.hashDataInputCallback = hashDataInputCallback;
- this.stal = stal;
- this.algorithm = algorithm;
- }
-
- /* (non-Javadoc)
- * @see java.security.Key#getAlgorithm()
- */
- @Override
- public String getAlgorithm() {
- return algorithm;
- }
-
- /* (non-Javadoc)
- * @see java.security.Key#getEncoded()
- */
- @Override
- public byte[] getEncoded() {
- throw new UnsupportedOperationException("STALPrivateKey does not support the getEncoded() method.");
- }
-
- /* (non-Javadoc)
- * @see java.security.Key#getFormat()
- */
- @Override
- public String getFormat() {
- return null;
- }
-
- /**
- * @return the STAL implementation
- */
- public STAL getStal() {
- return stal;
- }
-
- /**
- * @return the interface for obtaining the hash data input
- */
- public HashDataInputCallback getHashDataInputCallback() {
- return hashDataInputCallback;
- }
-
- /**
- * @return the keybox identifier
- */
- public String getKeyboxIdentifier() {
- return keyboxIdentifier;
- }
-
-}
+package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import at.gv.egiz.stal.HashDataInput;
+import java.security.PrivateKey;
+
+import at.gv.egiz.stal.STAL;
+//import at.gv.egiz.stal.HashDataInputCallback;
+import java.util.List;
+
+/**
+ * This class implements a private key used by the {@link STALSignature} class.
+ *
+ * @author mcentner
+ */
+public class STALPrivateKey implements PrivateKey {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The STAL implementation.
+ */
+ private STAL stal;
+
+ /**
+ * The callback interface for obtaining the hash input data.
+ */
+// private HashDataInputCallback hashDataInputCallback;
+
+
+ private ListSTALPrivateKey
with the given
+ * stal
implementation, signature algorithm
,
+ * keyboxIdentifier
and hashDataInputCallback
+ * interface.
+ *
+ * @param stal
+ * the STAL implementation
+ * @param algorithm
+ * the signature algorithm
+ * @param keyboxIdentifier
+ * the keybox identifier
+ * @param hashDataInputCallback
+ * the interface for obtaining the has input data
+ */
+ public STALPrivateKey(STAL stal,
+ String algorithm, String keyboxIdentifier, ListCreateXMLSignatureRequest
.
- *
- * @author mcentner
- */
-public class Signature implements HashDataInputCallback {
-
- /**
- * Logging facility.
- */
- private static Log log = LogFactory.getLog(Signature.class);
-
- /**
- * The DOM implementation used.
- */
- private DOMImplementationLS domImplLS;
-
- /**
- * The SignatureContext for the XMLSignature.
- */
- private SignatureContext ctx;
-
- /**
- * The list of {@link DataObject}s for this signature.
- */
- private ListId
-attribute values of this signature's
- * ds:Reference
s to the corresponding {@link DataObject}s.
- */
- private Mapxsd:ID
to be registered in the {@link DOMSignContext}.
- */
- private ListsignatureInfo
provided.
- *
- * @param signatureInfo
- * the SignatureInfo
- *
- * @throws SLCommandException
- * if processing fails for any reason
- * @throws IllegalStateException
- * if the parent
node has already been set
- * @throws NullPointerException
- * if signatureInfo
is null
- */
- public void setSignatureInfo(SignatureInfoCreationType signatureInfo) throws SLCommandException {
-
- if (signatureLocation != null) {
- throw new IllegalStateException("SignatureEnvironment already set.");
- }
-
- Base64XMLOptRefContentType signatureEnvironment = signatureInfo.getSignatureEnvironment();
-
- if (signatureEnvironment == null) {
-
- // no SignatureEnvironment, so we use an empty document and the document as parent
- ensureSignatureLocation();
-
- } else {
-
- // parse SignatureEnvrionment and use as document
- Document document = parseSignatureEnvironment(signatureEnvironment, signatureInfo.getSupplement());
- ctx.setDocument(document);
-
- signatureLocation = new SignatureLocation(ctx);
- signatureLocation.setSignatureInfo(signatureInfo);
-
- }
-
- }
-
- /**
- * Ensures a SignatureLocation for this Signature.
- */
- private void ensureSignatureLocation() {
-
- if (signatureLocation == null) {
- Document document = DOMUtils.createDocument();
- ctx.setDocument(document);
-
- signatureLocation = new SignatureLocation(ctx);
- signatureLocation.setParent(document);
- }
-
- }
-
- /**
- * Adds a DataObject with the information given by the
- * dataObjectInfo
provided to this Signature.
- *
- * @param dataObjectInfo
- * the DataObjectInfo
element
- *
- * @throws SLCommandException
- * if adding the DataObject fails
- * @throws SLRequestException
- * if the information provided by the given
- * dataObjectInfo
does not conform to the security
- * layer specification
- * @throws NullPointerException
- * if dataObjectInfo
is null
- */
- public void addDataObject(DataObjectInfoType dataObjectInfo) throws SLCommandException, SLRequestException {
-
- ensureSignatureLocation();
-
- DataObject dataObject = new DataObject(ctx);
- dataObject.setDataObjectInfo(dataObjectInfo);
-
- dataObjects.add(dataObject);
-
- dataObjectReferencIds.put(dataObject.getReference().getId(), dataObject);
-
- }
-
- /**
- * Sets the SigningTime
qualifying property of this Signature.
- *
- * @param signingTime the signing time to set
- */
- public void setSigningTime(Date signingTime) {
- this.signingTime = signingTime;
- }
-
- /**
- * Sets the SignerCertificate
qualifying property of this Signature.
- *
- * @param certificate the signer's certificate
- */
- public void setSignerCeritifcate(X509Certificate certificate) {
- this.signerCertificate = certificate;
- }
-
- /**
- * Builds the XMLSignature data structure of this Signature as configured by
- * the various setter methods.
- *
- * @throws SLCommandException if building this signature fails
- */
- public void buildXMLSignature() throws SLCommandException {
-
- ListsignContext
.
- * - * Call's {@link #buildXMLSignature()} if it has not been called yet. - *
- * - * @param signContext - * the signing context - * - * @throws MarshalException - * if marshalling the XMLSignature fails - * @throws XMLSignatureException - * if signing the XMLSignature fails - * @throws SLCommandException - * if building the XMLSignature fails - * @throws NullPointerException - * ifsignContext
is null
- */
- public void sign(DOMSignContext signContext) throws MarshalException, XMLSignatureException, SLCommandException {
-
- if (xmlSignature == null) {
- buildXMLSignature();
- }
-
- for (IdAttribute idAttribute : idAttributes) {
- signContext.setIdAttributeNS(idAttribute.element, idAttribute.namespaceURI, idAttribute.localName);
- }
-
- // DO NOT USE:
- // signContext.setProperty("iaik.xml.crypto.dsig.sign-over", Boolean.TRUE);
-
- signContext.setProperty("javax.xml.crypto.dsig.cacheReference", Boolean.TRUE);
-
- signContext.putNamespacePrefix(XMLSignature.XMLNS, "dsig");
-
- signContext.setURIDereferencer(new URIDereferncerAdapter(ctx.getDereferencerContext()));
-
- try {
- xmlSignature.sign(signContext);
- } catch (XMLSignatureException e) {
- Throwable cause = e.getCause();
- while (cause != null) {
- if (cause instanceof STALSignatureException) {
- int errorCode = ((STALSignatureException) cause).getErrorCode();
- SLCommandException commandException = new SLCommandException(errorCode);
- log.info("Failed to sign signature.", commandException);
- throw commandException;
- } else {
- cause = cause.getCause();
- }
- }
- throw e;
- }
-
- // debug
- if (log.isTraceEnabled()) {
- for (DataObject dataObject : dataObjects) {
- Reference reference = dataObject.getReference();
- InputStream digestInputStream = reference.getDigestInputStream();
- if (digestInputStream != null) {
- String mimeType = dataObject.getMimeType();
- StringBuilder sb = new StringBuilder();
- sb.append("DigestInput for Reference with id='");
- sb.append(reference.getId());
- sb.append("' (MIME-Type=");
- sb.append(dataObject.getMimeType());
- sb.append("):\n");
- try {
- if (mimeType != null && (
- mimeType.startsWith("text") ||
- "application/xhtml+xml".equals(mimeType))) {
- byte[] b = new byte[512];
- for (int l; (l = digestInputStream.read(b)) != -1;) {
- sb.append(new String(b, 0, l));
- }
- } else {
- sb.append(HexDump.hexDump(digestInputStream));
- }
- } catch (IOException e) {
- log.error(e);
- }
- log.trace(sb.toString());
- } else {
- log.trace("Reference caching is not enabled.");
- }
- }
- }
-
- }
-
- /**
- * Sign this Signature using the given stal
implementation and
- * keyboxIdentifier
.
- * - * This method configures an appropriate {@link DOMSignContext} and calls - * {@link #sign(DOMSignContext)}. If {@link #buildXMLSignature()} has not been - * called yet, it is called by this method. - *
- * - * @param stal - * the STAL implementation to use - * @param keyboxIdentifier - * the KeyboxIdentifier to use - * - * @throws MarshalException - * if marshalling this Signature fails - * @throws XMLSignatureException - * if signing this Signature fails - * @throws SLCommandException - * if building this Signature fails - * @throws NullPointerException - * ifstal
or keyboxIdentifier
is
- * null
- */
- public void sign(STAL stal, String keyboxIdentifier) throws MarshalException, XMLSignatureException, SLCommandException {
-
- if (stal == null) {
- throw new NullPointerException("Argument 'stal' must not be null.");
- }
-
- if (keyboxIdentifier == null) {
- throw new NullPointerException("Argument 'keyboxIdentifier' must not be null.");
- }
-
- if (xmlSignature == null) {
- buildXMLSignature();
- }
-
- SignatureMethod signatureMethod = xmlSignature.getSignedInfo().getSignatureMethod();
- String algorithm = signatureMethod.getAlgorithm();
-
- PrivateKey privateKey = new STALPrivateKey(stal, algorithm, keyboxIdentifier, this);
-
- DOMSignContext signContext;
- if (getNextSibling() == null) {
- signContext = new DOMSignContext(privateKey, getParent());
- } else {
- signContext = new DOMSignContext(privateKey, getParent(), getNextSibling());
- }
-
- sign(signContext);
- }
-
- @Override
- public InputStream getHashDataInput(String referenceId) {
-
- DataObject dataObject = dataObjectReferencIds.get(referenceId);
- if (dataObject != null) {
- return dataObject.getReference().getDigestInputStream();
- } else {
- return null;
- }
- }
-
- /**
- * Adds the XAdES QualifyingProperties
as an
- * ds:Object
and a corresponding ds:Reference
to
- * it's SignedProperties
element to this Signature.
- *
- * @param objects
- * the list of ds:Objects
to add the created
- * ds:Object
to
- * @param references
- * the list of ds:References
to add the created
- * ds:Reference
to
- *
- * @throws SLCommandException
- * if creating and adding the XAdES
- * QualifyingProperties
fails
- * @throws NullPointerException
- * if objects
or references
is
- * null
- */
- private void addXAdESObjectAndReference(ListSignatureEnvironment
element
- * @param supplements
- * an optional list of Supplements
(may be
- * null
)
- *
- * @return the parsed SignatureEnvironment document
- *
- * @throws SLCommandException
- * if parsing the SignatureEnvironment fails
- * @throws NullPointerException
- * if signatureEnvironment
is null
- */
- private Document parseSignatureEnvironment(
- Base64XMLOptRefContentType signatureEnvironment,
- Listreference
URI.
- *
- * @param reference
- * the reference URL
- *
- * @return an LSInput from the given reference
URI
- *
- * @throws IOException
- * if dereferencing the given reference
fails
- */
- private LSInput createLSInput(String reference) throws IOException {
-
- URLDereferencer urlDereferencer = URLDereferencer.getInstance();
- StreamData streamData = urlDereferencer.dereference(reference, ctx.getDereferencerContext());
-
- String contentType = streamData.getContentType();
- String charset = HttpUtil.getCharset(contentType, true);
- InputStreamReader streamReader;
- try {
- streamReader = new InputStreamReader(streamData.getStream(), charset);
- } catch (UnsupportedEncodingException e) {
- log.info("Charset " + charset + " not supported. Using default.");
- streamReader = new InputStreamReader(streamData.getStream());
- }
-
- LSInput input = domImplLS.createLSInput();
- input = domImplLS.createLSInput();
- input.setCharacterStream(streamReader);
-
- return input;
-
- }
-
- /**
- * Creates an LSInput from the given content
bytes.
- *
- * @param content
- * the content bytes
- *
- * @return an LSInput from the givne content
bytes
- */
- private LSInput createLSInput(byte[] content) {
-
- ByteArrayInputStream inputStream = new ByteArrayInputStream(content);
- LSInput input = domImplLS.createLSInput();
- input.setByteStream(inputStream);
-
- return input;
-
- }
-
- /**
- * Creates an LSInput from the given XML content
.
- *
- * @param content
- * the XML content
- * @return an LSInput from the given XML content
- *
- * @throws XMLStreamException
- * if reading the XMLStream from the given XML content fails
- */
- private LSInput createLSInput(XMLContentType content) throws XMLStreamException {
-
- ByteArrayOutputStream redirectedStream = content.getRedirectedStream();
- if (redirectedStream != null) {
- LSInput input = domImplLS.createLSInput();
- input.setByteStream(new ByteArrayInputStream(redirectedStream.toByteArray()));
- return input;
- } else {
- return null;
- }
-
- }
-
- /**
- * Represents an xsd:Id
-attribute value.
- *
- * @author mcentner
- */
- private class IdAttribute {
-
- private Element element;
-
- private String namespaceURI;
-
- private String localName;
-
- }
-
- /**
- * An implementation of the LSResourceResolver that uses a list of supplements
- * to resolve resources.
- *
- * @author mcentner
- */
- private class LSResourceResolverAdapter implements LSResourceResolver {
-
- ListCreateXMLSignatureRequest
.
+ *
+ * @author mcentner
+ */
+public class Signature {
+
+ /**
+ * Logging facility.
+ */
+ private static Log log = LogFactory.getLog(Signature.class);
+
+ /**
+ * The DOM implementation used.
+ */
+ private DOMImplementationLS domImplLS;
+
+ /**
+ * The SignatureContext for the XMLSignature.
+ */
+ private SignatureContext ctx;
+
+ /**
+ * The list of {@link DataObject}s for this signature.
+ */
+ private ListId
-attribute values of this signature's
+ * ds:Reference
s to the corresponding {@link DataObject}s.
+ */
+// private Mapxsd:ID
to be registered in the {@link DOMSignContext}.
+ */
+ private ListsignatureInfo
provided.
+ *
+ * @param signatureInfo
+ * the SignatureInfo
+ *
+ * @throws SLCommandException
+ * if processing fails for any reason
+ * @throws IllegalStateException
+ * if the parent
node has already been set
+ * @throws NullPointerException
+ * if signatureInfo
is null
+ */
+ public void setSignatureInfo(SignatureInfoCreationType signatureInfo) throws SLCommandException {
+
+ if (signatureLocation != null) {
+ throw new IllegalStateException("SignatureEnvironment already set.");
+ }
+
+ Base64XMLOptRefContentType signatureEnvironment = signatureInfo.getSignatureEnvironment();
+
+ if (signatureEnvironment == null) {
+
+ // no SignatureEnvironment, so we use an empty document and the document as parent
+ ensureSignatureLocation();
+
+ } else {
+
+ // parse SignatureEnvrionment and use as document
+ Document document = parseSignatureEnvironment(signatureEnvironment, signatureInfo.getSupplement());
+ ctx.setDocument(document);
+
+ signatureLocation = new SignatureLocation(ctx);
+ signatureLocation.setSignatureInfo(signatureInfo);
+
+ }
+
+ }
+
+ /**
+ * Ensures a SignatureLocation for this Signature.
+ */
+ private void ensureSignatureLocation() {
+
+ if (signatureLocation == null) {
+ Document document = DOMUtils.createDocument();
+ ctx.setDocument(document);
+
+ signatureLocation = new SignatureLocation(ctx);
+ signatureLocation.setParent(document);
+ }
+
+ }
+
+ /**
+ * Adds a DataObject with the information given by the
+ * dataObjectInfo
provided to this Signature.
+ *
+ * @param dataObjectInfo
+ * the DataObjectInfo
element
+ *
+ * @throws SLCommandException
+ * if adding the DataObject fails
+ * @throws SLRequestException
+ * if the information provided by the given
+ * dataObjectInfo
does not conform to the security
+ * layer specification
+ * @throws NullPointerException
+ * if dataObjectInfo
is null
+ */
+ public void addDataObject(DataObjectInfoType dataObjectInfo) throws SLCommandException, SLRequestException {
+
+ ensureSignatureLocation();
+
+ DataObject dataObject = new DataObject(ctx);
+ dataObject.setDataObjectInfo(dataObjectInfo);
+
+ dataObjects.add(dataObject);
+
+// dataObjectReferencIds.put(dataObject.getReference().getId(), dataObject);
+
+ }
+
+ /**
+ * Sets the SigningTime
qualifying property of this Signature.
+ *
+ * @param signingTime the signing time to set
+ */
+ public void setSigningTime(Date signingTime) {
+ this.signingTime = signingTime;
+ }
+
+ /**
+ * Sets the SignerCertificate
qualifying property of this Signature.
+ *
+ * @param certificate the signer's certificate
+ */
+ public void setSignerCeritifcate(X509Certificate certificate) {
+ this.signerCertificate = certificate;
+ }
+
+ /**
+ * Builds the XMLSignature data structure of this Signature as configured by
+ * the various setter methods.
+ *
+ * @throws SLCommandException if building this signature fails
+ */
+ public void buildXMLSignature() throws SLCommandException {
+
+ ListsignContext
.
+ * + * Call's {@link #buildXMLSignature()} if it has not been called yet. + *
+ * + * @param signContext + * the signing context + * + * @throws MarshalException + * if marshalling the XMLSignature fails + * @throws XMLSignatureException + * if signing the XMLSignature fails + * @throws SLCommandException + * if building the XMLSignature fails + * @throws NullPointerException + * ifsignContext
is null
+ */
+ public void sign(DOMSignContext signContext) throws MarshalException, XMLSignatureException, SLCommandException {
+
+ if (xmlSignature == null) {
+ buildXMLSignature();
+ }
+
+ for (IdAttribute idAttribute : idAttributes) {
+ signContext.setIdAttributeNS(idAttribute.element, idAttribute.namespaceURI, idAttribute.localName);
+ }
+
+ // DO NOT USE:
+ // signContext.setProperty("iaik.xml.crypto.dsig.sign-over", Boolean.TRUE);
+
+ signContext.setProperty("javax.xml.crypto.dsig.cacheReference", Boolean.TRUE);
+
+ signContext.putNamespacePrefix(XMLSignature.XMLNS, "dsig");
+
+ signContext.setURIDereferencer(new URIDereferncerAdapter(ctx.getDereferencerContext()));
+
+ try {
+ xmlSignature.sign(signContext);
+ } catch (XMLSignatureException e) {
+ Throwable cause = e.getCause();
+ while (cause != null) {
+ if (cause instanceof STALSignatureException) {
+ int errorCode = ((STALSignatureException) cause).getErrorCode();
+ SLCommandException commandException = new SLCommandException(errorCode);
+ log.info("Failed to sign signature.", commandException);
+ throw commandException;
+ } else {
+ cause = cause.getCause();
+ }
+ }
+ throw e;
+ }
+
+ // debug
+ if (log.isTraceEnabled()) {
+ for (DataObject dataObject : dataObjects) {
+ Reference reference = dataObject.getReference();
+ InputStream digestInputStream = reference.getDigestInputStream();
+ if (digestInputStream != null) {
+ String mimeType = dataObject.getMimeType();
+ StringBuilder sb = new StringBuilder();
+ sb.append("DigestInput for Reference with id='");
+ sb.append(reference.getId());
+ sb.append("' (MIME-Type=");
+ sb.append(dataObject.getMimeType());
+ sb.append("):\n");
+ try {
+ if (mimeType != null && (
+ mimeType.startsWith("text") ||
+ "application/xhtml+xml".equals(mimeType))) {
+ byte[] b = new byte[512];
+ for (int l; (l = digestInputStream.read(b)) != -1;) {
+ sb.append(new String(b, 0, l));
+ }
+ } else {
+ sb.append(HexDump.hexDump(digestInputStream));
+ }
+ } catch (IOException e) {
+ log.error(e);
+ }
+ log.trace(sb.toString());
+ } else {
+ log.trace("Reference caching is not enabled.");
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Sign this Signature using the given stal
implementation and
+ * keyboxIdentifier
.
+ * + * This method configures an appropriate {@link DOMSignContext} and calls + * {@link #sign(DOMSignContext)}. If {@link #buildXMLSignature()} has not been + * called yet, it is called by this method. + *
+ * + * @param stal + * the STAL implementation to use + * @param keyboxIdentifier + * the KeyboxIdentifier to use + * + * @throws MarshalException + * if marshalling this Signature fails + * @throws XMLSignatureException + * if signing this Signature fails + * @throws SLCommandException + * if building this Signature fails + * @throws NullPointerException + * ifstal
or keyboxIdentifier
is
+ * null
+ */
+ public void sign(STAL stal, String keyboxIdentifier) throws MarshalException, XMLSignatureException, SLCommandException {
+
+ if (stal == null) {
+ throw new NullPointerException("Argument 'stal' must not be null.");
+ }
+
+ if (keyboxIdentifier == null) {
+ throw new NullPointerException("Argument 'keyboxIdentifier' must not be null.");
+ }
+
+ if (xmlSignature == null) {
+ buildXMLSignature();
+ }
+
+ SignatureMethod signatureMethod = xmlSignature.getSignedInfo().getSignatureMethod();
+ String algorithm = signatureMethod.getAlgorithm();
+
+ //don't get hashDataInputs (digestInputStreams) now, only once Signature.sign() was called (cf STALSignature.engineSign)
+ PrivateKey privateKey = new STALPrivateKey(stal, algorithm, keyboxIdentifier, dataObjects); // hashDataInputs);
+
+ DOMSignContext signContext;
+ if (getNextSibling() == null) {
+ signContext = new DOMSignContext(privateKey, getParent());
+ } else {
+ signContext = new DOMSignContext(privateKey, getParent(), getNextSibling());
+ }
+
+ sign(signContext);
+ }
+
+// @Override
+// public HashDataInput getHashDataInput(final String referenceId) {
+// final DataObject dataObject = dataObjectReferencIds.get(referenceId);
+// if (dataObject != null) {
+// return new HashDataInput() {
+//
+// InputStream hashDataInput = dataObject.getReference().getDigestInputStream();
+//
+// @Override
+// public String getReferenceId() {
+// return referenceId;
+// }
+//
+// @Override
+// public String getMimeType() {
+// return dataObject.getMimeType();
+// }
+//
+// @Override
+// public InputStream getHashDataInput() {
+// return hashDataInput;
+// }
+// };
+// }
+// return null;
+// }
+
+ /**
+ * Adds the XAdES QualifyingProperties
as an
+ * ds:Object
and a corresponding ds:Reference
to
+ * it's SignedProperties
element to this Signature.
+ *
+ * @param objects
+ * the list of ds:Objects
to add the created
+ * ds:Object
to
+ * @param references
+ * the list of ds:References
to add the created
+ * ds:Reference
to
+ *
+ * @throws SLCommandException
+ * if creating and adding the XAdES
+ * QualifyingProperties
fails
+ * @throws NullPointerException
+ * if objects
or references
is
+ * null
+ */
+ private void addXAdESObjectAndReference(ListSignatureEnvironment
element
+ * @param supplements
+ * an optional list of Supplements
(may be
+ * null
)
+ *
+ * @return the parsed SignatureEnvironment document
+ *
+ * @throws SLCommandException
+ * if parsing the SignatureEnvironment fails
+ * @throws NullPointerException
+ * if signatureEnvironment
is null
+ */
+ private Document parseSignatureEnvironment(
+ Base64XMLOptRefContentType signatureEnvironment,
+ Listreference
URI.
+ *
+ * @param reference
+ * the reference URL
+ *
+ * @return an LSInput from the given reference
URI
+ *
+ * @throws IOException
+ * if dereferencing the given reference
fails
+ */
+ private LSInput createLSInput(String reference) throws IOException {
+
+ URLDereferencer urlDereferencer = URLDereferencer.getInstance();
+ StreamData streamData = urlDereferencer.dereference(reference, ctx.getDereferencerContext());
+
+ String contentType = streamData.getContentType();
+ String charset = HttpUtil.getCharset(contentType, true);
+ InputStreamReader streamReader;
+ try {
+ streamReader = new InputStreamReader(streamData.getStream(), charset);
+ } catch (UnsupportedEncodingException e) {
+ log.info("Charset " + charset + " not supported. Using default.");
+ streamReader = new InputStreamReader(streamData.getStream());
+ }
+
+ LSInput input = domImplLS.createLSInput();
+ input = domImplLS.createLSInput();
+ input.setCharacterStream(streamReader);
+
+ return input;
+
+ }
+
+ /**
+ * Creates an LSInput from the given content
bytes.
+ *
+ * @param content
+ * the content bytes
+ *
+ * @return an LSInput from the givne content
bytes
+ */
+ private LSInput createLSInput(byte[] content) {
+
+ ByteArrayInputStream inputStream = new ByteArrayInputStream(content);
+ LSInput input = domImplLS.createLSInput();
+ input.setByteStream(inputStream);
+
+ return input;
+
+ }
+
+ /**
+ * Creates an LSInput from the given XML content
.
+ *
+ * @param content
+ * the XML content
+ * @return an LSInput from the given XML content
+ *
+ * @throws XMLStreamException
+ * if reading the XMLStream from the given XML content fails
+ */
+ private LSInput createLSInput(XMLContentType content) throws XMLStreamException {
+
+ ByteArrayOutputStream redirectedStream = content.getRedirectedStream();
+ if (redirectedStream != null) {
+ LSInput input = domImplLS.createLSInput();
+ input.setByteStream(new ByteArrayInputStream(redirectedStream.toByteArray()));
+ return input;
+ } else {
+ return null;
+ }
+
+ }
+
+ /**
+ * Represents an xsd:Id
-attribute value.
+ *
+ * @author mcentner
+ */
+ private class IdAttribute {
+
+ private Element element;
+
+ private String namespaceURI;
+
+ private String localName;
+
+ }
+
+ /**
+ * An implementation of the LSResourceResolver that uses a list of supplements
+ * to resolve resources.
+ *
+ * @author mcentner
+ */
+ private class LSResourceResolverAdapter implements LSResourceResolver {
+
+ Listtrue
if validation should be enabled, or
+ * false
otherwise.
+ */
+ public static void enableHashDataInputValidation(boolean validate) {
+ DataObject.validate = validate;
+ }
+
+ /**
+ * @return true
if hash data input validation is enabled,
+ * or false
otherwise.
+ */
+ public static boolean isHashDataInputValidationEnabled() {
+ return validate;
+ }
+
+ /**
+ * Valid MIME types.
+ */
+ private static String[] validMimeTypes = DEFAULT_PREFFERED_MIME_TYPES;
+
+ /**
+ * Sets the list of valid hash data input media types.
+ * The array is also used for transformation path selection. + * The transformation path with a final type, that appears in the + * given array in the earliest position is used selected.
+ * + * @param mediaTypes an array of MIME media types. + */ + public static void setValidHashDataInputMediaTypes(String[] mediaTypes) { + validMimeTypes = mediaTypes; + } + /** * The DOM implementation used. */ @@ -184,7 +230,70 @@ public class DataObject { public String getDescription() { return description; } - + + public void validateHashDataInput() throws SLViewerException { + + if (validate) { + + if (reference == null) { + log.error("Medthod validateHashDataInput() called before reference has been created."); + throw new SLViewerException(5000); + } + + InputStream digestInputStream = reference.getDigestInputStream(); + if (digestInputStream == null) { + log.error("Method validateHashDataInput() called before reference has been generated " + + "or reference caching is not enabled."); + throw new SLViewerException(5000); + } + + if (mimeType == null) { + log.info("FinalDataMetaInfo does not specify MIME type of to be signed data."); + // TODO: add detailed message + throw new SLViewerException(5000); + } + + // get MIME media type + String mediaType = mimeType.split(";")[0].trim(); + // and optional charset + String charset = HttpUtil.getCharset(mimeType, false); + + if (Arrays.asList(validMimeTypes).contains(mediaType)) { + + Validator validator; + try { + validator = ValidatorFactory.newValidator(mediaType); + } catch (IllegalArgumentException e) { + log.error("No validator found for mime type '" + mediaType + "'."); + throw new SLViewerException(5000); + } + + try { + validator.validate(digestInputStream, charset); + } catch (ValidationException e) { + if ("text/plain".equals(mediaType)) { + log.info("Data to be displayed contains unsupported characters.", e); + // TODO: add detailed message + throw new SLViewerException(5003); + } else if ("application/xhtml+xml".equals(mediaType)) { + // TODO: add detailed message + log.info("Standard display format: HTML does not conform to specification.", e); + throw new SLViewerException(5004); + } else { + // TODO: add detailed message + log.info("Data to be displayed is invalid.", e); + throw new SLViewerException(5000); + } + } + + } else { + log.info("MIME media type '" + mediaType + "' is not a valid digest input."); + throw new SLViewerException(5001); + } + } + + } + /** * Configures this DataObject with the information provided within the given *sl:DataObjectInfo
.
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALSignature.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALSignature.java
index eba1d96d..2d89c8ae 100644
--- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALSignature.java
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALSignature.java
@@ -17,6 +17,8 @@
package at.gv.egiz.bku.slcommands.impl.xsect;
import at.gv.egiz.bku.slcommands.impl.HashDataInputImpl;
+import at.gv.egiz.bku.slexceptions.SLViewerException;
+
import java.io.ByteArrayOutputStream;
import java.security.InvalidKeyException;
import java.security.InvalidParameterException;
@@ -123,9 +125,14 @@ public class STALSignature extends SignatureSpi {
// log.debug("got " + dataObjects.size() + " DataObjects, passing HashDataInputs to STAL SignRequest");
ListsignContext
is null
*/
- public void sign(DOMSignContext signContext) throws MarshalException, XMLSignatureException, SLCommandException {
+ public void sign(DOMSignContext signContext) throws MarshalException, XMLSignatureException, SLCommandException, SLViewerException {
if (xmlSignature == null) {
buildXMLSignature();
@@ -415,6 +417,9 @@ public class Signature {
Throwable cause = e.getCause();
while (cause != null) {
if (cause instanceof STALSignatureException) {
+ if (((STALSignatureException) cause).getCause() instanceof SLViewerException) {
+ throw (SLViewerException) ((STALSignatureException) cause).getCause();
+ }
int errorCode = ((STALSignatureException) cause).getErrorCode();
SLCommandException commandException = new SLCommandException(errorCode);
log.info("Failed to sign signature.", commandException);
@@ -482,11 +487,12 @@ public class Signature {
* if signing this Signature fails
* @throws SLCommandException
* if building this Signature fails
+ * @throws SLViewerException
* @throws NullPointerException
* if stal
or keyboxIdentifier
is
* null
*/
- public void sign(STAL stal, String keyboxIdentifier) throws MarshalException, XMLSignatureException, SLCommandException {
+ public void sign(STAL stal, String keyboxIdentifier) throws MarshalException, XMLSignatureException, SLCommandException, SLViewerException {
if (stal == null) {
throw new NullPointerException("Argument 'stal' must not be null.");
--
cgit v1.2.3
From 0df8bb10302989f41ed420ec0ff29b2fc2005471 Mon Sep 17 00:00:00 2001
From: wbauer DataObject
of an XML-Signature
- * created by the security layer command CreateXMLSignature
.
- *
- * @author mcentner
- */
-public class DataObject {
-
- /**
- * Logging facility.
- */
- private static Log log = LogFactory.getLog(DataObject.class);
-
- /**
- * DOM Implementation.
- */
- private static final String DOM_LS_3_0 = "LS 3.0";
-
- /**
- * The array of the default preferred MIME type order.
- */
- private static final String[] DEFAULT_PREFFERED_MIME_TYPES =
- new String[] {
+import at.gv.egiz.dom.DOMUtils;
+import at.gv.egiz.marshal.NamespacePrefix;
+import at.gv.egiz.marshal.NamespacePrefixMapperImpl;
+import at.gv.egiz.slbinding.impl.XMLContentType;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+/**
+ * This class represents a DataObject
of an XML-Signature
+ * created by the security layer command CreateXMLSignature
.
+ *
+ * @author mcentner
+ */
+public class DataObject {
+
+ /**
+ * Logging facility.
+ */
+ private static Log log = LogFactory.getLog(DataObject.class);
+
+ /**
+ * DOM Implementation.
+ */
+ private static final String DOM_LS_3_0 = "LS 3.0";
+
+ /**
+ * The array of the default preferred MIME type order.
+ */
+ private static final String[] DEFAULT_PREFFERED_MIME_TYPES =
+ new String[] {
"text/plain",
- "application/xhtml+xml"
+ "application/xhtml+xml"
};
/**
@@ -149,87 +156,87 @@ public class DataObject {
validMimeTypes = mediaTypes;
}
- /**
- * The DOM implementation used.
- */
- private DOMImplementationLS domImplLS;
-
- /**
- * The signature context.
- */
- private SignatureContext ctx;
-
- /**
- * The Reference for this DataObject.
- */
- private XSECTReference reference;
-
- /**
- * The XMLObject for this DataObject.
- */
- private XMLObject xmlObject;
-
- /**
- * The MIME-Type of the digest input.
- */
- private String mimeType;
-
- /**
- * An optional description of the digest input.
- */
- private String description;
-
- /**
- * Creates a new instance.
- *
- * @param document the document of the target signature
- */
- public DataObject(SignatureContext signatureContext) {
- this.ctx = signatureContext;
-
- DOMImplementationRegistry registry;
- try {
- registry = DOMImplementationRegistry.newInstance();
- } catch (Exception e) {
- log.error("Failed to get DOMImplementationRegistry.", e);
- throw new SLRuntimeException("Failed to get DOMImplementationRegistry.");
- }
-
- domImplLS = (DOMImplementationLS) registry.getDOMImplementation(DOM_LS_3_0);
- if (domImplLS == null) {
- log.error("Failed to get DOMImplementation " + DOM_LS_3_0);
- throw new SLRuntimeException("Failed to get DOMImplementation " + DOM_LS_3_0);
- }
-
- }
-
- /**
- * @return the reference
- */
- public Reference getReference() {
- return reference;
- }
-
- /**
- * @return the xmlObject
- */
- public XMLObject getXmlObject() {
- return xmlObject;
- }
-
- /**
- * @return the mimeType
- */
- public String getMimeType() {
- return mimeType;
- }
-
- /**
- * @return the description
- */
- public String getDescription() {
- return description;
- }
+ /**
+ * The DOM implementation used.
+ */
+ private DOMImplementationLS domImplLS;
+
+ /**
+ * The signature context.
+ */
+ private SignatureContext ctx;
+
+ /**
+ * The Reference for this DataObject.
+ */
+ private XSECTReference reference;
+
+ /**
+ * The XMLObject for this DataObject.
+ */
+ private XMLObject xmlObject;
+
+ /**
+ * The MIME-Type of the digest input.
+ */
+ private String mimeType;
+
+ /**
+ * An optional description of the digest input.
+ */
+ private String description;
+
+ /**
+ * Creates a new instance.
+ *
+ * @param document the document of the target signature
+ */
+ public DataObject(SignatureContext signatureContext) {
+ this.ctx = signatureContext;
+
+ DOMImplementationRegistry registry;
+ try {
+ registry = DOMImplementationRegistry.newInstance();
+ } catch (Exception e) {
+ log.error("Failed to get DOMImplementationRegistry.", e);
+ throw new SLRuntimeException("Failed to get DOMImplementationRegistry.");
+ }
+
+ domImplLS = (DOMImplementationLS) registry.getDOMImplementation(DOM_LS_3_0);
+ if (domImplLS == null) {
+ log.error("Failed to get DOMImplementation " + DOM_LS_3_0);
+ throw new SLRuntimeException("Failed to get DOMImplementation " + DOM_LS_3_0);
+ }
+
+ }
+
+ /**
+ * @return the reference
+ */
+ public Reference getReference() {
+ return reference;
+ }
+
+ /**
+ * @return the xmlObject
+ */
+ public XMLObject getXmlObject() {
+ return xmlObject;
+ }
+
+ /**
+ * @return the mimeType
+ */
+ public String getMimeType() {
+ return mimeType;
+ }
+
+ /**
+ * @return the description
+ */
+ public String getDescription() {
+ return description;
+ }
public void validateHashDataInput() throws SLViewerException {
@@ -293,823 +300,920 @@ public class DataObject {
}
}
-
- /**
- * Configures this DataObject with the information provided within the given
- * sl:DataObjectInfo
.
- *
- * @param dataObjectInfo
- * the sl:DataObjectInfo
- *
- * @throws SLCommandException
- * if configuring this DataObject with the information provided in
- * the sl:DataObjectInfo
fails.
- * @throws SLRequestException
- * if the information provided in the sl:DataObjectInfo
- * does not conform to the security layer specification.
- * @throws NullPointerException
- * if dataObjectInfo
is null
- */
- public void setDataObjectInfo(DataObjectInfoType dataObjectInfo) throws SLCommandException, SLRequestException {
-
- Base64XMLLocRefOptRefContentType dataObject = dataObjectInfo.getDataObject();
- String structure = dataObjectInfo.getStructure();
-
- // select and unmarshal an appropriate transformation path if provided
- // and set the final data meta information
- XSECTTransforms transforms = createTransformsAndSetFinalDataMetaInfo(dataObjectInfo.getTransformsInfo());
-
- if ("enveloping".equals(structure)) {
-
- // configure this DataObject as an enveloped DataObject
- setEnvelopedDataObject(dataObject, transforms);
-
- } else if ("detached".equals(structure)) {
-
- // configure this DataObject as an detached DataObject
- setDetachedDataObject(dataObject, transforms);
-
- }
- // other values are not allowed by the schema and are therefore ignored
-
- }
-
- /**
- * Configures this DataObject as an enveloped DataObject with the information
- * provided within the given sl:DataObject
.
- *
- * @param dataObject
- * the sl:DataObject
- * @param transforms
- * an optional Transforms
element (may be
- * null
)
- *
- * @throws SLCommandException
- * if configuring this DataObject with the information provided in
- * the sl:DataObject
fails.
- * @throws SLRequestException
- * if the information provided in the sl:DataObject
- * does not conform to the security layer specification.
- * @throws NullPointerException
- * if dataObject
is null
- */
- private void setEnvelopedDataObject(
- Base64XMLLocRefOptRefContentType dataObject, XSECTTransforms transforms)
- throws SLCommandException, SLRequestException {
-
- String reference = dataObject.getReference();
- if (reference == null) {
- //
- // case A
- //
- // The Reference attribute is not used; the content of sl:DataObject represents the data object.
- // If the data object is XML-coded (the sl:XMLContent element is used in sl:DataObject), then it
- // must be incorporated in the signature structure as parsed XML.
- //
-
- if (dataObject.getBase64Content() != null) {
-
- log.debug("Adding DataObject (Base64Content) without a reference URI.");
-
- // create XMLObject
- XMLObject xmlObject = createXMLObject(new ByteArrayInputStream(dataObject.getBase64Content()));
-
- setXMLObjectAndReferenceBase64(xmlObject, transforms);
-
- } else if (dataObject.getXMLContent() != null) {
-
- log.debug("Adding DataObject (XMLContent) without a reference URI.");
-
- // create XMLObject
- DocumentFragment content = parseDataObject((XMLContentType) dataObject.getXMLContent());
- XMLObject xmlObject = createXMLObject(content);
-
- setXMLObjectAndReferenceXML(xmlObject, transforms);
-
- } else if (dataObject.getLocRefContent() != null) {
-
- log.debug("Adding DataObject (LocRefContent) without a reference URI.");
-
- setEnvelopedDataObject(dataObject.getLocRefContent(), transforms);
-
- } else {
-
- // not allowed
- log.info("XML structure of the command request contains an " +
- "invalid combination of optional elements or attributes. " +
- "DataObject of structure='enveloped' without a reference must contain content.");
- throw new SLRequestException(3003);
-
- }
-
- } else {
-
- if (dataObject.getBase64Content() == null &&
- dataObject.getXMLContent() == null &&
- dataObject.getLocRefContent() == null) {
-
- //
- // case B
- //
- // The Reference attribute contains a URI that must be resolved by the
- // Citizen Card Environment to obtain the data object.
- // The content of sl:DataObject remains empty
- //
-
- log.debug("Adding DataObject from reference URI '" + reference + "'.");
-
- setEnvelopedDataObject(reference, transforms);
-
- } else {
-
- // not allowed
- log.info("XML structure of the command request contains an " +
- "invalid combination of optional elements or attributes. " +
- "DataObject of structure='enveloped' with reference must not contain content.");
- throw new SLRequestException(3003);
-
- }
-
-
- }
-
- }
-
- /**
- * Configures this DataObject as an enveloped DataObject with the content to
- * be dereferenced from the given reference
.
- *
- * @param reference
- * the reference
URI
- * @param transforms
- * an optional Transforms
element (may be
- * null
)
- *
- * @throws SLCommandException
- * if dereferencing the given reference
fails, or if
- * configuring this DataObject with the data dereferenced from the
- * given reference
fails.
- * @throws NullPointerException
- * if reference
is null
- */
- private void setEnvelopedDataObject(String reference, XSECTTransforms transforms) throws SLCommandException {
-
- if (reference == null) {
- throw new NullPointerException("Argument 'reference' must not be null.");
- }
-
- // dereference URL
- URLDereferencer dereferencer = URLDereferencer.getInstance();
-
- StreamData streamData;
- try {
- streamData = dereferencer.dereference(reference, ctx.getDereferencerContext());
- } catch (IOException e) {
- log.info("Failed to dereference XMLObject from '" + reference + "'.", e);
- throw new SLCommandException(4110);
- }
-
- Node childNode;
-
- String contentType = streamData.getContentType();
- if (contentType.startsWith("text/xml")) {
-
- // If content type is text/xml parse content.
- String charset = HttpUtil.getCharset(contentType, true);
-
- Document doc = parseDataObject(streamData.getStream(), charset);
-
- childNode = doc.getDocumentElement();
-
- if (childNode == null) {
- log.info("Failed to parse XMLObject from '" + reference + "'.");
- throw new SLCommandException(4111);
- }
-
- XMLObject xmlObject = createXMLObject(childNode);
-
- setXMLObjectAndReferenceXML(xmlObject, transforms);
-
- } else {
-
- // Include content Base64 encoded.
- XMLObject xmlObject = createXMLObject(streamData.getStream());
-
- setXMLObjectAndReferenceBase64(xmlObject, transforms);
-
- }
-
- }
-
- /**
- * Configures this DataObject as an detached DataObject with the information
- * provided in the given sl:DataObject
and optionally
- * transforms
.
- *
- * @param dataObject
- * the sl:DataObject
- * @param transforms
- * an optional Transforms object, may be null
- *
- * @throws SLCommandException
- * if configuring this DataObject with the information provided in
- * the sl:DataObject
fails.
- * @throws SLRequestException
- * if the information provided in the sl:DataObject
- * does not conform to the security layer specification.
- * @throws NullPointerException
- * if dataObject
is null
- */
- private void setDetachedDataObject(
- Base64XMLLocRefOptRefContentType dataObject, XSECTTransforms transforms)
- throws SLCommandException, SLRequestException {
-
- String referenceURI = dataObject.getReference();
-
- if (referenceURI == null) {
-
- // not allowed
- log.info("XML structure of the command request contains an " +
- "invalid combination of optional elements or attributes. " +
- "DataObject of structure='detached' must contain a reference.");
- throw new SLRequestException(3003);
-
- } else {
-
- DigestMethod dm;
- try {
- dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx);
- } catch (NoSuchAlgorithmException e) {
- log.error("Failed to get DigestMethod.", e);
- throw new SLCommandException(4006);
- } catch (InvalidAlgorithmParameterException e) {
- log.error("Failed to get DigestMethod.", e);
- throw new SLCommandException(4006);
- }
-
- String idValue = ctx.getIdValueFactory().createIdValue("Reference");
-
- reference = new XSECTReference(referenceURI, dm, transforms, null, idValue);
-
- // case D:
- //
- // The Reference attribute contains a URI that is used by the Citizen Card
- // Environment to code the reference to the data object as part of the XML
- // signature (attribute URI in the dsig:Reference) element. The content of
- // sl:DataObject represents the data object.
-
- if (dataObject.getLocRefContent() != null) {
- String locRef = dataObject.getLocRefContent();
- try {
- this.reference.setDereferencer(new LocRefDereferencer(ctx.getDereferencerContext(), locRef));
- } catch (URISyntaxException e) {
- log.info("Invalid URI '" + locRef + "' in DataObject.", e);
- throw new SLCommandException(4003);
- } catch (IllegalArgumentException e) {
- log.info("LocRef URI of '" + locRef + "' not supported in DataObject. ", e);
- throw new SLCommandException(4003);
- }
- } else if (dataObject.getBase64Content() != null) {
- byte[] base64Content = dataObject.getBase64Content();
- this.reference.setDereferencer(new ByteArrayDereferencer(base64Content));
- } else if (dataObject.getXMLContent() != null) {
- XMLContentType xmlContent = (XMLContentType) dataObject.getXMLContent();
- byte[] bytes = xmlContent.getRedirectedStream().toByteArray();
- this.reference.setDereferencer(new ByteArrayDereferencer(bytes));
- } else {
-
- // case C:
- //
- // The Reference attribute contains a URI that must be resolved by the
- // Citizen Card Environment to obtain the data object. The Reference
- // attribute contains a URI that is used by the Citizen Card Environment
- // to code the reference to the data object as part of the XML signature
- // (attribute URI in the dsig:Reference) element. The content of
- // sl:DataObject remains empty.
-
- }
-
- }
- }
-
- /**
- * Returns the preferred sl:TransformInfo
from the given list of
- * transformInfos
, or null
if none of the given
- * transformInfos
is preferred over the others.
- *
- * @param transformsInfos
- * a list of sl:TransformInfo
s
- *
- * @return the selected sl:TransformInfo
or null
, if
- * none is preferred over the others
- */
- private TransformsInfoType selectPreferredTransformsInfo(Listds:Transforms
from the given
- * sl:TransformsInfo
.
- *
- * @param transformsInfo
- * the sl:TransformsInfo
- *
- * @return a corresponding unmarshalled ds:Transforms
, or
- * null
if the given sl:TransformsInfo
does
- * not contain a dsig:Transforms
element
- *
- * @throws SLRequestException
- * if the ds:Transforms
in the given
- * transformsInfo
are not valid or cannot be parsed.
- *
- * @throws MarshalException
- * if the ds:Transforms
in the given
- * transformsInfo
cannot be unmarshalled.
- */
- private XSECTTransforms createTransforms(TransformsInfoType transformsInfo) throws SLRequestException, MarshalException {
-
- ByteArrayOutputStream redirectedStream = ((at.gv.egiz.slbinding.impl.TransformsInfoType) transformsInfo).getRedirectedStream();
- byte[] transformBytes = (redirectedStream != null) ? redirectedStream.toByteArray() : null;
-
- if (transformBytes != null && transformBytes.length > 0) {
-
- // debug
- if (log.isTraceEnabled()) {
- StringBuilder sb = new StringBuilder();
- sb.append("Trying to parse transforms:\n");
- sb.append(new String(transformBytes, Charset.forName("UTF-8")));
- log.trace(sb);
- }
-
- DOMImplementationLS domImplLS = DOMUtils.getDOMImplementationLS();
- LSInput input = domImplLS.createLSInput();
- input.setByteStream(new ByteArrayInputStream(transformBytes));
-
- LSParser parser = domImplLS.createLSParser(
- DOMImplementationLS.MODE_SYNCHRONOUS, null);
- DOMConfiguration domConfig = parser.getDomConfig();
- SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler();
- domConfig.setParameter("error-handler", errorHandler);
- domConfig.setParameter("validate", Boolean.FALSE);
-
- Document document;
- try {
- document = parser.parse(input);
- } catch (DOMException e) {
- log.info("Failed to parse dsig:Transforms.", e);
- throw new SLRequestException(3002);
- } catch (LSException e) {
- log.info("Failed to parse dsig:Transforms.", e);
- throw new SLRequestException(3002);
- }
-
- // adopt ds:Transforms
- Element documentElement = document.getDocumentElement();
- Node adoptedTransforms = ctx.getDocument().adoptNode(documentElement);
-
- DOMCryptoContext context = new DOMCryptoContext();
-
- // unmarshall ds:Transforms
- return new XSECTTransforms(context, adoptedTransforms);
-
- } else {
- return null;
- }
-
- }
-
- /**
- * Sets the mimeType
and the description
value
- * for this DataObject.
- *
- * @param metaInfoType the sl:FinalMetaDataInfo
- *
- * @throws NullPointerException if metaInfoType
is null
- */
- private void setFinalDataMetaInfo(MetaInfoType metaInfoType) {
-
- this.mimeType = metaInfoType.getMimeType();
- this.description = metaInfoType.getDescription();
-
- }
-
- /**
- * Selects an appropriate transformation path (if present) from the given list
- * of sl:TransformInfos
, sets the corresponding final data meta info and
- * returns the corresponding unmarshalled ds:Transforms
.
- *
- * @param transformsInfos the sl:TransformInfos
- *
- * @return the unmarshalled ds:Transforms
, or null
if
- * no transformation path has been selected.
- *
- * @throws SLRequestException if the given list ds:TransformsInfo
contains
- * an invalid ds:Transforms
element, or no suitable transformation path
- * can be found.
- */
- private XSECTTransforms createTransformsAndSetFinalDataMetaInfo(
- Listcontent
.
- *
- * @param content
- * the to-be Base64 encoded content
- * @return an XMLObject with the Base64 encoded content
- */
- private XMLObject createXMLObject(InputStream content) {
-
- Text textNode;
- try {
- textNode = at.gv.egiz.dom.DOMUtils.createBase64Text(content, ctx.getDocument());
- } catch (IOException e) {
- log.error(e);
- throw new SLRuntimeException(e);
- }
-
- DOMStructure structure = new DOMStructure(textNode);
-
- String idValue = ctx.getIdValueFactory().createIdValue("Object");
-
- return ctx.getSignatureFactory().newXMLObject(Collections.singletonList(structure), idValue, null, null);
-
- }
-
- /**
- * Create an XMLObject with the given content
node.
- *
- * @param content the content node
- *
- * @return an XMLObject with the given content
- */
- private XMLObject createXMLObject(Node content) {
-
- String idValue = ctx.getIdValueFactory().createIdValue("Object");
-
- ListxmlObject
and creates and sets a corresponding
- * Reference
.
- *
- * A transform to Base64-decode the xmlObject's content is inserted at the top
- * of to the optional transforms
if given, or to a newly created
- * Transforms
element if transforms
is
- * null
.
- *
- * @param xmlObject
- * the XMLObject
- * @param transforms
- * an optional Transforms
element (may be
- * null
)
- *
- * @throws SLCommandException
- * if creating the Reference fails
- * @throws NullPointerException
- * if xmlObject
is null
- */
- private void setXMLObjectAndReferenceBase64(XMLObject xmlObject, XSECTTransforms transforms) throws SLCommandException {
-
- // create reference URI
- //
- // NOTE: the ds:Object can be referenced directly, as the Base64 transform
- // operates on the text() of the input nodelist.
- //
- String referenceURI = "#" + xmlObject.getId();
-
- // create Base64 Transform
- Transform transform;
- try {
- transform = ctx.getSignatureFactory().newTransform(Transform.BASE64, (TransformParameterSpec) null);
- } catch (NoSuchAlgorithmException e) {
- // algorithm must be present
- throw new SLRuntimeException(e);
- } catch (InvalidAlgorithmParameterException e) {
- // algorithm does not take parameters
- throw new SLRuntimeException(e);
- }
-
- if (transforms == null) {
- transforms = new XSECTTransforms(Collections.singletonList(transform));
- } else {
- transforms.insertTransform(transform);
- }
-
- DigestMethod dm;
- try {
- dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx);
- } catch (NoSuchAlgorithmException e) {
- log.error("Failed to get DigestMethod.", e);
- throw new SLCommandException(4006);
- } catch (InvalidAlgorithmParameterException e) {
- log.error("Failed to get DigestMethod.", e);
- throw new SLCommandException(4006);
- }
- String id = ctx.getIdValueFactory().createIdValue("Reference");
-
- this.xmlObject = xmlObject;
- this.reference = new XSECTReference(referenceURI, dm, transforms, null, id);
-
- }
-
- /**
- * Sets the given xmlObject
and creates and sets a corresponding
- * Reference
.
- *
- * A transform to select the xmlObject's content is inserted at the top of to
- * the optional transforms
if given, or to a newly created
- * Transforms
element if transforms
is
- * null
.
- *
Transforms
element (may be
- * null
)
- *
- * @throws SLCommandException
- * if creating the Reference fails
- * @throws NullPointerException
- * if xmlObject
is null
- */
- private void setXMLObjectAndReferenceXML(XMLObject xmlObject, XSECTTransforms transforms) throws SLCommandException {
-
- // create reference URI
- String referenceURI = "#" + xmlObject.getId();
-
- // create Transform to select ds:Object's children
- Transform xpathTransform;
- Transform c14nTransform;
- try {
-
- XPathType xpath = new XPathType("id(\"" + xmlObject.getId() + "\")/node()", XPathType.Filter.INTERSECT);
- ListxmlContent
and returns a corresponding
- * document fragment.
- *
- *
- * The to-be parsed content is surrounded by
xmlContent
fails
- *
- * @throws NullPointerException
- * if xmlContent
is null
- */
- private DocumentFragment parseDataObject(XMLContentType xmlContent) throws SLCommandException {
-
- ByteArrayOutputStream redirectedStream = xmlContent.getRedirectedStream();
-
- // Note: We can assume a fixed character encoding of UTF-8 for the
- // content of the redirect stream as the content has already been parsed
- // and serialized again to the redirect stream.
-
- ListinputStream
using the given
- * encoding
and returns the parsed document.
- *
- * @param inputStream
- * the to-be parsed input
- *
- * @param encoding
- * the encoding to be used for parsing the given
- * inputStream
- *
- * @return the parsed document
- *
- * @throws SLCommandException
- * if parsing the inputStream
fails.
- *
- * @throws NullPointerException
- * if inputStram
is null
- */
- private Document parseDataObject(InputStream inputStream, String encoding) throws SLCommandException {
-
- LSInput input = domImplLS.createLSInput();
- input.setByteStream(inputStream);
-
- if (encoding != null) {
- input.setEncoding(encoding);
- }
-
- LSParser parser = domImplLS.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null);
- DOMConfiguration domConfig = parser.getDomConfig();
- SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler();
- domConfig.setParameter("error-handler", errorHandler);
- domConfig.setParameter("validate", Boolean.FALSE);
-
- Document doc;
- try {
- doc = parser.parse(input);
- } catch (DOMException e) {
- log.info("Existing XML document cannot be parsed.", e);
- throw new SLCommandException(4111);
- } catch (LSException e) {
- log.info("Existing XML document cannot be parsed. ", e);
- throw new SLCommandException(4111);
- }
-
- if (errorHandler.hasErrors()) {
- // log errors
- if (log.isInfoEnabled()) {
- Listsl:DataObjectInfo
.
+ *
+ * @param dataObjectInfo
+ * the sl:DataObjectInfo
+ *
+ * @throws SLCommandException
+ * if configuring this DataObject with the information provided in
+ * the sl:DataObjectInfo
fails.
+ * @throws SLRequestException
+ * if the information provided in the sl:DataObjectInfo
+ * does not conform to the security layer specification.
+ * @throws NullPointerException
+ * if dataObjectInfo
is null
+ */
+ public void setDataObjectInfo(DataObjectInfoType dataObjectInfo) throws SLCommandException, SLRequestException {
+
+ Base64XMLLocRefOptRefContentType dataObject = dataObjectInfo.getDataObject();
+ String structure = dataObjectInfo.getStructure();
+
+ // select and unmarshal an appropriate transformation path if provided
+ // and set the final data meta information
+ XSECTTransforms transforms = createTransformsAndSetFinalDataMetaInfo(dataObjectInfo.getTransformsInfo());
+
+ if ("enveloping".equals(structure)) {
+
+ // configure this DataObject as an enveloped DataObject
+ setEnvelopedDataObject(dataObject, transforms);
+
+ } else if ("detached".equals(structure)) {
+
+ // configure this DataObject as an detached DataObject
+ setDetachedDataObject(dataObject, transforms);
+
+ }
+ // other values are not allowed by the schema and are therefore ignored
+
+ }
+
+ private byte[] getTransformsBytes(at.gv.egiz.slbinding.impl.TransformsInfoType ti) {
+ return ti.getRedirectedStream().toByteArray();
+// byte[] transformsBytes = ti.getRedirectedStream().toByteArray();
+//
+// if (transformsBytes == null || transformsBytes.length == 0) {
+// return null;
+// }
+//
+// String dsigPrefix = ti.getNamespaceContext().getNamespaceURI("http://www.w3.org/2000/09/xmldsig#");
+// byte[] pre, post;
+// if (dsigPrefix == null) {
+// log.trace("XMLDSig not declared in outside dsig:Transforms");
+// pre = "sl:DataObject
.
+ *
+ * @param dataObject
+ * the sl:DataObject
+ * @param transforms
+ * an optional Transforms
element (may be
+ * null
)
+ *
+ * @throws SLCommandException
+ * if configuring this DataObject with the information provided in
+ * the sl:DataObject
fails.
+ * @throws SLRequestException
+ * if the information provided in the sl:DataObject
+ * does not conform to the security layer specification.
+ * @throws NullPointerException
+ * if dataObject
is null
+ */
+ private void setEnvelopedDataObject(
+ Base64XMLLocRefOptRefContentType dataObject, XSECTTransforms transforms)
+ throws SLCommandException, SLRequestException {
+
+ String reference = dataObject.getReference();
+ if (reference == null) {
+ //
+ // case A
+ //
+ // The Reference attribute is not used; the content of sl:DataObject represents the data object.
+ // If the data object is XML-coded (the sl:XMLContent element is used in sl:DataObject), then it
+ // must be incorporated in the signature structure as parsed XML.
+ //
+
+ if (dataObject.getBase64Content() != null) {
+
+ log.debug("Adding DataObject (Base64Content) without a reference URI.");
+
+ // create XMLObject
+ XMLObject xmlObject = createXMLObject(new ByteArrayInputStream(dataObject.getBase64Content()));
+
+ setXMLObjectAndReferenceBase64(xmlObject, transforms);
+
+ } else if (dataObject.getXMLContent() != null) {
+
+ log.debug("Adding DataObject (XMLContent) without a reference URI.");
+
+ // create XMLObject
+ DocumentFragment content = parseDataObject((XMLContentType) dataObject.getXMLContent());
+ XMLObject xmlObject = createXMLObject(content);
+
+ setXMLObjectAndReferenceXML(xmlObject, transforms);
+
+ } else if (dataObject.getLocRefContent() != null) {
+
+ log.debug("Adding DataObject (LocRefContent) without a reference URI.");
+
+ setEnvelopedDataObject(dataObject.getLocRefContent(), transforms);
+
+ } else {
+
+ // not allowed
+ log.info("XML structure of the command request contains an " +
+ "invalid combination of optional elements or attributes. " +
+ "DataObject of structure='enveloped' without a reference must contain content.");
+ throw new SLRequestException(3003);
+
+ }
+
+ } else {
+
+ if (dataObject.getBase64Content() == null &&
+ dataObject.getXMLContent() == null &&
+ dataObject.getLocRefContent() == null) {
+
+ //
+ // case B
+ //
+ // The Reference attribute contains a URI that must be resolved by the
+ // Citizen Card Environment to obtain the data object.
+ // The content of sl:DataObject remains empty
+ //
+
+ log.debug("Adding DataObject from reference URI '" + reference + "'.");
+
+ setEnvelopedDataObject(reference, transforms);
+
+ } else {
+
+ // not allowed
+ log.info("XML structure of the command request contains an " +
+ "invalid combination of optional elements or attributes. " +
+ "DataObject of structure='enveloped' with reference must not contain content.");
+ throw new SLRequestException(3003);
+
+ }
+
+
+ }
+
+ }
+
+ /**
+ * Configures this DataObject as an enveloped DataObject with the content to
+ * be dereferenced from the given reference
.
+ *
+ * @param reference
+ * the reference
URI
+ * @param transforms
+ * an optional Transforms
element (may be
+ * null
)
+ *
+ * @throws SLCommandException
+ * if dereferencing the given reference
fails, or if
+ * configuring this DataObject with the data dereferenced from the
+ * given reference
fails.
+ * @throws NullPointerException
+ * if reference
is null
+ */
+ private void setEnvelopedDataObject(String reference, XSECTTransforms transforms) throws SLCommandException {
+
+ if (reference == null) {
+ throw new NullPointerException("Argument 'reference' must not be null.");
+ }
+
+ // dereference URL
+ URLDereferencer dereferencer = URLDereferencer.getInstance();
+
+ StreamData streamData;
+ try {
+ streamData = dereferencer.dereference(reference, ctx.getDereferencerContext());
+ } catch (IOException e) {
+ log.info("Failed to dereference XMLObject from '" + reference + "'.", e);
+ throw new SLCommandException(4110);
+ }
+
+ Node childNode;
+
+ String contentType = streamData.getContentType();
+ if (contentType.startsWith("text/xml")) {
+
+ // If content type is text/xml parse content.
+ String charset = HttpUtil.getCharset(contentType, true);
+
+ Document doc = parseDataObject(streamData.getStream(), charset);
+
+ childNode = doc.getDocumentElement();
+
+ if (childNode == null) {
+ log.info("Failed to parse XMLObject from '" + reference + "'.");
+ throw new SLCommandException(4111);
+ }
+
+ XMLObject xmlObject = createXMLObject(childNode);
+
+ setXMLObjectAndReferenceXML(xmlObject, transforms);
+
+ } else {
+
+ // Include content Base64 encoded.
+ XMLObject xmlObject = createXMLObject(streamData.getStream());
+
+ setXMLObjectAndReferenceBase64(xmlObject, transforms);
+
+ }
+
+ }
+
+ /**
+ * Configures this DataObject as an detached DataObject with the information
+ * provided in the given sl:DataObject
and optionally
+ * transforms
.
+ *
+ * @param dataObject
+ * the sl:DataObject
+ * @param transforms
+ * an optional Transforms object, may be null
+ *
+ * @throws SLCommandException
+ * if configuring this DataObject with the information provided in
+ * the sl:DataObject
fails.
+ * @throws SLRequestException
+ * if the information provided in the sl:DataObject
+ * does not conform to the security layer specification.
+ * @throws NullPointerException
+ * if dataObject
is null
+ */
+ private void setDetachedDataObject(
+ Base64XMLLocRefOptRefContentType dataObject, XSECTTransforms transforms)
+ throws SLCommandException, SLRequestException {
+
+ String referenceURI = dataObject.getReference();
+
+ if (referenceURI == null) {
+
+ // not allowed
+ log.info("XML structure of the command request contains an " +
+ "invalid combination of optional elements or attributes. " +
+ "DataObject of structure='detached' must contain a reference.");
+ throw new SLRequestException(3003);
+
+ } else {
+
+ DigestMethod dm;
+ try {
+ dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx);
+ } catch (NoSuchAlgorithmException e) {
+ log.error("Failed to get DigestMethod.", e);
+ throw new SLCommandException(4006);
+ } catch (InvalidAlgorithmParameterException e) {
+ log.error("Failed to get DigestMethod.", e);
+ throw new SLCommandException(4006);
+ }
+
+ String idValue = ctx.getIdValueFactory().createIdValue("Reference");
+
+ reference = new XSECTReference(referenceURI, dm, transforms, null, idValue);
+
+ // case D:
+ //
+ // The Reference attribute contains a URI that is used by the Citizen Card
+ // Environment to code the reference to the data object as part of the XML
+ // signature (attribute URI in the dsig:Reference) element. The content of
+ // sl:DataObject represents the data object.
+
+ if (dataObject.getLocRefContent() != null) {
+ String locRef = dataObject.getLocRefContent();
+ try {
+ this.reference.setDereferencer(new LocRefDereferencer(ctx.getDereferencerContext(), locRef));
+ } catch (URISyntaxException e) {
+ log.info("Invalid URI '" + locRef + "' in DataObject.", e);
+ throw new SLCommandException(4003);
+ } catch (IllegalArgumentException e) {
+ log.info("LocRef URI of '" + locRef + "' not supported in DataObject. ", e);
+ throw new SLCommandException(4003);
+ }
+ } else if (dataObject.getBase64Content() != null) {
+ byte[] base64Content = dataObject.getBase64Content();
+ this.reference.setDereferencer(new ByteArrayDereferencer(base64Content));
+ } else if (dataObject.getXMLContent() != null) {
+ XMLContentType xmlContent = (XMLContentType) dataObject.getXMLContent();
+ byte[] bytes = xmlContent.getRedirectedStream().toByteArray();
+ this.reference.setDereferencer(new ByteArrayDereferencer(bytes));
+ } else {
+
+ // case C:
+ //
+ // The Reference attribute contains a URI that must be resolved by the
+ // Citizen Card Environment to obtain the data object. The Reference
+ // attribute contains a URI that is used by the Citizen Card Environment
+ // to code the reference to the data object as part of the XML signature
+ // (attribute URI in the dsig:Reference) element. The content of
+ // sl:DataObject remains empty.
+
+ }
+
+ }
+ }
+
+ /**
+ * Returns the preferred sl:TransformInfo
from the given list of
+ * transformInfos
, or null
if none of the given
+ * transformInfos
is preferred over the others.
+ *
+ * @param transformsInfos
+ * a list of sl:TransformInfo
s
+ *
+ * @return the selected sl:TransformInfo
or null
, if
+ * none is preferred over the others
+ */
+ private TransformsInfoType selectPreferredTransformsInfo(Listds:Transforms
from the given
+ * sl:TransformsInfo
.
+ *
+ * @param transformsInfo
+ * the sl:TransformsInfo
+ *
+ * @return a corresponding unmarshalled ds:Transforms
, or
+ * null
if the given sl:TransformsInfo
does
+ * not contain a dsig:Transforms
element
+ *
+ * @throws SLRequestException
+ * if the ds:Transforms
in the given
+ * transformsInfo
are not valid or cannot be parsed.
+ *
+ * @throws MarshalException
+ * if the ds:Transforms
in the given
+ * transformsInfo
cannot be unmarshalled.
+ */
+ private XSECTTransforms createTransforms(TransformsInfoType transformsInfo) throws SLRequestException, MarshalException {
+
+ byte[] transforms = getTransformsBytes((at.gv.egiz.slbinding.impl.TransformsInfoType) transformsInfo);
+
+ if (transforms != null && transforms.length > 0) {
+ // debug
+ if (log.isTraceEnabled()) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Trying to parse transforms:\n");
+ sb.append(new String(transforms, Charset.forName("UTF-8")));
+ log.trace(sb);
+ }
+
+ DOMImplementationLS domImplLS = DOMUtils.getDOMImplementationLS();
+ LSInput input = domImplLS.createLSInput();
+ input.setByteStream(new ByteArrayInputStream(transforms));
+
+ LSParser parser = domImplLS.createLSParser(
+ DOMImplementationLS.MODE_SYNCHRONOUS, null);
+ DOMConfiguration domConfig = parser.getDomConfig();
+ SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler();
+ domConfig.setParameter("error-handler", errorHandler);
+ domConfig.setParameter("validate", Boolean.FALSE);
+
+ Document document;
+ try {
+ document = parser.parse(input);
+ } catch (DOMException e) {
+ log.info("Failed to parse dsig:Transforms.", e);
+ throw new SLRequestException(3002);
+ } catch (LSException e) {
+ log.info("Failed to parse dsig:Transforms.", e);
+ throw new SLRequestException(3002);
+ }
+
+ // adopt ds:Transforms
+ Element transformsElt = document.getDocumentElement();
+ Node adoptedTransforms = ctx.getDocument().adoptNode(transformsElt);
+
+ DOMCryptoContext context = new DOMCryptoContext();
+
+ // unmarshall ds:Transforms
+ return new XSECTTransforms(context, adoptedTransforms);
+
+ } else {
+ return null;
+ }
+
+
+// TransformsType transformsType = transformsInfo.getTransforms();
+// if (transformsType == null) {
+// return null;
+// }
+// ListmimeType
and the description
value
+ * for this DataObject.
+ *
+ * @param metaInfoType the sl:FinalMetaDataInfo
+ *
+ * @throws NullPointerException if metaInfoType
is null
+ */
+ private void setFinalDataMetaInfo(MetaInfoType metaInfoType) {
+
+ this.mimeType = metaInfoType.getMimeType();
+ this.description = metaInfoType.getDescription();
+
+ }
+
+ /**
+ * Selects an appropriate transformation path (if present) from the given list
+ * of sl:TransformInfos
, sets the corresponding final data meta info and
+ * returns the corresponding unmarshalled ds:Transforms
.
+ *
+ * @param transformsInfos the sl:TransformInfos
+ *
+ * @return the unmarshalled ds:Transforms
, or null
if
+ * no transformation path has been selected.
+ *
+ * @throws SLRequestException if the given list ds:TransformsInfo
contains
+ * an invalid ds:Transforms
element, or no suitable transformation path
+ * can be found.
+ */
+ private XSECTTransforms createTransformsAndSetFinalDataMetaInfo(
+ Listcontent
.
+ *
+ * @param content
+ * the to-be Base64 encoded content
+ * @return an XMLObject with the Base64 encoded content
+ */
+ private XMLObject createXMLObject(InputStream content) {
+
+ Text textNode;
+ try {
+ textNode = at.gv.egiz.dom.DOMUtils.createBase64Text(content, ctx.getDocument());
+ } catch (IOException e) {
+ log.error(e);
+ throw new SLRuntimeException(e);
+ }
+
+ DOMStructure structure = new DOMStructure(textNode);
+
+ String idValue = ctx.getIdValueFactory().createIdValue("Object");
+
+ return ctx.getSignatureFactory().newXMLObject(Collections.singletonList(structure), idValue, null, null);
+
+ }
+
+ /**
+ * Create an XMLObject with the given content
node.
+ *
+ * @param content the content node
+ *
+ * @return an XMLObject with the given content
+ */
+ private XMLObject createXMLObject(Node content) {
+
+ String idValue = ctx.getIdValueFactory().createIdValue("Object");
+
+ ListxmlObject
and creates and sets a corresponding
+ * Reference
.
+ *
+ * A transform to Base64-decode the xmlObject's content is inserted at the top
+ * of to the optional transforms
if given, or to a newly created
+ * Transforms
element if transforms
is
+ * null
.
+ *
+ * @param xmlObject
+ * the XMLObject
+ * @param transforms
+ * an optional Transforms
element (may be
+ * null
)
+ *
+ * @throws SLCommandException
+ * if creating the Reference fails
+ * @throws NullPointerException
+ * if xmlObject
is null
+ */
+ private void setXMLObjectAndReferenceBase64(XMLObject xmlObject, XSECTTransforms transforms) throws SLCommandException {
+
+ // create reference URI
+ //
+ // NOTE: the ds:Object can be referenced directly, as the Base64 transform
+ // operates on the text() of the input nodelist.
+ //
+ String referenceURI = "#" + xmlObject.getId();
+
+ // create Base64 Transform
+ Transform transform;
+ try {
+ transform = ctx.getSignatureFactory().newTransform(Transform.BASE64, (TransformParameterSpec) null);
+ } catch (NoSuchAlgorithmException e) {
+ // algorithm must be present
+ throw new SLRuntimeException(e);
+ } catch (InvalidAlgorithmParameterException e) {
+ // algorithm does not take parameters
+ throw new SLRuntimeException(e);
+ }
+
+ if (transforms == null) {
+ transforms = new XSECTTransforms(Collections.singletonList(transform));
+ } else {
+ transforms.insertTransform(transform);
+ }
+
+ DigestMethod dm;
+ try {
+ dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx);
+ } catch (NoSuchAlgorithmException e) {
+ log.error("Failed to get DigestMethod.", e);
+ throw new SLCommandException(4006);
+ } catch (InvalidAlgorithmParameterException e) {
+ log.error("Failed to get DigestMethod.", e);
+ throw new SLCommandException(4006);
+ }
+ String id = ctx.getIdValueFactory().createIdValue("Reference");
+
+ this.xmlObject = xmlObject;
+ this.reference = new XSECTReference(referenceURI, dm, transforms, null, id);
+
+ }
+
+ /**
+ * Sets the given xmlObject
and creates and sets a corresponding
+ * Reference
.
+ *
+ * A transform to select the xmlObject's content is inserted at the top of to
+ * the optional transforms
if given, or to a newly created
+ * Transforms
element if transforms
is
+ * null
.
+ *
Transforms
element (may be
+ * null
)
+ *
+ * @throws SLCommandException
+ * if creating the Reference fails
+ * @throws NullPointerException
+ * if xmlObject
is null
+ */
+ private void setXMLObjectAndReferenceXML(XMLObject xmlObject, XSECTTransforms transforms) throws SLCommandException {
+
+ // create reference URI
+ String referenceURI = "#" + xmlObject.getId();
+
+ // create Transform to select ds:Object's children
+ Transform xpathTransform;
+ Transform c14nTransform;
+ try {
+
+ XPathType xpath = new XPathType("id(\"" + xmlObject.getId() + "\")/node()", XPathType.Filter.INTERSECT);
+ ListxmlContent
and returns a corresponding
+ * document fragment.
+ *
+ *
+ * The to-be parsed content is surrounded by
xmlContent
fails
+ *
+ * @throws NullPointerException
+ * if xmlContent
is null
+ */
+ private DocumentFragment parseDataObject(XMLContentType xmlContent) throws SLCommandException {
+
+ ByteArrayOutputStream redirectedStream = xmlContent.getRedirectedStream();
+
+ // Note: We can assume a fixed character encoding of UTF-8 for the
+ // content of the redirect stream as the content has already been parsed
+ // and serialized again to the redirect stream.
+
+ ListinputStream
using the given
+ * encoding
and returns the parsed document.
+ *
+ * @param inputStream
+ * the to-be parsed input
+ *
+ * @param encoding
+ * the encoding to be used for parsing the given
+ * inputStream
+ *
+ * @return the parsed document
+ *
+ * @throws SLCommandException
+ * if parsing the inputStream
fails.
+ *
+ * @throws NullPointerException
+ * if inputStram
is null
+ */
+ private Document parseDataObject(InputStream inputStream, String encoding) throws SLCommandException {
+
+ LSInput input = domImplLS.createLSInput();
+ input.setByteStream(inputStream);
+
+ if (encoding != null) {
+ input.setEncoding(encoding);
+ }
+
+ LSParser parser = domImplLS.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null);
+ DOMConfiguration domConfig = parser.getDomConfig();
+ SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler();
+ domConfig.setParameter("error-handler", errorHandler);
+ domConfig.setParameter("validate", Boolean.FALSE);
+
+ Document doc;
+ try {
+ doc = parser.parse(input);
+ } catch (DOMException e) {
+ log.info("Existing XML document cannot be parsed.", e);
+ throw new SLCommandException(4111);
+ } catch (LSException e) {
+ log.info("Existing XML document cannot be parsed. ", e);
+ throw new SLCommandException(4111);
+ }
+
+ if (errorHandler.hasErrors()) {
+ // log errors
+ if (log.isInfoEnabled()) {
+ Listds:References
to add the created
* ds:Reference
to
- *
+ * @param signatureId TODO
* @throws SLCommandException
* if creating and adding the XAdES
* QualifyingProperties
fails
@@ -596,7 +592,7 @@ public class Signature {
* if objects
or references
is
* null
*/
- private void addXAdESObjectAndReference(ListsigningCertificate
.
*
- * @param siginingCertificate
+ * @param signingCertificate
*
* @throws NoSuchAlgorithmException
* if the public key algorithm of the given
* signingCertificate
is not supported
*/
- public AlgorithmMethodFactoryImpl(X509Certificate siginingCertificate)
+ public AlgorithmMethodFactoryImpl(X509Certificate signingCertificate)
throws NoSuchAlgorithmException {
-
- String algorithm = siginingCertificate.getPublicKey().getAlgorithm();
+
+ PublicKey publicKey = signingCertificate.getPublicKey();
+ String algorithm = publicKey.getAlgorithm();
if ("DSA".equals(algorithm)) {
signatureAlgorithmURI = SignatureMethod.DSA_SHA1;
- } else if ("RSA".equals(algorithm)) {
- signatureAlgorithmURI = SignatureMethod.RSA_SHA1;
- } else if (("EC".equals(algorithm)) || ("ECDSA".equals(algorithm))) {
- signatureAlgorithmURI = XmldsigMore.SIGNATURE_ECDSA_SHA1;
+ } else if ("RSA".equals(algorithm)) {
+
+ int keyLength = 0;
+ if (publicKey instanceof RSAPublicKey) {
+ keyLength = ((RSAPublicKey) publicKey).getModulus().bitLength();
+ }
+
+ if (keyLength >= 2048) {
+ signatureAlgorithmURI = XmldsigMore.SIGNATURE_RSA_SHA256;
+ digestAlgorithmURI = DigestMethod.SHA256;
+ } else {
+ signatureAlgorithmURI = SignatureMethod.RSA_SHA1;
+ }
+
+ } else if (("EC".equals(algorithm)) || ("ECDSA".equals(algorithm))) {
+
+ int fieldSize = 0;
+ if (publicKey instanceof iaik.security.ecc.ecdsa.ECPublicKey) {
+ ECDSAParams params = ((iaik.security.ecc.ecdsa.ECPublicKey) publicKey).getParameter();
+ fieldSize = params.getG().getCurve().getField().getSize().bitLength();
+ } else if (publicKey instanceof ECPublicKey) {
+ ECParameterSpec params = ((ECPublicKey) publicKey).getParams();
+ fieldSize = params.getCurve().getField().getFieldSize();
+ }
+
+ if (fieldSize < 256) {
+ signatureAlgorithmURI = XmldsigMore.SIGNATURE_ECDSA_SHA1;
+ } else if (fieldSize < 512) {
+ signatureAlgorithmURI = XmldsigMore.SIGNATURE_ECDSA_SHA256;
+ digestAlgorithmURI = DigestMethod.SHA256;
+ } else {
+ signatureAlgorithmURI = XmldsigMore.SIGNATURE_ECDSA_SHA512;
+ digestAlgorithmURI = DigestMethod.SHA512;
+ }
+
} else {
throw new NoSuchAlgorithmException("Public key algorithm '" + algorithm
+ "' not supported.");
@@ -104,7 +146,7 @@ public class AlgorithmMethodFactoryImpl implements AlgorithmMethodFactory {
throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
return signatureContext.getSignatureFactory().newDigestMethod(
- DigestMethod.SHA1, (DigestMethodParameterSpec) null);
+ digestAlgorithmURI, (DigestMethodParameterSpec) null);
}
/*
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALProvider.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALProvider.java
index 0ab30530..42c6a4c5 100644
--- a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALProvider.java
+++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALProvider.java
@@ -49,7 +49,11 @@ public class STALProvider extends Provider {
map.put("Signature." + SignatureMethod.RSA_SHA1,
IMPL_PACKAGE_NAME + ".STALSignature");
map.put("Signature." + XmldsigMore.SIGNATURE_ECDSA_SHA1,
- IMPL_PACKAGE_NAME + ".STALSignature");
+ IMPL_PACKAGE_NAME + ".STALSignature");
+ map.put("Signature." + XmldsigMore.SIGNATURE_RSA_SHA256,
+ IMPL_PACKAGE_NAME + ".STALSignature");
+ map.put("Signature." + XmldsigMore.SIGNATURE_ECDSA_SHA256,
+ IMPL_PACKAGE_NAME + ".STALSignature");
AccessController.doPrivileged(new PrivilegedActionDataObject
of an XML-Signature
@@ -184,7 +183,9 @@ public class DataObject {
* An optional description of the digest input.
*/
private String description;
-
+
+ private String filename;
+
/**
* Creates a new instance.
*
@@ -230,6 +231,10 @@ public class DataObject {
return mimeType;
}
+ public String getFilename() {
+ return filename;
+ }
+
/**
* @return the description
*/
@@ -336,7 +341,74 @@ public class DataObject {
}
// other values are not allowed by the schema and are therefore ignored
-
+
+ this.filename = deriveFilename();
+ }
+
+ /**
+ * Extract filename from reference URI
+ * or propose reference Id with an apropriate (mime-type) file extension
+ *
+ * @return if neither reference nor id can be extracted return null (or data.extension?)
+ */
+ private String deriveFilename() {
+
+ String filename = null;
+
+ if (reference != null) {
+ if (reference.getURI() != null && !"".equals(reference.getURI())) {
+ try {
+ log.info("deriving filename from reference URI " + reference.getURI());
+ URI refURI = new URI(reference.getURI());
+
+ if (refURI.isOpaque()) {
+ // could check scheme component, but also allow other schemes (e.g. testlocal)
+ log.trace("opaque reference URI, use scheme-specific part as filename");
+ filename = refURI.getSchemeSpecificPart();
+ if (!hasExtension(filename)) {
+ filename += MimeTypes.getExtension(mimeType);
+ }
+ // else hierarchical URI:
+ // for shorthand xpointer use fragment as filename,
+ // for any other xpointer use reference Id and
+ // for any other hierarchical (absolute or relative) use filename (ignore fragment, see xmldsig section 4.3.3.2: fragments not recommendet)
+ } else if ("".equals(refURI.getPath()) &&
+ refURI.getFragment() != null &&
+ refURI.getFragment().indexOf('(') < 0) { // exclude (schemebased) xpointer expressions
+ log.trace("fragment (shorthand xpointer) URI, use fragment as filename");
+ filename = refURI.getFragment();
+ if(!hasExtension(filename)) {
+ filename += MimeTypes.getExtension(mimeType);
+ }
+ } else if (!"".equals(refURI.getPath())) {
+ log.trace("hierarchical URI with path component, use path as filename");
+ File refFile = new File(refURI.getPath());
+ filename = refFile.getName();
+ if(!hasExtension(filename)) {
+ filename += MimeTypes.getExtension(mimeType);
+ }
+ } else {
+ log.info("failed to derive filename from URI '" + refURI + "', derive filename from reference ID");
+ filename = reference.getId() + MimeTypes.getExtension(mimeType);
+ }
+ } catch (URISyntaxException ex) {
+ log.error("failed to derive filename from invalid URI " + ex.getMessage());
+ filename = reference.getId() + MimeTypes.getExtension(mimeType);
+ }
+ } else {
+ log.info("same-document URI, derive filename from reference ID");
+ filename = reference.getId() + MimeTypes.getExtension(mimeType);
+ }
+ } else {
+ log.error("failed to derive filename, no reference created");
+ }
+ log.debug("derived filename for reference " + reference.getId() + ": " + filename);
+ return filename;
+ }
+
+ private static boolean hasExtension(String filename) {
+ int extDelimiterInd = filename.lastIndexOf('.');
+ return extDelimiterInd >= 0 && extDelimiterInd >= filename.length() - 4;
}
private byte[] getTransformsBytes(at.gv.egiz.slbinding.impl.TransformsInfoType ti) {
--
cgit v1.2.3
From fb20464dc8dc024568b439c460485c700137e0e2 Mon Sep 17 00:00:00 2001
From: clemenso