diff options
Diffstat (limited to 'bkucommon/src/main/java/at/gv/egiz')
4 files changed, 1251 insertions, 1174 deletions
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/HashDataInputImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/HashDataInputImpl.java new file mode 100644 index 00000000..49d3c63f --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/HashDataInputImpl.java @@ -0,0 +1,42 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package at.gv.egiz.bku.slcommands.impl; + +import at.gv.egiz.bku.slcommands.impl.xsect.DataObject; +import at.gv.egiz.stal.HashDataInput; +import java.io.InputStream; + +/** + * + * @author clemens + */ +public class HashDataInputImpl implements HashDataInput { + + String refId; + String mimeType; + InputStream hashDataInput; + + public HashDataInputImpl(DataObject dataObject) { + refId = dataObject.getReference().getId(); + mimeType = dataObject.getMimeType(); + hashDataInput = dataObject.getReference().getDigestInputStream(); + } + + @Override + public String getReferenceId() { + return refId; + } + + @Override + public String getMimeType() { + return mimeType; + } + + @Override + public InputStream getHashDataInput() { + return hashDataInput; + } + +} 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 index 64c758c9..25e2d4e5 100644 --- 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 @@ -14,109 +14,115 @@ * 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 <code>STALPrivateKey</code> with the given
- * <code>stal</code> implementation, signature <code>algorithm</code>,
- * <code>keyboxIdentifier</code> and <code>hashDataInputCallback</code>
- * 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 List<DataObject> dataObjects; + + /** + * The keybox identifier. + */ + private String keyboxIdentifier; + + /** + * The signature algorithm. + */ + private String algorithm; + + /** + * Creates a new instance of this <code>STALPrivateKey</code> with the given + * <code>stal</code> implementation, signature <code>algorithm</code>, + * <code>keyboxIdentifier</code> and <code>hashDataInputCallback</code> + * 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, List<DataObject> dataObjects) { + super(); + this.keyboxIdentifier = keyboxIdentifier; + this.dataObjects = dataObjects; + 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 List<DataObject> getDataObjects() { + + return dataObjects; + } + + /** + * @return the keybox identifier + */ + public String getKeyboxIdentifier() { + return keyboxIdentifier; + } + +} 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 f0fcb891..eba1d96d 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 @@ -14,152 +14,164 @@ * 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.ByteArrayOutputStream;
-import java.security.InvalidKeyException;
-import java.security.InvalidParameterException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SignatureException;
-import java.security.SignatureSpi;
-import java.util.Collections;
-import java.util.List;
-
-import at.gv.egiz.stal.ErrorResponse;
-import at.gv.egiz.stal.STAL;
-import at.gv.egiz.stal.STALRequest;
-import at.gv.egiz.stal.STALResponse;
-import at.gv.egiz.stal.SignRequest;
-import at.gv.egiz.stal.SignResponse;
-import at.gv.egiz.stal.HashDataInputCallback;
-
-/**
- * A signature service provider implementation that uses STAL to sign.
- *
- * @author mcentner
- */
-public class STALSignature extends SignatureSpi {
-
- /**
- * The private key.
- */
- protected STALPrivateKey privateKey;
-
- /**
- * The to-be signed data.
- */
- protected ByteArrayOutputStream data = new ByteArrayOutputStream();
-
- /* (non-Javadoc)
- * @see java.security.SignatureSpi#engineGetParameter(java.lang.String)
- */
- @Override
- protected Object engineGetParameter(String param)
- throws InvalidParameterException {
- throw new InvalidParameterException();
- }
-
- /* (non-Javadoc)
- * @see java.security.SignatureSpi#engineInitSign(java.security.PrivateKey)
- */
- @Override
- protected void engineInitSign(PrivateKey privateKey)
- throws InvalidKeyException {
-
- if (!(privateKey instanceof STALPrivateKey)) {
- throw new InvalidKeyException("STALSignature supports STALKeys only.");
- }
-
- this.privateKey = (STALPrivateKey) privateKey;
-
- }
-
- /* (non-Javadoc)
- * @see java.security.SignatureSpi#engineInitVerify(java.security.PublicKey)
- */
- @Override
- protected void engineInitVerify(PublicKey publicKey)
- throws InvalidKeyException {
-
- throw new UnsupportedOperationException("STALSignature does not support signature verification.");
- }
-
- /* (non-Javadoc)
- * @see java.security.SignatureSpi#engineSetParameter(java.lang.String, java.lang.Object)
- */
- @Override
- protected void engineSetParameter(String param, Object value)
- throws InvalidParameterException {
- }
-
- /* (non-Javadoc)
- * @see java.security.SignatureSpi#engineSign()
- */
- @Override
- protected byte[] engineSign() throws SignatureException {
-
- STAL stal = privateKey.getStal();
-
- if (stal == null) {
- throw new SignatureException("STALSignature requires the STALPrivateKey " +
- "to provide a STAL implementation reference.");
- }
-
- HashDataInputCallback signRefDataSupplier = privateKey.getHashDataInputCallback();
-
- String keyboxIdentifier = privateKey.getKeyboxIdentifier();
-
- if (keyboxIdentifier == null) {
- throw new SignatureException("STALSignature requires the STALPrivateKey " +
- "to provide a KeyboxIdentifier.");
- }
-
- SignRequest signRequest = new SignRequest();
- signRequest.setKeyIdentifier(keyboxIdentifier);
- signRequest.setSignedInfo(data.toByteArray());
- signRequest.setHashDataInput(signRefDataSupplier);
-
- List<STALResponse> responses = stal.handleRequest(Collections.singletonList((STALRequest) signRequest));
-
- if (responses == null || responses.size() != 1) {
- throw new SignatureException("Failed to access STAL.");
- }
-
- STALResponse response = responses.get(0);
- if (response instanceof SignResponse) {
- return ((SignResponse) response).getSignatureValue();
- } else if (response instanceof ErrorResponse) {
- throw new STALSignatureException(((ErrorResponse) response).getErrorCode());
- } else {
- throw new SignatureException("Failed to access STAL.");
- }
-
- }
-
- /* (non-Javadoc)
- * @see java.security.SignatureSpi#engineUpdate(byte)
- */
- @Override
- protected void engineUpdate(byte b) throws SignatureException {
- data.write(b);
- }
-
- /* (non-Javadoc)
- * @see java.security.SignatureSpi#engineUpdate(byte[], int, int)
- */
- @Override
- protected void engineUpdate(byte[] b, int off, int len)
- throws SignatureException {
- data.write(b, off, len);
- }
-
- /* (non-Javadoc)
- * @see java.security.SignatureSpi#engineVerify(byte[])
- */
- @Override
- protected boolean engineVerify(byte[] sigBytes) throws SignatureException {
- throw new UnsupportedOperationException("STALSignature des not support signature verification.");
- }
-
-}
+package at.gv.egiz.bku.slcommands.impl.xsect; + +import at.gv.egiz.bku.slcommands.impl.HashDataInputImpl; +import java.io.ByteArrayOutputStream; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.SignatureSpi; +import java.util.Collections; +import java.util.List; + +import at.gv.egiz.stal.ErrorResponse; +import at.gv.egiz.stal.HashDataInput; +import at.gv.egiz.stal.STAL; +import at.gv.egiz.stal.STALRequest; +import at.gv.egiz.stal.STALResponse; +import at.gv.egiz.stal.SignRequest; +import at.gv.egiz.stal.SignResponse; +//import at.gv.egiz.stal.HashDataInputCallback; +import java.util.ArrayList; + +/** + * A signature service provider implementation that uses STAL to sign. + * + * @author mcentner + */ +public class STALSignature extends SignatureSpi { + +// private static final Log log = LogFactory.getLog(STALSignature.class); + + /** + * The private key. + */ + protected STALPrivateKey privateKey; + + /** + * The to-be signed data. + */ + protected ByteArrayOutputStream data = new ByteArrayOutputStream(); + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineGetParameter(java.lang.String) + */ + @Override + protected Object engineGetParameter(String param) + throws InvalidParameterException { + throw new InvalidParameterException(); + } + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineInitSign(java.security.PrivateKey) + */ + @Override + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException { + + if (!(privateKey instanceof STALPrivateKey)) { + throw new InvalidKeyException("STALSignature supports STALKeys only."); + } + + this.privateKey = (STALPrivateKey) privateKey; + + } + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineInitVerify(java.security.PublicKey) + */ + @Override + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException { + + throw new UnsupportedOperationException("STALSignature does not support signature verification."); + } + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineSetParameter(java.lang.String, java.lang.Object) + */ + @Override + protected void engineSetParameter(String param, Object value) + throws InvalidParameterException { + } + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineSign() + */ + @Override + protected byte[] engineSign() throws SignatureException { + + STAL stal = privateKey.getStal(); + + if (stal == null) { + throw new SignatureException("STALSignature requires the STALPrivateKey " + + "to provide a STAL implementation reference."); + } + + String keyboxIdentifier = privateKey.getKeyboxIdentifier(); + + if (keyboxIdentifier == null) { + throw new SignatureException("STALSignature requires the STALPrivateKey " + + "to provide a KeyboxIdentifier."); + } + + // get hashDataInputs (DigestInputStreams) once slcommands.impl.xsect.Signature::sign() was called + List<DataObject> dataObjects = privateKey.getDataObjects(); +// log.debug("got " + dataObjects.size() + " DataObjects, passing HashDataInputs to STAL SignRequest"); + + List<HashDataInput> hashDataInputs = new ArrayList<HashDataInput>(); + for (DataObject dataObject : dataObjects) { + hashDataInputs.add(new HashDataInputImpl(dataObject)); + } + + SignRequest signRequest = new SignRequest(); + signRequest.setKeyIdentifier(keyboxIdentifier); + signRequest.setSignedInfo(data.toByteArray()); + signRequest.setHashDataInput(hashDataInputs); + + List<STALResponse> responses = stal.handleRequest(Collections.singletonList((STALRequest) signRequest)); + + if (responses == null || responses.size() != 1) { + throw new SignatureException("Failed to access STAL."); + } + + STALResponse response = responses.get(0); + if (response instanceof SignResponse) { + return ((SignResponse) response).getSignatureValue(); + } else if (response instanceof ErrorResponse) { + throw new STALSignatureException(((ErrorResponse) response).getErrorCode()); + } else { + throw new SignatureException("Failed to access STAL."); + } + + } + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineUpdate(byte) + */ + @Override + protected void engineUpdate(byte b) throws SignatureException { + data.write(b); + } + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineUpdate(byte[], int, int) + */ + @Override + protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException { + data.write(b, off, len); + } + + /* (non-Javadoc) + * @see java.security.SignatureSpi#engineVerify(byte[]) + */ + @Override + protected boolean engineVerify(byte[] sigBytes) throws SignatureException { + throw new UnsupportedOperationException("STALSignature des not support signature verification."); + } + +} 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 index 94a4a066..191f8371 100644 --- 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 @@ -14,922 +14,939 @@ * 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 <code>CreateXMLSignatureRequest</code>.
- *
- * @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 List<DataObject> dataObjects = new ArrayList<DataObject>();
-
- /**
- * A mapping from the <code>Id</code>-attribute values of this signature's
- * <code>ds:Reference</code>s to the corresponding {@link DataObject}s.
- */
- private Map<String, DataObject> dataObjectReferencIds = new HashMap<String, DataObject>();
-
- /**
- * The SignatureEnvironment for this signature.
- */
- private SignatureLocation signatureLocation;
-
- /**
- * The XML signature.
- */
- private XMLSignature xmlSignature;
-
- /**
- * A list of attributes of type <code>xsd:ID</code> to be registered in the {@link DOMSignContext}.
- */
- private List<IdAttribute> idAttributes = new ArrayList<IdAttribute>();
-
- /**
- * The signer's X509 certificate.
- */
- private X509Certificate signerCertificate;
-
- /**
- * The signing time.
- */
- private Date signingTime;
-
- /**
- * Creates a new SLXMLSignature instance.
- */
- public Signature(URLDereferencerContext dereferencerContext,
- IdValueFactory idValueFactory,
- AlgorithmMethodFactory algorithmMethodFactory) {
-
- domImplLS = DOMUtils.getDOMImplementationLS();
-
- ctx = new SignatureContext();
-
- ctx.setSignatureFactory(XMLSignatureFactory.getInstance());
-
- ctx.setDereferencerContext(dereferencerContext);
- ctx.setIdValueFactory(idValueFactory);
- ctx.setAlgorithmMethodFactory(algorithmMethodFactory);
-
- }
-
- /**
- * @return the Document containing this Signature
- */
- public Document getDocument() {
- return ctx.getDocument();
- }
-
- /**
- * @return the parent Node for this Signature
- */
- public Node getParent() {
- return (signatureLocation != null) ? signatureLocation.getParent() : null;
- }
-
- /**
- * @return the next sibling Node for this Signature
- */
- public Node getNextSibling() {
- return (signatureLocation != null) ? signatureLocation.getNextSibling() : null;
- }
-
- /**
- * @return the XMLSignature
- */
- public XMLSignature getXMLSignature() {
- return xmlSignature;
- }
-
- /**
- * @return the list of {@link Reference}s of this Signature
- */
- @SuppressWarnings("unchecked")
- public List<Reference> getReferences() {
- return (xmlSignature != null) ? xmlSignature.getSignedInfo().getReferences() : null;
- }
-
- /**
- * @return the list of {@link XMLObject}s of this Signature
- */
- @SuppressWarnings("unchecked")
- public List<XMLObject> getXMLObjects() {
- return (xmlSignature != null) ? xmlSignature.getObjects() : null;
- }
-
- /**
- * Prepares the signature document with the information given by the
- * <code>signatureInfo</code> provided.
- *
- * @param signatureInfo
- * the <code>SignatureInfo</code>
- *
- * @throws SLCommandException
- * if processing fails for any reason
- * @throws IllegalStateException
- * if the <code>parent</code> node has already been set
- * @throws NullPointerException
- * if <code>signatureInfo</code> is <code>null</code>
- */
- 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
- * <code>dataObjectInfo</code> provided to this Signature.
- *
- * @param dataObjectInfo
- * the <code>DataObjectInfo</code> element
- *
- * @throws SLCommandException
- * if adding the DataObject fails
- * @throws SLRequestException
- * if the information provided by the given
- * <code>dataObjectInfo</code> does not conform to the security
- * layer specification
- * @throws NullPointerException
- * if <code>dataObjectInfo</code> is <code>null</code>
- */
- 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 <code>SigningTime</code> qualifying property of this Signature.
- *
- * @param signingTime the signing time to set
- */
- public void setSigningTime(Date signingTime) {
- this.signingTime = signingTime;
- }
-
- /**
- * Sets the <code>SignerCertificate</code> 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 {
-
- List<XMLObject> objects = new ArrayList<XMLObject>();
- List<Reference> references = new ArrayList<Reference>();
-
- // add all data objects
- for (DataObject dataObject : dataObjects) {
- if (dataObject.getXmlObject() != null) {
- objects.add(dataObject.getXmlObject());
- }
- if (dataObject.getReference() != null) {
- references.add(dataObject.getReference());
- }
- }
-
- addXAdESObjectAndReference(objects, references);
-
- XMLSignatureFactory signatureFactory = ctx.getSignatureFactory();
- AlgorithmMethodFactory algorithmMethodFactory = ctx.getAlgorithmMethodFactory();
-
- CanonicalizationMethod cm;
- SignatureMethod sm;
- try {
- cm = algorithmMethodFactory.createCanonicalizationMethod(ctx);
- sm = algorithmMethodFactory.createSignatureMethod(ctx);
- } catch (NoSuchAlgorithmException e) {
- log.error("Failed to get Canonicalization or Signature algorithm.", e);
- throw new SLCommandException(4006);
- } catch (InvalidAlgorithmParameterException e) {
- log.error("Failed to get Canonicalization or Signature algorithm.", e);
- throw new SLCommandException(4006);
- }
-
- String siId = ctx.getIdValueFactory().createIdValue("SignedInfo");
-
- SignedInfo si = signatureFactory.newSignedInfo(cm, sm, references, siId);
-
- KeyInfo ki = null;
- if (signerCertificate != null) {
- KeyInfoFactory kif = KeyInfoFactory.getInstance();
- X509Data x509Data = kif.newX509Data(Collections.singletonList(signerCertificate));
- ki = kif.newKeyInfo(Collections.singletonList(x509Data));
- }
-
- String signatureId = ctx.getIdValueFactory().createIdValue("Signature");
- String signatureValueId = ctx.getIdValueFactory().createIdValue("SignatureValue");
-
- xmlSignature = signatureFactory.newXMLSignature(si, ki, objects, signatureId, signatureValueId);
-
- }
-
- /**
- * Sign this Signature using the given <code>signContext</code>.
- * <p>
- * Call's {@link #buildXMLSignature()} if it has not been called yet.
- * </p>
- *
- * @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
- * if <code>signContext</code> is <code>null</code>
- */
- 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 <code>stal</code> implementation and
- * <code>keyboxIdentifier</code>.
- * <p>
- * 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.
- * </p>
- *
- * @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
- * if <code>stal</code> or <code>keyboxIdentifier</code> is
- * <code>null</code>
- */
- 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 <code>QualifyingProperties</code> as an
- * <code>ds:Object</code> and a corresponding <code>ds:Reference</code> to
- * it's <code>SignedProperties</code> element to this Signature.
- *
- * @param objects
- * the list of <code>ds:Objects</code> to add the created
- * <code>ds:Object</code> to
- * @param references
- * the list of <code>ds:References</code> to add the created
- * <code>ds:Reference</code> to
- *
- * @throws SLCommandException
- * if creating and adding the XAdES
- * <code>QualifyingProperties</code> fails
- * @throws NullPointerException
- * if <code>objects</code> or <code>references</code> is
- * <code>null</code>
- */
- private void addXAdESObjectAndReference(List<XMLObject> objects, List<Reference> references) throws SLCommandException {
-
- QualifyingPropertiesFactory factory = QualifyingPropertiesFactory.getInstance();
-
- String idValue = ctx.getIdValueFactory().createIdValue("SignedProperties");
-
- Date date = (signingTime != null) ? signingTime : new Date();
-
- List<X509Certificate> signingCertificates;
- if (signerCertificate != null) {
- signingCertificates = Collections.singletonList(signerCertificate);
- } else {
- signingCertificates = Collections.emptyList();
- }
-
- // TODO: report MOA-SP bug
- //
- // The security layer specification mandates the use of version 1.2.2. of the
- // XAdES QualifyingProperties. However MOA-SP supports only version 1.1.1. Therefore,
- // the version 1.1.1 is used in order to be compatible with current MOA-SP versions.
-
- List<DataObjectFormatType> dataObjectFormats = new ArrayList<DataObjectFormatType>();
- for (DataObject dataObject : dataObjects) {
- if (dataObject.getMimeType() != null && dataObject.getReference() != null) {
- Reference reference = dataObject.getReference();
- if (reference.getId() != null) {
- String objectReference = "#" + reference.getId();
- dataObjectFormats.add(factory.createDataObjectFormatType(
- objectReference, dataObject.getMimeType(), dataObject
- .getDescription()));
- }
- }
- }
-
- JAXBElement<QualifyingPropertiesType> qualifyingProperties;
- try {
- qualifyingProperties = factory.createQualifyingProperties111(date, signingCertificates, idValue, dataObjectFormats);
- } catch (QualifyingPropertiesException e) {
- log.error("Failed to create QualifyingProperties.", e);
- throw new SLCommandException(4000);
- }
-
- DocumentFragment fragment = ctx.getDocument().createDocumentFragment();
-
- try {
- factory.marshallQualifyingProperties(qualifyingProperties, fragment);
- } catch (JAXBException e) {
- log.error("Failed to marshal QualifyingProperties.", e);
- throw new SLCommandException(4000);
- }
-
- List<DOMStructure> content = Collections.singletonList(new DOMStructure(fragment.getFirstChild()));
-
- String objectIdValue = ctx.getIdValueFactory().createIdValue("Object");
-
- XMLObject object = ctx.getSignatureFactory().newXMLObject(content, objectIdValue, null, null);
-
- objects.add(object);
-
- // TODO: Report MOA-SP Bug
- //
- // Direct referencing of the SignedPorperties Id-attribute is not supported by MOA-SP
- // because the QualifyingProperties are parsed without the XAdES schema. Therefore,
- // the shorthand XPointer could not be resolved.
- //
- // The following workaround uses an XPointer to select the SignedProperties in order
- // to allow the signature to be verified with MOA-SP.
-
- String referenceURI = "#xmlns(xades=http://uri.etsi.org/01903/v1.1.1%23)%20xpointer(id('"
- + objectIdValue
- + "')/child::xades:QualifyingProperties/child::xades:SignedProperties)";
- DigestMethod dm;
- try {
- dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx);
- } catch (NoSuchAlgorithmException e) {
- log.error("Failed to get DigestMethod algorithm.", e);
- throw new SLCommandException(4006);
- } catch (InvalidAlgorithmParameterException e) {
- log.error("Failed to get DigestMethod algorithm.", e);
- throw new SLCommandException(4006);
- }
-
- String referenceIdValue = ctx.getIdValueFactory().createIdValue("Reference");
- String referenceType = QualifyingPropertiesFactory.SIGNED_PROPERTIES_REFERENCE_TYPE_V1_1_1;
-
- Reference reference = ctx.getSignatureFactory().newReference(referenceURI, dm, null, referenceType, referenceIdValue);
-
- references.add(reference);
-
- Node child = fragment.getFirstChild();
- if (child instanceof Element) {
- NodeList nodes = ((Element) child).getElementsByTagNameNS(QualifyingPropertiesFactory.NS_URI_V1_1_1, "SignedProperties");
- if (nodes.getLength() > 0) {
- IdAttribute idAttribute = new IdAttribute();
- idAttribute.element = (Element) nodes.item(0);
- idAttribute.namespaceURI = null;
- idAttribute.localName = "Id";
- idAttributes.add(idAttribute);
- }
- }
-
- }
-
- /**
- * Parse the SignatureEnvironment.
- *
- * @param signatureEnvironment
- * the <code>SignatureEnvironment</code> element
- * @param supplements
- * an optional list of <code>Supplements</code> (may be
- * <code>null</code>)
- *
- * @return the parsed SignatureEnvironment document
- *
- * @throws SLCommandException
- * if parsing the SignatureEnvironment fails
- * @throws NullPointerException
- * if <code>signatureEnvironment</code> is <code>null</code>
- */
- private Document parseSignatureEnvironment(
- Base64XMLOptRefContentType signatureEnvironment,
- List<DataObjectAssociationType> supplements) throws SLCommandException {
-
- if (signatureEnvironment == null) {
- throw new NullPointerException("Argument 'signatureEnvironment' must not be null.");
- }
-
- LSInput input;
- try {
- if (signatureEnvironment.getReference() != null) {
- log.debug("SignatureEnvironment contains Reference " + signatureEnvironment.getReference() + ".");
- input = createLSInput(signatureEnvironment.getReference());
- } else if (signatureEnvironment.getBase64Content() != null) {
- log.debug("SignatureEnvironment contains Base64Content.");
- input = createLSInput(signatureEnvironment.getBase64Content());
- } else if (signatureEnvironment.getXMLContent() != null) {
- log.debug("SignatureEnvironment contains XMLContent.");
- input = createLSInput((XMLContentType) signatureEnvironment.getXMLContent());
- } else {
- // the schema does not allow us to reach this point
- throw new SLCommandException(4000);
- }
- } catch (IOException e) {
- log.info("XML document in which the signature is to be integrated cannot be resolved.", e);
- throw new SLCommandException(4100);
- } catch (XMLStreamException e) {
- log.info("XML document in which the signature is to be integrated cannot be resolved.", e);
- throw new SLCommandException(4100);
- }
-
- LSParser parser = domImplLS.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null);
- DOMConfiguration domConfig = parser.getDomConfig();
- SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler();
- domConfig.setParameter("error-handler", errorHandler);
- LSResourceResolverAdapter resourceResolver = new LSResourceResolverAdapter(supplements);
- domConfig.setParameter("resource-resolver", resourceResolver);
- domConfig.setParameter("validate", Boolean.TRUE);
-
- Document doc;
- try {
- doc = parser.parse(input);
- } catch (DOMException e) {
- log.info("XML document in which the signature is to be integrated cannot be parsed.", e);
- throw new SLCommandException(4101);
- } catch (LSException e) {
- log.info("XML document in which the signature is to be integrated cannot be parsed.", e);
- throw new SLCommandException(4101);
- }
-
- if (resourceResolver.getError() != null) {
- log.info("Failed to resolve resource while parsing SignatureEnvironment document.", resourceResolver.getError());
- // we don't stop here, as we only _try_ to parse validating
- }
-
- if (errorHandler.hasFatalErrors()) {
- // log fatal errors
- if (log.isInfoEnabled()) {
- List<String> errorMessages = errorHandler.getErrorMessages();
- StringBuffer sb = new StringBuffer();
- for (String errorMessage : errorMessages) {
- sb.append(" ");
- sb.append(errorMessage);
- }
- log.info("XML document in which the signature is to be integrated cannot be parsed." + sb.toString());
- }
- throw new SLCommandException(4101);
- }
-
- // log parsed document
- if (log.isTraceEnabled()) {
-
- StringWriter writer = new StringWriter();
-
- writer.write("SignatureEnvironment:\n");
-
- LSOutput output = domImplLS.createLSOutput();
- output.setCharacterStream(writer);
- output.setEncoding("UTF-8");
- LSSerializer serializer = domImplLS.createLSSerializer();
- serializer.write(doc, output);
-
- log.trace(writer.toString());
- }
-
- return doc;
-
- }
-
- /**
- * Creates an LSInput from the given <code>reference</code> URI.
- *
- * @param reference
- * the reference URL
- *
- * @return an LSInput from the given <code>reference</code> URI
- *
- * @throws IOException
- * if dereferencing the given <code>reference</code> 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 <code>content</code> bytes.
- *
- * @param content
- * the content bytes
- *
- * @return an LSInput from the givne <code>content</code> 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 <code>content</code>.
- *
- * @param content
- * the XML content
- * @return an LSInput from the given XML <code>content</code>
- *
- * @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 <code>xsd:Id</code>-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 {
-
- List<DataObjectAssociationType> supplements;
-
- private LSResourceResolverAdapter(
- List<DataObjectAssociationType> supplements) {
- this.supplements = supplements;
- }
-
- private Exception error;
-
- /**
- * @return the error
- */
- public Exception getError() {
- return error;
- }
-
- @Override
- public LSInput resolveResource(String type, String namespaceURI,
- String publicId, String systemId, String baseURI) {
-
- if (log.isTraceEnabled()) {
- log.trace("Resolve resource :" +
- "\n type=" + type +
- "\n namespaceURI=" + namespaceURI +
- "\n publicId=" + publicId +
- "\n systemId=" + systemId +
- "\n baseURI=" + baseURI);
- }
-
- if (systemId != null) {
-
- log.debug("Resolve resource '" + systemId + "'.");
-
- for (DataObjectAssociationType supplement : supplements) {
-
- Base64XMLLocRefReqRefContentType content = supplement.getContent();
- if (content != null) {
-
- String reference = content.getReference();
- if (systemId.equals(reference)) {
-
- try {
- if (content.getLocRefContent() != null) {
- log.trace("Resolved resource '" + reference + "' to supplement with LocRefContent.");
- return createLSInput(content.getLocRefContent());
- } else if (content.getBase64Content() != null) {
- log.trace("Resolved resource '" + reference + "' to supplement with Base64Content.");
- return createLSInput(content.getBase64Content());
- } else if (content.getXMLContent() != null) {
- log.trace("Resolved resource '" + reference + "' to supplement with XMLContent.");
- return createLSInput((XMLContentType) content.getXMLContent());
- } else {
- return null;
- }
- } catch (IOException e) {
- log.info("Failed to resolve resource '" + systemId + "' to supplement.", e);
- error = e;
- return null;
- } catch (XMLStreamException e) {
- log.info("Failed to resolve resource '" + systemId + "' to supplement.", e);
- error = e;
- return null;
- }
-
- }
-
- }
-
- }
-
- log.info("Failed to resolve resource '" + systemId + "' to supplement. No such supplement.");
-
- }
-
- return null;
-
- }
-
-
- }
-
-}
+package at.gv.egiz.bku.slcommands.impl.xsect; + +import at.gv.egiz.stal.HashDataInput; +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.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 <code>CreateXMLSignatureRequest</code>. + * + * @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 List<DataObject> dataObjects = new ArrayList<DataObject>(); + + /** + * A mapping from the <code>Id</code>-attribute values of this signature's + * <code>ds:Reference</code>s to the corresponding {@link DataObject}s. + */ +// private Map<String, DataObject> dataObjectReferencIds = new HashMap<String, DataObject>(); + + /** + * The SignatureEnvironment for this signature. + */ + private SignatureLocation signatureLocation; + + /** + * The XML signature. + */ + private XMLSignature xmlSignature; + + /** + * A list of attributes of type <code>xsd:ID</code> to be registered in the {@link DOMSignContext}. + */ + private List<IdAttribute> idAttributes = new ArrayList<IdAttribute>(); + + /** + * The signer's X509 certificate. + */ + private X509Certificate signerCertificate; + + /** + * The signing time. + */ + private Date signingTime; + + /** + * Creates a new SLXMLSignature instance. + */ + public Signature(URLDereferencerContext dereferencerContext, + IdValueFactory idValueFactory, + AlgorithmMethodFactory algorithmMethodFactory) { + + domImplLS = DOMUtils.getDOMImplementationLS(); + + ctx = new SignatureContext(); + + ctx.setSignatureFactory(XMLSignatureFactory.getInstance()); + + ctx.setDereferencerContext(dereferencerContext); + ctx.setIdValueFactory(idValueFactory); + ctx.setAlgorithmMethodFactory(algorithmMethodFactory); + + } + + /** + * @return the Document containing this Signature + */ + public Document getDocument() { + return ctx.getDocument(); + } + + /** + * @return the parent Node for this Signature + */ + public Node getParent() { + return (signatureLocation != null) ? signatureLocation.getParent() : null; + } + + /** + * @return the next sibling Node for this Signature + */ + public Node getNextSibling() { + return (signatureLocation != null) ? signatureLocation.getNextSibling() : null; + } + + /** + * @return the XMLSignature + */ + public XMLSignature getXMLSignature() { + return xmlSignature; + } + + /** + * @return the list of {@link Reference}s of this Signature + */ + @SuppressWarnings("unchecked") + public List<Reference> getReferences() { + return (xmlSignature != null) ? xmlSignature.getSignedInfo().getReferences() : null; + } + + /** + * @return the list of {@link XMLObject}s of this Signature + */ + @SuppressWarnings("unchecked") + public List<XMLObject> getXMLObjects() { + return (xmlSignature != null) ? xmlSignature.getObjects() : null; + } + + /** + * Prepares the signature document with the information given by the + * <code>signatureInfo</code> provided. + * + * @param signatureInfo + * the <code>SignatureInfo</code> + * + * @throws SLCommandException + * if processing fails for any reason + * @throws IllegalStateException + * if the <code>parent</code> node has already been set + * @throws NullPointerException + * if <code>signatureInfo</code> is <code>null</code> + */ + 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 + * <code>dataObjectInfo</code> provided to this Signature. + * + * @param dataObjectInfo + * the <code>DataObjectInfo</code> element + * + * @throws SLCommandException + * if adding the DataObject fails + * @throws SLRequestException + * if the information provided by the given + * <code>dataObjectInfo</code> does not conform to the security + * layer specification + * @throws NullPointerException + * if <code>dataObjectInfo</code> is <code>null</code> + */ + 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 <code>SigningTime</code> qualifying property of this Signature. + * + * @param signingTime the signing time to set + */ + public void setSigningTime(Date signingTime) { + this.signingTime = signingTime; + } + + /** + * Sets the <code>SignerCertificate</code> 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 { + + List<XMLObject> objects = new ArrayList<XMLObject>(); + List<Reference> references = new ArrayList<Reference>(); + + // add all data objects + for (DataObject dataObject : dataObjects) { + if (dataObject.getXmlObject() != null) { + objects.add(dataObject.getXmlObject()); + } + if (dataObject.getReference() != null) { + references.add(dataObject.getReference()); + } + } + + addXAdESObjectAndReference(objects, references); + + XMLSignatureFactory signatureFactory = ctx.getSignatureFactory(); + AlgorithmMethodFactory algorithmMethodFactory = ctx.getAlgorithmMethodFactory(); + + CanonicalizationMethod cm; + SignatureMethod sm; + try { + cm = algorithmMethodFactory.createCanonicalizationMethod(ctx); + sm = algorithmMethodFactory.createSignatureMethod(ctx); + } catch (NoSuchAlgorithmException e) { + log.error("Failed to get Canonicalization or Signature algorithm.", e); + throw new SLCommandException(4006); + } catch (InvalidAlgorithmParameterException e) { + log.error("Failed to get Canonicalization or Signature algorithm.", e); + throw new SLCommandException(4006); + } + + String siId = ctx.getIdValueFactory().createIdValue("SignedInfo"); + + SignedInfo si = signatureFactory.newSignedInfo(cm, sm, references, siId); + + KeyInfo ki = null; + if (signerCertificate != null) { + KeyInfoFactory kif = KeyInfoFactory.getInstance(); + X509Data x509Data = kif.newX509Data(Collections.singletonList(signerCertificate)); + ki = kif.newKeyInfo(Collections.singletonList(x509Data)); + } + + String signatureId = ctx.getIdValueFactory().createIdValue("Signature"); + String signatureValueId = ctx.getIdValueFactory().createIdValue("SignatureValue"); + + xmlSignature = signatureFactory.newXMLSignature(si, ki, objects, signatureId, signatureValueId); + + } + + /** + * Sign this Signature using the given <code>signContext</code>. + * <p> + * Call's {@link #buildXMLSignature()} if it has not been called yet. + * </p> + * + * @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 + * if <code>signContext</code> is <code>null</code> + */ + 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 <code>stal</code> implementation and + * <code>keyboxIdentifier</code>. + * <p> + * 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. + * </p> + * + * @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 + * if <code>stal</code> or <code>keyboxIdentifier</code> is + * <code>null</code> + */ + 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 <code>QualifyingProperties</code> as an + * <code>ds:Object</code> and a corresponding <code>ds:Reference</code> to + * it's <code>SignedProperties</code> element to this Signature. + * + * @param objects + * the list of <code>ds:Objects</code> to add the created + * <code>ds:Object</code> to + * @param references + * the list of <code>ds:References</code> to add the created + * <code>ds:Reference</code> to + * + * @throws SLCommandException + * if creating and adding the XAdES + * <code>QualifyingProperties</code> fails + * @throws NullPointerException + * if <code>objects</code> or <code>references</code> is + * <code>null</code> + */ + private void addXAdESObjectAndReference(List<XMLObject> objects, List<Reference> references) throws SLCommandException { + + QualifyingPropertiesFactory factory = QualifyingPropertiesFactory.getInstance(); + + String idValue = ctx.getIdValueFactory().createIdValue("SignedProperties"); + + Date date = (signingTime != null) ? signingTime : new Date(); + + List<X509Certificate> signingCertificates; + if (signerCertificate != null) { + signingCertificates = Collections.singletonList(signerCertificate); + } else { + signingCertificates = Collections.emptyList(); + } + + // TODO: report MOA-SP bug + // + // The security layer specification mandates the use of version 1.2.2. of the + // XAdES QualifyingProperties. However MOA-SP supports only version 1.1.1. Therefore, + // the version 1.1.1 is used in order to be compatible with current MOA-SP versions. + + List<DataObjectFormatType> dataObjectFormats = new ArrayList<DataObjectFormatType>(); + for (DataObject dataObject : dataObjects) { + if (dataObject.getMimeType() != null && dataObject.getReference() != null) { + Reference reference = dataObject.getReference(); + if (reference.getId() != null) { + String objectReference = "#" + reference.getId(); + dataObjectFormats.add(factory.createDataObjectFormatType( + objectReference, dataObject.getMimeType(), dataObject + .getDescription())); + } + } + } + + JAXBElement<QualifyingPropertiesType> qualifyingProperties; + try { + qualifyingProperties = factory.createQualifyingProperties111(date, signingCertificates, idValue, dataObjectFormats); + } catch (QualifyingPropertiesException e) { + log.error("Failed to create QualifyingProperties.", e); + throw new SLCommandException(4000); + } + + DocumentFragment fragment = ctx.getDocument().createDocumentFragment(); + + try { + factory.marshallQualifyingProperties(qualifyingProperties, fragment); + } catch (JAXBException e) { + log.error("Failed to marshal QualifyingProperties.", e); + throw new SLCommandException(4000); + } + + List<DOMStructure> content = Collections.singletonList(new DOMStructure(fragment.getFirstChild())); + + String objectIdValue = ctx.getIdValueFactory().createIdValue("Object"); + + XMLObject object = ctx.getSignatureFactory().newXMLObject(content, objectIdValue, null, null); + + objects.add(object); + + // TODO: Report MOA-SP Bug + // + // Direct referencing of the SignedPorperties Id-attribute is not supported by MOA-SP + // because the QualifyingProperties are parsed without the XAdES schema. Therefore, + // the shorthand XPointer could not be resolved. + // + // The following workaround uses an XPointer to select the SignedProperties in order + // to allow the signature to be verified with MOA-SP. + + String referenceURI = "#xmlns(xades=http://uri.etsi.org/01903/v1.1.1%23)%20xpointer(id('" + + objectIdValue + + "')/child::xades:QualifyingProperties/child::xades:SignedProperties)"; + DigestMethod dm; + try { + dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx); + } catch (NoSuchAlgorithmException e) { + log.error("Failed to get DigestMethod algorithm.", e); + throw new SLCommandException(4006); + } catch (InvalidAlgorithmParameterException e) { + log.error("Failed to get DigestMethod algorithm.", e); + throw new SLCommandException(4006); + } + + String referenceIdValue = ctx.getIdValueFactory().createIdValue("Reference"); + String referenceType = QualifyingPropertiesFactory.SIGNED_PROPERTIES_REFERENCE_TYPE_V1_1_1; + + Reference reference = ctx.getSignatureFactory().newReference(referenceURI, dm, null, referenceType, referenceIdValue); + + references.add(reference); + + Node child = fragment.getFirstChild(); + if (child instanceof Element) { + NodeList nodes = ((Element) child).getElementsByTagNameNS(QualifyingPropertiesFactory.NS_URI_V1_1_1, "SignedProperties"); + if (nodes.getLength() > 0) { + IdAttribute idAttribute = new IdAttribute(); + idAttribute.element = (Element) nodes.item(0); + idAttribute.namespaceURI = null; + idAttribute.localName = "Id"; + idAttributes.add(idAttribute); + } + } + + } + + /** + * Parse the SignatureEnvironment. + * + * @param signatureEnvironment + * the <code>SignatureEnvironment</code> element + * @param supplements + * an optional list of <code>Supplements</code> (may be + * <code>null</code>) + * + * @return the parsed SignatureEnvironment document + * + * @throws SLCommandException + * if parsing the SignatureEnvironment fails + * @throws NullPointerException + * if <code>signatureEnvironment</code> is <code>null</code> + */ + private Document parseSignatureEnvironment( + Base64XMLOptRefContentType signatureEnvironment, + List<DataObjectAssociationType> supplements) throws SLCommandException { + + if (signatureEnvironment == null) { + throw new NullPointerException("Argument 'signatureEnvironment' must not be null."); + } + + LSInput input; + try { + if (signatureEnvironment.getReference() != null) { + log.debug("SignatureEnvironment contains Reference " + signatureEnvironment.getReference() + "."); + input = createLSInput(signatureEnvironment.getReference()); + } else if (signatureEnvironment.getBase64Content() != null) { + log.debug("SignatureEnvironment contains Base64Content."); + input = createLSInput(signatureEnvironment.getBase64Content()); + } else if (signatureEnvironment.getXMLContent() != null) { + log.debug("SignatureEnvironment contains XMLContent."); + input = createLSInput((XMLContentType) signatureEnvironment.getXMLContent()); + } else { + // the schema does not allow us to reach this point + throw new SLCommandException(4000); + } + } catch (IOException e) { + log.info("XML document in which the signature is to be integrated cannot be resolved.", e); + throw new SLCommandException(4100); + } catch (XMLStreamException e) { + log.info("XML document in which the signature is to be integrated cannot be resolved.", e); + throw new SLCommandException(4100); + } + + LSParser parser = domImplLS.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null); + DOMConfiguration domConfig = parser.getDomConfig(); + SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler(); + domConfig.setParameter("error-handler", errorHandler); + LSResourceResolverAdapter resourceResolver = new LSResourceResolverAdapter(supplements); + domConfig.setParameter("resource-resolver", resourceResolver); + domConfig.setParameter("validate", Boolean.TRUE); + + Document doc; + try { + doc = parser.parse(input); + } catch (DOMException e) { + log.info("XML document in which the signature is to be integrated cannot be parsed.", e); + throw new SLCommandException(4101); + } catch (LSException e) { + log.info("XML document in which the signature is to be integrated cannot be parsed.", e); + throw new SLCommandException(4101); + } + + if (resourceResolver.getError() != null) { + log.info("Failed to resolve resource while parsing SignatureEnvironment document.", resourceResolver.getError()); + // we don't stop here, as we only _try_ to parse validating + } + + if (errorHandler.hasFatalErrors()) { + // log fatal errors + if (log.isInfoEnabled()) { + List<String> errorMessages = errorHandler.getErrorMessages(); + StringBuffer sb = new StringBuffer(); + for (String errorMessage : errorMessages) { + sb.append(" "); + sb.append(errorMessage); + } + log.info("XML document in which the signature is to be integrated cannot be parsed." + sb.toString()); + } + throw new SLCommandException(4101); + } + + // log parsed document + if (log.isTraceEnabled()) { + + StringWriter writer = new StringWriter(); + + writer.write("SignatureEnvironment:\n"); + + LSOutput output = domImplLS.createLSOutput(); + output.setCharacterStream(writer); + output.setEncoding("UTF-8"); + LSSerializer serializer = domImplLS.createLSSerializer(); + serializer.write(doc, output); + + log.trace(writer.toString()); + } + + return doc; + + } + + /** + * Creates an LSInput from the given <code>reference</code> URI. + * + * @param reference + * the reference URL + * + * @return an LSInput from the given <code>reference</code> URI + * + * @throws IOException + * if dereferencing the given <code>reference</code> 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 <code>content</code> bytes. + * + * @param content + * the content bytes + * + * @return an LSInput from the givne <code>content</code> 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 <code>content</code>. + * + * @param content + * the XML content + * @return an LSInput from the given XML <code>content</code> + * + * @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 <code>xsd:Id</code>-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 { + + List<DataObjectAssociationType> supplements; + + private LSResourceResolverAdapter( + List<DataObjectAssociationType> supplements) { + this.supplements = supplements; + } + + private Exception error; + + /** + * @return the error + */ + public Exception getError() { + return error; + } + + @Override + public LSInput resolveResource(String type, String namespaceURI, + String publicId, String systemId, String baseURI) { + + if (log.isTraceEnabled()) { + log.trace("Resolve resource :" + + "\n type=" + type + + "\n namespaceURI=" + namespaceURI + + "\n publicId=" + publicId + + "\n systemId=" + systemId + + "\n baseURI=" + baseURI); + } + + if (systemId != null) { + + log.debug("Resolve resource '" + systemId + "'."); + + for (DataObjectAssociationType supplement : supplements) { + + Base64XMLLocRefReqRefContentType content = supplement.getContent(); + if (content != null) { + + String reference = content.getReference(); + if (systemId.equals(reference)) { + + try { + if (content.getLocRefContent() != null) { + log.trace("Resolved resource '" + reference + "' to supplement with LocRefContent."); + return createLSInput(content.getLocRefContent()); + } else if (content.getBase64Content() != null) { + log.trace("Resolved resource '" + reference + "' to supplement with Base64Content."); + return createLSInput(content.getBase64Content()); + } else if (content.getXMLContent() != null) { + log.trace("Resolved resource '" + reference + "' to supplement with XMLContent."); + return createLSInput((XMLContentType) content.getXMLContent()); + } else { + return null; + } + } catch (IOException e) { + log.info("Failed to resolve resource '" + systemId + "' to supplement.", e); + error = e; + return null; + } catch (XMLStreamException e) { + log.info("Failed to resolve resource '" + systemId + "' to supplement.", e); + error = e; + return null; + } + + } + + } + + } + + log.info("Failed to resolve resource '" + systemId + "' to supplement. No such supplement."); + + } + + return null; + + } + + + } + +} |