/*
* Copyright 2011 by Graz University of Technology, Austria
* MOCCA has been developed by the E-Government Innovation Center EGIZ, a joint
* initiative of the Federal Chancellery Austria and Graz University of Technology.
*
* Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
* the European Commission - subsequent versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
* http://www.osor.eu/eupl/
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*
* This product combines work with different licenses. See the "NOTICE" text
* file for details on the various modules and licenses.
* The "NOTICE" text file is part of the distribution. Any derivative works
* that you distribute must include a readable copy of the "NOTICE" text file.
*/
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.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
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.bind.Marshaller;
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 javax.xml.namespace.QName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.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_2_3.Base64XMLLocRefOptRefContentType;
import at.buergerkarte.namespaces.securitylayer._1_2_3.DataObjectInfoType;
import at.buergerkarte.namespaces.securitylayer._1_2_3.MetaInfoType;
import at.buergerkarte.namespaces.securitylayer._1_2_3.TransformsInfoType;
import at.gv.egiz.bku.binding.HttpUtil;
import at.gv.egiz.bku.gui.viewer.MimeTypes;
import at.gv.egiz.bku.slcommands.SLMarshallerFactory;
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.slexceptions.SLViewerException;
import at.gv.egiz.bku.utils.urldereferencer.StreamData;
import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer;
import at.gv.egiz.bku.viewer.ValidationException;
import at.gv.egiz.bku.viewer.Validator;
import at.gv.egiz.bku.viewer.ValidatorFactory;
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 final Logger log = LoggerFactory.getLogger(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",
"text/html"
};
/**
* Validate hash input.
*/
private static boolean validate = false;
/**
* Enable validation of hash data input.
*
* @param validate
* true
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. */ 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; private String filename; /** * 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; } public String getFilename() { return filename; } /** * @return the description */ 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, e); 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.debug("MIME media type '{}' is not a s/valid/SUPPORTED digest input, omitting validation.", mediaType); } } } /** * 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
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.debug("Failed to derive filename from URI '{}', derive filename from reference ID.", refURI);
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.debug("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) {
ByteArrayOutputStream redirectedStream = ti.getRedirectedStream();
if (redirectedStream != null) {
return redirectedStream.toByteArray();
} else {
return null;
}
}
/**
* 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());
setXMLObjectAndReferenceXML(createXMLObject(content), 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 = ctx.getUrlDereferencer();
StreamData streamData;
try {
streamData = dereferencer.dereference(reference);
} 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.getUrlDereferencer(), locRef));
} catch (URISyntaxException e) {
log.info("Invalid URI '{}' in DataObject.", locRef, e);
throw new SLCommandException(4003);
} catch (IllegalArgumentException e) {
log.info("LocRef URI of '{}' not supported in DataObject. ", locRef, 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.toString());
}
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("Failed to create XMLObject.", 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.
DocumentFragment fragment;
if (redirectedStream != null) {
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);
domConfig.setParameter("entities", Boolean.TRUE);
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()) {
List