diff options
author | mcentner <mcentner@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2010-01-26 16:27:27 +0000 |
---|---|---|
committer | mcentner <mcentner@8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4> | 2010-01-26 16:27:27 +0000 |
commit | 84cd553cc40d9850fcd865887219d907693af0e6 (patch) | |
tree | 2d1d9054f42845ce951c9b2c2239178c803443d3 /bkucommon/src/main/java/at/gv/egiz/bku/slcommands | |
parent | 667af128d0adfeee2aa4748ab58411c91bc4905f (diff) | |
parent | 7a5310b43849124095d97af3103c4fdaeeacbbbb (diff) | |
download | mocca-84cd553cc40d9850fcd865887219d907693af0e6.tar.gz mocca-84cd553cc40d9850fcd865887219d907693af0e6.tar.bz2 mocca-84cd553cc40d9850fcd865887219d907693af0e6.zip |
git-svn-id: https://joinup.ec.europa.eu/svn/mocca/branches/mocca-1.2.11-sha2@602 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4
Diffstat (limited to 'bkucommon/src/main/java/at/gv/egiz/bku/slcommands')
67 files changed, 8697 insertions, 0 deletions
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/AccessControlInvocation.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/AccessControlInvocation.java new file mode 100644 index 00000000..014b7fd7 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/AccessControlInvocation.java @@ -0,0 +1,21 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands;
+
+public class AccessControlInvocation implements
+ at.gv.egiz.bku.slcommands.InvocationStrategy {
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateXMLSignatureCommand.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateXMLSignatureCommand.java new file mode 100644 index 00000000..2d87c39f --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateXMLSignatureCommand.java @@ -0,0 +1,25 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands;
+
+import at.gv.egiz.bku.slexceptions.SLCommandException;
+import at.gv.egiz.bku.slexceptions.SLRequestException;
+
+public interface CreateXMLSignatureCommand extends SLCommand {
+
+ public void prepareXMLSignature() throws SLCommandException, SLRequestException;
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateXMLSignatureResult.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateXMLSignatureResult.java new file mode 100644 index 00000000..4bc2820b --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/CreateXMLSignatureResult.java @@ -0,0 +1,20 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands;
+
+public interface CreateXMLSignatureResult extends SLResult {
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/ErrorResult.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/ErrorResult.java new file mode 100644 index 00000000..5d52c0ea --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/ErrorResult.java @@ -0,0 +1,20 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands;
+
+public interface ErrorResult extends SLResult {
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/GetStatusCommand.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/GetStatusCommand.java new file mode 100644 index 00000000..19099b63 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/GetStatusCommand.java @@ -0,0 +1,26 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package at.gv.egiz.bku.slcommands; + +/** + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public interface GetStatusCommand extends SLCommand { + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/GetStatusResult.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/GetStatusResult.java new file mode 100644 index 00000000..c5518f51 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/GetStatusResult.java @@ -0,0 +1,26 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package at.gv.egiz.bku.slcommands; + +/** + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public interface GetStatusResult extends SLResult { + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxReadCommand.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxReadCommand.java new file mode 100644 index 00000000..73fddf1f --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxReadCommand.java @@ -0,0 +1,28 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands;
+
+public interface InfoboxReadCommand extends SLCommand { + public String getInfoboxIdentifier(); + + /** + * Convenience method to get the domain identifier if the infobox + * referes to a Identitylink. + * @return the domain id or null if the Infobox is not of type Identitylink or no domain parameter was specified + */ + public String getIdentityLinkDomainId();
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxReadResult.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxReadResult.java new file mode 100644 index 00000000..c6a51362 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxReadResult.java @@ -0,0 +1,20 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands;
+
+public interface InfoboxReadResult extends SLResult {
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxUpdateCommand.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxUpdateCommand.java new file mode 100644 index 00000000..c2974785 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxUpdateCommand.java @@ -0,0 +1,23 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands; + +public interface InfoboxUpdateCommand extends SLCommand { + + public String getInfoboxIdentifier(); + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxUpdateResult.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxUpdateResult.java new file mode 100644 index 00000000..d180facf --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InfoboxUpdateResult.java @@ -0,0 +1,21 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands; + +public interface InfoboxUpdateResult extends SLResult { + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InvocationStrategy.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InvocationStrategy.java new file mode 100644 index 00000000..6b410fac --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/InvocationStrategy.java @@ -0,0 +1,20 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands;
+
+public interface InvocationStrategy {
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/NullOperationCommand.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/NullOperationCommand.java new file mode 100644 index 00000000..0651f882 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/NullOperationCommand.java @@ -0,0 +1,20 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands;
+
+public interface NullOperationCommand extends SLCommand {
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/NullOperationResult.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/NullOperationResult.java new file mode 100644 index 00000000..c36c879e --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/NullOperationResult.java @@ -0,0 +1,20 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands;
+
+public interface NullOperationResult extends SLResult {
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommand.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommand.java new file mode 100644 index 00000000..a8625946 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommand.java @@ -0,0 +1,31 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands;
+
+import at.gv.egiz.bku.slexceptions.SLCommandException;
+
+public interface SLCommand {
+
+ public final String NAMESPACE_URI = "http://www.buergerkarte.at/namespaces/securitylayer/1.2#";
+
+ public String getName();
+
+ public void init(SLCommandContext aCtx, Object aUnmarshalledRequest) throws SLCommandException;
+
+ public SLResult execute();
+
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandContext.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandContext.java new file mode 100644 index 00000000..5af2afac --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandContext.java @@ -0,0 +1,55 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands;
+
+import java.util.Locale; + +import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext;
+import at.gv.egiz.stal.STAL;
+
+public class SLCommandContext {
+
+ private STAL stal;
+ private URLDereferencerContext urlDerefCtx; + + private Locale locale;
+
+ public void setSTAL(STAL aStal) {
+ this.stal = aStal;
+ }
+
+ public void setURLDereferencerContext(URLDereferencerContext aCtx) {
+ this.urlDerefCtx = aCtx;
+ }
+
+ public STAL getSTAL() {
+ return stal;
+ }
+
+ public URLDereferencerContext getURLDereferencerContext() {
+ return urlDerefCtx;
+ } + + public Locale getLocale() { + return locale; + } + + public void setLocale(Locale locale) { + this.locale = locale; + } +
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandFactory.java new file mode 100644 index 00000000..6e84867e --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandFactory.java @@ -0,0 +1,395 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands; + +import java.io.IOException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.XMLConstants; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.UnmarshalException; +import javax.xml.bind.Unmarshaller; +import javax.xml.bind.ValidationEvent; +import javax.xml.bind.ValidationEventLocator; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.transform.Source; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; + +import at.gv.egiz.bku.slexceptions.SLCommandException; +import at.gv.egiz.bku.slexceptions.SLExceptionMessages; +import at.gv.egiz.bku.slexceptions.SLRequestException; +import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import at.gv.egiz.bku.slexceptions.SLVersionException; +import at.gv.egiz.bku.utils.DebugReader; +import at.gv.egiz.slbinding.RedirectEventFilter; +import at.gv.egiz.slbinding.RedirectUnmarshallerListener; +import at.gv.egiz.validation.ReportingValidationEventHandler; + +public class SLCommandFactory { + + /** + * Schema files required for Security Layer command validation. + */ + public static final String[] SCHEMA_FILES = new String[]{ + "at/gv/egiz/bku/slcommands/schema/xml.xsd", + "at/gv/egiz/bku/slcommands/schema/xmldsig-core-schema.xsd", + "at/gv/egiz/bku/slcommands/schema/Core-1.2.xsd", + "at/gv/egiz/bku/slcommands/schema/Core.20020225.xsd", + "at/gv/egiz/bku/slcommands/schema/Core.20020831.xsd" + }; + /** + * Logging facility. + */ + static Log log = LogFactory.getLog(SLCommandFactory.class); + /** + * The instance returned by {@link #getInstance()}. + */ + private static SLCommandFactory instance; + /** + * Schema for Security Layer command validation. + */ + private Schema slSchema; + /** + * The JAXBContext. + */ + private JAXBContext jaxbContext; + /** + * The map of <namespaceURI>:<localName> to implementation class of the + * corresponding {@link SLCommand}. + */ + private Map<String, Class<? extends SLCommand>> slRequestTypeMap = new HashMap<String, Class<? extends SLCommand>>(); + + /** + * The mapping of a requests's qualified name to a concrete command factories. + */ + private Map<QName, AbstractSLCommandFactory> slCommandFactories = new HashMap<QName, AbstractSLCommandFactory>(); + + /** + * Configures the singleton instance with command implementations + * @param commandImplMap + * @throws ClassNotFoundException + */ + @SuppressWarnings("unchecked") + public void setCommandImpl(Map<String, String> commandImplMap) throws ClassNotFoundException { + ClassLoader cl = getClass().getClassLoader(); + for (String key : commandImplMap.keySet()) { + Class<? extends SLCommand> impl = (Class<? extends SLCommand>) cl.loadClass(commandImplMap.get(key)); + log.debug("Registering sl command implementation for :"+key+ "; implementation class: "+impl.getCanonicalName()); + slRequestTypeMap.put(key, impl); + } + } + + public void setConcreteFactories(Map<QName, AbstractSLCommandFactory> factories) { + if (log.isDebugEnabled()) { + StringBuilder sb = new StringBuilder(); + sb.append("Registered sl command factory for"); + for (QName qname : factories.keySet()) { + sb.append("\n " + qname + " : " + factories.get(qname).getClass()); + } + log.debug(sb); + } + slCommandFactories = factories; + } + + /** + * Register an {@link SLCommand} implementation class of a Security Layer + * command with the given <code>namespaceUri</code> and <code>localname</code> + * . + * + * @param namespaceUri + * the namespace URI of the Security Layer command + * @param localname + * the localname of the Security Layer command + * @param slCommandClass + * the implementation class, or <code>null</code> to deregister a + * currently registered class + */ + public void setImplClass(String namespaceUri, String localname, + Class<? extends SLCommand> slCommandClass) { + if (slCommandClass != null) { + slRequestTypeMap.put(namespaceUri + ":" + localname, slCommandClass); + } else { + slRequestTypeMap.remove(namespaceUri + ":" + localname); + } + } + + /** + * Returns the implementation class of an {@link SLCommand} with the given + * <code>name</code>, or <code>null</code> if no such class is registered. + * + * @param name + * the <code>QName</code> of the Security Layer command + * @return the implementation class, or <code>null</code> if no class is + * registered for the given <code>name</code> + */ + public Class<? extends SLCommand> getImplClass(QName name) { + String namespaceURI = name.getNamespaceURI(); + String localPart = name.getLocalPart(); + return slRequestTypeMap.get(namespaceURI + ":" + localPart); + } + + /** + * Sets the schema to validate Security Layer commands with. + * + * @param slSchema the schema to validate Security Layer commands with + */ + public void setSLSchema(Schema slSchema) { + this.slSchema = slSchema; + } + + /** + * @return the jaxbContext + */ + public JAXBContext getJaxbContext() { + ensureJaxbContext(); + return jaxbContext; + } + + /** + * @param jaxbContext the jaxbContext to set + */ + public void setJaxbContext(JAXBContext jaxbContext) { + this.jaxbContext = jaxbContext; + } + + /** + * Initialize the JAXBContext. + */ + private synchronized void ensureJaxbContext() { + if (jaxbContext == null) { + try { + String slPkg = at.buergerkarte.namespaces.securitylayer._1.ObjectFactory.class.getPackage().getName(); + String xmldsigPkg = org.w3._2000._09.xmldsig_.ObjectFactory.class.getPackage().getName(); + String cardChannelPkg = at.buergerkarte.namespaces.cardchannel.ObjectFactory.class.getPackage().getName(); + String slPkgLegacy1_0 = at.buergerkarte.namespaces.securitylayer._20020225_.ObjectFactory.class.getPackage().getName(); + String slPkgLegacy1_1 = at.buergerkarte.namespaces.securitylayer._20020831_.ObjectFactory.class.getPackage().getName(); + setJaxbContext(JAXBContext.newInstance(slPkg + ":" + xmldsigPkg + ":" + cardChannelPkg + + ":" + slPkgLegacy1_0 + ":" + slPkgLegacy1_1)); + } catch (JAXBException e) { + log.error("Failed to setup JAXBContext security layer request.", e); + throw new SLRuntimeException(e); + } + } + } + + /** + * Initialize the security layer schema. + */ + private synchronized void ensureSchema() { + if (slSchema == null) { + try { + SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + ClassLoader cl = SLCommandFactory.class.getClassLoader(); + Source[] sources = new Source[SCHEMA_FILES.length]; + for (int i = 0; i < SCHEMA_FILES.length; i++) { + String schemaFile = SCHEMA_FILES[i]; + URL schemaURL = cl.getResource(schemaFile); + if (schemaURL == null) { + throw new SLRuntimeException("Failed to load schema file " + schemaFile + "."); + } + log.debug("Schema location: " + schemaURL); + sources[i] = new StreamSource(schemaURL.openStream()); + } + Schema schema = schemaFactory.newSchema(sources); + log.debug("Schema successfully created."); + setSLSchema(schema); + } catch (SAXException e) { + log.error("Failed to load security layer schema.", e); + throw new SLRuntimeException("Failed to load security layer schema.", e); + } catch (IOException e) { + log.error("Failed to load security layer schema.", e); + throw new SLRuntimeException("Failed to load security layer schema.", e); + } + + } + } + + /** + * Get an instance of the <code>SLCommandFactory</code>. + */ + public synchronized static SLCommandFactory getInstance() { + if (instance == null) { + instance = new SLCommandFactory(); + instance.ensureJaxbContext(); + instance.ensureSchema(); + } + return instance; + } + + /** + * Private constructor used by {@link #getInstance()}. + */ + private SLCommandFactory() { + } + + /** + * Unmarshalls from the given <code>source</code>. + * + * @see Unmarshaller#unmarshal(Source) + * + * <em>Note:</em>Could replace JAXB's unmarshal-time validation engine (see commented code), however, + * we need a redirect filter. + * + * @param source + * the source to unmarshal from + * @return the object returned by {@link Unmarshaller#unmarshal(Source)} + * @throws SLRequestException + * if unmarshalling fails + * @throws SLRuntimeException + * if an unexpected error occurs configuring the unmarshaller or if + * unmarshalling fails with an unexpected error + */ + protected Object unmarshal(Source source) throws SLRuntimeException, + SLRequestException { + + Object object; + ReportingValidationEventHandler validationEventHandler = new ReportingValidationEventHandler(); + try { + + XMLInputFactory inputFactory = XMLInputFactory.newInstance(); + XMLEventReader eventReader = inputFactory.createXMLEventReader(source); + RedirectEventFilter redirectEventFilter = new RedirectEventFilter(); + XMLEventReader filteredReader = inputFactory.createFilteredReader(eventReader, redirectEventFilter); + + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + unmarshaller.setListener(new RedirectUnmarshallerListener(redirectEventFilter)); + if (slSchema != null) { + unmarshaller.setSchema(slSchema); + } + log.trace("Before unmarshal()."); + unmarshaller.setEventHandler(validationEventHandler); + object = unmarshaller.unmarshal(filteredReader); + log.trace("After unmarshal()."); + } catch (UnmarshalException e) { + if (log.isDebugEnabled()) { + log.debug("Failed to unmarshall security layer request.", e); + } else { + log.info("Failed to unmarshall security layer request." + e.getMessage()); + } + if (validationEventHandler.getErrorEvent() != null) { + // Validation Error + ValidationEvent errorEvent = validationEventHandler.getErrorEvent(); + ValidationEventLocator locator = errorEvent.getLocator(); + throw new SLRequestException(3002, + SLExceptionMessages.EC3002_INVALID, new Object[]{errorEvent.getMessage()}); + } + Throwable cause = e.getCause(); + if (cause instanceof SAXParseException) { + throw new SLRequestException(3000, + SLExceptionMessages.EC3000_UNCLASSIFIED, new Object[]{cause.getMessage()}); + } else { + throw new SLRequestException(3000, + SLExceptionMessages.EC3000_UNCLASSIFIED, new Object[]{e}); + } + } catch (JAXBException e) { + // unexpected error + log.error("Failed to unmarshall security layer request.", e); + throw new SLRuntimeException(e); + } catch (XMLStreamException e) { + // unexpected error + log.error("Failed to unmarshall security layer request.", e); + throw new SLRuntimeException(e); + } + + return object; + + } + + /** + * Creates a new <code>SLCommand</code> from the given <code>source</code> and + * <code>context</code>. + * + * @param source + * the <code>Source</code> to unmarshall from + * @param context + * the context for the created <code>SLCommand</code> + * @return the <code>SLCommand</code> unmarshalled from the given + * <code>source</code> + * @throws SLRequestException + * if unmarshalling fails + * @throws SLCommandException + * if command ist not supported + * @throws SLRuntimeException + * if an unexpected error occurs configuring the unmarshaller, if + * unmarshalling fails with an unexpected error or if the + * corresponding <code>SLCommand</code> could not be instantiated + * @throws SLVersionException + */ + @SuppressWarnings("unchecked") + public SLCommand createSLCommand(Source source, SLCommandContext context) + throws SLCommandException, SLRuntimeException, SLRequestException, SLVersionException { + + DebugReader dr = null; + if (log.isTraceEnabled() && source instanceof StreamSource) { + StreamSource streamSource = (StreamSource) source; + if (streamSource.getReader() != null) { + dr = new DebugReader(streamSource.getReader(), "SLCommand unmarshalled from:\n"); + streamSource.setReader(dr); + } + } + + Object object; + try { + object = unmarshal(source); + } catch (SLRequestException e) { + throw e; + } finally { + if (dr != null) { + log.trace(dr.getCachedString()); + } + } + + if (!(object instanceof JAXBElement)) { + // invalid request + log.info("Invalid security layer request. " + object.toString()); + throw new SLRequestException(3002, SLExceptionMessages.EC3002_INVALID, + new Object[]{object.toString()}); + } + + QName qName = ((JAXBElement) object).getName(); + if (!SLCommand.NAMESPACE_URI.equals(qName.getNamespaceURI())) { + // security layer request version not supported + log.info("Unsupported security layer request version : " + qName.getNamespaceURI()); + throw new SLVersionException(qName.getNamespaceURI()); + } + + AbstractSLCommandFactory concreteFactory = slCommandFactories.get(qName); + if (concreteFactory == null) { + // command not supported + log.info("Unsupported command received: " + qName.toString()); + throw new SLCommandException(4011, + SLExceptionMessages.EC4011_NOTIMPLEMENTED, new Object[]{qName.toString()}); + } + + return concreteFactory.createSLCommand(context, (JAXBElement<?>) object); + + } +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandInvoker.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandInvoker.java new file mode 100644 index 00000000..c28288c9 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLCommandInvoker.java @@ -0,0 +1,46 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands;
+
+import at.gv.egiz.bku.slexceptions.SLCanceledException; +import at.gv.egiz.bku.slexceptions.SLException; +
+public interface SLCommandInvoker {
+
+ /**
+ *
+ * @param aContext
+ * @throws SLCanceledException if the security management prevents execution of this command
+ */
+ public void invoke(SLSourceContext aContext) throws SLException;
+
+ /**
+ *
+ * @param aContext
+ * @return
+ * @throws SLCanceledException if the security management prevents execution of this command
+ */
+ public SLResult getResult(SLTargetContext aContext) throws SLException;
+
+ public void setCommand(at.gv.egiz.bku.slcommands.SLCommand aCmd);
+
+ /**
+ * Prototype creation
+ * @return
+ */
+ public SLCommandInvoker newInstance();
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLMarshallerFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLMarshallerFactory.java new file mode 100644 index 00000000..e0a375cf --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLMarshallerFactory.java @@ -0,0 +1,172 @@ +/* +* Copyright 2009 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import at.gv.egiz.marshal.MarshallerFactory; + +public class SLMarshallerFactory { + + static Log log = LogFactory.getLog(SLMarshallerFactory.class); + + /** + * The JAXBContext used for result marshaling. + * <p> + * Note: Different contexts are used for marshaling and unmarshaling of + * security layer requests and responses to avoid propagation of namespace + * declarations of legacy namespaces into marshaled results. + * </p> + * @see #jaxbContextLegacy + */ + protected static JAXBContext context; + + /** + * The JAXBContext used for marshaling of of results in the legacy namespace. + */ + protected static JAXBContext legacyContext; + + // ------------------- initialization on demand idiom ------------------- + // see http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom + // ---------------------------------------------------------------------- + + /** + * Private constructor called by {@link SLMarshallerFactoryInstanceHolder}. + */ + private SLMarshallerFactory() { + // context is initialized immediately while the legacy context is initialized only on demand + try { + String slPkg = at.buergerkarte.namespaces.securitylayer._1.ObjectFactory.class.getPackage().getName(); + String xmldsigPkg = org.w3._2000._09.xmldsig_.ObjectFactory.class.getPackage().getName(); + String cardChannelPkg = at.buergerkarte.namespaces.cardchannel.ObjectFactory.class.getPackage().getName(); + context = JAXBContext.newInstance(slPkg + ":" + xmldsigPkg + ":" + cardChannelPkg); + } catch (JAXBException e) { + log.error("Failed to setup JAXBContext security layer request.", e); + throw new SLRuntimeException(e); + } + } + + /** + * The lazy instance holder for this SLMarshallerFactory. + */ + private static class SLMarshallerFactoryInstanceHolder { + /** + * The instance returned by {@link SLMarshallerFactory#getInstance()} + */ + private static final SLMarshallerFactory instance = new SLMarshallerFactory(); + } + + /** + * Get an instance of the <code>SLMarshallerFactory</code>. + */ + public static SLMarshallerFactory getInstance() { + return SLMarshallerFactoryInstanceHolder.instance; + } + + // ---------------------------------------------------------------------- + + /** + * Initialize the JAXBContext for the legacy namespace. + */ + private static synchronized void ensureLegacyContext() { + // legacy marshaller is initialized only on demand + if (legacyContext == null) { + try { + String slPkgLegacy1_0 = at.buergerkarte.namespaces.securitylayer._20020225_.ObjectFactory.class.getPackage().getName(); + String slPkgLegacy1_1 = at.buergerkarte.namespaces.securitylayer._20020831_.ObjectFactory.class.getPackage().getName(); + String xmldsigPkg = org.w3._2000._09.xmldsig_.ObjectFactory.class.getPackage().getName(); + String cardChannelPkg = at.buergerkarte.namespaces.cardchannel.ObjectFactory.class.getPackage().getName(); + legacyContext = JAXBContext.newInstance(slPkgLegacy1_0 + ":" + slPkgLegacy1_1 + ":" + xmldsigPkg + ":" + cardChannelPkg); + } catch (JAXBException e) { + log.error("Failed to setup JAXBContext security layer request.", e); + throw new SLRuntimeException(e); + } + } + } + + /** + * Creates an SL marshaller. + * + * @param formattedOutput + * <code>true</code> if the marshaller should produce formated + * output, <code>false</code> otherwise + * @return an SL marshaller + */ + public Marshaller createMarshaller(boolean formattedOutput) { + return createMarshaller(formattedOutput, false); + } + + /** + * Creates an SL marshaller. + * + * @param formattedOutput + * <code>true</code> if the marshaller should produce formated + * output, <code>false</code> otherwise + * @param fragment + * <code>true</code> if the marshaller should produce a XML fragment + * (omit XML declaration), <code>false</code> otherwise + * @return an SL marshaller + */ + public Marshaller createMarshaller(boolean formattedOutput, boolean fragment) { + try { + return MarshallerFactory.createMarshaller(context, formattedOutput, fragment); + } catch (JAXBException e) { + log.fatal("Failed to marshall error response.", e); + throw new SLRuntimeException("Failed to marshall error response.", e); + } + } + + /** + * Creates a legacy SL marshaller. + * + * @param formattedOutput + * <code>true</code> if the marshaller should produce formated + * output, <code>false</code> otherwise + * @return a legacy SL marshaller + */ + public Marshaller createLegacyMarshaller(boolean formattedOutput) { + return createLegacyMarshaller(formattedOutput, false); + } + + /** + * Creates a legacy SL marshaller. + * + * @param formattedOutput + * <code>true</code> if the marshaller should produce formated + * output, <code>false</code> otherwise + * @param fragment + * <code>true</code> if the marshaller should produce a XML fragment + * (omit XML declaration), <code>false</code> otherwise + * @return a legacy SL marshaller + */ + public Marshaller createLegacyMarshaller(boolean formattedOutput, boolean fragment) { + try { + ensureLegacyContext(); + return MarshallerFactory.createMarshaller(legacyContext, formattedOutput, fragment); + } catch (JAXBException e) { + log.fatal("Failed to marshall error response.", e); + throw new SLRuntimeException("Failed to marshall error response.", e); + } + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLResult.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLResult.java new file mode 100644 index 00000000..e9e483c5 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLResult.java @@ -0,0 +1,45 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands;
+
+import javax.xml.transform.Result; +import javax.xml.transform.Templates; +
+public interface SLResult {
+
+ public static enum SLResultType {BINARY, XML};
+
+ public SLResultType getResultType();
+
+ /**
+ * The MIME Type of the Result.
+ *
+ * @return may result null if unknown.
+ */
+ public String getMimeType();
+
+ public void writeTo(Result aResult, boolean fragment);
+
+ /**
+ *
+ * @param result + * @param fragment TODO + * @param transformer may be null.
+ */
+ public void writeTo(Result result, Templates templates, boolean fragment); + +}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLSourceContext.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLSourceContext.java new file mode 100644 index 00000000..f25a0ea4 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLSourceContext.java @@ -0,0 +1,64 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands;
+
+import java.net.URL; +import java.security.cert.X509Certificate;
+
+import at.gv.egiz.bku.utils.binding.Protocol;
+
+
+public class SLSourceContext {
+
+ private URL sourceUrl;
+ private boolean sourceIsDataURL;
+ private X509Certificate sourceCertificate;
+ private String sourceHTTPReferer;
+
+ public URL getSourceUrl() {
+ return sourceUrl;
+ }
+
+ public void setSourceUrl(URL sourceProtocol) {
+ this.sourceUrl = sourceProtocol;
+ }
+
+ public boolean isSourceIsDataURL() {
+ return sourceIsDataURL;
+ }
+
+ public void setSourceIsDataURL(boolean sourceIsDataURL) {
+ this.sourceIsDataURL = sourceIsDataURL;
+ }
+
+ public X509Certificate getSourceCertificate() {
+ return sourceCertificate;
+ }
+
+ public void setSourceCertificate(X509Certificate sourceCertificate) {
+ this.sourceCertificate = sourceCertificate;
+ }
+
+ public String getSourceHTTPReferer() {
+ return sourceHTTPReferer;
+ }
+
+ public void setSourceHTTPReferer(String sourceHTTPReferer) {
+ this.sourceHTTPReferer = sourceHTTPReferer;
+ }
+
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLTargetContext.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLTargetContext.java new file mode 100644 index 00000000..f9df3ced --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/SLTargetContext.java @@ -0,0 +1,51 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands;
+
+import java.net.URL; +import java.security.cert.X509Certificate; +
+public class SLTargetContext {
+ private URL targetUrl;
+ private boolean targetIsDataURL;
+ private X509Certificate targetCertificate;
+
+ public URL getTargetUrl() {
+ return targetUrl;
+ }
+
+ public void setTargetUrl(URL targetUrl) {
+ this.targetUrl = targetUrl;
+ }
+
+ public boolean isTargetIsDataURL() {
+ return targetIsDataURL;
+ }
+
+ public void setTargetIsDataURL(boolean targetIsDataURL) {
+ this.targetIsDataURL = targetIsDataURL;
+ }
+
+ public X509Certificate getTargetCertificate() {
+ return targetCertificate;
+ }
+
+ public void setTargetCertificate(X509Certificate targetCertificate) {
+ this.targetCertificate = targetCertificate;
+ }
+
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractAssocArrayInfobox.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractAssocArrayInfobox.java new file mode 100644 index 00000000..9a4536e6 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractAssocArrayInfobox.java @@ -0,0 +1,299 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package at.gv.egiz.bku.slcommands.impl; + +import java.io.ByteArrayOutputStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.buergerkarte.namespaces.securitylayer._1.InfoboxAssocArrayPairType; +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadDataAssocArrayType; +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadParamsAssocArrayType; +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadRequestType; +import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory; +import at.buergerkarte.namespaces.securitylayer._1.XMLContentType; +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadParamsAssocArrayType.ReadKeys; +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadParamsAssocArrayType.ReadPairs; +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadParamsAssocArrayType.ReadValue; +import at.gv.egiz.bku.slcommands.InfoboxReadResult; +import at.gv.egiz.bku.slcommands.SLCommandContext; +import at.gv.egiz.bku.slcommands.SLMarshallerFactory; +import at.gv.egiz.bku.slexceptions.SLCommandException; + +/** + * An abstract base class for {@link Infobox} implementations of type associative array. + * + * @author mcentner + */ +public abstract class AbstractAssocArrayInfobox extends AbstractInfoboxImpl + implements AssocArrayInfobox { + + /** + * Logging facility. + */ + private static Log log = LogFactory.getLog(AbstractAssocArrayInfobox.class); + + /** + * The search string pattern. + */ + public static final String SEARCH_STRING_PATTERN = "(.&&[^/])+(/.&&[^/])*"; + + /** + * @return the keys available in this infobox. + */ + public abstract String[] getKeys(); + + /** + * @return <code>true</code> if the values are XML entities, or <code>false</code> otherwise. + */ + public abstract boolean isValuesAreXMLEntities(); + + /** + * Returns a key to value mapping for the given <code>keys</code>. + * + * @param keys a list of keys + * @param cmdCtx the command context + * + * @return a key to value mapping for the given <code>keys</code>. + * + * @throws SLCommandException if obtaining the values fails + */ + public abstract Map<String, Object> getValues(List<String> keys, SLCommandContext cmdCtx) throws SLCommandException; + + /** + * Returns all keys that match the given <code>searchString</code>. + * + * @param searchString the search string + * + * @return all keys that match the given <code>searchString</code> + * + * @throws SLCommandException if the given search string is invalid + */ + protected List<String> selectKeys(String searchString) throws SLCommandException { + + if ("*".equals(searchString) || "**".equals(searchString)) { + return Arrays.asList(getKeys()); + } + + if (!searchString.contains("*")) { + Arrays.asList(getKeys()).contains(searchString); + return Collections.singletonList(searchString); + } + + if (Pattern.matches(SEARCH_STRING_PATTERN, searchString)) { + +// for (int i = 0; i < searchString.length(); i++) { +// int codePoint = searchString.codePointAt(i); +// +// } + + // TODO : build pattern + return Collections.emptyList(); + } else { + log.info("Got invalid search string '" + searchString + "'"); + throw new SLCommandException(4010); + } + + } + + /** + * Read all keys specified by <code>readKeys</code>. + * + * @param readKeys + * the ReadKeys element + * @param cmdCtx + * the command context + * @return a corresponding InfoboxReadResult + * + * @throws SLCommandException + * if the ReadKeys element is invalid or obtaining the corresponding + * values fails + */ + protected InfoboxReadResult readKeys(ReadKeys readKeys, SLCommandContext cmdCtx) throws SLCommandException { + + List<String> selectedKeys = selectKeys(readKeys.getSearchString()); + + if (readKeys.isUserMakesUnique() && selectedKeys.size() > 1) { + log.info("UserMakesUnique not supported"); + // TODO: give more specific error message + throw new SLCommandException(4010); + } + + ObjectFactory objectFactory = new ObjectFactory(); + + InfoboxReadDataAssocArrayType infoboxReadDataAssocArrayType = objectFactory + .createInfoboxReadDataAssocArrayType(); + + List<String> keys = infoboxReadDataAssocArrayType.getKey(); + keys.addAll(selectedKeys); + + return new InfoboxReadResultImpl(infoboxReadDataAssocArrayType); + + } + + /** + * Read all pairs specified by <code>readPairs</code>. + * + * @param readPairs + * the readPairs element + * @param cmdCtx + * the command context + * @return a corresponding InfoboxReadResult + * + * @throws SLCommandException + * if the ReadPairs element is invalid or obtaining the corresponding + * values fails + */ + protected InfoboxReadResult readPairs(ReadPairs readPairs, SLCommandContext cmdCtx) throws SLCommandException { + + if (readPairs.isValuesAreXMLEntities() && !isValuesAreXMLEntities()) { + log.info("Got valuesAreXMLEntities=" + readPairs.isValuesAreXMLEntities() + " but infobox type is binary."); + throw new SLCommandException(4010); + } + + List<String> selectedKeys = selectKeys(readPairs.getSearchString()); + + if (readPairs.isUserMakesUnique() && selectedKeys.size() > 1) { + log.info("UserMakesUnique not supported"); + // TODO: give more specific error message + throw new SLCommandException(4010); + } + + return new InfoboxReadResultImpl(marshallPairs(selectedKeys, getValues( + selectedKeys, cmdCtx), readPairs.isValuesAreXMLEntities())); + } + + /** + * Read the value specified by <code>readPairs</code>. + * + * @param readValue + * the readValue element + * @param cmdCtx + * the command context + * @return a corresponding InfoboxReadResult + * + * @throws SLCommandException + * if the ReadValue element is invalid or obtaining the corresponding + * values fails + */ + protected InfoboxReadResult readValue(ReadValue readValue, SLCommandContext cmdCtx) throws SLCommandException { + + if (readValue.isValueIsXMLEntity() && !isValuesAreXMLEntities()) { + log.info("Got valuesAreXMLEntities=" + readValue.isValueIsXMLEntity() + " but infobox type is binary."); + throw new SLCommandException(4010); + } + + List<String> selectedKeys; + + if (Arrays.asList(getKeys()).contains(readValue.getKey())) { + selectedKeys = Collections.singletonList(readValue.getKey()); + } else { + selectedKeys = Collections.emptyList(); + } + + return new InfoboxReadResultImpl(marshallPairs(selectedKeys, getValues( + selectedKeys, cmdCtx), readValue.isValueIsXMLEntity())); + + } + + protected InfoboxReadDataAssocArrayType marshallPairs(List<String> selectedKeys, Map<String, Object> values, boolean areXMLEntities) throws SLCommandException { + + ObjectFactory objectFactory = new ObjectFactory(); + + InfoboxReadDataAssocArrayType infoboxReadDataAssocArrayType = objectFactory.createInfoboxReadDataAssocArrayType(); + + for (String key : selectedKeys) { + InfoboxAssocArrayPairType infoboxAssocArrayPairType = objectFactory.createInfoboxAssocArrayPairType(); + infoboxAssocArrayPairType.setKey(key); + + Object value = values.get(key); + if (areXMLEntities) { + if (value instanceof byte[]) { + log.info("Got valuesAreXMLEntities=" + areXMLEntities + " but infobox type is binary."); + throw new SLCommandException(4122); + } else { + XMLContentType contentType = objectFactory.createXMLContentType(); + contentType.getContent().add(value); + infoboxAssocArrayPairType.setXMLContent(contentType); + } + } else { + infoboxAssocArrayPairType.setBase64Content((value instanceof byte[]) ? (byte[]) value : marshallValue(value)); + } + + infoboxReadDataAssocArrayType.getPair().add(infoboxAssocArrayPairType); + } + + return infoboxReadDataAssocArrayType; + + } + + protected byte[] marshallValue(Object jaxbElement) throws SLCommandException { + + Marshaller marshaller = SLMarshallerFactory.getInstance().createMarshaller(false); + ByteArrayOutputStream result = new ByteArrayOutputStream(); + try { + marshaller.marshal(jaxbElement, result); + } catch (JAXBException e) { + log.info("Failed to marshall infobox content.", e); + throw new SLCommandException(4122); + } + + return result.toByteArray(); + + } + + @Override + public InfoboxReadResult read(InfoboxReadRequestType req, + SLCommandContext cmdCtx) throws SLCommandException { + + InfoboxReadParamsAssocArrayType assocArrayParameters = req + .getAssocArrayParameters(); + + if (assocArrayParameters == null) { + log.info("Infobox type is AssocArray but got no AssocArrayParameters."); + throw new SLCommandException(4010); + } + + if (assocArrayParameters.getReadKeys() != null) { + return readKeys(assocArrayParameters.getReadKeys(), cmdCtx); + } + + if (assocArrayParameters.getReadPairs() != null) { + return readPairs(assocArrayParameters.getReadPairs(), cmdCtx); + } + + // ReadValue + if (assocArrayParameters.getReadValue() != null) { + return readValue(assocArrayParameters.getReadValue(), cmdCtx); + } + + log + .info("Infobox type is AssocArray but got invalid AssocArrayParameters."); + throw new SLCommandException(4010); + + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractBinaryFileInfobox.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractBinaryFileInfobox.java new file mode 100644 index 00000000..23394bd5 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractBinaryFileInfobox.java @@ -0,0 +1,66 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadParamsBinaryFileType; +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadRequestType; + +/** + * An abstract base class for {@link Infobox} implementations of type binary file. + * + * @author mcentner + */ +public abstract class AbstractBinaryFileInfobox extends AbstractInfoboxImpl implements BinaryFileInfobox { + + /** + * Logging facility. + */ + private static Log log = LogFactory.getLog(AbstractBinaryFileInfobox.class); + + /** + * Is this infobox' content an XML entity? + */ + protected boolean isXMLEntity = false; + + /** + * @return <code>true</code> if this infobox' content is an XML entity or <code>false</code> otherwise. + */ + public boolean isXMLEntity() { + return isXMLEntity; + } + + /** + * Sets the value returned by {@link #isXMLEntity()} according to the given + * <code>request</code>. + * + * @param request the InfoboxReadRequest + */ + public void setIsXMLEntity(InfoboxReadRequestType request) { + + InfoboxReadParamsBinaryFileType binaryFileParameters = request.getBinaryFileParameters(); + if (binaryFileParameters != null) { + isXMLEntity = binaryFileParameters.isContentIsXMLEntity(); + log.debug("Got ContentIsXMLEntity=" + isXMLEntity + "."); + } + + } + + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractInfoboxCommandImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractInfoboxCommandImpl.java new file mode 100644 index 00000000..b8e4030d --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractInfoboxCommandImpl.java @@ -0,0 +1,83 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import at.gv.egiz.bku.slcommands.SLCommandContext; +import at.gv.egiz.bku.slexceptions.SLCommandException; + +/** + * An abstract base class for implementations of security layer infobox requests. + * + * @author mcentner + * + * @param <T> + */ +public abstract class AbstractInfoboxCommandImpl<T> extends SLCommandImpl<T> { + + /** + * The infobox implementation. + */ + protected Infobox infobox; + + /** + * The infobox factory. + */ + protected InfoboxFactory infoboxFactory; + + /** + * @return the infoboxFactory + */ + public InfoboxFactory getInfoboxFactory() { + return infoboxFactory; + } + + /** + * @param infoboxFactory the infoboxFactory to set + */ + public void setInfoboxFactory(InfoboxFactory infoboxFactory) { + this.infoboxFactory = infoboxFactory; + } + + @Override + public void init(SLCommandContext ctx, Object request) + throws SLCommandException { + super.init(ctx, request); + + String infoboxIdentifier = getInfoboxIdentifier(getRequestValue()); + + infobox = infoboxFactory.createInfobox(infoboxIdentifier); + } + + /** + * Returns the infobox identifier given in <code>request</code>. + * + * @param request the request value + * + * @return the infobox identifier givne in <code>request</code> + */ + protected abstract String getInfoboxIdentifier(T request); + + + public String getInfoboxIdentifier() { + if (infobox != null) { + return infobox.getIdentifier(); + } else { + return null; + } + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractInfoboxImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractInfoboxImpl.java new file mode 100644 index 00000000..564cb8ff --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AbstractInfoboxImpl.java @@ -0,0 +1,45 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package at.gv.egiz.bku.slcommands.impl; + +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadRequestType; +import at.buergerkarte.namespaces.securitylayer._1.InfoboxUpdateRequestType; +import at.gv.egiz.bku.slcommands.InfoboxReadResult; +import at.gv.egiz.bku.slcommands.InfoboxUpdateResult; +import at.gv.egiz.bku.slcommands.SLCommandContext; +import at.gv.egiz.bku.slexceptions.SLCommandException; + +/** + * An abstract base class for {@link Infobox} implementations. + * + * @author mcentner + */ +public abstract class AbstractInfoboxImpl implements Infobox { + + @Override + public InfoboxReadResult read(InfoboxReadRequestType request, + SLCommandContext cmdCtx) throws SLCommandException { + throw new SLCommandException(4011); + } + + @Override + public InfoboxUpdateResult update(InfoboxUpdateRequestType request, + SLCommandContext cmdCtx) throws SLCommandException { + throw new SLCommandException(4011); + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AssocArrayInfobox.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AssocArrayInfobox.java new file mode 100644 index 00000000..908d95da --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/AssocArrayInfobox.java @@ -0,0 +1,27 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package at.gv.egiz.bku.slcommands.impl; + +/** + * An {@link Infobox} of type associative array as defined in Security Layer + * 1.2. + * + * @author mcentner + */ +public interface AssocArrayInfobox extends Infobox { + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/BinaryFileInfobox.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/BinaryFileInfobox.java new file mode 100644 index 00000000..c27f9446 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/BinaryFileInfobox.java @@ -0,0 +1,27 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +/** + * An {@link Infobox} of type binary file as defined in Security Layer + * 1.2. + * + * @author mcentner + */ +public interface BinaryFileInfobox extends Infobox { + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CardChannelInfoboxImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CardChannelInfoboxImpl.java new file mode 100644 index 00000000..19b84ac7 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CardChannelInfoboxImpl.java @@ -0,0 +1,235 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.WeakHashMap; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.buergerkarte.namespaces.cardchannel.ATRType; +import at.buergerkarte.namespaces.cardchannel.CommandAPDUType; +import at.buergerkarte.namespaces.cardchannel.ObjectFactory; +import at.buergerkarte.namespaces.cardchannel.ResetType; +import at.buergerkarte.namespaces.cardchannel.ResponseAPDUType; +import at.buergerkarte.namespaces.cardchannel.ResponseType; +import at.buergerkarte.namespaces.cardchannel.ScriptType; +import at.buergerkarte.namespaces.cardchannel.VerifyAPDUType; +import at.buergerkarte.namespaces.securitylayer._1.Base64XMLContentType; +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadRequestType; +import at.buergerkarte.namespaces.securitylayer._1.InfoboxUpdateRequestType; +import at.buergerkarte.namespaces.securitylayer._1.XMLContentType; +import at.gv.egiz.bku.slcommands.InfoboxReadResult; +import at.gv.egiz.bku.slcommands.InfoboxUpdateResult; +import at.gv.egiz.bku.slcommands.SLCommandContext; +import at.gv.egiz.bku.slexceptions.SLCommandException; +import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import at.gv.egiz.stal.STAL; +import at.gv.egiz.stal.ext.APDUScriptRequest; +import at.gv.egiz.stal.ext.APDUScriptResponse; +import at.gv.egiz.stal.ext.APDUScriptRequest.RequestScriptElement; +import at.gv.egiz.stal.ext.APDUScriptResponse.ResponseScriptElement; + +public class CardChannelInfoboxImpl extends AbstractBinaryFileInfobox { + + private static Log log = LogFactory.getLog(CardChannelInfoboxImpl.class); + + private static WeakHashMap<STAL, JAXBElement<ResponseType>> scriptResults = new WeakHashMap<STAL, JAXBElement<ResponseType>>(); + + private static JAXBContext jaxbContext; + + static { + try { + jaxbContext = JAXBContext.newInstance(ObjectFactory.class.getPackage().getName()); + } catch (JAXBException e) { + throw new SLRuntimeException("Failed to initalize CardChannel infobox.", e); + } + } + + public CardChannelInfoboxImpl() { + isXMLEntity = true; + } + + @Override + public String getIdentifier() { + return "CardChannel"; + } + + @Override + public InfoboxReadResult read(InfoboxReadRequestType request, + SLCommandContext cmdCtx) throws SLCommandException { + + at.buergerkarte.namespaces.securitylayer._1.ObjectFactory objectFactory + = new at.buergerkarte.namespaces.securitylayer._1.ObjectFactory(); + + Base64XMLContentType content = objectFactory.createBase64XMLContentType(); + XMLContentType xmlContent = objectFactory.createXMLContentType(); + content.setXMLContent(xmlContent); + + JAXBElement<ResponseType> response = scriptResults.get(cmdCtx.getSTAL()); + if (response != null) { + xmlContent.getContent().add(response); + } + + return new InfoboxReadResultImpl(content); + + } + + @SuppressWarnings("unchecked") + @Override + public InfoboxUpdateResult update(InfoboxUpdateRequestType request, + SLCommandContext cmdCtx) throws SLCommandException { + + Base64XMLContentType binaryFileParameters = request.getBinaryFileParameters(); + + if (binaryFileParameters.getBase64Content() != null) { + log.info("Got Base64Content but ContentIsXMLEntity is true."); + throw new SLCommandException(4010); + } + + XMLContentType content = binaryFileParameters.getXMLContent(); + if (content instanceof at.gv.egiz.slbinding.impl.XMLContentType) { + + ByteArrayOutputStream redirectedStream = ((at.gv.egiz.slbinding.impl.XMLContentType) content).getRedirectedStream(); + if (redirectedStream != null) { + + if (log.isDebugEnabled()) { + + StringBuilder sb = new StringBuilder(); + sb.append("CardChannel script:\n"); + try { + sb.append(new String(redirectedStream.toByteArray(), "UTF-8")); + } catch (UnsupportedEncodingException e) { + sb.append(e.getMessage()); + } + log.debug(sb.toString()); + } + + Object object; + try { + Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); + object = unmarshaller.unmarshal(new ByteArrayInputStream(redirectedStream.toByteArray())); + } catch (JAXBException e) { + log.info("Failed to parse CardChannel script.", e); + throw new SLCommandException(4011); + } + + if (object instanceof JAXBElement) { + executeCardChannelScript(((JAXBElement<ScriptType>) object).getValue(), cmdCtx); + return new InfoboxUpdateResultImpl(); + } + + } + + + } + log.info("Infobox identifier is '" + getIdentifier() + "' but XMLContent does not contain 'Script'."); + throw new SLCommandException(4010); + + } + + protected void executeCardChannelScript(ScriptType script, + SLCommandContext cmdCtx) throws SLCommandException { + + List<Object> resetOrCommandAPDUOrVerifyAPDU = script.getResetOrCommandAPDUOrVerifyAPDU(); + List<RequestScriptElement> requestScript = new ArrayList<RequestScriptElement>(); + + for (Object element : resetOrCommandAPDUOrVerifyAPDU) { + + if (element instanceof ResetType) { + + requestScript.add(new APDUScriptRequest.Reset()); + + } else if (element instanceof CommandAPDUType) { + + CommandAPDUType commandAPDU = (CommandAPDUType) element; + int sequence = (commandAPDU.getSequence() != null) + ? commandAPDU.getSequence().intValue() + : 0; + + requestScript.add( + new APDUScriptRequest.Command( + sequence, + commandAPDU.getValue(), + commandAPDU.getExpectedSW())); + + } else if (element instanceof VerifyAPDUType) { + log.warn("CardChannel script command 'VerifyAPDU' not implemented."); + throw new SLCommandException(4011); + } + } + + APDUScriptRequest scriptRequest = new APDUScriptRequest(requestScript); + + STAL stal = cmdCtx.getSTAL(); + STALHelper helper = new STALHelper(stal); + + helper.transmitSTALRequest(Collections.singletonList(scriptRequest)); + + List<ResponseScriptElement> responseScript = ((APDUScriptResponse) helper + .nextResponse(APDUScriptResponse.class)).getScript(); + + ObjectFactory objectFactory = new ObjectFactory(); + + ResponseType responseType = objectFactory.createResponseType(); + + + for (ResponseScriptElement element : responseScript) { + + if (element instanceof APDUScriptResponse.ATR) { + + byte[] atr = ((APDUScriptResponse.ATR) element).getAtr(); + + ATRType atrType = objectFactory.createATRType(); + atrType.setValue(atr); + atrType.setRc(BigInteger.ZERO); + responseType.getATROrResponseAPDU().add(atrType); + + } else if (element instanceof APDUScriptResponse.Response) { + + APDUScriptResponse.Response response = (APDUScriptResponse.Response) element; + + ResponseAPDUType responseAPDUType = objectFactory.createResponseAPDUType(); + responseAPDUType.setSequence(BigInteger.valueOf(response.getSequence())); +// if (response.getRc() != 0) { + responseAPDUType.setRc(BigInteger.valueOf(response.getRc())); +// } + responseAPDUType.setSw(response.getSw()); + responseAPDUType.setValue(response.getApdu()); + + responseType.getATROrResponseAPDU().add(responseAPDUType); + } + + } + + scriptResults.put(stal, objectFactory.createResponse(responseType)); + } + + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CertificatesInfoboxImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CertificatesInfoboxImpl.java new file mode 100644 index 00000000..0208f137 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CertificatesInfoboxImpl.java @@ -0,0 +1,112 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.bku.slcommands.SLCommandContext; +import at.gv.egiz.bku.slexceptions.SLCommandException; +import at.gv.egiz.stal.InfoboxReadRequest; +import at.gv.egiz.stal.STALRequest; + +/** + * An implementation of the {@link Infobox} <em>Certificates</em> as + * specified in Security Layer 1.2. + * + * @author mcentner + */ +public class CertificatesInfoboxImpl extends AbstractAssocArrayInfobox { + + /** + * Logging facility. + */ + private static Log log = LogFactory.getLog(CertificatesInfoboxImpl.class); + + /** + * The valid keys. + */ + public static final String[] CERTIFICATES_KEYS = new String[] { + "SecureSignatureKeypair", + "CertifiedKeypair" }; + + @Override + public String getIdentifier() { + return "Certificates"; + } + + @Override + public String[] getKeys() { + return CERTIFICATES_KEYS; + } + + @Override + public boolean isValuesAreXMLEntities() { + return false; + } + + @Override + public Map<String, Object> getValues(List<String> certificates, SLCommandContext cmdCtx) throws SLCommandException { + + STALHelper stalHelper = new STALHelper(cmdCtx.getSTAL()); + + if (certificates != null && !certificates.isEmpty()) { + + List<STALRequest> stalRequests = new ArrayList<STALRequest>(); + + // get certificates + InfoboxReadRequest infoboxReadRequest; + for (int i = 0; i < certificates.size(); i++) { + infoboxReadRequest = new InfoboxReadRequest(); + infoboxReadRequest.setInfoboxIdentifier(certificates.get(i)); + stalRequests.add(infoboxReadRequest); + } + + stalHelper.transmitSTALRequest(stalRequests); + + List<X509Certificate> x509Certs = stalHelper.getCertificatesFromResponses(); + + Map<String, Object> values = new HashMap<String, Object>(); + + for (int i = 0; i < certificates.size(); i++) { + try { + values.put(certificates.get(i), x509Certs.get(i).getEncoded()); + } catch (CertificateEncodingException e) { + log.error("Failed to encode certificate.", e); + throw new SLCommandException(4000); + } + } + + return values; + + } else { + + return new HashMap<String, Object>(); + + } + + + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureCommandImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureCommandImpl.java new file mode 100644 index 00000000..01686641 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureCommandImpl.java @@ -0,0 +1,219 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package at.gv.egiz.bku.slcommands.impl; + +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +import javax.xml.crypto.MarshalException; +import javax.xml.crypto.URIReferenceException; +import javax.xml.crypto.dsig.XMLSignatureException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSSerializer; + +import at.buergerkarte.namespaces.securitylayer._1.CreateXMLSignatureRequestType; +import at.buergerkarte.namespaces.securitylayer._1.DataObjectInfoType; +import at.gv.egiz.bku.slcommands.CreateXMLSignatureCommand; +import at.gv.egiz.bku.slcommands.SLCommandContext; +import at.gv.egiz.bku.slcommands.SLResult; +import at.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactory; +import at.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactoryImpl; +import at.gv.egiz.bku.slcommands.impl.xsect.IdValueFactory; +import at.gv.egiz.bku.slcommands.impl.xsect.IdValueFactoryImpl; +import at.gv.egiz.bku.slcommands.impl.xsect.Signature; +import at.gv.egiz.bku.slexceptions.SLCommandException; +import at.gv.egiz.bku.slexceptions.SLException; +import at.gv.egiz.bku.slexceptions.SLRequestException; +import at.gv.egiz.bku.slexceptions.SLViewerException; +import at.gv.egiz.dom.DOMUtils; +import at.gv.egiz.stal.InfoboxReadRequest; +import at.gv.egiz.stal.STALRequest; + +/** + * This class implements the security layer command + * <code>CreateXMLSignatureRequest</code>. + * + * @author mcentner + */ +public class CreateXMLSignatureCommandImpl extends + SLCommandImpl<CreateXMLSignatureRequestType> implements + CreateXMLSignatureCommand { + + /** + * Logging facility. + */ + protected static Log log = LogFactory + .getLog(CreateXMLSignatureCommandImpl.class); + + /** + * The signing certificate. + */ + protected X509Certificate signingCertificate; + + /** + * The keybox identifier of the key used for signing. + */ + protected String keyboxIdentifier; + + /** + * The to-be signed signature. + */ + protected Signature signature; + + @Override + public void init(SLCommandContext ctx, Object unmarshalledRequest) + throws SLCommandException { + super.init(ctx, unmarshalledRequest); + } + + @Override + public void prepareXMLSignature() throws SLCommandException, + SLRequestException { + + CreateXMLSignatureRequestType request = getRequestValue(); + + // TODO: make configurable?
+ IdValueFactory idValueFactory = new IdValueFactoryImpl(); + + // TODO: make configurable?
+ AlgorithmMethodFactory algorithmMethodFactory; + try { + algorithmMethodFactory = new AlgorithmMethodFactoryImpl( + signingCertificate); + } catch (NoSuchAlgorithmException e) { + log.error("Failed to get DigestMethod.", e); + throw new SLCommandException(4006); + } + + signature = new Signature(getCmdCtx().getURLDereferencerContext(), + idValueFactory, algorithmMethodFactory); + + // SigningTime
+ signature.setSigningTime(new Date()); + + // SigningCertificate
+ signature.setSignerCeritifcate(signingCertificate); + + // SignatureInfo
+ if (request.getSignatureInfo() != null) { + signature.setSignatureInfo(request.getSignatureInfo()); + } + + // DataObjects
+ for (DataObjectInfoType dataObjectInfo : request.getDataObjectInfo()) { + signature.addDataObject(dataObjectInfo); + } + + signature.buildXMLSignature(); + + } + + /** + * Gets the signing certificate from STAL. + * + * @throws SLCommandException + * if getting the singing certificate fails + */ + private void getSigningCertificate() throws SLCommandException { + + CreateXMLSignatureRequestType request = getRequestValue(); + keyboxIdentifier = request.getKeyboxIdentifier(); + + InfoboxReadRequest stalRequest = new InfoboxReadRequest(); + stalRequest.setInfoboxIdentifier(keyboxIdentifier); + + stalHelper.transmitSTALRequest(Collections.singletonList((STALRequest) stalRequest)); + List<X509Certificate> certificates = stalHelper.getCertificatesFromResponses(); + if (certificates == null || certificates.size() != 1) { + log.info("Got an unexpected number of certificates from STAL."); + throw new SLCommandException(4000); + } + signingCertificate = certificates.get(0); + + } + + /** + * Signs the signature. + * + * @throws SLCommandException + * if signing the signature fails + * @throws SLViewerException + */ + private void signXMLSignature() throws SLCommandException, SLViewerException { + + try { + signature.sign(getCmdCtx().getSTAL(), keyboxIdentifier); + } catch (MarshalException e) { + log.error("Failed to marshall XMLSignature.", e); + throw new SLCommandException(4000); + } catch (XMLSignatureException e) { + if (e.getCause() instanceof URIReferenceException) { + URIReferenceException uriReferenceException = (URIReferenceException) e + .getCause(); + if (uriReferenceException.getCause() instanceof SLCommandException) { + throw (SLCommandException) uriReferenceException.getCause(); + } + } + log.error("Failed to sign XMLSignature.", e); + throw new SLCommandException(4000); + } + + } + + @Override + public SLResult execute() { + try { + + // get certificate in order to select appropriate algorithms for hashing + // and signing
+ getSigningCertificate(); + + // prepare the XMLSignature for signing
+ prepareXMLSignature(); + + // sign the XMLSignature
+ signXMLSignature(); + + if (log.isTraceEnabled()) { + + DOMImplementationLS domImplLS = DOMUtils.getDOMImplementationLS(); + LSSerializer serializer = domImplLS.createLSSerializer(); + String debugString = serializer.writeToString(signature.getDocument()); + + log.trace(debugString); + + } + + return new CreateXMLSignatureResultImpl(signature.getDocument()); + + } catch (SLException e) { + return new ErrorResultImpl(e, cmdCtx.getLocale()); + } + } + + @Override + public String getName() { + return "CreateXMLSignatureRequest"; + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureResultImpl.java new file mode 100644 index 00000000..19df4334 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/CreateXMLSignatureResultImpl.java @@ -0,0 +1,107 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.transform.Result; +import javax.xml.transform.Templates; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentFragment; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import at.buergerkarte.namespaces.securitylayer._1.CreateXMLSignatureResponseType; +import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory; +import at.gv.egiz.bku.slcommands.SLMarshallerFactory; +import at.gv.egiz.bku.slexceptions.SLRuntimeException; + +/** + * This calls implements the result of the security layer command <code>CreateXMLSignature</code>. + * + * @author mcentner + */ +public class CreateXMLSignatureResultImpl extends SLResultImpl { + + /** + * Logging facility. + */ + private static Log log = LogFactory.getLog(CreateXMLSignatureResultImpl.class); + + /** + * The document containing the XMLSignature. + */ + protected Document doc; + + /** + * Creates a new instance of this CreateXMLSignatureResultImpl with the given + * signature <code>document</code>. + * + * @param document the signature document + * + * @throws NullPointerException if <code>document</code> is <code>null</code> + */ + public CreateXMLSignatureResultImpl(Document document) { + super(); + + if (document == null) { + throw new NullPointerException("Argument 'document' must not be null."); + } + + this.doc = document; + + marshallCreateXMLSignatureResponse(); + } + + /** + * Marshalls the <code>CreateXMLSignatureResponse</code>. + */ + private void marshallCreateXMLSignatureResponse() { + + ObjectFactory factory = new ObjectFactory(); + + CreateXMLSignatureResponseType createCreateXMLSignatureResponseType = factory.createCreateXMLSignatureResponseType(); + JAXBElement<CreateXMLSignatureResponseType> createCreateXMLSignatureResponse = factory.createCreateXMLSignatureResponse(createCreateXMLSignatureResponseType); + + DocumentFragment fragment = doc.createDocumentFragment(); + + Marshaller marshaller = SLMarshallerFactory.getInstance().createMarshaller(false); + try { + marshaller.marshal(createCreateXMLSignatureResponse, fragment); + } catch (JAXBException e) { + log.error("Failed to marshall 'CreateXMLSignatureResponse'", e); + throw new SLRuntimeException(e); + } + + Node child = fragment.getFirstChild(); + if (child instanceof Element) { + Node node = doc.replaceChild(child, doc.getDocumentElement()); + child.appendChild(node); + } + + } + + @Override + public void writeTo(Result result, Templates templates, boolean fragment) { + writeTo(doc, result, templates, fragment); + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/DataObjectHashDataInput.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/DataObjectHashDataInput.java new file mode 100644 index 00000000..57358ba0 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/DataObjectHashDataInput.java @@ -0,0 +1,59 @@ +/* + * 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.binding.HttpUtil; +import at.gv.egiz.bku.slcommands.impl.xsect.DataObject; +import at.gv.egiz.stal.HashDataInput; +import java.io.InputStream; + +/** + * DataObject-backed HashDataInput + * If <a href="XMLSignContext.html#Supported Properties">reference caching</a> is enabled, + * the hashdata input stream can be obtained repeatedly. + * @author clemens + */ +public class DataObjectHashDataInput implements HashDataInput { + + protected DataObject dataObject; + + public DataObjectHashDataInput(DataObject dataObject) { + if (dataObject.getReference() == null) + throw new NullPointerException("DataObject reference must not be null"); + this.dataObject = dataObject; + } + + @Override + public String getReferenceId() { + return dataObject.getReference().getId(); + } + + @Override + public String getMimeType() { + String contentType = dataObject.getMimeType(); + return contentType.split(";")[0].trim(); + } + + /** + * may be called repeatedly + * @return the pre-digested input stream if reference caching is enabled, null otherwise + */ + @Override + public InputStream getHashDataInput() { + return dataObject.getReference().getDigestInputStream(); + } + + @Override + public String getEncoding() { + return HttpUtil.getCharset(dataObject.getMimeType(), false); + } + + @Override + public String getFilename() { + //TODO obtain filename from dataObject, if not set return null or get filename (extension!) from mimetype + return dataObject.getFilename(); + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImpl.java new file mode 100644 index 00000000..aedde238 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/ErrorResultImpl.java @@ -0,0 +1,67 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl;
+
+import java.util.Locale; + +import javax.xml.transform.Result; +import javax.xml.transform.Templates; + +import at.gv.egiz.bku.slcommands.ErrorResult; +import at.gv.egiz.bku.slexceptions.SLException; +
+/**
+ * This class implements the security layer result <code>ErrorResponse</code>.
+ *
+ * @author mcentner
+ */
+public class ErrorResultImpl extends SLResultImpl implements ErrorResult {
+
+ /**
+ * The exception containing information provided in the <code>ErrorResponse</code>.
+ */
+ protected SLException slException; + + /** + * The locale to be used for rendering an <code>ErrorResponse</code>. + */ + protected Locale locale; + + /** + * Creates a new instance of this ErrorResultImpl with the given + * <code>slException</code> containing information provided in the + * <code>ErrorResponse</code> and the <code>locale</code> for rendering + * the <code>ErrorResponse</code>. + * + * @param slException the exception + * @param locale the locale + */ + public ErrorResultImpl(SLException slException, Locale locale) { + this.slException = slException; + this.locale = locale; + } + + @Override + public void writeTo(Result result, Templates templates, boolean fragment) { + if (locale == null) { + writeErrorTo(slException, result, templates, fragment); + } else { + writeErrorTo(slException, result, templates, locale, fragment); + } + } +
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/GetStatusCommandImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/GetStatusCommandImpl.java new file mode 100644 index 00000000..0c2b96f9 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/GetStatusCommandImpl.java @@ -0,0 +1,77 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package at.gv.egiz.bku.slcommands.impl; + +import at.buergerkarte.namespaces.securitylayer._1.GetStatusRequestType; +import at.gv.egiz.bku.slcommands.GetStatusCommand; +import at.gv.egiz.bku.slcommands.SLResult; +import at.gv.egiz.bku.slexceptions.SLCommandException; +import at.gv.egiz.stal.ErrorResponse; +import at.gv.egiz.stal.STAL; +import at.gv.egiz.stal.STALResponse; +import at.gv.egiz.stal.StatusRequest; +import at.gv.egiz.stal.StatusResponse; +import java.util.Collections; +import java.util.List; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public class GetStatusCommandImpl extends SLCommandImpl<GetStatusRequestType> implements GetStatusCommand { + + protected static Log log = LogFactory.getLog(GetStatusCommandImpl.class); + + @Override + public String getName() { + return "GetStatusRequest"; + } + + @Override + public SLResult execute() { + + //ignore maxDelay and TokenStatus +// GetStatusRequestType req = getRequestValue(); + + log.debug("execute GetStatusRequest"); + + StatusRequest stalRequest = new StatusRequest(); + + STAL stal = cmdCtx.getSTAL(); + + List<STALResponse> responses = stal.handleRequest(Collections.singletonList(stalRequest)); + + if (responses != null && responses.size() == 1) { + STALResponse stalResponse = responses.get(0); + if (stalResponse instanceof StatusResponse) { + boolean ready = ((StatusResponse) stalResponse).isCardReady(); + log.trace("received status response cardReady: " + ready); + return new GetStatusResultImpl(ready); + } else if (stalResponse instanceof ErrorResponse) { + log.debug("received error response"); + SLCommandException ex = new SLCommandException(((ErrorResponse) stalResponse).getErrorCode()); + return new ErrorResultImpl(ex, cmdCtx.getLocale()); + } + } + log.error("received unexpected responses"); + return new ErrorResultImpl(new SLCommandException(4000), cmdCtx.getLocale()); + + } +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/GetStatusResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/GetStatusResultImpl.java new file mode 100644 index 00000000..fb1f627f --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/GetStatusResultImpl.java @@ -0,0 +1,52 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package at.gv.egiz.bku.slcommands.impl; + +import at.buergerkarte.namespaces.securitylayer._1.GetStatusResponseType; +import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory; +import at.buergerkarte.namespaces.securitylayer._1.TokenStatusType; +import at.gv.egiz.bku.slcommands.GetStatusResult; +import javax.xml.bind.JAXBElement; +import javax.xml.transform.Result; +import javax.xml.transform.Templates; + +/** + * + * @author Clemens Orthacker <clemens.orthacker@iaik.tugraz.at> + */ +public class GetStatusResultImpl extends SLResultImpl implements GetStatusResult { + + protected ObjectFactory of; + protected GetStatusResponseType responseType; + + public GetStatusResultImpl(boolean ready) { + of = new ObjectFactory(); + responseType = of.createGetStatusResponseType(); + if (ready) { + responseType.setTokenStatus(TokenStatusType.READY); + } else { + responseType.setTokenStatus(TokenStatusType.REMOVED); + } + + } + + @Override + public void writeTo(Result result, Templates templates, boolean fragment) { + JAXBElement<GetStatusResponseType> response = of.createGetStatusResponse(responseType); + writeTo(response, result, templates, fragment); + } +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/IdentityLinkInfoboxImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/IdentityLinkInfoboxImpl.java new file mode 100644 index 00000000..160e9589 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/IdentityLinkInfoboxImpl.java @@ -0,0 +1,290 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Result; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Node; + +import at.buergerkarte.namespaces.personenbindung._20020506_.CompressedIdentityLinkType; +import at.buergerkarte.namespaces.securitylayer._1.AnyChildrenType; +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadRequestType; +import at.gv.egiz.bku.slcommands.InfoboxReadResult; +import at.gv.egiz.bku.slcommands.SLCommand; +import at.gv.egiz.bku.slcommands.SLCommandContext; +import at.gv.egiz.bku.slexceptions.SLCommandException; +import at.gv.egiz.bku.slexceptions.SLExceptionMessages; +import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import at.gv.egiz.idlink.CompressedIdentityLinkFactory; +import at.gv.egiz.idlink.IdentityLinkTransformer; +import at.gv.egiz.idlink.ans1.IdentityLink; +import at.gv.egiz.stal.InfoboxReadRequest; +import at.gv.egiz.stal.STALRequest; + +/** + * An implementation of the {@link Infobox} <em>IdentityLink</em> as + * specified in Security Layer 1.2 + * + * @author mcentner + */ +public class IdentityLinkInfoboxImpl extends AbstractBinaryFileInfobox { + + /** + * Logging facility. + */ + private static Log log = LogFactory.getLog(IdentityLinkInfoboxImpl.class); + + /** + * The box specific parameter <code>IdentityLinkDomainIdentifier</code>. + */ + public static final String BOX_SPECIFIC_PARAMETER_IDENTITY_LINK_DOMAIN_IDENTIFIER = "IdentityLinkDomainIdentifier"; + + /** + * The value of the box specific parameter <code>IdentityLinkDomainIdentifier</code>. + */ + private String domainIdentifier; + + @Override + public String getIdentifier() { + return "IdentityLink"; + } + + /** + * @return the value of the box specific parameter <code>IdentityLinkDomainIdentifier</code> + */ + public String getDomainIdentifier() { + return domainIdentifier; + } + + @Override + public InfoboxReadResult read(InfoboxReadRequestType req, SLCommandContext cmdCtx) throws SLCommandException { + + AnyChildrenType boxSpecificParameters = req.getBoxSpecificParameters(); + + if (boxSpecificParameters != null) { + // check BoxSpecificParameters + List<Object> parameter = boxSpecificParameters.getAny(); + JAXBElement<?> element; + if (parameter != null + && parameter.size() == 1 + && parameter.get(0) instanceof JAXBElement<?> + && SLCommand.NAMESPACE_URI.equals((element = (JAXBElement<?>) parameter.get(0)).getName().getNamespaceURI()) + && BOX_SPECIFIC_PARAMETER_IDENTITY_LINK_DOMAIN_IDENTIFIER.equals(element.getName().getLocalPart()) + && element.getValue() instanceof String) { + domainIdentifier = (String) element.getValue(); + log.debug("Got sl:IdentityLinkDomainIdentifier: " + domainIdentifier); + } else { + log.info("Got invalid BoxSpecificParameters."); + throw new SLCommandException(4010); + } + } + + setIsXMLEntity(req); + + STALHelper stalHelper = new STALHelper(cmdCtx.getSTAL()); + + List<STALRequest> stalRequests = new ArrayList<STALRequest>(); + + InfoboxReadRequest infoboxReadRequest; + // get raw identity link + infoboxReadRequest = new InfoboxReadRequest(); + infoboxReadRequest.setInfoboxIdentifier(getIdentifier()); + infoboxReadRequest.setDomainIdentifier(domainIdentifier); + stalRequests.add(infoboxReadRequest); + + // get certificates + infoboxReadRequest = new InfoboxReadRequest(); + infoboxReadRequest.setInfoboxIdentifier("SecureSignatureKeypair"); + stalRequests.add(infoboxReadRequest); + infoboxReadRequest = new InfoboxReadRequest(); + infoboxReadRequest.setInfoboxIdentifier("CertifiedKeypair"); + stalRequests.add(infoboxReadRequest); + + stalHelper.transmitSTALRequest(stalRequests); + log.trace("Got STAL response"); + + IdentityLink identityLink = stalHelper.getIdentityLinkFromResponses(); + List<X509Certificate> certificates = stalHelper.getCertificatesFromResponses(); + + + CompressedIdentityLinkFactory idLinkFactory = CompressedIdentityLinkFactory.getInstance(); + JAXBElement<CompressedIdentityLinkType> compressedIdentityLink = idLinkFactory + .createCompressedIdentityLink(identityLink, certificates, getDomainIdentifier()); + + IdentityLinkTransformer identityLinkTransformer = IdentityLinkTransformer.getInstance(); + String issuerTemplate = identityLink.getIssuerTemplate(); + + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder db; + try { + db = dbf.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + log.error("Failed to create XML document.", e); + throw new SLRuntimeException(e); + } + + Document document = db.newDocument(); + try { + idLinkFactory.marshallCompressedIdentityLink(compressedIdentityLink, document, null, true); + } catch (JAXBException e) { + log.info("Failed to marshall CompressedIdentityLink.", e); + throw new SLCommandException(4000, + SLExceptionMessages.EC4000_UNCLASSIFIED_INFOBOX_INVALID, + new Object[] { getIdentifier() }); + } + + InfoboxReadResultFileImpl result = new InfoboxReadResultFileImpl(); + ByteArrayOutputStream resultBytes = null; + Result xmlResult; + if (isXMLEntity()) { + // we will return the result as XML entity + xmlResult = result.getXmlResult(true); + } else { + // we will return the result as binary data + if (getDomainIdentifier() != null) { + // we need an XML result to be able to replace the domain identifier below + Document doc; + try { + doc = dbf.newDocumentBuilder().newDocument(); + } catch (ParserConfigurationException e) { + // it should always be possible to create a new Document + log.error("Failed to create XML document.", e); + throw new SLRuntimeException(e); + } + xmlResult = new DOMResult(doc); + } else { + resultBytes = new ByteArrayOutputStream(); + xmlResult = new StreamResult(resultBytes); + } + } + + try { + log.trace("Trying to transform identitylink"); + identityLinkTransformer.transformIdLink(issuerTemplate, new DOMSource(document), xmlResult); + } catch (MalformedURLException e) { + log.warn("Malformed issuer template URL '" + issuerTemplate + "'."); + throw new SLCommandException(4000, + SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED, + new Object[] { issuerTemplate }); + } catch (IOException e) { + log.warn("Failed to dereferene issuer template URL '" + issuerTemplate + "'." ,e); + throw new SLCommandException(4000, + SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED, + new Object[] { issuerTemplate }); + } catch (TransformerConfigurationException e) { + log.warn("Failed to create transformation template from issuer template URL '" + issuerTemplate + "'", e); + throw new SLCommandException(4000, + SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED, + new Object[] { issuerTemplate }); + } catch (TransformerException e) { + log.info("Faild to transform CompressedIdentityLink.", e); + throw new SLCommandException(4000, + SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED, + new Object[] { issuerTemplate }); + } + + // TODO: Report BUG in IssuerTemplates + // Some IssuerTemplate stylesheets do not consider the pr:Type-Element of the CompressedIdentityLink ... + if (getDomainIdentifier() != null) { + if (xmlResult instanceof DOMResult) { + Node node = ((DOMResult) xmlResult).getNode(); + Node nextSibling = ((DOMResult) xmlResult).getNextSibling(); + Node idLinkNode; + if (nextSibling != null) { + idLinkNode = nextSibling.getPreviousSibling(); + } else if (node != null) { + idLinkNode = node.getFirstChild(); + } else { + log + .error("An IdentityLinkDomainIdentifier of '" + + getDomainIdentifier() + + "' has been given. However, it cannot be set, as the transformation result does not contain a node."); + throw new SLCommandException(4000, + SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED, + new Object[] { issuerTemplate }); + } + IdentityLinkTransformer.setDomainIdentifier(idLinkNode, getDomainIdentifier()); + } else { + log + .error("An IdentityLinkDomainIdentifier of '" + + getDomainIdentifier() + + "' has been given. However, it cannot be set, as the transformation result is not of type DOM."); + throw new SLCommandException(4000, + SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED, + new Object[] { issuerTemplate }); + } + } + + if (!isXMLEntity()) { + if (resultBytes == null) { + resultBytes = new ByteArrayOutputStream(); + if (xmlResult instanceof DOMResult) { + Node node = ((DOMResult) xmlResult).getNode(); + DOMSource xmlSource = new DOMSource(node); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + try { + Transformer transformer = transformerFactory.newTransformer(); + transformer.transform(xmlSource, new StreamResult(resultBytes)); + } catch (TransformerConfigurationException e) { + log.error(e); + throw new SLCommandException(4000, + SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED, + new Object[] { issuerTemplate }); + } catch (TransformerException e) { + log.error(e); + throw new SLCommandException(4000, + SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED, + new Object[] { issuerTemplate }); + } + } else { + log.error("ContentIsXMLEntity is set to 'false'. However, an XMLResult has already been set."); + throw new SLCommandException(4000, + SLExceptionMessages.EC4000_UNCLASSIFIED_IDLINK_TRANSFORMATION_FAILED, + new Object[] { issuerTemplate }); + } + } + result.setResultBytes(resultBytes.toByteArray()); + } + + return result; + + } + + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/Infobox.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/Infobox.java new file mode 100644 index 00000000..99d62721 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/Infobox.java @@ -0,0 +1,70 @@ +/* + * Copyright 2008 Federal Chancellery Austria and + * Graz University of Technology + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package at.gv.egiz.bku.slcommands.impl; + +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadRequestType; +import at.buergerkarte.namespaces.securitylayer._1.InfoboxUpdateRequestType; +import at.gv.egiz.bku.slcommands.InfoboxReadResult; +import at.gv.egiz.bku.slcommands.InfoboxUpdateResult; +import at.gv.egiz.bku.slcommands.SLCommandContext; +import at.gv.egiz.bku.slexceptions.SLCommandException; + +/** + * An implementation of this interface represents a infobox as defined in + * Security-Layer 1.2. + * + * @author mcentner + */ +public interface Infobox { + + /** + * @return the identifier of this infobox + */ + public String getIdentifier(); + + /** + * Read data from this infobox. + * + * @param request + * the InfoboxReadRequest + * @param cmdCtx + * the command context + * + * @return the data read from this infobox as InfoboxReadResult + * + * @throws SLCommandException + * + * if reading from this infobox fails + */ + public InfoboxReadResult read(InfoboxReadRequestType request, + SLCommandContext cmdCtx) throws SLCommandException; + + /** + * Update data in this infobox. + * + * @param request + * the InfoboxUpdateRequest + * @param cmdCtx + * the command context + * @return a corresponding InfoboxUpdateResult + * @throws SLCommandException + * if updating this infobox fails + */ + public InfoboxUpdateResult update(InfoboxUpdateRequestType request, + SLCommandContext cmdCtx) throws SLCommandException; + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxFactory.java new file mode 100644 index 00000000..fdf94297 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxFactory.java @@ -0,0 +1,90 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import java.util.HashMap; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.bku.slexceptions.SLCommandException; +import at.gv.egiz.bku.slexceptions.SLExceptionMessages; +import at.gv.egiz.bku.slexceptions.SLRuntimeException; + +/** + * A factory for creating {@link Infobox}es. + * + * @author mcentner + */ +public class InfoboxFactory { + + /** + * Logging facility. + */ + private static Log log = LogFactory.getLog(InfoboxFactory.class); + + /** + * The mapping of Infobox name to concrete Infobox factory. + */ + private HashMap<String, AbstractInfoboxFactory> infoboxFactories = new HashMap<String, AbstractInfoboxFactory>(); + + /** + * @param infoboxFactories the infoboxFactories to set + */ + public void setInfoboxFactories( + HashMap<String, AbstractInfoboxFactory> factories) { + if (log.isDebugEnabled()) { + StringBuilder sb = new StringBuilder(); + sb.append("Registered infobox factories for"); + for (String name : factories.keySet()) { + sb.append("\n " + name + " : " + factories.get(name).getClass()); + } + log.debug(sb); + } + this.infoboxFactories = factories; + } + + /** + * Create a new {@link Infobox} instance for the given + * <code>infoboxIdentifier</code>. + * + * @param infoboxIdentifier + * the infobox identifier + * + * @return an {@link Infobox} implementation for the given infobox identifier + * + * @throws SLCommandException + * if there is no implementation for the given infobox identifier + * @throws SLRuntimeException + * if creating an {@link Infobox} instance fails + */ + public Infobox createInfobox(String infoboxIdentifier) throws SLCommandException, SLRuntimeException { + + AbstractInfoboxFactory factory = infoboxFactories.get(infoboxIdentifier); + if (factory == null) { + log.info("Unsupported infobox '" + infoboxIdentifier + "."); + throw new SLCommandException(4002, + SLExceptionMessages.EC4002_INFOBOX_UNKNOWN, + new Object[] { infoboxIdentifier }); + } + + return factory.createInfobox(); + + } + + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadCommandImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadCommandImpl.java new file mode 100644 index 00000000..693f444f --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadCommandImpl.java @@ -0,0 +1,96 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl;
+
+import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadRequestType; +import at.gv.egiz.bku.slcommands.InfoboxReadCommand; +import at.gv.egiz.bku.slcommands.SLCommandContext; +import at.gv.egiz.bku.slcommands.SLResult; +import at.gv.egiz.bku.slexceptions.SLCommandException; +
+/**
+ * This class implements the security layer command
+ * <code>InfoboxReadRequest</code>.
+ * <p>
+ * <b>NOTE:</b> Currently the only supported infobox identifier is '
+ * <code>IdentityLink</code>'.
+ * </p>
+ *
+ * @author mcentner
+ */
+public class InfoboxReadCommandImpl extends AbstractInfoboxCommandImpl<InfoboxReadRequestType> implements
+ InfoboxReadCommand {
+
+ /**
+ * Logging facility.
+ */
+ protected static Log log = LogFactory.getLog(InfoboxReadCommandImpl.class); + + @Override
+ public String getName() {
+ return "InfoboxReadRequest";
+ }
+
+ @Override + protected String getInfoboxIdentifier(InfoboxReadRequestType request) { + return request.getInfoboxIdentifier(); + } + + @Override
+ public void init(SLCommandContext ctx, Object request) throws SLCommandException {
+ super.init(ctx, request);
+
+ InfoboxReadRequestType req = getRequestValue();
+ + if (req.getAssocArrayParameters() != null && + !(infobox instanceof AssocArrayInfobox)) { + log.info("Got AssocArrayParameters but Infobox type is not AssocArray."); + throw new SLCommandException(4010); + } + + if (req.getBinaryFileParameters() != null && + !(infobox instanceof BinaryFileInfobox)) { + log.info("Got BinaryFileParameters but Infobox type is not BinaryFile."); + throw new SLCommandException(4010); + } +
+ }
+
+ @Override
+ public SLResult execute() { + + try { + return infobox.read(getRequestValue(), getCmdCtx()); + } catch (SLCommandException e) { + return new ErrorResultImpl(e, getCmdCtx().getLocale()); + } + + }
+
+ @Override + public String getIdentityLinkDomainId() { + if (infobox instanceof IdentityLinkInfoboxImpl) { + return ((IdentityLinkInfoboxImpl) infobox).getDomainIdentifier(); + } else { + return null; + } + } + +}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultFileImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultFileImpl.java new file mode 100644 index 00000000..422b424f --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultFileImpl.java @@ -0,0 +1,160 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.Result; +import javax.xml.transform.Templates; +import javax.xml.transform.dom.DOMResult; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import at.buergerkarte.namespaces.securitylayer._1.Base64XMLContentType; +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadResponseType; +import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory; +import at.buergerkarte.namespaces.securitylayer._1.XMLContentType; +import at.gv.egiz.bku.slcommands.InfoboxReadResult; +import at.gv.egiz.bku.slcommands.SLCommand; +import at.gv.egiz.bku.slcommands.SLMarshallerFactory; +import at.gv.egiz.bku.slexceptions.SLRuntimeException; + +/** + * This class implements the result of the security layer command <code>InfoboxReadRequest</code>. + * + * @author mcentner + */ +public class InfoboxReadResultFileImpl extends SLResultImpl implements + InfoboxReadResult { + + /** + * Logging facility. + */ + protected static Log log = LogFactory.getLog(InfoboxReadResultFileImpl.class); + + /** + * The XML document containing the infobox content. + */ + protected Document xmlDocument; + + /** + * Creates the response document from the given <code>binaryContent</code>. + * + * @param binaryContent the infobox content + * @param preserveSpace the value of the <code>preserveSpace</code> parameter + * + * @return the created response document + */ + private Document createResponseDocument(byte[] binaryContent, boolean preserveSpace) { + + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + Document doc; + try { + doc = dbf.newDocumentBuilder().newDocument(); + } catch (ParserConfigurationException e) { + // it should always be possible to create a new Document + log.error("Failed to create XML document.", e); + throw new SLRuntimeException(e); + } + + ObjectFactory factory = new ObjectFactory(); + + Base64XMLContentType base64XMLContentType = factory.createBase64XMLContentType(); + if (binaryContent == null) { + XMLContentType xmlContentType = factory.createXMLContentType(); + if (preserveSpace) { + xmlContentType.setSpace("preserve"); + } + base64XMLContentType.setXMLContent(xmlContentType); + } else { + base64XMLContentType.setBase64Content(binaryContent); + } + InfoboxReadResponseType infoboxReadResponseType = factory.createInfoboxReadResponseType(); + infoboxReadResponseType.setBinaryFileData(base64XMLContentType); + + JAXBElement<InfoboxReadResponseType> infoboxReadResponse = factory.createInfoboxReadResponse(infoboxReadResponseType); + + Marshaller marshaller = SLMarshallerFactory.getInstance().createMarshaller(false); + try { + marshaller.marshal(infoboxReadResponse, doc); + } catch (JAXBException e) { + log.error("Failed to marshal 'InfoboxReadResponse' document.", e); + throw new SLRuntimeException(e); + } + + return doc; + + } + + + /** + * @return an XMLResult for marshalling the infobox to + */ + public Result getXmlResult(boolean preserveSpace) { + + xmlDocument = createResponseDocument(null, preserveSpace); + + NodeList nodeList = xmlDocument.getElementsByTagNameNS(SLCommand.NAMESPACE_URI, "XMLContent"); + return new DOMResult(nodeList.item(0)); + + } + + /** + * Creates a new <code>InfoboxReadResponse</code> document and appends + * the given <code>node</code> as child node of the <code>XMLContent</code> element. + * + * @param node the node to be appended as child node of the <code>XMLContnet</code> element + * @param preserveSpace if <code>true</code> the value of the <code>XMLContent</code>'s <code>space</code> + * attribute is set to <code>preserve</code>. + */ + public void setResultXMLContent(Node node, boolean preserveSpace) { + + xmlDocument = createResponseDocument(null, preserveSpace); + + NodeList nodeList = xmlDocument.getElementsByTagNameNS(SLCommand.NAMESPACE_URI, "XMLContent"); + if (node.getOwnerDocument() != xmlDocument) { + node = xmlDocument.importNode(node, true); + } + nodeList.item(0).appendChild(node); + + } + + /** + * Creates a new result document for this <code>InfoboxReadResult</code> + * and sets the given <code>resultBytes</code> as content. + * + * @param resultBytes + */ + public void setResultBytes(byte[] resultBytes) { + + xmlDocument = createResponseDocument(resultBytes, false); + + } + + @Override + public void writeTo(Result result, Templates templates, boolean fragment) { + writeTo(xmlDocument, result, templates, fragment); + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultImpl.java new file mode 100644 index 00000000..271ec955 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxReadResultImpl.java @@ -0,0 +1,64 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import javax.xml.bind.JAXBElement; +import javax.xml.transform.Result; +import javax.xml.transform.Templates; + +import at.buergerkarte.namespaces.securitylayer._1.Base64XMLContentType; +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadDataAssocArrayType; +import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadResponseType; +import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory; +import at.gv.egiz.bku.slcommands.InfoboxReadResult; + +public class InfoboxReadResultImpl extends SLResultImpl implements InfoboxReadResult { + + /** + * The <code>InfoboxReadResponse</code> + */ + protected InfoboxReadResponseType infoboxReadResponse; + + public InfoboxReadResultImpl(InfoboxReadDataAssocArrayType assocArray) { + + ObjectFactory objectFactory = new ObjectFactory(); + InfoboxReadResponseType infoboxReadResponseType = objectFactory.createInfoboxReadResponseType(); + + infoboxReadResponseType.setAssocArrayData(assocArray); + + this.infoboxReadResponse = infoboxReadResponseType; + } + + public InfoboxReadResultImpl(Base64XMLContentType value) { + + ObjectFactory objectFactory = new ObjectFactory(); + InfoboxReadResponseType infoboxReadResponseType = objectFactory.createInfoboxReadResponseType(); + + infoboxReadResponseType.setBinaryFileData(value); + + this.infoboxReadResponse = infoboxReadResponseType; + + } + + @Override + public void writeTo(Result result, Templates templates, boolean fragment) { + ObjectFactory objectFactory = new ObjectFactory(); + JAXBElement<InfoboxReadResponseType> response = objectFactory.createInfoboxReadResponse(infoboxReadResponse); + writeTo(response, result, templates, fragment); + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxUpdateCommandImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxUpdateCommandImpl.java new file mode 100644 index 00000000..1cdeda94 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxUpdateCommandImpl.java @@ -0,0 +1,74 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.buergerkarte.namespaces.securitylayer._1.InfoboxUpdateRequestType; +import at.gv.egiz.bku.slcommands.InfoboxUpdateCommand; +import at.gv.egiz.bku.slcommands.SLCommandContext; +import at.gv.egiz.bku.slcommands.SLResult; +import at.gv.egiz.bku.slexceptions.SLCommandException; + +public class InfoboxUpdateCommandImpl extends + AbstractInfoboxCommandImpl<InfoboxUpdateRequestType> implements InfoboxUpdateCommand { + + private static Log log = LogFactory.getLog(InfoboxUpdateCommandImpl.class); + + @Override + public String getName() { + return "InfoboxUpdateRequest"; + } + + @Override + protected String getInfoboxIdentifier(InfoboxUpdateRequestType request) { + return request.getInfoboxIdentifier(); + } + + @Override + public void init(SLCommandContext ctx, Object request) throws SLCommandException { + super.init(ctx, request); + + InfoboxUpdateRequestType req = getRequestValue(); + + if (req.getAssocArrayParameters() != null && + !(infobox instanceof AssocArrayInfobox)) { + log.info("Got AssocArrayParameters but Infobox type is not AssocArray."); + throw new SLCommandException(4010); + } + + if (req.getBinaryFileParameters() != null && + !(infobox instanceof BinaryFileInfobox)) { + log.info("Got BinaryFileParameters but Infobox type is not BinaryFile."); + throw new SLCommandException(4010); + } + + } + + @Override + public SLResult execute() { + + try { + return infobox.update(getRequestValue(), getCmdCtx()); + } catch (SLCommandException e) { + return new ErrorResultImpl(e, getCmdCtx().getLocale()); + } + + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxUpdateResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxUpdateResultImpl.java new file mode 100644 index 00000000..e12536ba --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/InfoboxUpdateResultImpl.java @@ -0,0 +1,43 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import javax.xml.bind.JAXBElement; +import javax.xml.transform.Result; +import javax.xml.transform.Templates; + +import at.buergerkarte.namespaces.securitylayer._1.InfoboxUpdateResponseType; +import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory; +import at.gv.egiz.bku.slcommands.InfoboxUpdateResult; + +public class InfoboxUpdateResultImpl extends SLResultImpl implements + InfoboxUpdateResult { + + protected static JAXBElement<InfoboxUpdateResponseType> RESPONSE; + + static { + ObjectFactory factory = new ObjectFactory(); + InfoboxUpdateResponseType type = factory.createInfoboxUpdateResponseType(); + RESPONSE = factory.createInfoboxUpdateResponse(type); + } + + @Override + public void writeTo(Result result, Templates templates, boolean fragment) { + writeTo(RESPONSE, result, templates, fragment); + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationCommandImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationCommandImpl.java new file mode 100644 index 00000000..1b6fb237 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationCommandImpl.java @@ -0,0 +1,43 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import at.buergerkarte.namespaces.securitylayer._1.NullOperationRequestType; +import at.gv.egiz.bku.slcommands.NullOperationCommand; +import at.gv.egiz.bku.slcommands.NullOperationResult; +import at.gv.egiz.bku.slcommands.SLResult; + +/** + * This class implements the security layer command <code>NullOperation</code>. + * + * @author mcentner + */ +public class NullOperationCommandImpl extends SLCommandImpl<NullOperationRequestType> implements NullOperationCommand { + + protected static NullOperationResult RESULT = new NullOperationResultImpl(); + + @Override + public SLResult execute() { + return RESULT; + } + + @Override + public String getName() { + return "NullOperationRequest"; + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationResultImpl.java new file mode 100644 index 00000000..87733e39 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/NullOperationResultImpl.java @@ -0,0 +1,48 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import javax.xml.bind.JAXBElement; +import javax.xml.transform.Result; +import javax.xml.transform.Templates; + +import at.buergerkarte.namespaces.securitylayer._1.NullOperationResponseType; +import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory; +import at.gv.egiz.bku.slcommands.NullOperationResult; + +/** + * This class represents the result of the security layer command + * <code>NullOperation</code>. + * + * @author mcentner + */ +public class NullOperationResultImpl extends SLResultImpl implements NullOperationResult { + + protected static JAXBElement<NullOperationResponseType> RESPONSE; + + static { + ObjectFactory factory = new ObjectFactory(); + NullOperationResponseType type = factory.createNullOperationResponseType(); + RESPONSE = factory.createNullOperationResponse(type); + } + + @Override + public void writeTo(Result result, Templates templates, boolean fragment) { + super.writeTo(RESPONSE, result, templates, fragment); + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLCommandImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLCommandImpl.java new file mode 100644 index 00000000..ed055b69 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLCommandImpl.java @@ -0,0 +1,81 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl;
+
+import javax.xml.bind.JAXBElement; + +import at.gv.egiz.bku.slcommands.SLCommand; +import at.gv.egiz.bku.slcommands.SLCommandContext; +import at.gv.egiz.bku.slexceptions.SLCommandException; +
+/**
+ * This class serves as abstract base class for the implementation of a security
+ * layer command.
+ *
+ * @author mcentner
+ *
+ * @param <T>
+ * the type of the corresponding request value
+ */
+public abstract class SLCommandImpl<T> implements SLCommand {
+
+ /**
+ * The <code>SLCommandContext</code> for this <code>SLCommand</code>.
+ */
+ protected SLCommandContext cmdCtx; + + /** + * The STAL helper. + */ + protected STALHelper stalHelper;
+
+ /**
+ * The request element of this command.
+ */
+ protected JAXBElement<T> request;
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void init(SLCommandContext ctx, Object request)
+ throws SLCommandException {
+
+ this.request = (JAXBElement<T>) request;
+
+ this.cmdCtx = ctx; + stalHelper = new STALHelper(cmdCtx.getSTAL());
+
+ }
+
+ /**
+ * Returns the request value.
+ *
+ * It is a convenience method for <code>request.getValue()</code>.
+ *
+ * @see JAXBElement#getValue()
+ * @return the request value
+ */
+ protected T getRequestValue() {
+ return request.getValue();
+ }
+
+ /**
+ * @return the corresponding <code>SLCommandContext</code>
+ */
+ protected SLCommandContext getCmdCtx() {
+ return cmdCtx;
+ }
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLResultImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLResultImpl.java new file mode 100644 index 00000000..0077b7b2 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SLResultImpl.java @@ -0,0 +1,297 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.util.Locale; + +import javax.xml.XMLConstants; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Result; +import javax.xml.transform.Templates; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerConfigurationException; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.TransformerFactoryConfigurationError; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.sax.SAXTransformerFactory; +import javax.xml.transform.sax.TransformerHandler; +import javax.xml.transform.stream.StreamResult; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.Node; + +import at.buergerkarte.namespaces.securitylayer._1.ErrorResponseType; +import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory; +import at.gv.egiz.bku.slcommands.SLMarshallerFactory; +import at.gv.egiz.bku.slcommands.SLResult; +import at.gv.egiz.bku.slexceptions.SLBindingException; +import at.gv.egiz.bku.slexceptions.SLCommandException; +import at.gv.egiz.bku.slexceptions.SLException; +import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import at.gv.egiz.bku.slexceptions.SLVersionException; +import at.gv.egiz.bku.utils.DebugOutputStream; +import at.gv.egiz.bku.utils.DebugWriter; + +/** + * This class serves as an abstract base class for the implementation of a + * security layer result. + * + * @author mcentner + */ +public abstract class SLResultImpl implements SLResult { + + /** + * Logging facility. + */ + private static Log log = LogFactory.getLog(SLResult.class); + + /** + * The security layer result type (default = XML). + */ + protected SLResultType resultType = SLResultType.XML; + + /** + * The security layer result MIME-type (default = <code>text/xml</code>). + */ + protected String resultingMimeType = "text/xml"; + + /* (non-Javadoc) + * @see at.gv.egiz.bku.slcommands.SLResult#getResultType() + */ + public SLResultType getResultType() { + return resultType; + } + + /* (non-Javadoc) + * @see at.gv.egiz.bku.slcommands.SLResult#getMimeType() + */ + public String getMimeType() { + return resultingMimeType; + } + + @Override + public void writeTo(Result result, boolean fragment) { + writeTo(result, null, false); + } + + @Override + public abstract void writeTo(Result result, Templates templates, boolean fragment); + + private TransformerHandler getTransformerHandler(Templates templates, Result result) throws SLException { + try { + SAXTransformerFactory transformerFactory = (SAXTransformerFactory) SAXTransformerFactory.newInstance(); + transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + TransformerHandler transformerHandler = transformerFactory.newTransformerHandler(templates); + transformerHandler.setResult(result); + return transformerHandler; + } catch (TransformerFactoryConfigurationError e) { + log.error("Failed to create an instance of SAXTransformerFactory.", e); + throw new SLBindingException(2000); + } catch (IllegalArgumentException e) { + log.error("Failed to set result for transformation.", e); + throw new SLBindingException(2000); + } catch (TransformerConfigurationException e) { + log.info("Failed to create an instance of SAXTransformerFactory.", e); + throw new SLBindingException(2008); + } + } + + /** + * Writes the given <code>response</code> to the SAX <code>result</code> using + * the given transform <code>templates</code>. + * + * @param response + * @param result + * @param templates + */ + protected void writeTo(JAXBElement<?> response, Result result, Templates templates, boolean fragment) { + + DebugWriter dw = null; + DebugOutputStream ds = null; + if (log.isTraceEnabled() && result instanceof StreamResult) { + StreamResult streamResult = (StreamResult) result; + if (streamResult.getOutputStream() != null) { + ds = new DebugOutputStream(streamResult.getOutputStream()); + streamResult.setOutputStream(ds); + } + if (streamResult.getWriter() != null) { + dw = new DebugWriter(streamResult.getWriter()); + streamResult.setWriter(dw); + } + } + + TransformerHandler transformerHandler = null; + if (templates != null) { + try { + transformerHandler = getTransformerHandler(templates, result); + } catch (SLException e) { + writeErrorTo(e, result, templates, fragment); + } + } + + Marshaller marshaller = SLMarshallerFactory.getInstance().createMarshaller(true); + try { + if (transformerHandler != null) { + marshaller.marshal(response, transformerHandler); + } else { + marshaller.marshal(response, result); + } + } catch (JAXBException e) { + log.info("Failed to marshall " + response.getName() + " result." , e); + SLCommandException commandException = new SLCommandException(4000); + writeErrorTo(commandException, result, templates, fragment); + } + + if (ds != null) { + try { + log.trace("Marshalled result:\n" + new String(ds.getBufferedBytes(), "UTF-8")); + } catch (UnsupportedEncodingException e) { + log.trace(e.getMessage()); + } + } + + if (dw != null) { + log.trace("Marshalled result:\n" + dw.getBufferedString()); + } + + } + + protected void writeTo(Node node, Result result, Templates templates, boolean fragment) { + + DebugWriter dw = null; + DebugOutputStream ds = null; + if (log.isTraceEnabled() && result instanceof StreamResult) { + StreamResult streamResult = (StreamResult) result; + if (streamResult.getOutputStream() != null) { + ds = new DebugOutputStream(streamResult.getOutputStream()); + streamResult.setOutputStream(ds); + } + if (streamResult.getWriter() != null) { + dw = new DebugWriter(streamResult.getWriter()); + streamResult.setWriter(dw); + } + } + + if (templates == null) { + try { + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + if (fragment) { + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + } + transformer.transform(new DOMSource(node), result); + } catch (TransformerConfigurationException e) { + log.error("Failed to create Transformer.", e); + writeErrorTo(new SLException(4000), result, null, fragment); + } catch (TransformerException e) { + log.error("Failed to transform result.", e); + writeErrorTo(new SLException(4000), result, null, fragment); + } + } else { + try { + Transformer transformer = templates.newTransformer(); + if (fragment) { + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + } + transformer.transform(new DOMSource(node), result); + } catch (TransformerConfigurationException e) { + log.info("Failed to create transformer.", e); + writeErrorTo(new SLException(2008), result, templates, fragment); + } catch (TransformerException e) { + log.error("Failed to transform result.", e); + writeErrorTo(new SLException(2008), result, templates, fragment); + } + } + + if (ds != null) { + try { + log.trace("Marshalled result:\n" + new String(ds.getBufferedBytes(), "UTF-8")); + } catch (UnsupportedEncodingException e) { + log.trace(e.getMessage()); + } + } + + if (dw != null) { + log.trace("Marshalled result:\n" + dw.getBufferedString()); + } + + } + + protected void writeErrorTo(SLException slException, Result result, Templates templates, boolean fragment) { + writeErrorTo(slException, result, templates, Locale.getDefault(), fragment); + } + + protected void writeErrorTo(SLException slException, Result result, Templates templates, Locale locale, boolean fragment) { + + TransformerHandler transformerHandler = null; + if (templates != null) { + try { + transformerHandler = getTransformerHandler(templates, result); + } catch (SLException e) { + // write the exception thrown instead of the given one + slException = e; + } + } + + Object response; + + Marshaller marshaller; + if (slException instanceof SLVersionException + && ("http://www.buergerkarte.at/namespaces/securitylayer/20020225#" + .equals(((SLVersionException) slException).getNamespaceURI()) || + "http://www.buergerkarte.at/namespaces/securitylayer/20020831#" + .equals(((SLVersionException) slException).getNamespaceURI()))) { + // issue ErrorResponse in the legacy namespace + at.buergerkarte.namespaces.securitylayer._20020225_.ObjectFactory factory + = new at.buergerkarte.namespaces.securitylayer._20020225_.ObjectFactory(); + at.buergerkarte.namespaces.securitylayer._20020225_.ErrorResponseType errorResponseType = factory + .createErrorResponseType(); + errorResponseType.setErrorCode(BigInteger.valueOf(slException + .getErrorCode())); + errorResponseType.setInfo(slException.getLocalizedMessage(locale)); + response = factory.createErrorResponse(errorResponseType); + marshaller = SLMarshallerFactory.getInstance().createLegacyMarshaller(true, fragment); + } else { + ObjectFactory factory = new ObjectFactory(); + ErrorResponseType responseType = factory.createErrorResponseType(); + responseType.setErrorCode(slException.getErrorCode()); + responseType.setInfo(slException.getLocalizedMessage(locale)); + response = factory.createErrorResponse(responseType); + marshaller = SLMarshallerFactory.getInstance().createMarshaller(true, fragment); + } + + try { + if (transformerHandler != null) { + marshaller.marshal(response, transformerHandler); + } else { + marshaller.marshal(response, result); + } + } catch (JAXBException e) { + log.fatal("Failed to marshall error result." , e); + throw new SLRuntimeException("Failed to marshall error result."); + } + + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/STALHelper.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/STALHelper.java new file mode 100644 index 00000000..e903c608 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/STALHelper.java @@ -0,0 +1,242 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import iaik.asn1.CodingException; +import iaik.asn1.DerCoder; +import iaik.utils.Base64OutputStream; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.StringWriter; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.bku.slexceptions.SLCommandException; +import at.gv.egiz.bku.slexceptions.SLExceptionMessages; +import at.gv.egiz.bku.slexceptions.SLRuntimeException; +import at.gv.egiz.idlink.ans1.IdentityLink; +import at.gv.egiz.stal.ErrorResponse; +import at.gv.egiz.stal.InfoboxReadResponse; +import at.gv.egiz.stal.STAL; +import at.gv.egiz.stal.STALRequest; +import at.gv.egiz.stal.STALResponse; + +/** + * A helper class for transmitting {@link STALRequest}s and obtaining their + * respective {@link STALResponse}s. + * + * @author mcentner + */ +public class STALHelper { + + /** + * Logging facility. + */ + private static Log log = LogFactory.getLog(STALHelper.class); + + /** + * The STAL implementation. + */ + private STAL stal; + + /** + * An iterator over the <code>STALResponse</code>s received in + * {@link SLCommandImpl#transmitSTALRequest(List)}. + */ + protected Iterator<STALResponse> stalResponses; + + /** + * Creates a new instance of this STALHelper with the given + * <code>stal</code>. + * + * @param stal the STAL to be used + */ + public STALHelper(STAL stal) { + if (stal == null) { + throw new NullPointerException("Argument 'stal' must not be null."); + } + this.stal = stal; + } + + /** + * Calls {@link STAL#handleRequest(List)} with the given + * <code>stalRequests</code>. + * + * @param stalRequests + * @throws SLCommandException + */ + public void transmitSTALRequest(List<? extends STALRequest> stalRequests) throws SLCommandException { + List<STALResponse> responses = stal.handleRequest(stalRequests); + if (responses == null) { + Log log = LogFactory.getLog(this.getClass()); + log.info("Received no responses from STAL."); + throw new SLCommandException(4000); + } else if (responses.size() != stalRequests.size()) { + Log log = LogFactory.getLog(this.getClass()); + log.info("Received invalid count of responses from STAL. Expected " + + stalRequests.size() + ", but got " + responses.size() + "."); + // throw new SLCommandException(4000); + } + stalResponses = responses.iterator(); + } + + /** + * @return <code>true</code> if there are more {@link STALResponse}s to be + * fetched with {@link #nextResponse(Class)}, or <code>false</code> + * otherwise. + */ + public boolean hasNextResponse() { + return (stalResponses != null) ? stalResponses.hasNext() : false; + } + + /** + * Returns the next response of type <code>responseClass</code> that has been + * received by {@link #transmitSTALRequest(List)}. + * + * @param responseClass + * the response must be an instance of + * @return the next response of type <code>responseClass</code> + * + * @throws NoSuchElementException + * if there is no more response + * @throws SLCommandException + * if the next response is of type {@link ErrorResponse} or not of + * type <code>responseClass</code> + */ + public STALResponse nextResponse( + Class<? extends STALResponse> responseClass) throws SLCommandException { + + if (stalResponses == null) { + throw new NoSuchElementException(); + } + + STALResponse response = stalResponses.next(); + + if (response instanceof ErrorResponse) { + throw new SLCommandException(((ErrorResponse) response).getErrorCode()); + } + + if (!(responseClass.isAssignableFrom(response.getClass()))) { + Log log = LogFactory.getLog(this.getClass()); + log.info("Received " + response.getClass() + " from STAL but expected " + + responseClass); + throw new SLCommandException(4000); + } + + return response; + + } + + /** + * Gets the list of certificates from the next STAL responses. + * + * @return the list of certificates + * + * @throws SLCommandException if getting the list of certificates fails + */ + public List<X509Certificate> getCertificatesFromResponses() throws SLCommandException { + + List<X509Certificate> certificates = new ArrayList<X509Certificate>(); + + CertificateFactory certFactory; + try { + certFactory = CertificateFactory.getInstance("X509"); + } catch (CertificateException e) { + // we should always be able to get an X509 certificate factory + log.error("CertificateFactory.getInstance(\"X509\") failed.", e); + throw new SLRuntimeException(e); + } + + InfoboxReadResponse response; + while(hasNextResponse()) { + response = (InfoboxReadResponse) nextResponse(InfoboxReadResponse.class); + byte[] cert = response.getInfoboxValue(); + try { + certificates.add((X509Certificate) certFactory.generateCertificate(new ByteArrayInputStream(cert))); + } catch (CertificateException e) { + if (log.isDebugEnabled()) { + ByteArrayOutputStream certDump = new ByteArrayOutputStream(); + OutputStreamWriter writer = new OutputStreamWriter(certDump); + try { + writer.write("-----BEGIN CERTIFICATE-----\n"); + writer.flush(); + Base64OutputStream b64os = new Base64OutputStream(certDump); + b64os.write(cert); + b64os.flush(); + writer.write("\n-----END CERTIFICATE-----"); + writer.flush(); + } catch (IOException e1) { + log.info("Failed to decode certificate.", e); + } + log.debug("Failed to decode certificate.\n" + certDump.toString(), e); + } else { + log.info("Failed to decode certificate.", e); + } + throw new SLCommandException(4000, + SLExceptionMessages.EC4000_UNCLASSIFIED_INFOBOX_INVALID, + new Object[] { "Certificates" }); + } + } + + return certificates; + + } + + /** + * Gets the IdentitiyLink form the next STAL response. + * + * @return the IdentityLink + * + * @throws SLCommandException if getting the IdentitiyLink fails + */ + public IdentityLink getIdentityLinkFromResponses() throws SLCommandException { + + // IdentityLink + InfoboxReadResponse response; + if (hasNextResponse()) { + response = (InfoboxReadResponse) nextResponse(InfoboxReadResponse.class); + byte[] idLink = response.getInfoboxValue(); + try { + return new IdentityLink(DerCoder.decode(idLink)); + } catch (CodingException e) { + log.info("Failed to decode infobox 'IdentityLink'.", e); + throw new SLCommandException(4000, + SLExceptionMessages.EC4000_UNCLASSIFIED_INFOBOX_INVALID, + new Object[] { "IdentityLink" }); + } + } else { + log.info("No infobox 'IdentityLink' returned from STAL."); + throw new SLCommandException(4000); + } + + } + + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SVPersonendatenInfoboxImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SVPersonendatenInfoboxImpl.java new file mode 100644 index 00000000..7e204632 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/SVPersonendatenInfoboxImpl.java @@ -0,0 +1,323 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl; + +import iaik.asn1.ASN; +import iaik.asn1.ASN1Object; +import iaik.asn1.CodingException; +import iaik.asn1.DerCoder; +import iaik.asn1.NumericString; +import iaik.asn1.OCTET_STRING; +import iaik.asn1.ObjectID; +import iaik.asn1.SEQUENCE; +import iaik.asn1.SET; +import iaik.asn1.UNKNOWN; +import iaik.asn1.structures.ChoiceOfTime; + +import java.io.IOException; +import java.math.BigInteger; +import java.nio.charset.Charset; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; + +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.buergerkarte.namespaces.cardchannel.AttributeList; +import at.buergerkarte.namespaces.cardchannel.AttributeType; +import at.buergerkarte.namespaces.cardchannel.ObjectFactory; +import at.gv.egiz.bku.slcommands.SLCommandContext; +import at.gv.egiz.bku.slexceptions.SLCommandException; +import at.gv.egiz.bku.slexceptions.SLExceptionMessages; +import at.gv.egiz.stal.InfoboxReadRequest; +import at.gv.egiz.stal.InfoboxReadResponse; +import at.gv.egiz.stal.STALRequest; + +/** + * An implementation of the {@link Infobox} <em>Certificates</em> as + * specified in Security Layer 1.2. + * + * @author mcentner + */ +public class SVPersonendatenInfoboxImpl extends AbstractAssocArrayInfobox { + + /** + * Logging facility. + */ + private static Log log = LogFactory.getLog(SVPersonendatenInfoboxImpl.class); + + public static final String EHIC = "EHIC"; + + public static final String GRUNDDATEN = "Grunddaten"; + + public static final String STATUS = "Status"; + + public static final String SV_PERSONENBINDUNG = "SV-Personenbindung"; + + /** + * The valid keys. + */ + public static final String[] KEYS = new String[] { + GRUNDDATEN, EHIC, STATUS, SV_PERSONENBINDUNG + }; + + @Override + public String getIdentifier() { + return "SV-Personendaten"; + } + + @Override + public String[] getKeys() { + return KEYS; + } + + @Override + public boolean isValuesAreXMLEntities() { + return true; + } + + @Override + public Map<String, Object> getValues(List<String> keys, SLCommandContext cmdCtx) throws SLCommandException { + + STALHelper stalHelper = new STALHelper(cmdCtx.getSTAL()); + + if (keys != null && !keys.isEmpty()) { + + List<STALRequest> stalRequests = new ArrayList<STALRequest>(); + + // get values + InfoboxReadRequest infoboxReadRequest; + for (int i = 0; i < keys.size(); i++) { + infoboxReadRequest = new InfoboxReadRequest(); + infoboxReadRequest.setInfoboxIdentifier(keys.get(i)); + stalRequests.add(infoboxReadRequest); + } + + stalHelper.transmitSTALRequest(stalRequests); + + Map<String, Object> values = new HashMap<String, Object>(); + + try { + for (int i = 0; i < keys.size(); i++) { + + String key = keys.get(i); + InfoboxReadResponse nextResponse = (InfoboxReadResponse) stalHelper.nextResponse(InfoboxReadResponse.class); + + + ObjectFactory objectFactory = new ObjectFactory(); + + if (EHIC.equals(key)) { + AttributeList attributeList = createAttributeList(nextResponse.getInfoboxValue()); + values.put(key, objectFactory.createEHIC(attributeList)); + } else if (GRUNDDATEN.equals(key)) { + AttributeList attributeList = createAttributeList(nextResponse.getInfoboxValue()); + values.put(key, objectFactory.createGrunddaten(attributeList)); + } else if (SV_PERSONENBINDUNG.equals(key)) { + values.put(key, objectFactory.createSVPersonenbindung(nextResponse.getInfoboxValue())); + } else if (STATUS.equals(key)) { + AttributeList attributeList = createAttributeListFromRecords(nextResponse.getInfoboxValue()); + values.put(key, objectFactory.createStatus(attributeList)); + } + + } + } catch (CodingException e) { + log.info("Failed to decode '" + getIdentifier() + "' infobox.", e); + throw new SLCommandException(4000, + SLExceptionMessages.EC4000_UNCLASSIFIED_INFOBOX_INVALID, + new Object[] { "IdentityLink" }); + + } + + return values; + + } else { + + return new HashMap<String, Object>(); + + } + + + } + + public static AttributeList createAttributeList(byte[] infoboxValue) throws CodingException { + + ObjectFactory objectFactory = new ObjectFactory(); + + ASN1Object asn1 = DerCoder.decode(infoboxValue); + + AttributeList attributeList = objectFactory.createAttributeList(); + List<AttributeType> attributes = attributeList.getAttribute(); + + if (asn1.isA(ASN.SEQUENCE)) { + for (int i = 0; i < ((SEQUENCE) asn1).countComponents(); i++) { + + AttributeType attributeType = objectFactory.createAttributeType(); + + if (asn1.getComponentAt(i).isA(ASN.SEQUENCE)) { + SEQUENCE attribute = (SEQUENCE) asn1.getComponentAt(i); + if (attribute.getComponentAt(0).isA(ASN.ObjectID)) { + ObjectID objectId = (ObjectID) attribute.getComponentAt(0); + attributeType.setOid("urn:oid:" + objectId.getID()); + } + if (attribute.getComponentAt(1).isA(ASN.SET)) { + SET values = (SET) attribute.getComponentAt(1); + for (int j = 0; j < values.countComponents(); j++) { + setAttributeValue(attributeType, values.getComponentAt(j)); + } + } + } + + attributes.add(attributeType); + + } + + } + + return attributeList; + + } + + public static AttributeList createAttributeListFromRecords(byte[] infoboxValue) throws CodingException { + + ObjectFactory objectFactory = new ObjectFactory(); + + AttributeList attributeList = objectFactory.createAttributeList(); + List<AttributeType> attributes = attributeList.getAttribute(); + + byte[] records = infoboxValue; + + while (records != null && records.length > 0) { + + int length; + + if (records[0] != 0x00) { + + ASN1Object asn1 = DerCoder.decode(records); + + AttributeType attributeType = objectFactory.createAttributeType(); + + if (asn1.isA(ASN.SEQUENCE)) { + SEQUENCE attribute = (SEQUENCE) asn1; + if (attribute.getComponentAt(0).isA(ASN.ObjectID)) { + ObjectID objectId = (ObjectID) attribute.getComponentAt(0); + attributeType.setOid("urn:oid:" + objectId.getID()); + } + if (attribute.getComponentAt(1).isA(ASN.SET)) { + SET values = (SET) attribute.getComponentAt(1); + for (int j = 0; j < values.countComponents(); j++) { + setAttributeValue(attributeType, values.getComponentAt(j)); + } + } + } + + attributes.add(attributeType); + + length = DerCoder.encode(asn1).length; + + } else { + length = 1; + } + + if (length < records.length) { + records = Arrays.copyOfRange(records, length + 1, records.length); + } else { + records = null; + } + + } + + return attributeList; + + } + + private static void setAttributeValue(AttributeType attributeType, ASN1Object value) { + + if (value.isA(ASN.OCTET_STRING)) { + + try { + byte[] octets = ((OCTET_STRING) value).getWholeValue(); + attributeType.setLatin1String(new String(octets, Charset.forName("ISO-8859-1"))); + } catch (IOException e) { + log.info("Failed to set Latin1String.", e); + } + + } else if (value.isA(ASN.NumericString)) { + + attributeType.setNumericString((String) ((NumericString) value).getValue()); + + } else if (value.isA(ASN.GeneralizedTime)) { + + try { + ChoiceOfTime choiceOfTime = new ChoiceOfTime(value); + + GregorianCalendar gregorianCalendar = new GregorianCalendar(); + gregorianCalendar.setTimeZone(TimeZone.getTimeZone("UTC")); + gregorianCalendar.setTime(choiceOfTime.getDate()); + + DatatypeFactory datatypeFactory = DatatypeFactory.newInstance(); + XMLGregorianCalendar xmlGregorianCalendar = datatypeFactory.newXMLGregorianCalendar(gregorianCalendar); + xmlGregorianCalendar.setTimezone(0); + + attributeType.setGeneralizedTime(xmlGregorianCalendar); + } catch (Exception e) { + log.info("Failed to set GeneralizedTime.", e); + } + + } else if (value.isA(ASN.INTEGER)) { + + attributeType.setInteger((BigInteger) value.getValue()); + + } else if (value.isA(ASN.UTF8String)) { + + attributeType.setUTF8String((String) value.getValue()); + + } else if (value.isA(ASN.PrintableString)) { + + attributeType.setPrintableString((String) value.getValue()); + + } else if (value.isA(ASN.UNKNOWN)) { + + byte[] bytes = (byte[]) ((UNKNOWN) value).getValue(); + + try { + BigInteger bigInteger = new BigInteger(bytes); + String string = bigInteger.toString(16); + + Date date = new SimpleDateFormat("yyyyMMdd").parse(string); + attributeType.setDate(new SimpleDateFormat("yyyy-MM-dd").format(date)); + } catch (Exception e) { + log.info("Failed to set Date.", e); + } + } + + } + + + + + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/AlgorithmMethodFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/AlgorithmMethodFactory.java new file mode 100644 index 00000000..d6cbaefa --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/AlgorithmMethodFactory.java @@ -0,0 +1,79 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+
+import javax.xml.crypto.AlgorithmMethod;
+import javax.xml.crypto.dsig.CanonicalizationMethod;
+import javax.xml.crypto.dsig.DigestMethod;
+import javax.xml.crypto.dsig.SignatureMethod;
+
+/**
+ * A factory for creating {@link AlgorithmMethod}s.
+ *
+ * @author mcentner
+ */
+public interface AlgorithmMethodFactory {
+
+ /**
+ * Creates a new DigestMethod for the given <code>signatureContext</code>.
+ *
+ * @param signatureContext
+ * the signature context
+ *
+ * @return a DigestMethod for the given <code>signatureContext</code>
+ *
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidAlgorithmParameterException
+ */
+ public DigestMethod createDigestMethod(SignatureContext signatureContext)
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException;
+
+ /**
+ * Creates a new SignatureMethod for the given <code>signatureContext</code>.
+ *
+ * @param signatureContext
+ * the signature context
+ *
+ * @return a SignatureMethod for the given <code>signatureContext</code>
+ *
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidAlgorithmParameterException
+ */
+ public SignatureMethod createSignatureMethod(SignatureContext signatureContext)
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException;
+
+ /**
+ * Creates a new CanonicalizationMethod for the given
+ * <code>signatureContext</code>.
+ *
+ * @param signatureContext
+ * the signature context
+ *
+ * @return a CanonicalizationMethod for the given
+ * <code>signatureContext</code>
+ *
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidAlgorithmParameterException
+ */
+ public CanonicalizationMethod createCanonicalizationMethod(
+ SignatureContext signatureContext) throws NoSuchAlgorithmException,
+ InvalidAlgorithmParameterException;
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/AlgorithmMethodFactoryImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/AlgorithmMethodFactoryImpl.java new file mode 100644 index 00000000..8391e450 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/AlgorithmMethodFactoryImpl.java @@ -0,0 +1,172 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import iaik.security.ecc.interfaces.ECDSAParams; +import iaik.xml.crypto.XmldsigMore; + +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.ECParameterSpec; + +import javax.xml.crypto.dsig.CanonicalizationMethod; +import javax.xml.crypto.dsig.DigestMethod; +import javax.xml.crypto.dsig.SignatureMethod; +import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; +import javax.xml.crypto.dsig.spec.DigestMethodParameterSpec; +import javax.xml.crypto.dsig.spec.SignatureMethodParameterSpec; +
+/**
+ * An implementation of the AlgorithmMethod factory that uses the signing
+ * certificate to choose appropriate algorithms.
+ *
+ * @author mcentner
+ */
+public class AlgorithmMethodFactoryImpl implements AlgorithmMethodFactory {
+ + /** + * Use SHA-2? + */ + private static boolean SHA2 = false; +
+ /**
+ * The signature algorithm URI.
+ */
+ private String signatureAlgorithmURI; + + /** + * the digest algorithm URI. + */ + private String digestAlgorithmURI = DigestMethod.SHA1;
+
+ /**
+ * The algorithm parameters for the signature algorithm.
+ */
+ private SignatureMethodParameterSpec signatureMethodParameterSpec;
+
+ /**
+ * Creates a new AlgrithmMethodFactory with the given
+ * <code>signingCertificate</code>.
+ *
+ * @param signingCertificate
+ *
+ * @throws NoSuchAlgorithmException
+ * if the public key algorithm of the given
+ * <code>signingCertificate</code> is not supported
+ */
+ public AlgorithmMethodFactoryImpl(X509Certificate signingCertificate)
+ throws NoSuchAlgorithmException {
+ + PublicKey publicKey = signingCertificate.getPublicKey();
+ String algorithm = publicKey.getAlgorithm(); +
+ if ("DSA".equals(algorithm)) {
+ signatureAlgorithmURI = SignatureMethod.DSA_SHA1;
+ } else if ("RSA".equals(algorithm)) { + + int keyLength = 0; + if (publicKey instanceof RSAPublicKey) { + keyLength = ((RSAPublicKey) publicKey).getModulus().bitLength(); + } + + if (SHA2 && keyLength >= 2048) { + signatureAlgorithmURI = XmldsigMore.SIGNATURE_RSA_SHA256; + digestAlgorithmURI = DigestMethod.SHA256; + } else { + signatureAlgorithmURI = SignatureMethod.RSA_SHA1; + } +
+ } else if (("EC".equals(algorithm)) || ("ECDSA".equals(algorithm))) { + + int fieldSize = 0; + if (publicKey instanceof iaik.security.ecc.ecdsa.ECPublicKey) { + ECDSAParams params = ((iaik.security.ecc.ecdsa.ECPublicKey) publicKey).getParameter(); + fieldSize = params.getG().getCurve().getField().getSize().bitLength(); + } else if (publicKey instanceof ECPublicKey) { + ECParameterSpec params = ((ECPublicKey) publicKey).getParams(); + fieldSize = params.getCurve().getField().getFieldSize(); + } + + if (SHA2 && fieldSize >= 512) { + signatureAlgorithmURI = XmldsigMore.SIGNATURE_ECDSA_SHA512; + digestAlgorithmURI = DigestMethod.SHA512; + } else if (SHA2 && fieldSize >= 256) { + signatureAlgorithmURI = XmldsigMore.SIGNATURE_ECDSA_SHA256; + digestAlgorithmURI = DigestMethod.SHA256; + } else { + signatureAlgorithmURI = XmldsigMore.SIGNATURE_ECDSA_SHA1; + } +
+ } else {
+ throw new NoSuchAlgorithmException("Public key algorithm '" + algorithm
+ + "' not supported.");
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @seeat.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactory#
+ * createCanonicalizationMethod
+ * (at.gv.egiz.bku.slcommands.impl.xsect.SignatureContext)
+ */
+ @Override
+ public CanonicalizationMethod createCanonicalizationMethod(
+ SignatureContext signatureContext) throws NoSuchAlgorithmException,
+ InvalidAlgorithmParameterException {
+
+ return signatureContext.getSignatureFactory().newCanonicalizationMethod(
+ CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null);
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactory#createDigestMethod
+ * (at.gv.egiz.bku.slcommands.impl.xsect.SignatureContext)
+ */
+ @Override
+ public DigestMethod createDigestMethod(SignatureContext signatureContext)
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
+
+ return signatureContext.getSignatureFactory().newDigestMethod(
+ digestAlgorithmURI, (DigestMethodParameterSpec) null);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @seeat.gv.egiz.bku.slcommands.impl.xsect.AlgorithmMethodFactory#
+ * createSignatureMethod
+ * (at.gv.egiz.bku.slcommands.impl.xsect.SignatureContext)
+ */
+ @Override
+ public SignatureMethod createSignatureMethod(SignatureContext signatureContext)
+ throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
+
+ return signatureContext.getSignatureFactory().newSignatureMethod(
+ signatureAlgorithmURI, signatureMethodParameterSpec);
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/ByteArrayDereferencer.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/ByteArrayDereferencer.java new file mode 100644 index 00000000..a6473a05 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/ByteArrayDereferencer.java @@ -0,0 +1,65 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import java.io.ByteArrayInputStream;
+
+import javax.xml.crypto.Data;
+import javax.xml.crypto.OctetStreamData;
+import javax.xml.crypto.URIDereferencer;
+import javax.xml.crypto.URIReference;
+import javax.xml.crypto.URIReferenceException;
+import javax.xml.crypto.XMLCryptoContext;
+
+/**
+ * An URIDereferencer implementation that dereferences the given
+ * byte array.
+ *
+ * @author mcentner
+ */
+public class ByteArrayDereferencer implements URIDereferencer {
+
+ /**
+ * The dereferenced data.
+ */
+ protected byte[] dereferencedData;
+
+ /**
+ * Creates a new instance of this ByteArrayDereferencer with
+ * the given <code>dereferencedData</code>.
+ *
+ * @param dereferencedData the octets to be returned by {@link #dereference(URIReference, XMLCryptoContext)}
+ *
+ * @throws NullPointerException if <code>dereferencedData</code> is <code>null</code>
+ */
+ public ByteArrayDereferencer(byte[] dereferencedData) {
+ if (dereferencedData == null) {
+ throw new NullPointerException("Parameter 'dereferencedData' must not be null.");
+ }
+ this.dereferencedData = dereferencedData;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.xml.crypto.URIDereferencer#dereference(javax.xml.crypto.URIReference, javax.xml.crypto.XMLCryptoContext)
+ */
+ @Override
+ public Data dereference(URIReference uriReference, XMLCryptoContext context)
+ throws URIReferenceException {
+ return new OctetStreamData(new ByteArrayInputStream(dereferencedData));
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java new file mode 100644 index 00000000..a57a11dd --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/DataObject.java @@ -0,0 +1,1289 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect; + +import iaik.xml.crypto.dom.DOMCryptoContext; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.SequenceInputStream; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.crypto.MarshalException; +import javax.xml.crypto.dom.DOMStructure; +import javax.xml.crypto.dsig.CanonicalizationMethod; +import javax.xml.crypto.dsig.DigestMethod; +import javax.xml.crypto.dsig.Reference; +import javax.xml.crypto.dsig.Transform; +import javax.xml.crypto.dsig.XMLObject; +import javax.xml.crypto.dsig.spec.TransformParameterSpec; +import javax.xml.crypto.dsig.spec.XPathFilter2ParameterSpec; +import javax.xml.crypto.dsig.spec.XPathType; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.w3c.dom.DOMConfiguration; +import org.w3c.dom.DOMException; +import org.w3c.dom.Document; +import org.w3c.dom.DocumentFragment; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.Text; +import org.w3c.dom.bootstrap.DOMImplementationRegistry; +import org.w3c.dom.ls.DOMImplementationLS; +import org.w3c.dom.ls.LSException; +import org.w3c.dom.ls.LSInput; +import org.w3c.dom.ls.LSOutput; +import org.w3c.dom.ls.LSParser; +import org.w3c.dom.ls.LSSerializer; + +import at.buergerkarte.namespaces.securitylayer._1.Base64XMLLocRefOptRefContentType; +import at.buergerkarte.namespaces.securitylayer._1.DataObjectInfoType; +import at.buergerkarte.namespaces.securitylayer._1.MetaInfoType; +import at.buergerkarte.namespaces.securitylayer._1.TransformsInfoType; +import at.gv.egiz.bku.binding.HttpUtil; +import at.gv.egiz.bku.gui.viewer.MimeTypes; +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; +import java.io.File; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; + +/** + * This class represents a <code>DataObject</code> of an XML-Signature + * created by the security layer command <code>CreateXMLSignature</code>. + * + * @author mcentner + */ +public class DataObject { + + /** + * Logging facility. + */ + private static Log log = LogFactory.getLog(DataObject.class); + + /** + * DOM Implementation. + */ + private static final String DOM_LS_3_0 = "LS 3.0"; + + /** + * The array of the default preferred MIME type order. + */ + private static final String[] DEFAULT_PREFFERED_MIME_TYPES = + new String[] { + "text/plain", + "application/xhtml+xml" + }; + + /** + * Validate hash input. + */ + private static boolean validate = false; + + /** + * Enable validation of hash data input. + * + * @param validate + * <code>true</code> if validation should be enabled, or + * <code>false</code> otherwise. + */ + public static void enableHashDataInputValidation(boolean validate) { + DataObject.validate = validate; + } + + /** + * @return <code>true</code> if hash data input validation is enabled, + * or <code>false</code> 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. + * <p>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.</p> + * + * @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 + "'."); + 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 '" + mediaType + "' is not a s/valid/SUPPORTED digest input, omitting validation."); + } + } + + } + + /** + * Configures this DataObject with the information provided within the given + * <code>sl:DataObjectInfo</code>. + * + * @param dataObjectInfo + * the <code>sl:DataObjectInfo</code> + * + * @throws SLCommandException + * if configuring this DataObject with the information provided in + * the <code>sl:DataObjectInfo</code> fails. + * @throws SLRequestException + * if the information provided in the <code>sl:DataObjectInfo</code> + * does not conform to the security layer specification. + * @throws NullPointerException + * if <code>dataObjectInfo</code> is <code>null</code> + */ + 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 '" + refURI + "', derive filename from reference ID"); + filename = reference.getId() + MimeTypes.getExtension(mimeType); + } + } catch (URISyntaxException ex) { + log.error("failed to derive filename from invalid URI " + ex.getMessage()); + filename = reference.getId() + MimeTypes.getExtension(mimeType); + } + } else { + log.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) { + return ti.getRedirectedStream().toByteArray(); +// byte[] transformsBytes = ti.getRedirectedStream().toByteArray(); +// +// if (transformsBytes == null || transformsBytes.length == 0) { +// return null; +// } +// +// String dsigPrefix = ti.getNamespaceContext().getNamespaceURI("http://www.w3.org/2000/09/xmldsig#"); +// byte[] pre, post; +// if (dsigPrefix == null) { +// log.trace("XMLDSig not declared in outside dsig:Transforms"); +// pre = "<AssureDSigNS>".getBytes(); +// post = "</AssureDSigNS>".getBytes(); +// } else { +// log.trace("XMLDSig bound to prefix " + dsigPrefix); +// pre = ("<AssureDSigNS xmlns:" + dsigPrefix + "=\"http://www.w3.org/2000/09/xmldsig#\">").getBytes(); +// post = "</AssureDSigNS>".getBytes(); +// } +// +// byte[] workaround = new byte[pre.length + transformsBytes.length + post.length]; +// System.arraycopy(pre, 0, workaround, 0, pre.length); +// System.arraycopy(transformsBytes, 0, workaround, pre.length, transformsBytes.length); +// System.arraycopy(post, 0, workaround, pre.length + transformsBytes.length, post.length); +// return workaround; + } + + /** + * Configures this DataObject as an enveloped DataObject with the information + * provided within the given <code>sl:DataObject</code>. + * + * @param dataObject + * the <code>sl:DataObject</code> + * @param transforms + * an optional <code>Transforms</code> element (may be + * <code>null</code>) + * + * @throws SLCommandException + * if configuring this DataObject with the information provided in + * the <code>sl:DataObject</code> fails. + * @throws SLRequestException + * if the information provided in the <code>sl:DataObject</code> + * does not conform to the security layer specification. + * @throws NullPointerException + * if <code>dataObject</code> is <code>null</code> + */ + private void setEnvelopedDataObject( + Base64XMLLocRefOptRefContentType dataObject, XSECTTransforms transforms) + throws SLCommandException, SLRequestException { + + String reference = dataObject.getReference(); + if (reference == null) { + // + // case A + // + // The Reference attribute is not used; the content of sl:DataObject represents the data object. + // If the data object is XML-coded (the sl:XMLContent element is used in sl:DataObject), then it + // must be incorporated in the signature structure as parsed XML. + // + + if (dataObject.getBase64Content() != null) { + + log.debug("Adding DataObject (Base64Content) without a reference URI."); + + // create XMLObject + XMLObject xmlObject = createXMLObject(new ByteArrayInputStream(dataObject.getBase64Content())); + + setXMLObjectAndReferenceBase64(xmlObject, transforms); + + } else if (dataObject.getXMLContent() != null) { + + log.debug("Adding DataObject (XMLContent) without a reference URI."); + + // create XMLObject + DocumentFragment content = parseDataObject((XMLContentType) dataObject.getXMLContent()); + XMLObject xmlObject = createXMLObject(content); + + setXMLObjectAndReferenceXML(xmlObject, transforms); + + } else if (dataObject.getLocRefContent() != null) { + + log.debug("Adding DataObject (LocRefContent) without a reference URI."); + + setEnvelopedDataObject(dataObject.getLocRefContent(), transforms); + + } else { + + // not allowed + log.info("XML structure of the command request contains an " + + "invalid combination of optional elements or attributes. " + + "DataObject of structure='enveloped' without a reference must contain content."); + throw new SLRequestException(3003); + + } + + } else { + + if (dataObject.getBase64Content() == null && + dataObject.getXMLContent() == null && + dataObject.getLocRefContent() == null) { + + // + // case B + // + // The Reference attribute contains a URI that must be resolved by the + // Citizen Card Environment to obtain the data object. + // The content of sl:DataObject remains empty + // + + log.debug("Adding DataObject from reference URI '" + reference + "'."); + + setEnvelopedDataObject(reference, transforms); + + } else { + + // not allowed + log.info("XML structure of the command request contains an " + + "invalid combination of optional elements or attributes. " + + "DataObject of structure='enveloped' with reference must not contain content."); + throw new SLRequestException(3003); + + } + + + } + + } + + /** + * Configures this DataObject as an enveloped DataObject with the content to + * be dereferenced from the given <code>reference</code>. + * + * @param reference + * the <code>reference</code> URI + * @param transforms + * an optional <code>Transforms</code> element (may be + * <code>null</code>) + * + * @throws SLCommandException + * if dereferencing the given <code>reference</code> fails, or if + * configuring this DataObject with the data dereferenced from the + * given <code>reference</code> fails. + * @throws NullPointerException + * if <code>reference</code> is <code>null</code> + */ + private void setEnvelopedDataObject(String reference, XSECTTransforms transforms) throws SLCommandException { + + if (reference == null) { + throw new NullPointerException("Argument 'reference' must not be null."); + } + + // dereference URL + URLDereferencer dereferencer = URLDereferencer.getInstance(); + + StreamData streamData; + try { + streamData = dereferencer.dereference(reference, ctx.getDereferencerContext()); + } catch (IOException e) { + log.info("Failed to dereference XMLObject from '" + reference + "'.", e); + throw new SLCommandException(4110); + } + + Node childNode; + + String contentType = streamData.getContentType(); + if (contentType.startsWith("text/xml")) { + + // If content type is text/xml parse content. + String charset = HttpUtil.getCharset(contentType, true); + + Document doc = parseDataObject(streamData.getStream(), charset); + + childNode = doc.getDocumentElement(); + + if (childNode == null) { + log.info("Failed to parse XMLObject from '" + reference + "'."); + throw new SLCommandException(4111); + } + + XMLObject xmlObject = createXMLObject(childNode); + + setXMLObjectAndReferenceXML(xmlObject, transforms); + + } else { + + // Include content Base64 encoded. + XMLObject xmlObject = createXMLObject(streamData.getStream()); + + setXMLObjectAndReferenceBase64(xmlObject, transforms); + + } + + } + + /** + * Configures this DataObject as an detached DataObject with the information + * provided in the given <code>sl:DataObject</code> and optionally + * <code>transforms</code>. + * + * @param dataObject + * the <code>sl:DataObject</code> + * @param transforms + * an optional Transforms object, may be <code>null</code> + * + * @throws SLCommandException + * if configuring this DataObject with the information provided in + * the <code>sl:DataObject</code> fails. + * @throws SLRequestException + * if the information provided in the <code>sl:DataObject</code> + * does not conform to the security layer specification. + * @throws NullPointerException + * if <code>dataObject</code> is <code>null</code> + */ + private void setDetachedDataObject( + Base64XMLLocRefOptRefContentType dataObject, XSECTTransforms transforms) + throws SLCommandException, SLRequestException { + + String referenceURI = dataObject.getReference(); + + if (referenceURI == null) { + + // not allowed + log.info("XML structure of the command request contains an " + + "invalid combination of optional elements or attributes. " + + "DataObject of structure='detached' must contain a reference."); + throw new SLRequestException(3003); + + } else { + + DigestMethod dm; + try { + dm = ctx.getAlgorithmMethodFactory().createDigestMethod(ctx); + } catch (NoSuchAlgorithmException e) { + log.error("Failed to get DigestMethod.", e); + throw new SLCommandException(4006); + } catch (InvalidAlgorithmParameterException e) { + log.error("Failed to get DigestMethod.", e); + throw new SLCommandException(4006); + } + + String idValue = ctx.getIdValueFactory().createIdValue("Reference"); + + reference = new XSECTReference(referenceURI, dm, transforms, null, idValue); + + // case D: + // + // The Reference attribute contains a URI that is used by the Citizen Card + // Environment to code the reference to the data object as part of the XML + // signature (attribute URI in the dsig:Reference) element. The content of + // sl:DataObject represents the data object. + + if (dataObject.getLocRefContent() != null) { + String locRef = dataObject.getLocRefContent(); + try { + this.reference.setDereferencer(new LocRefDereferencer(ctx.getDereferencerContext(), locRef)); + } catch (URISyntaxException e) { + log.info("Invalid URI '" + locRef + "' in DataObject.", e); + throw new SLCommandException(4003); + } catch (IllegalArgumentException e) { + log.info("LocRef URI of '" + locRef + "' not supported in DataObject. ", e); + throw new SLCommandException(4003); + } + } else if (dataObject.getBase64Content() != null) { + byte[] base64Content = dataObject.getBase64Content(); + this.reference.setDereferencer(new ByteArrayDereferencer(base64Content)); + } else if (dataObject.getXMLContent() != null) { + XMLContentType xmlContent = (XMLContentType) dataObject.getXMLContent(); + byte[] bytes = xmlContent.getRedirectedStream().toByteArray(); + this.reference.setDereferencer(new ByteArrayDereferencer(bytes)); + } else { + + // case C: + // + // The Reference attribute contains a URI that must be resolved by the + // Citizen Card Environment to obtain the data object. The Reference + // attribute contains a URI that is used by the Citizen Card Environment + // to code the reference to the data object as part of the XML signature + // (attribute URI in the dsig:Reference) element. The content of + // sl:DataObject remains empty. + + } + + } + } + + /** + * Returns the preferred <code>sl:TransformInfo</code> from the given list of + * <code>transformInfos</code>, or <code>null</code> if none of the given + * <code>transformInfos</code> is preferred over the others. + * + * @param transformsInfos + * a list of <code>sl:TransformInfo</code>s + * + * @return the selected <code>sl:TransformInfo</code> or <code>null</code>, if + * none is preferred over the others + */ + private TransformsInfoType selectPreferredTransformsInfo(List<TransformsInfoType> transformsInfos) { + + Map<String, TransformsInfoType> mimeTypes = new HashMap<String, TransformsInfoType>(); + + StringBuilder debugString = null; + if (log.isDebugEnabled()) { + debugString = new StringBuilder(); + debugString.append("Got " + transformsInfos.size() + " TransformsInfo(s):"); + } + + for (TransformsInfoType transformsInfoType : transformsInfos) { + MetaInfoType finalDataMetaInfo = transformsInfoType.getFinalDataMetaInfo(); + String mimeType = finalDataMetaInfo.getMimeType(); + String description = finalDataMetaInfo.getDescription(); + mimeTypes.put(mimeType, transformsInfoType); + if (debugString != null) { + debugString.append("\n FinalDataMetaInfo: MIME-Type="); + debugString.append(mimeType); + if (description != null) { + debugString.append(" "); + debugString.append(description); + } + } + } + + if (debugString != null) { + log.debug(debugString); + } + + // look for preferred transform + for (String mimeType : DEFAULT_PREFFERED_MIME_TYPES) { + if (mimeTypes.containsKey(mimeType)) { + return mimeTypes.get(mimeType); + } + } + + // no preferred transform + return null; + + } + + /** + * Create an instance of <code>ds:Transforms</code> from the given + * <code>sl:TransformsInfo</code>. + * + * @param transformsInfo + * the <code>sl:TransformsInfo</code> + * + * @return a corresponding unmarshalled <code>ds:Transforms</code>, or + * <code>null</code> if the given <code>sl:TransformsInfo</code> does + * not contain a <code>dsig:Transforms</code> element + * + * @throws SLRequestException + * if the <code>ds:Transforms</code> in the given + * <code>transformsInfo</code> are not valid or cannot be parsed. + * + * @throws MarshalException + * if the <code>ds:Transforms</code> in the given + * <code>transformsInfo</code> cannot be unmarshalled. + */ + private XSECTTransforms createTransforms(TransformsInfoType transformsInfo) throws SLRequestException, MarshalException { + + byte[] transforms = getTransformsBytes((at.gv.egiz.slbinding.impl.TransformsInfoType) transformsInfo); + + if (transforms != null && transforms.length > 0) { + // debug + if (log.isTraceEnabled()) { + StringBuilder sb = new StringBuilder(); + sb.append("Trying to parse transforms:\n"); + sb.append(new String(transforms, Charset.forName("UTF-8"))); + log.trace(sb); + } + + DOMImplementationLS domImplLS = DOMUtils.getDOMImplementationLS(); + LSInput input = domImplLS.createLSInput(); + input.setByteStream(new ByteArrayInputStream(transforms)); + + LSParser parser = domImplLS.createLSParser( + DOMImplementationLS.MODE_SYNCHRONOUS, null); + DOMConfiguration domConfig = parser.getDomConfig(); + SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler(); + domConfig.setParameter("error-handler", errorHandler); + domConfig.setParameter("validate", Boolean.FALSE); + + Document document; + try { + document = parser.parse(input); + } catch (DOMException e) { + log.info("Failed to parse dsig:Transforms.", e); + throw new SLRequestException(3002); + } catch (LSException e) { + log.info("Failed to parse dsig:Transforms.", e); + throw new SLRequestException(3002); + } + + // adopt ds:Transforms + Element transformsElt = document.getDocumentElement(); + Node adoptedTransforms = ctx.getDocument().adoptNode(transformsElt); + + DOMCryptoContext context = new DOMCryptoContext(); + + // unmarshall ds:Transforms + return new XSECTTransforms(context, adoptedTransforms); + + } else { + return null; + } + + +// TransformsType transformsType = transformsInfo.getTransforms(); +// if (transformsType == null) { +// return null; +// } +// List<TransformType> transformList = transformsType.getTransform(); +// +// DOMImplementationLS domImplLS = DOMUtils.getDOMImplementationLS(); +//// Document transformsDoc = ((DOMImplementation) domImplLS).createDocument("http://www.w3.org/2000/09/xmldsig#", "Transforms", null); +//// Element transforms = transformsDoc.getDocumentElement(); +// Document transformsDoc = DOMUtils.createDocument(); +// Element transforms = transformsDoc.createElementNS( +// "http://www.w3.org/2000/09/xmldsig#", +// Signature.XMLDSIG_PREFIX + ":Transforms"); +// transformsDoc.appendChild(transforms); +// +// for (TransformType transformType : transformList) { +// log.trace("found " + transformType.getClass().getName()); +// Element transform = transformsDoc.createElementNS( +// "http://www.w3.org/2000/09/xmldsig#", +// Signature.XMLDSIG_PREFIX + ":Transform"); +// String algorithm = transformType.getAlgorithm(); +// if (algorithm != null) { +// log.trace("found algorithm " + algorithm); +// transform.setAttribute("Algorithm", algorithm); +// } +// +// at.gv.egiz.slbinding.impl.TransformType t = (at.gv.egiz.slbinding.impl.TransformType) transformType; +// byte[] redirectedBytes = t.getRedirectedStream().toByteArray(); +// if (redirectedBytes != null && redirectedBytes.length > 0) { +// if (log.isTraceEnabled()) { +// StringBuilder sb = new StringBuilder(); +// sb.append("Trying to parse dsig:Transform:\n"); +// sb.append(new String(redirectedBytes, Charset.forName("UTF-8"))); +// log.trace(sb); +// } +// LSInput input = domImplLS.createLSInput(); +// input.setByteStream(new ByteArrayInputStream(redirectedBytes)); +// +// 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); +// +// try { +// Document redirectedDoc = parser.parse(input); +// Node redirected = transformsDoc.adoptNode(redirectedDoc.getDocumentElement()); +// transform.appendChild(redirected); +// +// //not supported by Xerces2.9.1 +//// Node redirected = parser.parseWithContext(input, transform, LSParser.ACTION_APPEND_AS_CHILDREN); +// +// } catch (DOMException e) { +// log.info("Failed to parse dsig:Transform.", e); +// throw new SLRequestException(3002); +// } catch (LSException e) { +// log.info("Failed to parse dsig:Transform.", e); +// throw new SLRequestException(3002); +// } +// } +// transforms.appendChild(transform); +// } +// +// //adopt ds:Transforms +// Node adoptedTransforms = ctx.getDocument().adoptNode(transforms); +// DOMCryptoContext context = new DOMCryptoContext(); +// +// // unmarshall ds:Transforms +// return new XSECTTransforms(context, adoptedTransforms); + + } + + /** + * Sets the <code>mimeType</code> and the <code>description</code> value + * for this DataObject. + * + * @param metaInfoType the <code>sl:FinalMetaDataInfo</code> + * + * @throws NullPointerException if <code>metaInfoType</code> is <code>null</code> + */ + 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 <code>sl:TransformInfos</code>, sets the corresponding final data meta info and + * returns the corresponding unmarshalled <code>ds:Transforms</code>. + * + * @param transformsInfos the <code>sl:TransformInfos</code> + * + * @return the unmarshalled <code>ds:Transforms</code>, or <code>null</code> if + * no transformation path has been selected. + * + * @throws SLRequestException if the given list <code>ds:TransformsInfo</code> contains + * an invalid <code>ds:Transforms</code> element, or no suitable transformation path + * can be found. + */ + private XSECTTransforms createTransformsAndSetFinalDataMetaInfo( + List<TransformsInfoType> transformsInfos) throws SLRequestException { + + TransformsInfoType preferredTransformsInfo = selectPreferredTransformsInfo(transformsInfos); + // try preferred transform + if (preferredTransformsInfo != null) { + + try { + XSECTTransforms transforms = createTransforms(preferredTransformsInfo); + setFinalDataMetaInfo(preferredTransformsInfo.getFinalDataMetaInfo()); + return transforms; + } catch (MarshalException e) { + + String mimeType = preferredTransformsInfo.getFinalDataMetaInfo().getMimeType(); + log.info("Failed to unmarshal preferred transformation path (MIME-Type=" + + mimeType + ").", e); + + } + + } + + // look for another suitable transformation path + for (TransformsInfoType transformsInfoType : transformsInfos) { + + try { + XSECTTransforms transforms = createTransforms(transformsInfoType); + setFinalDataMetaInfo(transformsInfoType.getFinalDataMetaInfo()); + return transforms; + } catch (MarshalException e) { + + String mimeType = transformsInfoType.getFinalDataMetaInfo().getMimeType(); + log.info("Failed to unmarshal transformation path (MIME-Type=" + + mimeType + ").", e); + } + + } + + // no suitable transformation path found + throw new SLRequestException(3003); + + } + + /** + * Create an XMLObject with the Base64 encoding of the given + * <code>content</code>. + * + * @param content + * the to-be Base64 encoded content + * @return an XMLObject with the Base64 encoded <code>content</code> + */ + private XMLObject createXMLObject(InputStream content) { + + Text textNode; + try { + textNode = at.gv.egiz.dom.DOMUtils.createBase64Text(content, ctx.getDocument()); + } catch (IOException e) { + log.error(e); + throw new SLRuntimeException(e); + } + + DOMStructure structure = new DOMStructure(textNode); + + String idValue = ctx.getIdValueFactory().createIdValue("Object"); + + return ctx.getSignatureFactory().newXMLObject(Collections.singletonList(structure), idValue, null, null); + + } + + /** + * Create an XMLObject with the given <code>content</code> node. + * + * @param content the content node + * + * @return an XMLObject with the given <code>content</code> + */ + private XMLObject createXMLObject(Node content) { + + String idValue = ctx.getIdValueFactory().createIdValue("Object"); + + List<DOMStructure> structures = Collections.singletonList(new DOMStructure(content)); + + return ctx.getSignatureFactory().newXMLObject(structures, idValue, null, null); + + } + + /** + * Sets the given <code>xmlObject</code> and creates and sets a corresponding + * <code>Reference</code>. + * <p> + * A transform to Base64-decode the xmlObject's content is inserted at the top + * of to the optional <code>transforms</code> if given, or to a newly created + * <code>Transforms</code> element if <code>transforms</code> is + * <code>null</code>. + * + * @param xmlObject + * the XMLObject + * @param transforms + * an optional <code>Transforms</code> element (may be + * <code>null</code>) + * + * @throws SLCommandException + * if creating the Reference fails + * @throws NullPointerException + * if <code>xmlObject</code> is <code>null</code> + */ + 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 <code>xmlObject</code> and creates and sets a corresponding + * <code>Reference</code>. + * <p> + * A transform to select the xmlObject's content is inserted at the top of to + * the optional <code>transforms</code> if given, or to a newly created + * <code>Transforms</code> element if <code>transforms</code> is + * <code>null</code>. + * </p> + * + * @param xmlObject + * the XMLObject + * @param transforms + * an optional <code>Transforms</code> element (may be + * <code>null</code>) + * + * @throws SLCommandException + * if creating the Reference fails + * @throws NullPointerException + * if <code>xmlObject</code> is <code>null</code> + */ + 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); + List<XPathType> xpaths = Collections.singletonList(xpath); + XPathFilter2ParameterSpec params = new XPathFilter2ParameterSpec(xpaths); + + xpathTransform = ctx.getSignatureFactory().newTransform(Transform.XPATH2, params); + + // add exclusive canonicalization to avoid signing the namespace context of the ds:Object + c14nTransform = ctx.getSignatureFactory().newTransform(CanonicalizationMethod.EXCLUSIVE, (TransformParameterSpec) null); + + } catch (NoSuchAlgorithmException e) { + // algorithm must be present + throw new SLRuntimeException(e); + } catch (InvalidAlgorithmParameterException e) { + // params must be appropriate + throw new SLRuntimeException(e); + } + + if (transforms == null) { + List<Transform> newTransfroms = new ArrayList<Transform>(); + newTransfroms.add(xpathTransform); + newTransfroms.add(c14nTransform); + transforms = new XSECTTransforms(newTransfroms); + } else { + transforms.insertTransform(xpathTransform); + } + + 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); + + } + + /** + * Parses the given <code>xmlContent</code> and returns a corresponding + * document fragment. + * + * <p> + * The to-be parsed content is surrounded by <dummy> ... </dummy> elements to + * allow for mixed (e.g. Text and Element) content in XMLContent. + * </p> + * + * @param xmlContent + * the XMLContent to-be parsed + * + * @return a document fragment containing the parsed nodes + * + * @throws SLCommandException + * if parsing the given <code>xmlContent</code> fails + * + * @throws NullPointerException + * if <code>xmlContent</code> is <code>null</code> + */ + 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. + + List<InputStream> inputStreams = new ArrayList<InputStream>(); + try { + // dummy start element + inputStreams.add(new ByteArrayInputStream("<dummy>".getBytes("UTF-8"))); + + // content + inputStreams.add(new ByteArrayInputStream(redirectedStream.toByteArray())); + + // dummy end element + inputStreams.add(new ByteArrayInputStream("</dummy>".getBytes("UTF-8"))); + } catch (UnsupportedEncodingException e) { + throw new SLRuntimeException(e); + } + + SequenceInputStream inputStream = new SequenceInputStream(Collections.enumeration(inputStreams)); + + // parse DataObject + Document doc = parseDataObject(inputStream, "UTF-8"); + + Element documentElement = doc.getDocumentElement(); + + if (documentElement == null || + !"dummy".equals(documentElement.getLocalName())) { + log.info("Failed to parse DataObject XMLContent."); + throw new SLCommandException(4111); + } + + DocumentFragment fragment = doc.createDocumentFragment(); + while (documentElement.getFirstChild() != null) { + fragment.appendChild(documentElement.getFirstChild()); + } + + // log parsed document + if (log.isTraceEnabled()) { + + StringWriter writer = new StringWriter(); + + writer.write("DataObject:\n"); + + LSOutput output = domImplLS.createLSOutput(); + output.setCharacterStream(writer); + output.setEncoding("UTF-8"); + LSSerializer serializer = domImplLS.createLSSerializer(); + serializer.getDomConfig().setParameter("xml-declaration", Boolean.FALSE); + serializer.write(fragment, output); + + log.trace(writer.toString()); + } + + return fragment; + + } + + /** + * Parses the given <code>inputStream</code> using the given + * <code>encoding</code> and returns the parsed document. + * + * @param inputStream + * the to-be parsed input + * + * @param encoding + * the encoding to be used for parsing the given + * <code>inputStream</code> + * + * @return the parsed document + * + * @throws SLCommandException + * if parsing the <code>inputStream</code> fails. + * + * @throws NullPointerException + * if <code>inputStram</code> is <code>null</code> + */ + private Document parseDataObject(InputStream inputStream, String encoding) throws SLCommandException { + + LSInput input = domImplLS.createLSInput(); + input.setByteStream(inputStream); + + if (encoding != null) { + input.setEncoding(encoding); + } + + LSParser parser = domImplLS.createLSParser(DOMImplementationLS.MODE_SYNCHRONOUS, null); + DOMConfiguration domConfig = parser.getDomConfig(); + SimpleDOMErrorHandler errorHandler = new SimpleDOMErrorHandler(); + domConfig.setParameter("error-handler", errorHandler); + domConfig.setParameter("validate", Boolean.FALSE); + + Document doc; + try { + doc = parser.parse(input); + } catch (DOMException e) { + log.info("Existing XML document cannot be parsed.", e); + throw new SLCommandException(4111); + } catch (LSException e) { + log.info("Existing XML document cannot be parsed. ", e); + throw new SLCommandException(4111); + } + + if (errorHandler.hasErrors()) { + // log errors + if (log.isInfoEnabled()) { + List<String> errorMessages = errorHandler.getErrorMessages(); + StringBuffer sb = new StringBuffer(); + for (String errorMessage : errorMessages) { + sb.append(" "); + sb.append(errorMessage); + } + log.info("Existing XML document cannot be parsed. " + sb.toString()); + } + throw new SLCommandException(4111); + } + + return doc; + + } + + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/IdValueFactory.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/IdValueFactory.java new file mode 100644 index 00000000..df42bd11 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/IdValueFactory.java @@ -0,0 +1,37 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect;
+
+/**
+ * A factory for creating <code>xsd:Id</code>-attribute values.
+ *
+ * @author mcentner
+ */
+public interface IdValueFactory {
+
+ /**
+ * Creates a new <code>xsd:Id</code>-attribute value for an Element of the
+ * given <code>elementName</code>.
+ *
+ * @param elementName
+ * the local name of the element to create the value for
+ *
+ * @return a <code>xsd:Id</code>-attribute value
+ */
+ public String createIdValue(String elementName);
+
+}
\ No newline at end of file diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/IdValueFactoryImpl.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/IdValueFactoryImpl.java new file mode 100644 index 00000000..b9824655 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/IdValueFactoryImpl.java @@ -0,0 +1,127 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * An implementation of the IdValueFactory.
+ * <p>
+ * This IdValueFactory creates <code>xsd:Id</code>-attribute values of the form
+ * '<code><elementName>-<random>-<sequenceNumber></code>',
+ * where
+ * <ul>
+ * <li><code><elementName></code> is the name provided at
+ * {@link #createIdValue(String)},</li>
+ * <li><code><random></code> is a random generated fixed value for an
+ * instance of this IdValueFactory and</li>
+ * <li><code><sequenceNumber></code> is the sequence number of the value
+ * generated for a given <code>elementName</code> by an instance of this
+ * IdValueFactory.</li>
+ * </ul>
+ * </p>
+ *
+ * @author mcentner
+ */
+public class IdValueFactoryImpl implements IdValueFactory {
+
+ /**
+ * A generator for <code>xsd:Id</code>-attribute values.
+ *
+ * @author mcentner
+ */
+ private class IdGenerator {
+
+ /**
+ * The salt.
+ */
+ private String salt;
+
+ /**
+ * The element name.
+ */
+ private String elementName;
+
+ /**
+ * The sequence number.
+ */
+ private int i = 0;
+
+ /**
+ * Creates a new instance of this IdGenerator with the given
+ * <code>elementName</code> and <code>salt</code> value.
+ *
+ * @param elementName the element name
+ * @param salt the salt valeu
+ */
+ private IdGenerator(String elementName, String salt) {
+ super();
+ this.elementName = elementName;
+ this.salt = salt;
+ }
+
+ /**
+ * @return returns the next <code>xsd:Id</code>-attribute value.
+ */
+ public String getNextId() {
+ return elementName + "-" + salt + "-" + Integer.toString(++i);
+ }
+
+ }
+
+ /**
+ * A map of element names to <code>xsd:Id</code>-value generators.
+ */
+ private Map<String, IdGenerator> generators = new HashMap<String, IdGenerator>();
+
+ /**
+ * The seed value.
+ */
+ private String seed;
+
+ /**
+ * Creates a new instance of this IdValueFactory.
+ */
+ public IdValueFactoryImpl() {
+
+ Random random = new Random();
+ int rand = random.nextInt();
+ seed = Integer.toHexString(rand);
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * at.gv.egiz.bku.slcommands.impl.IdValueFactory#createIdValue(java.lang.String
+ * )
+ */
+ public String createIdValue(String elementName) {
+
+ IdGenerator generator = generators.get(elementName);
+ if (generator == null) {
+ generator = new IdGenerator(elementName, seed);
+ generators.put(elementName, generator);
+ }
+ return generator.getNextId();
+
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/LocRefDereferencer.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/LocRefDereferencer.java new file mode 100644 index 00000000..f5394157 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/LocRefDereferencer.java @@ -0,0 +1,112 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.xml.crypto.Data;
+import javax.xml.crypto.OctetStreamData;
+import javax.xml.crypto.URIDereferencer;
+import javax.xml.crypto.URIReference;
+import javax.xml.crypto.URIReferenceException;
+import javax.xml.crypto.XMLCryptoContext;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import at.gv.egiz.bku.utils.urldereferencer.StreamData;
+import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer;
+import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext;
+
+/**
+ * An URIDereferencer implementation that dereferences <code>LocRef</code>
+ * references.
+ *
+ * @author mcentner
+ */
+public class LocRefDereferencer implements URIDereferencer {
+
+ /**
+ * Logging facility.
+ */
+ private static Log log = LogFactory.getLog(LocRefDereferencer.class);
+
+ /**
+ * The <code>LocRef</code>-reference to be dereferenced by
+ * {@link #dereference(URIReference, XMLCryptoContext)}.
+ */
+ protected String locRef;
+
+ /**
+ * The context to be used for dereferencing.
+ */
+ protected URLDereferencerContext dereferencerContext;
+
+ /**
+ * Creates a new instance of this LocRefDereferencer with the given
+ * <code>dereferencerContext</code> and <code>locRef</code> reference.
+ *
+ * @param dereferencerContext
+ * the context to be used for dereferencing
+ * @param locRef
+ * the <code>LocRef</code>-reference (must be an absolute URI)
+ *
+ * @throws URISyntaxException
+ * if <code>LocRef</code> is not an absolute URI
+ */
+ public LocRefDereferencer(URLDereferencerContext dereferencerContext,
+ String locRef) throws URISyntaxException {
+
+ this.dereferencerContext = dereferencerContext;
+
+ URI locRefUri = new URI(locRef);
+ if (locRefUri.isAbsolute()) {
+ this.locRef = locRef;
+ } else {
+ throw new IllegalArgumentException(
+ "Parameter 'locRef' must be an absolut URI.");
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * javax.xml.crypto.URIDereferencer#dereference(javax.xml.crypto.URIReference,
+ * javax.xml.crypto.XMLCryptoContext)
+ */
+ @Override
+ public Data dereference(URIReference uriReference, XMLCryptoContext context)
+ throws URIReferenceException {
+
+ URLDereferencer dereferencer = URLDereferencer.getInstance();
+ StreamData streamData;
+ try {
+ streamData = dereferencer.dereference(locRef, dereferencerContext);
+ } catch (IOException e) {
+ log.info("Failed to dereference URI'" + locRef + "'. " + e.getMessage(),
+ e);
+ throw new URIReferenceException("Failed to dereference URI '" + locRef
+ + "'. " + e.getMessage(), e);
+ }
+ return new OctetStreamData(streamData.getStream(), locRef, streamData
+ .getContentType());
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALPrivateKey.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALPrivateKey.java new file mode 100644 index 00000000..25e2d4e5 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALPrivateKey.java @@ -0,0 +1,128 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect; + +import 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/STALProvider.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALProvider.java new file mode 100644 index 00000000..9fb9a3f1 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALProvider.java @@ -0,0 +1,71 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import iaik.xml.crypto.XmldsigMore;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.Provider;
+import java.security.Signature;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.crypto.dsig.SignatureMethod;
+
+/**
+ * A security provider implementation that provides {@link Signature} implementations
+ * based on STAL.
+ *
+ * @author mcentner
+ */
+public class STALProvider extends Provider {
+
+ private static final long serialVersionUID = 1L;
+
+ private static String IMPL_PACKAGE_NAME = "at.gv.egiz.bku.slcommands.impl.xsect";
+
+ public STALProvider() {
+
+ super("STAL", 1.0, "Security Token Abstraction Layer Provider");
+
+ final Map<String, String> map = new HashMap<String, String>();
+
+ // TODO: register further algorithms
+ map.put("Signature." + SignatureMethod.RSA_SHA1,
+ IMPL_PACKAGE_NAME + ".STALSignature");
+ map.put("Signature." + XmldsigMore.SIGNATURE_ECDSA_SHA1,
+ IMPL_PACKAGE_NAME + ".STALSignature"); + map.put("Signature." + XmldsigMore.SIGNATURE_RSA_SHA256, + IMPL_PACKAGE_NAME + ".STALSignature"); + map.put("Signature." + XmldsigMore.SIGNATURE_ECDSA_SHA256, + IMPL_PACKAGE_NAME + ".STALSignature"); + map.put("Signature." + XmldsigMore.SIGNATURE_ECDSA_SHA512, + IMPL_PACKAGE_NAME + ".STALSignature"); + +
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ @Override
+ public Void run() {
+ putAll(map);
+ return null;
+ }
+ });
+
+ }
+
+}
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 new file mode 100644 index 00000000..dd7c7d8a --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALSignature.java @@ -0,0 +1,184 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect; + +import at.gv.egiz.bku.slcommands.impl.DataObjectHashDataInput; +import at.gv.egiz.bku.slexceptions.SLViewerException; + +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) { + try { + dataObject.validateHashDataInput(); + } catch (SLViewerException e) { + throw new STALSignatureException(e); + } + hashDataInputs.add(new DataObjectHashDataInput(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/STALSignatureException.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALSignatureException.java new file mode 100644 index 00000000..4e86b07c --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/STALSignatureException.java @@ -0,0 +1,92 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import java.security.SignatureException;
+
+/**
+ * A SignatureException thrown by the {@link STALSignature}.
+ *
+ * @author mcentner
+ */
+public class STALSignatureException extends SignatureException {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * The STAL error code.
+ */
+ private int errorCode;
+
+ /**
+ * Creates a new instance of this STALSignatureException.
+ */
+ public STALSignatureException() {
+ }
+
+ /**
+ * Creates a new instance of this STALSigantureException with
+ * the given <code>errorCode</code>.
+ *
+ * @param errorCode the error code
+ */
+ public STALSignatureException(int errorCode) {
+ this.errorCode = errorCode;
+ }
+
+ /**
+ * Creates a new instance of this STALSignatureException with
+ * the given error <code>msg</code>.
+ *
+ * @param msg the error message
+ * @see SignatureException#SignatureException(String)
+ */
+ public STALSignatureException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * Creates a new instance of this STALSignatureException with
+ * the given root <code>cause</code>.
+ *
+ * @param cause the cause
+ * @see SignatureException#SignatureException(Throwable)
+ */
+ public STALSignatureException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Creates a new instance of this STALSignautureException with
+ * the given error <code>message</code> and root <code>cause</code>.
+ *
+ * @param message the error message
+ * @param cause the cause
+ * @see SignatureException#SignatureException(String, Throwable)
+ */
+ public STALSignatureException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * @return the error code
+ */
+ public int getErrorCode() {
+ return errorCode;
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java new file mode 100644 index 00000000..3cebb6a3 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/Signature.java @@ -0,0 +1,995 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +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.slexceptions.SLViewerException; +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 { + public static final String XMLDSIG_PREFIX = "dsig"; + + /** + * 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 { + + String signatureId = ctx.getIdValueFactory().createIdValue("Signature"); + + 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, signatureId); + + 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 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 SLViewerException + * @throws NullPointerException + * if <code>signContext</code> is <code>null</code> + */ + public void sign(DOMSignContext signContext) throws MarshalException, XMLSignatureException, SLCommandException, SLViewerException { + + 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,XMLDSIG_PREFIX); + + signContext.setURIDereferencer(new URIDereferncerAdapter(ctx.getDereferencerContext())); + + try { + xmlSignature.sign(signContext); + } catch (XMLSignatureException e) { + Throwable cause = e.getCause(); + while (cause != null) { + if (cause instanceof STALSignatureException) { + if (((STALSignatureException) cause).getCause() instanceof SLViewerException) { + throw (SLViewerException) ((STALSignatureException) cause).getCause(); + } + int errorCode = ((STALSignatureException) cause).getErrorCode(); + SLCommandException commandException = new SLCommandException(errorCode); + log.info("Failed to sign signature.", commandException); + 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."); + } + } + for (Reference reference : getReferences()) { + if (reference.getType() != null) { + InputStream digestInputStream = reference.getDigestInputStream(); + if (digestInputStream != null) { + StringBuilder sb = new StringBuilder(); + sb.append("DigestInput for Reference with id='"); + sb.append(reference.getId()); + sb.append("'; Type:"); + sb.append(reference.getType()); + sb.append("):\n"); + try { + byte[] b = new byte[512]; + for (int l; (l = digestInputStream.read(b)) != -1;) { + sb.append(new String(b, 0, l)); + } + } 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 SLViewerException + * @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, SLViewerException { + + 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 + * @param signatureId TODO + * @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, String signatureId) 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())); + } + } + } + + String target = "#" + signatureId; + + 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); + } + + JAXBElement<QualifyingPropertiesType> qualifyingProperties; + try { + qualifyingProperties = factory.createQualifyingProperties111(target, date, signingCertificates, idValue, dataObjectFormats, dm); + } 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)"; + + String referenceIdValue = ctx.getIdValueFactory().createIdValue("Reference"); + String referenceType = QualifyingPropertiesFactory.SIGNED_PROPERTIES_REFERENCE_TYPE_V1_1_1; + + 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); + } + + 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; + + } + + + } + +} diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureContext.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureContext.java new file mode 100644 index 00000000..0925f2fd --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureContext.java @@ -0,0 +1,129 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import javax.xml.crypto.dsig.DigestMethod;
+import javax.xml.crypto.dsig.XMLSignatureFactory;
+
+import org.w3c.dom.Document;
+
+import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext;
+
+/**
+ * An instance of this class carries context information for a XML-Signature
+ * created by the security layer command <code>CreateXMLSignature</code>.
+ *
+ * @author mcentner
+ */
+public class SignatureContext {
+
+ /**
+ * The document going to contain the XML signature.
+ */
+ private Document document;
+
+ /**
+ * The IdValueFactory used to create <code>xsd:ID</code>-attribute values.
+ */
+ private IdValueFactory idValueFactory;
+
+ /**
+ * The XMLSignatureFactory to create XML signature objects.
+ */
+ private XMLSignatureFactory signatureFactory;
+
+ /**
+ * The URLDereferencerContext for dereferencing URLs.
+ */
+ private URLDereferencerContext dereferencerContext;
+
+ /**
+ * The DigestMethodFactory to create {@link DigestMethod} objects.
+ */
+ private AlgorithmMethodFactory digestMethodFactory;
+
+ /**
+ * @return the document
+ */
+ public Document getDocument() {
+ return document;
+ }
+
+ /**
+ * @param document the document to set
+ */
+ public void setDocument(Document document) {
+ this.document = document;
+ }
+
+ /**
+ * @return the idValueFactory
+ */
+ public IdValueFactory getIdValueFactory() {
+ return idValueFactory;
+ }
+
+ /**
+ * @param idValueFactory the idValueFactory to set
+ */
+ public void setIdValueFactory(IdValueFactory idValueFactory) {
+ this.idValueFactory = idValueFactory;
+ }
+
+ /**
+ * @return the signatureFactory
+ */
+ public XMLSignatureFactory getSignatureFactory() {
+ return signatureFactory;
+ }
+
+ /**
+ * @param signatureFactory the signatureFactory to set
+ */
+ public void setSignatureFactory(XMLSignatureFactory signatureFactory) {
+ this.signatureFactory = signatureFactory;
+ }
+
+ /**
+ * @return the dereferencerContext
+ */
+ public URLDereferencerContext getDereferencerContext() {
+ return dereferencerContext;
+ }
+
+ /**
+ * @param dereferencerContext the dereferencerContext to set
+ */
+ public void setDereferencerContext(URLDereferencerContext dereferencerContext) {
+ this.dereferencerContext = dereferencerContext;
+ }
+
+ /**
+ * @return the digestMethodFactory
+ */
+ public AlgorithmMethodFactory getAlgorithmMethodFactory() {
+ return digestMethodFactory;
+ }
+
+ /**
+ * @param digestMethodFactory the digestMethodFactory to set
+ */
+ public void setAlgorithmMethodFactory(AlgorithmMethodFactory digestMethodFactory) {
+ this.digestMethodFactory = digestMethodFactory;
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureLocation.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureLocation.java new file mode 100644 index 00000000..5ec02893 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SignatureLocation.java @@ -0,0 +1,235 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import java.util.Iterator;
+
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import at.buergerkarte.namespaces.securitylayer._1.SignatureInfoCreationType;
+import at.gv.egiz.bku.slexceptions.SLCommandException;
+import at.gv.egiz.slbinding.impl.SignatureLocationType;
+
+/**
+ * This class implements the <code>SignatureLocation</code> of an XML-Signature
+ * to be created by the security layer command <code>CreateXMLSignature</code>.
+ *
+ * @author mcentner
+ */
+public class SignatureLocation {
+
+ /**
+ * Logging facility.
+ */
+ private static Log log = LogFactory.getLog(SignatureLocation.class);
+
+ /**
+ * The SignatureContext for the XML signature
+ */
+ private SignatureContext ctx;
+
+ /**
+ * The parent node for the XML signature.
+ */
+ private Node parent;
+
+ /**
+ * The next sibling node for the XML signature.
+ */
+ private Node nextSibling;
+
+ /**
+ * Creates a new SignatureLocation with the given <code>signatureContext</code>
+ *
+ * @param signatureContext the context for the XML signature creation
+ */
+ public SignatureLocation(SignatureContext signatureContext) {
+ this.ctx = signatureContext;
+ }
+
+ /**
+ * @return the parent node for the XML signature
+ */
+ public Node getParent() {
+ return parent;
+ }
+
+ /**
+ * @param parent the parent for the XML signature
+ */
+ public void setParent(Node parent) {
+ this.parent = parent;
+ }
+
+ /**
+ * @return the next sibling node for the XML signature
+ */
+ public Node getNextSibling() {
+ return nextSibling;
+ }
+
+ /**
+ * @param nextSibling the next sibling node for the XML signature
+ */
+ public void setNextSibling(Node nextSibling) {
+ this.nextSibling = nextSibling;
+ }
+
+ /**
+ * Configures this SignatureLocation with the information provided by the
+ * given <code>SignatureInfo</code> element.
+ *
+ * @param signatureInfo
+ * the <code>SignatureInfo</code> element
+ *
+ * @throws SLCommandException
+ * if configuring this SignatureLocation with given
+ * <code>signatureInfo</code>fails
+ */
+ public void setSignatureInfo(SignatureInfoCreationType signatureInfo)
+ throws SLCommandException {
+
+ // evaluate signature location XPath ...
+ SignatureLocationType signatureLocation = (SignatureLocationType) signatureInfo
+ .getSignatureLocation();
+
+ NamespaceContext namespaceContext = new MOAIDWorkaroundNamespaceContext(
+ signatureLocation.getNamespaceContext());
+
+ parent = evaluateSignatureLocation(signatureInfo.getSignatureLocation()
+ .getValue(), namespaceContext, ctx.getDocument().getDocumentElement());
+
+ // ... and index
+ nextSibling = findNextSibling(parent, signatureInfo.getSignatureLocation()
+ .getIndex().intValue());
+
+ }
+
+ /**
+ * Evaluates the given <code>xpath</code> with the document element as context node
+ * and returns the resulting node.
+ *
+ * @param xpath the XPath expression
+ * @param nsContext the namespace context of the XPath expression
+ * @param contextNode the context node for the XPath evaluation
+ *
+ * @return the result of evaluating the XPath expression
+ *
+ * @throws SLCommandException
+ */
+ private Node evaluateSignatureLocation(String xpath, NamespaceContext nsContext, Node contextNode) throws SLCommandException {
+
+ Node node = null;
+ try {
+ XPathFactory xpathFactory = XPathFactory.newInstance();
+ XPath xPath = xpathFactory.newXPath();
+ xPath.setNamespaceContext(nsContext);
+ XPathExpression xpathExpr = xPath.compile(xpath);
+ node = (Node) xpathExpr.evaluate(contextNode, XPathConstants.NODE);
+ } catch (XPathExpressionException e) {
+ log.info("Failed to evaluate SignatureLocation XPath expression '" + xpath + "' on context node.", e);
+ throw new SLCommandException(4102);
+ }
+
+ if (node == null) {
+ log.info("Failed to evaluate SignatureLocation XPath expression '" + xpath + "'. Result is empty.");
+ throw new SLCommandException(4102);
+ }
+
+ return node;
+
+ }
+
+ /**
+ * Finds the next sibling node of the <code>parent</code>'s <code>n</code>-th child node
+ * or <code>null</code> if there is no next sibling.
+ *
+ * @param parent the parent node
+ * @param n the index of the child node
+ *
+ * @return the next sibling node of the node specified by <code>parent</code> and index <code>n</code>,
+ * or <code>null</code> if there is no next sibling node.
+ *
+ * @throws SLCommandException if the <code>n</code>-th child of <code>parent</code> does not exist
+ */
+ private Node findNextSibling(Node parent, int n) throws SLCommandException {
+
+ NodeList childNodes = parent.getChildNodes();
+ Node childNode = childNodes.item(n);
+ if (childNode == null) {
+ log.info("SingatureLocation Index '" + n + "' not found in document.");
+ throw new SLCommandException(4102);
+ } else {
+ return childNode.getNextSibling();
+ }
+
+ }
+
+ /**
+ * Workaround for a missing namespace prefix declaration in MOA-ID.
+ *
+ * @author mcentner
+ */
+ private class MOAIDWorkaroundNamespaceContext implements NamespaceContext {
+
+ private NamespaceContext namespaceContext;
+
+ public MOAIDWorkaroundNamespaceContext(NamespaceContext namespaceContext) {
+ super();
+ this.namespaceContext = namespaceContext;
+ }
+
+ @Override
+ public String getNamespaceURI(String prefix) {
+
+ String namespaceURI = namespaceContext.getNamespaceURI(prefix);
+
+ if ((namespaceURI == null || XMLConstants.NULL_NS_URI.equals(namespaceURI)) && "saml".equals(prefix)) {
+ namespaceURI = "urn:oasis:names:tc:SAML:1.0:assertion";
+ log.debug("Namespace prefix '" + prefix + "' resolved to '" + namespaceURI + "' (MOA-ID Workaround).");
+ } else {
+ log.trace("Namespace prefix '" + prefix + "' resolved to '" + namespaceURI + "'.");
+ }
+
+ return namespaceURI;
+ }
+
+ @Override
+ public String getPrefix(String namespaceURI) {
+ return namespaceContext.getPrefix(namespaceURI);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Iterator getPrefixes(String namespaceURI) {
+ return namespaceContext.getPrefixes(namespaceURI);
+ }
+
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SimpleDOMErrorHandler.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SimpleDOMErrorHandler.java new file mode 100644 index 00000000..0d54adce --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/SimpleDOMErrorHandler.java @@ -0,0 +1,98 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.DOMError;
+import org.w3c.dom.DOMErrorHandler;
+
+/**
+ * A simple DOMErrorHandler implementation.
+ *
+ * @author mcentner
+ */
+public class SimpleDOMErrorHandler implements DOMErrorHandler {
+
+ /**
+ * Have there been errors reported?
+ */
+ private boolean errors = false;
+
+ /**
+ * Have there been fatal error reported?
+ */
+ private boolean fatalErrors = false;
+
+ /**
+ * The list of error messages of reported errors.
+ */
+ private List<String> errorMessages = new ArrayList<String>();
+
+ /**
+ * @return <code>true</code> if errors have been reported, or <code>false</code> otherwise
+ */
+ public boolean hasErrors() {
+ return errors;
+ }
+
+ /**
+ * @return <code>true</code> if fatal errors have been reported, or <code>false</code> otherwise
+ */
+ public boolean hasFatalErrors() {
+ return fatalErrors;
+ }
+
+ /**
+ * @return a list of error messages that have been reported
+ */
+ public List<String> getErrorMessages() {
+ return errorMessages;
+ }
+
+ /* (non-Javadoc)
+ * @see org.w3c.dom.DOMErrorHandler#handleError(org.w3c.dom.DOMError)
+ */
+ @Override
+ public boolean handleError(DOMError error) {
+
+ switch (error.getSeverity()) {
+
+ case DOMError.SEVERITY_WARNING :
+// log.debug("[warning] " + error.getMessage());
+ return true;
+
+ case DOMError.SEVERITY_ERROR :
+// log.debug("[error] " + error.getMessage());
+ errorMessages.add(error.getMessage());
+ errors = true;
+ return false;
+
+ case DOMError.SEVERITY_FATAL_ERROR :
+// log.debug("[fatal error] " + error.getMessage());
+ errorMessages.add(error.getMessage());
+ fatalErrors = true;
+ return false;
+
+ default:
+ return false;
+ }
+
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/URIDereferncerAdapter.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/URIDereferncerAdapter.java new file mode 100644 index 00000000..c94937be --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/URIDereferncerAdapter.java @@ -0,0 +1,103 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import iaik.xml.crypto.utils.URIDereferencerImpl;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import javax.xml.crypto.Data;
+import javax.xml.crypto.OctetStreamData;
+import javax.xml.crypto.URIDereferencer;
+import javax.xml.crypto.URIReference;
+import javax.xml.crypto.URIReferenceException;
+import javax.xml.crypto.XMLCryptoContext;
+
+import at.gv.egiz.bku.utils.urldereferencer.StreamData;
+import at.gv.egiz.bku.utils.urldereferencer.URLDereferencer;
+import at.gv.egiz.bku.utils.urldereferencer.URLDereferencerContext;
+
+/**
+ * An URIDereferencer implementation that uses an {@link URLDereferencer} to
+ * dereference.
+ *
+ * @author mcentner
+ */
+public class URIDereferncerAdapter implements URIDereferencer {
+
+ /**
+ * The context for dereferencing.
+ */
+ protected URLDereferencerContext urlDereferencerContext;
+
+ /**
+ * Creates a new URIDereferencerAdapter instance with the given
+ * <code>urlDereferencerContext</code>.
+ *
+ * @param urlDereferencerContext the context to be used for dereferencing
+ */
+ public URIDereferncerAdapter(URLDereferencerContext urlDereferencerContext) {
+ super();
+ this.urlDereferencerContext = urlDereferencerContext;
+ }
+
+ /* (non-Javadoc)
+ * @see javax.xml.crypto.URIDereferencer#dereference(javax.xml.crypto.URIReference, javax.xml.crypto.XMLCryptoContext)
+ */
+ @Override
+ public Data dereference(URIReference uriReference, XMLCryptoContext context)
+ throws URIReferenceException {
+
+ String uriString = uriReference.getURI();
+ if (uriString == null) {
+ return null;
+ }
+
+ URI uri;
+ try {
+ uri = new URI(uriString);
+ } catch (URISyntaxException e) {
+ throw new URIReferenceException(e.getMessage(), e);
+ }
+
+ if (uri.isAbsolute()) {
+
+ URLDereferencer dereferencer = URLDereferencer.getInstance();
+ StreamData streamData;
+ try {
+ streamData = dereferencer.dereference(uriString, urlDereferencerContext);
+ } catch (IOException e) {
+ throw new URIReferenceException(e.getMessage(), e);
+ }
+ return new OctetStreamData(streamData.getStream(), uriString, streamData.getContentType());
+
+ } else {
+
+ URIDereferencer uriDereferencer = context.getURIDereferencer();
+ if (uriDereferencer == null || uriDereferencer == this) {
+ uriDereferencer = new URIDereferencerImpl();
+ }
+
+ return uriDereferencer.dereference(uriReference, context);
+
+ }
+
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTReference.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTReference.java new file mode 100644 index 00000000..6b388f2a --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTReference.java @@ -0,0 +1,112 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import iaik.xml.crypto.dsig.DigestMethodImpl;
+import iaik.xml.crypto.dsig.DigestValueImpl;
+import iaik.xml.crypto.dsig.ReferenceImpl;
+import iaik.xml.crypto.dsig.TransformImpl;
+import iaik.xml.crypto.dsig.TransformsImpl;
+
+import javax.xml.crypto.Data;
+import javax.xml.crypto.URIDereferencer;
+import javax.xml.crypto.URIReferenceException;
+import javax.xml.crypto.XMLCryptoContext;
+import javax.xml.crypto.dsig.DigestMethod;
+import javax.xml.crypto.dsig.TransformException;
+
+import at.gv.egiz.bku.slexceptions.SLCommandException;
+import at.gv.egiz.bku.slexceptions.SLExceptionMessages;
+
+/**
+ * This class extends the XSECT ReferenceImpl to allow for the use
+ * of already marshalled <code>ds:Transforms</code> elements for initialization.
+ *
+ * @author mcentner
+ */
+public class XSECTReference extends ReferenceImpl {
+
+ /**
+ * The URIDereferencer to be used for dereferencing.
+ */
+ protected URIDereferencer dereferencer;
+
+ /**
+ * Creates a new instance of this XSECTReference with the given
+ * <code>uri</code>, digest method, <code>transforms</code>, <code>type</code>
+ * and <code>id</code> value.
+ *
+ * @param uri
+ * the <code>URI</code>-attribute value (may be <code>null</code>)
+ * @param dm
+ * the digest method
+ * @param transforms
+ * a TransformsImpl element (may be <code>null</code>)
+ * @param type
+ * the <code>Type</code>-attribute value (may be <code>null</code>)
+ * @param id
+ * the <code>Id</code>-attribute value (may be <code>null</code>)
+ *
+ * @throws NullPointerException
+ * if <code>digestMethod</code> is <code>null</code>
+ * @throws IllegalArgumentException
+ * if <code>uri</code> is not RFC 2396 compliant
+ * @throws ClassCastException
+ * if any of the <code>transforms</code> is not of type
+ * {@link TransformImpl}
+ */
+ public XSECTReference(String uri, DigestMethod dm, TransformsImpl transforms, String type,
+ String id) {
+ super(uri, transforms, type, id);
+ digestMethod_ = (DigestMethodImpl) dm;
+ digestValue_ = new DigestValueImpl();
+ }
+
+ /* (non-Javadoc)
+ * @see iaik.xml.crypto.dsig.ReferenceType#dereference(javax.xml.crypto.XMLCryptoContext)
+ */
+ @Override
+ public Data dereference(XMLCryptoContext context) throws TransformException,
+ URIReferenceException {
+ if (dereferencer != null) {
+ return dereferencer.dereference(this, context);
+ } else {
+ try {
+ return super.dereference(context);
+ } catch (URIReferenceException e) {
+ SLCommandException commandException = new SLCommandException(4003,
+ SLExceptionMessages.EC4003_NOT_RESOLVED, new Object[] { getURI() });
+ throw new URIReferenceException("Failed to dereference data to-be signed.", commandException);
+ }
+ }
+ }
+
+ /**
+ * @return the dereferencer to be used for dereferencing this reference
+ */
+ public URIDereferencer getDereferencer() {
+ return dereferencer;
+ }
+
+ /**
+ * @param dereferencer the dereferencer to be used for dereferencing this reference
+ */
+ public void setDereferencer(URIDereferencer dereferencer) {
+ this.dereferencer = dereferencer;
+ }
+
+}
diff --git a/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTTransforms.java b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTTransforms.java new file mode 100644 index 00000000..a98e4236 --- /dev/null +++ b/bkucommon/src/main/java/at/gv/egiz/bku/slcommands/impl/xsect/XSECTTransforms.java @@ -0,0 +1,124 @@ +/* +* Copyright 2008 Federal Chancellery Austria and +* Graz University of Technology +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package at.gv.egiz.bku.slcommands.impl.xsect;
+
+import iaik.xml.crypto.dsig.TransformImpl;
+import iaik.xml.crypto.dsig.TransformsImpl;
+
+import java.util.List;
+
+import javax.xml.crypto.MarshalException;
+import javax.xml.crypto.dom.DOMCryptoContext;
+import javax.xml.crypto.dsig.Transform;
+
+import org.w3c.dom.Node;
+
+/**
+ * This class extends the XSECT TransformsImpl to allow for the use of an
+ * unmarshalled <code>ds:Transforms</code> element for initalization.
+ *
+ * @author mcentner
+ */
+public class XSECTTransforms extends TransformsImpl {
+
+ /**
+ * Creates a new XSECTTransforms with the given list of <code>transforms</code>.
+ *
+ * @param transforms a list of {@link TransformImpl}s
+ * @see TransformsImpl#TransformsImpl(List)
+ */
+ @SuppressWarnings("unchecked")
+ public XSECTTransforms(List transforms) {
+ super(transforms);
+ }
+
+ /**
+ * Creates a new XSECTTransforms and initializes it from the given
+ * <code>ds:Transforms</code> node.
+ *
+ * @param context the context used for unmarshalling
+ * @param node the <code>ds:Transforms</code> node
+ *
+ * @throws MarshalException if unmarshalling the <code>ds:Transforms</code> fails
+ */
+ public XSECTTransforms(DOMCryptoContext context, Node node)
+ throws MarshalException {
+ super(context, node);
+ }
+
+ /**
+ * Inserts the given <code>transform</code> at the top of the
+ * transform list.
+ *
+ * @param transform the <code>ds:Transform</code> to instert
+ */
+ @SuppressWarnings("unchecked")
+ public void insertTransform(Transform transform) {
+ if (transform == null) {
+ throw new NullPointerException("Parameter 'transform' must not be null.");
+ }
+ if (!(transform instanceof TransformImpl)) {
+ throw new ClassCastException("Transform 'transform' must be of type '" + TransformImpl.class.getName() + "'.");
+ }
+ transforms_.add(0, transform);
+ }
+
+ /**
+ * @return
+ */
+ @SuppressWarnings("unchecked")
+ private List<TransformImpl> getTransformImpls() {
+ return transforms_;
+ }
+
+ /* (non-Javadoc)
+ * @see iaik.xml.crypto.dsig.TransformsType#marshal(javax.xml.crypto.dom.DOMCryptoContext, org.w3c.dom.Node, org.w3c.dom.Node)
+ */
+ @Override
+ public Node marshal(DOMCryptoContext context, Node parent, Node nextSibling)
+ throws MarshalException {
+
+ if (getNode() != null) {
+ // If this TransformsImpl has been unmarshalled from exiting nodes,
+ // we don't want to re-marshal ...
+ state_ = STATE_MARSHALED;
+
+ // ... but append the existing node to the parent ...
+ Node transformsNode = parent.insertBefore(getNode(), nextSibling);
+
+ // ... and marshal any Transforms not yet marshalled (e.g. that
+ // have been added via insertTransform().
+ Node transformNextSibling = transformsNode.getFirstChild();
+ List<TransformImpl> transforms = getTransformImpls();
+ for (int i = 0; i < transforms.size(); i++) {
+ TransformImpl transform = transforms.get(i);
+ Node transformNode = transform.getNode();
+ if (transformNode == null) {
+ // marshall TransformImpl
+ transformNode = transform.marshal(context, transformsNode, transformNextSibling);
+ }
+ transformNextSibling = transformNode.getNextSibling();
+ }
+
+ return transformsNode;
+ } else {
+ return super.marshal(context, parent, nextSibling);
+ }
+
+ }
+
+}
|