summaryrefslogtreecommitdiff
path: root/BKUOnline/src/main/java/at/gv/egiz/mocca
diff options
context:
space:
mode:
Diffstat (limited to 'BKUOnline/src/main/java/at/gv/egiz/mocca')
-rw-r--r--BKUOnline/src/main/java/at/gv/egiz/mocca/id/AbstractCommandSequenceBindingProcessor.java123
-rw-r--r--BKUOnline/src/main/java/at/gv/egiz/mocca/id/DataURLServerServlet.java225
-rw-r--r--BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLink.java346
-rw-r--r--BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkException.java43
-rw-r--r--BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkFactory.java154
-rw-r--r--BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkKeySelector.java88
-rw-r--r--BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkPersonData.java76
-rw-r--r--BKUOnline/src/main/java/at/gv/egiz/mocca/id/PersonalIdentifier.java81
-rw-r--r--BKUOnline/src/main/java/at/gv/egiz/mocca/id/QESTemplates.java115
-rw-r--r--BKUOnline/src/main/java/at/gv/egiz/mocca/id/SAMLBindingProcessorFactory.java44
-rw-r--r--BKUOnline/src/main/java/at/gv/egiz/mocca/id/SAMLBindingProcessorImpl.java357
-rw-r--r--BKUOnline/src/main/java/at/gv/egiz/mocca/id/SAMLRequestHandler.java33
-rw-r--r--BKUOnline/src/main/java/at/gv/egiz/mocca/id/SLCommandBrocker.java100
13 files changed, 1785 insertions, 0 deletions
diff --git a/BKUOnline/src/main/java/at/gv/egiz/mocca/id/AbstractCommandSequenceBindingProcessor.java b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/AbstractCommandSequenceBindingProcessor.java
new file mode 100644
index 00000000..897ec227
--- /dev/null
+++ b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/AbstractCommandSequenceBindingProcessor.java
@@ -0,0 +1,123 @@
+/*
+* 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.mocca.id;
+
+import java.util.Collections;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import at.gv.egiz.bku.binding.AbstractBindingProcessor;
+import at.gv.egiz.bku.slcommands.SLCommand;
+import at.gv.egiz.bku.slcommands.SLCommandContext;
+import at.gv.egiz.bku.slcommands.SLResult;
+import at.gv.egiz.bku.slexceptions.SLCommandException;
+import at.gv.egiz.bku.slexceptions.SLException;
+import at.gv.egiz.stal.QuitRequest;
+
+public abstract class AbstractCommandSequenceBindingProcessor extends AbstractBindingProcessor {
+
+ protected static Logger log = LoggerFactory.getLogger(AbstractCommandSequenceBindingProcessor.class);
+
+ /**
+ * @return the error
+ */
+ protected Exception getError() {
+ return error;
+ }
+
+ /**
+ * @param error the error to set
+ */
+ protected void setError(Exception error) {
+ this.error = error;
+ }
+
+ private Exception error;
+
+ private SLCommandBrocker commandBrocker = new SLCommandBrocker();
+
+ /**
+ * External processing?
+ */
+ private boolean external;
+
+ /**
+ * Constructs a new instance of this IdBindingProcessorImpl with
+ * the given ID.
+ */
+ public AbstractCommandSequenceBindingProcessor() {
+ super();
+ }
+
+ /**
+ * @return the external
+ */
+ public boolean isExternal() {
+ return external;
+ }
+
+ /**
+ * @param external the external to set
+ */
+ public void setExternal(boolean external) {
+ this.external = external;
+ }
+
+ protected abstract SLCommand getNextCommand();
+
+ protected abstract void processResult(SLResult result);
+
+ @Override
+ public synchronized void process() {
+
+ try {
+
+ SLCommand command;
+ do {
+ command = getNextCommand();
+ SLCommandContext context = new SLCommandContext(getSTAL(), getUrlDereferencer(), locale);
+ SLResult result = null;
+ if (external) {
+ result = commandBrocker.execute(command, context, 3 * 60 * 1000);
+ } else {
+ if (command != null) {
+ result = command.execute(context);
+ } else {
+ stal.handleRequest(Collections.singletonList(new QuitRequest()));
+ }
+ }
+ if (result != null) {
+ processResult(result);
+ }
+ } while (command != null);
+
+ } catch (InterruptedException e) {
+ setError(new SLException(6000));
+ } catch (Exception e) {
+ log.info("BindingProcessor error.", e);
+ setError(e);
+ }
+
+ }
+
+ public SLCommand setExternalResult(SLResult slResult) throws SLCommandException, InterruptedException {
+ return commandBrocker.nextCommand(slResult, 3 * 60 * 1000);
+ }
+
+}
diff --git a/BKUOnline/src/main/java/at/gv/egiz/mocca/id/DataURLServerServlet.java b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/DataURLServerServlet.java
new file mode 100644
index 00000000..b40fd35f
--- /dev/null
+++ b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/DataURLServerServlet.java
@@ -0,0 +1,225 @@
+/*
+* 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.mocca.id;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Marshaller;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+import at.gv.egiz.bku.binding.BindingProcessor;
+import at.gv.egiz.bku.binding.FormParameter;
+import at.gv.egiz.bku.binding.IdFactory;
+import at.gv.egiz.bku.binding.InputDecoder;
+import at.gv.egiz.bku.binding.InputDecoderFactory;
+import at.gv.egiz.bku.online.webapp.SpringBKUServlet;
+import at.gv.egiz.bku.slcommands.SLCommand;
+import at.gv.egiz.bku.slcommands.SLMarshallerFactory;
+import at.gv.egiz.bku.slcommands.SLResult;
+import at.gv.egiz.bku.slcommands.impl.DomCreateXMLSignatureResultImpl;
+import at.gv.egiz.bku.slcommands.impl.DomErrorResultImpl;
+import at.gv.egiz.bku.slcommands.impl.DomInfoboxReadResultImpl;
+import at.gv.egiz.bku.slcommands.impl.ErrorResultImpl;
+import at.gv.egiz.bku.slcommands.impl.SLCommandImpl;
+import at.gv.egiz.bku.slexceptions.SLCommandException;
+import at.gv.egiz.bku.utils.DebugInputStream;
+import at.gv.egiz.bku.utils.StreamUtil;
+import at.gv.egiz.slbinding.SLUnmarshaller;
+
+public class DataURLServerServlet extends SpringBKUServlet {
+
+ private static Logger log = LoggerFactory.getLogger(DataURLServerServlet.class);
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ /* (non-Javadoc)
+ * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
+ */
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp)
+ throws ServletException, IOException {
+
+ String userAgent = req.getHeader("User-Agent");
+ String contentType = req.getContentType();
+ log.debug("Content-Type: " + contentType + " User-Agent: " + userAgent);
+
+ InputDecoder dec = InputDecoderFactory.getDecoder(contentType, req.getInputStream());
+
+ String sessionId = null;
+ Element respElement = null;
+
+ Iterator<FormParameter> formParams = dec.getFormParameterIterator();
+ while(formParams.hasNext()) {
+ FormParameter parameter = formParams.next();
+ String name = parameter.getFormParameterName();
+ if ("SessionID_".equals(name)) {
+ sessionId = StreamUtil.asString(parameter.getFormParameterValue(), "UTF-8");
+ log.debug("SessionID: {}", sessionId);
+ } else if ("ResponseType".equals(name)) {
+ String parameterContentType = parameter.getFormParameterContentType();
+ if (log.isDebugEnabled()) {
+ log.debug("ResponseType: ({}) {}.", parameterContentType, StreamUtil.asString(parameter.getFormParameterValue(), "UTF-8"));
+ }
+ } else if ("XMLResponse".equals(name)) {
+ InputStream inputStream = parameter.getFormParameterValue();
+
+ DebugInputStream di = null;
+ if (log.isDebugEnabled()) {
+ di = new DebugInputStream(inputStream);
+ inputStream = di;
+ }
+
+ SLUnmarshaller slUnmarshaller = new SLUnmarshaller();
+
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ dbf.setNamespaceAware(true);
+ dbf.setSchema(slUnmarshaller.getSlSchema());
+ try {
+ dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+ } catch (ParserConfigurationException e) {
+ log.warn("Failed to enable secure processing.", e);
+ }
+
+ // http://www.w3.org/TR/xmldsig-bestpractices/#be-aware-schema-normalization
+ try {
+ dbf.setAttribute("http://apache.org/xml/features/validation/schema/normalized-value", Boolean.FALSE);
+ } catch (IllegalArgumentException e) {
+ log.warn("Failed to disable schema normalization " +
+ "(see http://www.w3.org/TR/xmldsig-bestpractices/#be-aware-schema-normalization)", e);
+ }
+
+ DocumentBuilder documentBuilder;
+ try {
+ documentBuilder = dbf.newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ log.error("Failed to create parser for Security Layer response." , e);
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ try {
+ Document doc = documentBuilder.parse(inputStream);
+ respElement = doc.getDocumentElement();
+ } catch (SAXException e) {
+ log.info("Failed to parse Security Layer response.", e);
+ // TODO set error and redirect
+ resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ return;
+ }
+
+ if (di != null) {
+ log.debug("XMLResponse:\n{}", new String(di.getBufferedBytes(), "UTF-8"));
+ }
+
+ }
+
+ }
+
+ SAMLBindingProcessorImpl bindingProcessor = null;
+ if (sessionId != null) {
+ bindingProcessor = getBindingProcessor(sessionId);
+ }
+
+ if (bindingProcessor != null && respElement != null) {
+
+ SLResult slResult = null;
+ if ("http://www.buergerkarte.at/namespaces/securitylayer/1.2#".equals(respElement.getNamespaceURI())) {
+ if ("NullOperationResponse".equals(respElement.getLocalName())) {
+ slResult = null;
+ } else if ("InfoboxReadResponse".equals(respElement.getLocalName())) {
+ slResult = new DomInfoboxReadResultImpl(respElement);
+ } else if ("CreateXMLSignatureResponse".equals(respElement.getLocalName())) {
+ slResult = new DomCreateXMLSignatureResultImpl(respElement);
+ } else if ("ErrorResponse".equals(respElement.getLocalName())) {
+ slResult = new DomErrorResultImpl(respElement);
+ } else {
+ // TODO: report proper error
+ at.gv.egiz.bku.slexceptions.SLException slException = new at.gv.egiz.bku.slexceptions.SLException(0);
+ slResult = new ErrorResultImpl(slException, null);
+ }
+
+ }
+
+ SLCommand slCommand = null;
+ try {
+ slCommand = bindingProcessor.setExternalResult(slResult);
+ } catch (SLCommandException e) {
+ log.debug(e.getMessage());
+ } catch (InterruptedException e) {
+ // interrupted
+ }
+
+ if (slCommand instanceof SLCommandImpl<?>) {
+ JAXBElement<?> request = ((SLCommandImpl<?>) slCommand).getRequest();
+ Marshaller marshaller = SLMarshallerFactory.getInstance().createMarshaller(false, false);
+ try {
+
+ resp.setCharacterEncoding("UTF-8");
+ resp.setContentType("text/xml");
+
+ marshaller.marshal(request, resp.getOutputStream());
+
+ return;
+
+ } catch (JAXBException e) {
+ log.error("Failed to marshall Security Layer request.", e);
+ }
+
+ }
+
+ }
+
+ resp.sendRedirect("bkuResult");
+
+ }
+
+ protected SAMLBindingProcessorImpl getBindingProcessor(String sessionId) {
+
+ BindingProcessor bp = getBindingProcessorManager().getBindingProcessor(
+ IdFactory.getInstance().createId(sessionId));
+
+ if (bp instanceof SAMLBindingProcessorImpl) {
+ log.debug("Found active BindingProcessor, using this one.");
+ return (SAMLBindingProcessorImpl) bp;
+ }
+
+ return null;
+
+ }
+
+
+}
diff --git a/BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLink.java b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLink.java
new file mode 100644
index 00000000..fd4ef8e7
--- /dev/null
+++ b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLink.java
@@ -0,0 +1,346 @@
+/*
+* 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.mocca.id;
+
+import iaik.xml.crypto.dom.DOMCryptoContext;
+import iaik.xml.crypto.dsig.keyinfo.KeyValueType;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+import java.text.ParseException;
+import java.util.ArrayList;
+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.Manifest;
+import javax.xml.crypto.dsig.Reference;
+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.DOMValidateContext;
+import javax.xml.crypto.dsig.keyinfo.KeyInfo;
+import javax.xml.crypto.dsig.keyinfo.X509Data;
+
+import oasis.names.tc.saml._1_0.assertion.AnyType;
+import oasis.names.tc.saml._1_0.assertion.AssertionType;
+import oasis.names.tc.saml._1_0.assertion.AttributeStatementType;
+import oasis.names.tc.saml._1_0.assertion.AttributeType;
+import oasis.names.tc.saml._1_0.assertion.StatementAbstractType;
+import oasis.names.tc.saml._1_0.assertion.SubjectConfirmationType;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import at.gv.e_government.reference.namespace.persondata._20020228_.PhysicalPersonType;
+import at.gv.egiz.bku.utils.StreamUtil;
+
+public class IdLink {
+
+ protected Logger log = LoggerFactory.getLogger(IdLink.class);
+
+ /**
+ * The IdLink is backed by a DOM.
+ */
+ protected Node node;
+
+ /**
+ * The <code>Assertion</code> (root element) of the IdLink.
+ */
+ protected AssertionType assertion;
+
+ /**
+ * The citizen's asserted public keys.
+ */
+ protected List<PublicKey> citizenPublicKeys;
+
+ /**
+ * The XMLSignature.
+ */
+ protected XMLSignature signature;
+
+ /**
+ * The assertion's signer certificate.
+ */
+ protected X509Certificate signerCert;
+
+ /**
+ * Is the assertion's signature manifest valid?
+ */
+ protected Boolean manifestValid;
+
+ /**
+ * Is the assertion's signature valid?
+ */
+ protected Boolean signatureValid;
+
+ /**
+ * The personal identifier
+ */
+ protected IdLinkPersonData personData;
+
+ public IdLink(Element node, AssertionType assertion) throws JAXBException {
+ this.node = node;
+ this.assertion = assertion;
+ }
+
+ public PhysicalPersonType getPhysicalPerson() {
+
+ AttributeStatementType attributeStatement = getAttributeStatement();
+ if (attributeStatement != null) {
+ JAXBElement<?> subjectConfirmation = attributeStatement.getSubject().getContent().get(0);
+ if (subjectConfirmation.getDeclaredType() == SubjectConfirmationType.class) {
+ Object data = ((SubjectConfirmationType) subjectConfirmation.getValue())
+ .getSubjectConfirmationData().getContent().get(0);
+ if (data instanceof JAXBElement<?>
+ && ((JAXBElement<?>) data).getValue() instanceof PhysicalPersonType) {
+ return (PhysicalPersonType) ((JAXBElement<?>) data).getValue();
+ }
+ }
+ }
+
+ return null;
+ }
+
+ public AttributeStatementType getAttributeStatement() {
+
+ StatementAbstractType statement =
+ assertion.getStatementOrSubjectStatementOrAuthenticationStatement().get(0);
+
+ if (statement instanceof AttributeStatementType) {
+ return (AttributeStatementType) statement;
+ }
+
+ return null;
+
+ }
+
+ public IdLinkPersonData getPersonData() throws MarshalException {
+ if (personData == null) {
+ try {
+ personData = new IdLinkPersonData(getPhysicalPerson());
+ } catch (ParseException e) {
+ throw new MarshalException(e);
+ }
+ }
+ return personData;
+ }
+
+ public List<PublicKey> getCitizenPublicKeys() throws MarshalException {
+ if (citizenPublicKeys == null) {
+
+ citizenPublicKeys = new ArrayList<PublicKey>();
+
+ AttributeStatementType attributeStatement = getAttributeStatement();
+ if (attributeStatement != null) {
+ List<AttributeType> attributes = attributeStatement.getAttribute();
+ for (AttributeType attribute : attributes) {
+ if ("urn:publicid:gv.at:namespaces:identitylink:1.2".equals(attribute.getAttributeNamespace())
+ && "CitizenPublicKey".equals(attribute.getAttributeName())) {
+ List<AnyType> value = attribute.getAttributeValue();
+ if (value.size() == 1 && value.get(0).getContent().size() == 1) {
+ Object object = value.get(0).getContent().get(0);
+ if (object instanceof Element) {
+ Element element = (Element) object;
+ DOMStructure structure = iaik.xml.crypto.dom.DOMStructure.getInstance(element, new DOMCryptoContext());
+ if (structure instanceof KeyValueType) {
+ citizenPublicKeys.add(((KeyValueType) structure).getPublicKey());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+ return citizenPublicKeys;
+ }
+
+ public XMLSignature getXMLSignature() throws MarshalException {
+ if (signature == null) {
+
+ Node n = node.getLastChild();
+ while (n != null && n.getNodeType() != Node.ELEMENT_NODE) {
+ n = n.getPreviousSibling();
+ }
+
+ if (n != null
+ && XMLSignature.XMLNS.equals(n.getNamespaceURI())
+ && "Signature".equals(n.getLocalName())) {
+
+ XMLSignatureFactory signatureFactory = XMLSignatureFactory.getInstance();
+ signature = signatureFactory.unmarshalXMLSignature(new DOMStructure(n));
+ }
+
+
+ }
+ return signature;
+ }
+
+ public X509Certificate getSignerCert() throws MarshalException {
+ if (signerCert == null) {
+
+ if (getXMLSignature() != null) {
+
+ KeyInfo keyInfo = signature.getKeyInfo();
+ if (keyInfo != null) {
+ List<?> content = keyInfo.getContent();
+ for (Object data : content) {
+ if (data instanceof X509Data) {
+ List<?> x509Data = ((X509Data) data).getContent();
+ for (Object object : x509Data) {
+ if (object instanceof X509Certificate) {
+ signerCert = (X509Certificate) object;
+ return signerCert;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return signerCert;
+ }
+
+
+ @SuppressWarnings("unchecked")
+ public boolean verifySignature() throws MarshalException, XMLSignatureException {
+ if (signatureValid == null) {
+ if (getXMLSignature() != null && getSignerCert() != null) {
+
+ DOMValidateContext validateContext = new DOMValidateContext(signerCert.getPublicKey(), node);
+ validateContext.setProperty("javax.xml.crypto.dsig.cacheReference", Boolean.TRUE);
+
+ signatureValid = signature.validate(validateContext);
+
+ // logging
+ if (!signatureValid && log.isTraceEnabled()) {
+ List<Reference> references = signature.getSignedInfo().getReferences();
+ for (Reference reference : references) {
+ if (!Manifest.TYPE.equals(reference.getType())) {
+ if (!reference.validate(validateContext)) {
+ InputStream digestInputStream = reference.getDigestInputStream();
+ if (digestInputStream != null) {
+ try {
+ log.trace("SignedInfo's reference digest input:\n{}",
+ StreamUtil.asString(digestInputStream, "UTF-8"));
+ } catch (IOException e) {
+ log.info("Failed to get SignedInfos's reference digest input", e.toString());
+ }
+ }
+ } else {
+ try {
+ log.trace("Signature canonicalized data:\n{}", StreamUtil.asString(signature
+ .getSignedInfo().getCanonicalizedData(), "UTF-8"));
+ } catch (IOException e) {
+ log.info("Failed to get canonicalized data.", e);
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ }
+ }
+ return signatureValid;
+ }
+
+ @SuppressWarnings("unchecked")
+ public boolean verifyManifest() throws MarshalException, XMLSignatureException {
+ if (manifestValid == null) {
+ if (getXMLSignature() != null && getSignerCert() != null) {
+
+ DOMValidateContext validateContext = new DOMValidateContext(signerCert.getPublicKey(), node);
+ if (log.isTraceEnabled()) {
+ // enable reference caching in trace log-level
+ validateContext.setProperty("javax.xml.crypto.dsig.cacheReference", Boolean.TRUE);
+ }
+ boolean valid = false;
+
+ // validate manifest
+ List<XMLObject> objects = signature.getObjects();
+ for (XMLObject object : objects) {
+ List<?> content = object.getContent();
+ if (content.get(0) instanceof Manifest) {
+ Manifest manifest = (Manifest) content.get(0);
+ List<Reference> references = manifest.getReferences();
+ for (Reference reference : references) {
+
+ valid = reference.validate(validateContext);
+
+ // logging
+ if (!valid && log.isTraceEnabled()) {
+ InputStream digestInputStream = reference.getDigestInputStream();
+ if (digestInputStream != null) {
+ try {
+ log.trace("Manifest's reference digest input:\n{}",
+ StreamUtil.asString(digestInputStream, "UTF-8"));
+ } catch (IOException e) {
+ log.info("Failed to get Manifest's reference digest input", e.toString());
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // validate reference to manifest
+ if (valid) {
+ List<Reference> references = signature.getSignedInfo().getReferences();
+ for (Reference reference : references) {
+ if (Manifest.TYPE.equals(reference.getType())) {
+
+ boolean refValid = reference.validate(validateContext);
+
+ // logging
+ if (!refValid && log.isTraceEnabled()) {
+ InputStream digestInputStream = reference.getDigestInputStream();
+ if (digestInputStream != null) {
+ try {
+ log.trace("SignedInfo's manifest reference digest input:\n{}",
+ StreamUtil.asString(digestInputStream, "UTF-8"));
+ } catch (IOException e) {
+ log.info("Failed to get SignedInfos's manifest reference digest input", e.toString());
+ }
+ }
+ }
+
+ valid &= refValid;
+
+ }
+ }
+ }
+
+ manifestValid = valid;
+
+ }
+
+ }
+ return manifestValid;
+ }
+
+}
diff --git a/BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkException.java b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkException.java
new file mode 100644
index 00000000..12383861
--- /dev/null
+++ b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkException.java
@@ -0,0 +1,43 @@
+/*
+* 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.mocca.id;
+
+public class IdLinkException extends Exception {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public IdLinkException() {
+ super();
+ }
+
+ public IdLinkException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public IdLinkException(String message) {
+ super(message);
+ }
+
+ public IdLinkException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkFactory.java b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkFactory.java
new file mode 100644
index 00000000..90312af3
--- /dev/null
+++ b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkFactory.java
@@ -0,0 +1,154 @@
+/*
+* 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.mocca.id;
+
+import java.io.IOException;
+import java.net.URL;
+
+import javax.xml.XMLConstants;
+import javax.xml.bind.JAXBContext;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.bind.Unmarshaller;
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+
+import oasis.names.tc.saml._1_0.assertion.AssertionType;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+public class IdLinkFactory {
+
+ protected static Logger log = LoggerFactory.getLogger(IdLinkFactory.class);
+
+ public static final String[] SCHEMA_FILES = new String[] {
+ "at/gv/egiz/mocca/id/idlschema/xmldsig-more.xsd",
+ "at/gv/egiz/mocca/id/idlschema/xmldsig-core-schema.xsd",
+ "at/gv/egiz/mocca/id/idlschema/PersonData.xsd",
+ "at/gv/egiz/mocca/id/idlschema/oasis-sstc-saml-schema-assertion-1.0.xsd"};
+
+ private static class InstanceHolder {
+ private static final IdLinkFactory INSTANCE = new IdLinkFactory();
+ }
+
+ public static IdLinkFactory getInstance() {
+ return InstanceHolder.INSTANCE;
+ }
+
+ static {
+// InitDOMStructure.init();
+ }
+
+ private final Schema idlSchema;
+
+ private final JAXBContext jaxbContext;
+
+
+ private IdLinkFactory() {
+
+ try {
+ SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+ 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 RuntimeException("Failed to load schema file " + schemaFile + ".");
+ }
+ log.debug("Schema location: " + schemaURL);
+ sources[i] = new StreamSource(schemaURL.openStream());
+ }
+ idlSchema = schemaFactory.newSchema(sources);
+ } catch (IOException e) {
+ log.error("Failed to load identity link schema.", e);
+ throw new RuntimeException(e);
+ } catch (SAXException e) {
+ log.error("Failed to load identity link schema.", e);
+ throw new RuntimeException(e);
+ }
+
+ StringBuffer packageNames = new StringBuffer();
+ packageNames.append(at.gv.e_government.reference.namespace.persondata._20020228_.ObjectFactory.class.getPackage().getName());
+ packageNames.append(":");
+ packageNames.append(oasis.names.tc.saml._1_0.assertion.ObjectFactory.class.getPackage().getName());
+
+ try {
+ jaxbContext = JAXBContext.newInstance(packageNames.toString());
+ } catch (JAXBException e) {
+ // we should not get an JAXBException initializing the JAXBContext
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ public IdLink unmarshallIdLink(InputSource source) throws IdLinkException,
+ ParserConfigurationException, SAXException, IOException, JAXBException {
+
+ DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+ dbf.setNamespaceAware(true);
+ dbf.setSchema(idlSchema);
+ dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
+
+ // http://www.w3.org/TR/xmldsig-bestpractices/#be-aware-schema-normalization
+ try {
+ dbf.setAttribute("http://apache.org/xml/features/validation/schema/normalized-value", Boolean.FALSE);
+ } catch (IllegalArgumentException e) {
+ log.warn("Failed to disable schema normalization " +
+ "(see http://www.w3.org/TR/xmldsig-bestpractices/#be-aware-schema-normalization)", e);
+ }
+
+ DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
+ Document doc = documentBuilder.parse(source);
+
+ return unmarshallIdLink(doc.getDocumentElement());
+
+ }
+
+ public IdLink unmarshallIdLink(Element element) throws IdLinkException, JAXBException {
+
+ Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
+ unmarshaller.setSchema(idlSchema);
+
+ Object object = unmarshaller.unmarshal(element);
+
+ IdLink idLink;
+ if (object instanceof JAXBElement<?>
+ && ((JAXBElement<?>) object).getDeclaredType() == AssertionType.class) {
+ idLink = new IdLink(element, (AssertionType) ((JAXBElement<?>) object).getValue());
+ } else {
+ throw new IllegalArgumentException("Parameter node is not a "
+ + new QName("urn:oasis:names:tc:SAML:1.0:assertion", "Assertion"));
+ }
+
+ return idLink;
+
+ }
+
+}
diff --git a/BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkKeySelector.java b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkKeySelector.java
new file mode 100644
index 00000000..493b92af
--- /dev/null
+++ b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkKeySelector.java
@@ -0,0 +1,88 @@
+/*
+* 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.mocca.id;
+
+import java.security.Key;
+import java.security.PublicKey;
+import java.security.cert.X509Certificate;
+
+import javax.xml.crypto.AlgorithmMethod;
+import javax.xml.crypto.KeySelector;
+import javax.xml.crypto.KeySelectorException;
+import javax.xml.crypto.KeySelectorResult;
+import javax.xml.crypto.MarshalException;
+import javax.xml.crypto.XMLCryptoContext;
+import javax.xml.crypto.dsig.keyinfo.KeyInfo;
+import javax.xml.crypto.dsig.keyinfo.X509Data;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class IdLinkKeySelector extends KeySelector {
+
+ private static Logger log = LoggerFactory.getLogger(IdLinkKeySelector.class);
+
+ private IdLink idLink;
+
+ public IdLinkKeySelector(IdLink idLink) {
+ super();
+ if (idLink == null) {
+ throw new NullPointerException("Parameter 'idLink' must not be null.");
+ }
+ this.idLink = idLink;
+ }
+
+ @Override
+ public KeySelectorResult select(KeyInfo keyInfo, Purpose purpose,
+ AlgorithmMethod method, XMLCryptoContext context)
+ throws KeySelectorException {
+
+ if (purpose != Purpose.VERIFY) {
+ throw new KeySelectorException("KeySelector does not support purpose "
+ + purpose + ".");
+ }
+
+ try {
+ for (Object ki : keyInfo.getContent()) {
+ if (ki instanceof X509Data) {
+ for (Object xd : ((X509Data) ki).getContent()) {
+ if (xd instanceof X509Certificate) {
+ final PublicKey publicKey = ((X509Certificate) xd).getPublicKey();
+ if (idLink.getCitizenPublicKeys().contains(publicKey)) {
+ log.trace("Found matching key {} in identiy link and KeyInfo.", publicKey);
+ return new KeySelectorResult() {
+ @Override
+ public Key getKey() {
+ return publicKey;
+ }
+ };
+ }
+ }
+ }
+ }
+ }
+ } catch (MarshalException e) {
+ log.info("Failed to get public keys from identity link.", e);
+ throw new KeySelectorException(e);
+ }
+
+ log.info("Did not find matching public keys in the identity link and the KeyInfo.");
+ return null;
+ }
+
+}
diff --git a/BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkPersonData.java b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkPersonData.java
new file mode 100644
index 00000000..5b6f4453
--- /dev/null
+++ b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkPersonData.java
@@ -0,0 +1,76 @@
+/*
+* 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.mocca.id;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import at.gv.e_government.reference.namespace.persondata._20020228_.IdentificationType;
+import at.gv.e_government.reference.namespace.persondata._20020228_.PhysicalPersonType;
+
+public class IdLinkPersonData {
+
+ private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
+
+ protected PersonalIdentifier identifier;
+
+ protected String familyName;
+
+ protected String givenName;
+
+ protected Date dateOfBirth;
+
+ public IdLinkPersonData(PhysicalPersonType physicalPerson) throws ParseException {
+ familyName = physicalPerson.getName().getFamilyName().get(0).getValue();
+ givenName = physicalPerson.getName().getGivenName().get(0);
+ dateOfBirth = DATE_FORMAT.parse(physicalPerson.getDateOfBirth());
+ IdentificationType identificationType = physicalPerson.getIdentification().get(0);
+ if (identificationType != null) {
+ identifier = new PersonalIdentifier(identificationType.getType(),
+ identificationType.getValue().getValue());
+ }
+ }
+
+ public String getGivenName() {
+ return givenName;
+ }
+
+ public String getFamilyName() {
+ return familyName;
+ }
+
+ public Date getDateOfBirth() throws ParseException {
+ return dateOfBirth;
+ }
+
+ public PersonalIdentifier getIdentifier() {
+ return identifier;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ return familyName + ", " + givenName + ", " + DATE_FORMAT.format(dateOfBirth);
+ }
+
+
+}
diff --git a/BKUOnline/src/main/java/at/gv/egiz/mocca/id/PersonalIdentifier.java b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/PersonalIdentifier.java
new file mode 100644
index 00000000..ad108dc2
--- /dev/null
+++ b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/PersonalIdentifier.java
@@ -0,0 +1,81 @@
+/*
+* 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.mocca.id;
+
+import iaik.utils.Base64OutputStream;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class PersonalIdentifier {
+
+ public static final String PREFIX = "urn:publicid:gv.at:";
+
+ public static final String BASE_ID = PREFIX + "baseid";
+
+
+ protected String type;
+
+ protected String value;
+
+ public PersonalIdentifier(String type, String value) {
+ this.type = type;
+ this.value = value;
+ }
+
+ /**
+ * @return the type
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * @return the value
+ */
+ public String getValue() {
+ return value;
+ }
+
+ public PersonalIdentifier getDerivedValue(String domainId) {
+
+ if (BASE_ID.equals(type)) {
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA");
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ Base64OutputStream bos = new Base64OutputStream(os);
+ bos.write(md.digest((value + '+' + domainId).getBytes("ISO-8859-1")));
+ bos.flush();
+ return new PersonalIdentifier(domainId, os.toString("ASCII"));
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return null;
+
+ }
+
+
+}
diff --git a/BKUOnline/src/main/java/at/gv/egiz/mocca/id/QESTemplates.java b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/QESTemplates.java
new file mode 100644
index 00000000..8737d39c
--- /dev/null
+++ b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/QESTemplates.java
@@ -0,0 +1,115 @@
+/*
+* 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.mocca.id;
+
+import java.io.InputStream;
+import java.io.StringWriter;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.xml.crypto.MarshalException;
+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.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import at.gv.egiz.bku.slexceptions.SLRuntimeException;
+
+public class QESTemplates {
+
+ private Map<String, Templates> templatesMap = Collections.synchronizedMap(new HashMap<String, Templates>());
+
+ private synchronized Templates getTemplates(String id) {
+
+ Templates templates = templatesMap.get(id);
+ if (templates == null) {
+ templates = loadTemplates(id);
+ templatesMap.put(id, templates);
+ }
+ return templates;
+
+ }
+
+ protected Templates loadTemplates(String id) {
+
+ InputStream xsl = QESTemplates.class.getResourceAsStream("/templates/template.xsl");
+ if (xsl == null) {
+ throw new IllegalArgumentException("Template not found.");
+ }
+ TransformerFactory transformerFactory = TransformerFactory.newInstance();
+ try {
+ return transformerFactory.newTemplates(new StreamSource(xsl));
+ } catch (TransformerConfigurationException e) {
+ throw new SLRuntimeException(e);
+ }
+
+ }
+
+ public String createQESTemplate(String id, Locale locale, IdLink idLink, String url, PersonalIdentifier derivedIdentifier, Date dateTime) {
+
+ Templates templates = getTemplates(id);
+ try {
+ Transformer transformer = templates.newTransformer();
+
+ DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM, locale);
+ DateFormat timeFormat = DateFormat.getTimeInstance(DateFormat.MEDIUM, locale);
+
+ IdLinkPersonData personData = idLink.getPersonData();
+
+ transformer.setParameter("givenName", personData.getGivenName());
+ transformer.setParameter("familyName", personData.getFamilyName());
+ transformer.setParameter("dateOfBirth", dateFormat.format(personData.getDateOfBirth()));
+
+ transformer.setParameter("url", url);
+ transformer.setParameter("identifierType", derivedIdentifier.getType());
+ transformer.setParameter("identifierValue", derivedIdentifier.getValue());
+
+ transformer.setParameter("date", dateFormat.format(dateTime));
+ transformer.setParameter("time", timeFormat.format(dateTime));
+
+
+ StringWriter writer = new StringWriter();
+ transformer.transform(new StreamSource(), new StreamResult(writer));
+
+
+ return writer.toString();
+ } catch (TransformerConfigurationException e) {
+ throw new SLRuntimeException(e);
+ } catch (TransformerException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (MarshalException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (ParseException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ return null;
+
+ }
+
+}
diff --git a/BKUOnline/src/main/java/at/gv/egiz/mocca/id/SAMLBindingProcessorFactory.java b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/SAMLBindingProcessorFactory.java
new file mode 100644
index 00000000..9a71b32f
--- /dev/null
+++ b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/SAMLBindingProcessorFactory.java
@@ -0,0 +1,44 @@
+/*
+* 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.mocca.id;
+
+import java.util.Collections;
+import java.util.Set;
+
+import at.gv.egiz.bku.binding.AbstractBindingProcessorFactory;
+import at.gv.egiz.bku.binding.BindingProcessor;
+import at.gv.egiz.bku.binding.BindingProcessorFactory;
+import at.gv.egiz.bku.utils.binding.Protocol;
+
+public class SAMLBindingProcessorFactory extends AbstractBindingProcessorFactory implements BindingProcessorFactory {
+
+ private Set<Protocol> supportedProtocols = Collections.singleton(Protocol.SAML);
+
+ @Override
+ public Set<Protocol> getSupportedProtocols() {
+ return supportedProtocols;
+ }
+
+ @Override
+ public BindingProcessor createBindingProcessor() {
+ SAMLBindingProcessorImpl bindingProcessor = new SAMLBindingProcessorImpl();
+ configureBindingProcessor(bindingProcessor);
+ return bindingProcessor;
+ }
+
+}
diff --git a/BKUOnline/src/main/java/at/gv/egiz/mocca/id/SAMLBindingProcessorImpl.java b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/SAMLBindingProcessorImpl.java
new file mode 100644
index 00000000..ce4ac425
--- /dev/null
+++ b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/SAMLBindingProcessorImpl.java
@@ -0,0 +1,357 @@
+/*
+ * 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.mocca.id;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+import javax.xml.crypto.MarshalException;
+import javax.xml.crypto.dsig.XMLSignature;
+import javax.xml.crypto.dsig.XMLSignatureException;
+import javax.xml.crypto.dsig.XMLSignatureFactory;
+import javax.xml.crypto.dsig.dom.DOMValidateContext;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import at.buergerkarte.namespaces.securitylayer._1.AnyChildrenType;
+import at.buergerkarte.namespaces.securitylayer._1.Base64XMLLocRefOptRefContentType;
+import at.buergerkarte.namespaces.securitylayer._1.CreateXMLSignatureRequestType;
+import at.buergerkarte.namespaces.securitylayer._1.DataObjectInfoType;
+import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadParamsBinaryFileType;
+import at.buergerkarte.namespaces.securitylayer._1.InfoboxReadRequestType;
+import at.buergerkarte.namespaces.securitylayer._1.MetaInfoType;
+import at.buergerkarte.namespaces.securitylayer._1.ObjectFactory;
+import at.buergerkarte.namespaces.securitylayer._1.TransformsInfoType;
+import at.gv.egiz.bku.binding.FormParameter;
+import at.gv.egiz.bku.binding.HTTPBindingProcessor;
+import at.gv.egiz.bku.binding.HttpUtil;
+import at.gv.egiz.bku.binding.InputDecoder;
+import at.gv.egiz.bku.binding.InputDecoderFactory;
+import at.gv.egiz.bku.slcommands.CreateXMLSignatureResult;
+import at.gv.egiz.bku.slcommands.ErrorResult;
+import at.gv.egiz.bku.slcommands.InfoboxReadResult;
+import at.gv.egiz.bku.slcommands.SLCommand;
+import at.gv.egiz.bku.slcommands.SLCommandFactory;
+import at.gv.egiz.bku.slcommands.SLResult;
+import at.gv.egiz.bku.slexceptions.SLCommandException;
+import at.gv.egiz.bku.slexceptions.SLVersionException;
+
+public class SAMLBindingProcessorImpl extends
+ AbstractCommandSequenceBindingProcessor implements HTTPBindingProcessor {
+
+ private static final Logger log = LoggerFactory
+ .getLogger(SAMLBindingProcessorImpl.class);
+
+ private String requestContentType;
+
+ private String domainIdentifier = "urn:publicid:gv.at:wbpk+FN+468924i";
+
+ private String keyBoxIdentifier = "SecureSignatureKeypair";
+
+ private String url = "www.egiz.gv.at";
+
+ private QESTemplates templates = new QESTemplates();
+
+ private IdLink idLink;
+
+ private Element signature;
+
+ private SLResult errorResponse;
+
+ @Override
+ protected void processResult(SLResult result) {
+ if (result instanceof ErrorResult) {
+ ErrorResult errorResult = (ErrorResult) result;
+ log.info("Got ErrorResponse {}: {}", errorResult.getErrorCode(),
+ errorResult.getInfo());
+ errorResponse = result;
+ return;
+ } else if (result instanceof InfoboxReadResult) {
+ try {
+ processInfoboxReadResult((InfoboxReadResult) result);
+ if (idLink != null) {
+ try {
+ IdLinkPersonData personData = idLink.getPersonData();
+ log.info("Got idLink for {}.", personData);
+ } catch (MarshalException e) {
+ log.info("Failed to unmarshal idLink.");
+ }
+ }
+ } catch (JAXBException e) {
+ log.info("InfoboxReadResult contains unexpected data.", e);
+ errorResponse = result;
+ } catch (IdLinkException e) {
+ log.info("InfoboxReadResult contains invalid identity link.", e);
+ errorResponse = result;
+ }
+ } else if (result instanceof CreateXMLSignatureResult) {
+ signature = ((CreateXMLSignatureResult) result).getContent();
+ log.info("Got signature.");
+ boolean valid = validate(signature) && validate(idLink);
+ log.info("Signature is valid: " + valid);
+ }
+ }
+
+ @Override
+ protected SLCommand getNextCommand() {
+
+ JAXBElement<?> request = null;
+ if (errorResponse == null) {
+ if (idLink == null) {
+ request = createReadInfoboxRequest(domainIdentifier);
+ } else if (signature == null) {
+ request = createXMLSignatureRequest();
+ }
+ }
+
+ if (request != null) {
+ SLCommandFactory commandFactory = SLCommandFactory.getInstance();
+ try {
+ return commandFactory.createSLCommand(request);
+ } catch (SLCommandException e) {
+ log.error("Failed to create SLCommand.", e);
+ setError(e);
+ } catch (SLVersionException e) {
+ log.error("Failed to create SLCommand.", e);
+ setError(e);
+ }
+ }
+
+ return null;
+ }
+
+ protected void processInfoboxReadResult(InfoboxReadResult result)
+ throws JAXBException, IdLinkException {
+
+ Object object = result.getContent();
+ if (object instanceof byte[]) {
+ log.info("InfoboxReadResult contains unexpected binary data.");
+ errorResponse = result;
+ return;
+ } else if (object instanceof List<?>) {
+ JAXBException exception = null;
+ for (Object content : (List<?>) object) {
+ if (content instanceof Element) {
+ try {
+ idLink = IdLinkFactory.getInstance().unmarshallIdLink(
+ (Element) content);
+ return;
+ } catch (JAXBException e) {
+ exception = e;
+ }
+ }
+ }
+ if (exception != null) {
+ throw exception;
+ }
+ }
+
+ }
+
+ @Override
+ public void setHTTPHeaders(Map<String, String> headerMap) {
+ for (String header : headerMap.keySet()) {
+ if (HttpUtil.HTTP_HEADER_CONTENT_TYPE.equalsIgnoreCase(header)) {
+ requestContentType = headerMap.get(header);
+ }
+ }
+ }
+
+ @Override
+ public void consumeRequestStream(String url, InputStream is) {
+ InputDecoder inputDecoder = InputDecoderFactory.getDecoder(
+ requestContentType, is);
+ Iterator<FormParameter> fpi = inputDecoder.getFormParameterIterator();
+ while (fpi.hasNext()) {
+ FormParameter formParameter = fpi.next();
+ if ("BKUUrl".equals(formParameter.getFormParameterName())) {
+ setExternal(true);
+ }
+ }
+ }
+
+ @Override
+ public String getResultContentType() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ @Override
+ public void writeResultTo(OutputStream os, String encoding)
+ throws IOException {
+ // TODO Auto-generated method stub
+
+ }
+
+ protected JAXBElement<InfoboxReadRequestType> createReadInfoboxRequest(
+ String domainIdentifier) {
+
+ ObjectFactory factory = new ObjectFactory();
+
+ InfoboxReadRequestType infoboxReadRequestType = factory
+ .createInfoboxReadRequestType();
+ infoboxReadRequestType.setInfoboxIdentifier("IdentityLink");
+
+ InfoboxReadParamsBinaryFileType infoboxReadParamsBinaryFileType = factory
+ .createInfoboxReadParamsBinaryFileType();
+ infoboxReadParamsBinaryFileType.setContentIsXMLEntity(true);
+ infoboxReadRequestType
+ .setBinaryFileParameters(infoboxReadParamsBinaryFileType);
+
+ if (domainIdentifier != null) {
+ JAXBElement<String> identityLinkDomainIdentifier = factory
+ .createIdentityLinkDomainIdentifier(domainIdentifier);
+ AnyChildrenType anyChildrenType = factory.createAnyChildrenType();
+ anyChildrenType.getAny().add(identityLinkDomainIdentifier);
+
+ infoboxReadRequestType.setBoxSpecificParameters(anyChildrenType);
+ }
+
+ return factory.createInfoboxReadRequest(infoboxReadRequestType);
+
+ }
+
+ protected JAXBElement<CreateXMLSignatureRequestType> createXMLSignatureRequest() {
+
+ ObjectFactory factory = new ObjectFactory();
+
+ CreateXMLSignatureRequestType createXMLSignatureRequest = factory
+ .createCreateXMLSignatureRequestType();
+ createXMLSignatureRequest.setKeyboxIdentifier(keyBoxIdentifier);
+
+ DataObjectInfoType dataObjectInfoType = factory.createDataObjectInfoType();
+ dataObjectInfoType.setStructure("enveloping");
+
+ TransformsInfoType transformsInfoType = factory.createTransformsInfoType();
+ MetaInfoType metaInfoType = factory.createMetaInfoType();
+ metaInfoType.setMimeType("application/xhtml+xml");
+ transformsInfoType.setFinalDataMetaInfo(metaInfoType);
+
+ dataObjectInfoType.getTransformsInfo().add(transformsInfoType);
+
+ Base64XMLLocRefOptRefContentType contentType = factory
+ .createBase64XMLLocRefOptRefContentType();
+
+ PersonalIdentifier identifier;
+ try {
+ identifier = idLink.getPersonData().getIdentifier();
+ } catch (MarshalException e) {
+ setError(e);
+ return null;
+ }
+ if ("urn:publicid:gv.at:baseid".equals(identifier.getType())) {
+ identifier = identifier.getDerivedValue(domainIdentifier);
+ }
+ String template = templates.createQESTemplate("test", locale, idLink, "",
+ identifier, new Date());
+
+ contentType.setBase64Content(template.getBytes(Charset.forName("UTF-8")));
+
+ dataObjectInfoType.setDataObject(contentType);
+
+ createXMLSignatureRequest.getDataObjectInfo().add(dataObjectInfoType);
+
+ return factory.createCreateXMLSignatureRequest(createXMLSignatureRequest);
+
+ }
+
+ protected boolean validate(IdLink idLink) {
+ try {
+ if (domainIdentifier != null && domainIdentifier.startsWith("urn:publicid:gv.at:ccid")) {
+ if (!idLink.verifyManifest()) {
+ log.info("Identity link manifest verification failed.");
+ return false;
+ }
+ }
+ if (idLink.verifySignature()) {
+ return true;
+ }
+ } catch (MarshalException e) {
+ log.info("Identity link signature verification failed.", e);
+ } catch (XMLSignatureException e) {
+ log.info("Identity link signature verification failed.", e);
+ }
+ log.info("Identity link signature verification failed.");
+ return false;
+ }
+
+
+ protected boolean validate(Element signature) {
+
+ Document doc = signature.getOwnerDocument();
+ if (signature != signature.getOwnerDocument().getDocumentElement()) {
+ doc.replaceChild(signature, doc.getDocumentElement());
+ }
+
+ XMLSignatureFactory xmlSignatureFactory = XMLSignatureFactory.getInstance();
+
+ try {
+ IdLinkKeySelector keySelector = new IdLinkKeySelector(idLink);
+ DOMValidateContext validateContext = new DOMValidateContext(keySelector, signature);
+
+ XMLSignature xmlSignature = xmlSignatureFactory
+ .unmarshalXMLSignature(validateContext);
+
+ return xmlSignature.validate(validateContext);
+ } catch (MarshalException e) {
+ log.info("Failed to unmarshall signature.", e);
+ } catch (XMLSignatureException e) {
+ log.info("Failed to validate signature.", e);
+ }
+ return false;
+ }
+
+ @Override
+ public InputStream getFormData(String parameterName) {
+ if ("appletPage".equals(parameterName)) {
+ String appletPage = (isExternal()) ? "local.jsp" : "applet.jsp";
+ return new ByteArrayInputStream(appletPage.getBytes());
+ }
+ return null;
+ }
+
+ @Override
+ public String getRedirectURL() {
+ return null;
+ }
+
+ @Override
+ public int getResponseCode() {
+ return HttpServletResponse.SC_OK;
+ }
+
+ @Override
+ public Map<String, String> getResponseHeaders() {
+ return Collections.emptyMap();
+ }
+
+}
diff --git a/BKUOnline/src/main/java/at/gv/egiz/mocca/id/SAMLRequestHandler.java b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/SAMLRequestHandler.java
new file mode 100644
index 00000000..0209ca79
--- /dev/null
+++ b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/SAMLRequestHandler.java
@@ -0,0 +1,33 @@
+/*
+* 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.mocca.id;
+
+import javax.servlet.http.HttpServletRequest;
+
+import at.gv.egiz.bku.online.webapp.AbstractWebRequestHandler;
+
+public class SAMLRequestHandler extends AbstractWebRequestHandler {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ protected String getRequestProtocol(HttpServletRequest req) {
+ return "SAML";
+ }
+
+}
diff --git a/BKUOnline/src/main/java/at/gv/egiz/mocca/id/SLCommandBrocker.java b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/SLCommandBrocker.java
new file mode 100644
index 00000000..2e46a220
--- /dev/null
+++ b/BKUOnline/src/main/java/at/gv/egiz/mocca/id/SLCommandBrocker.java
@@ -0,0 +1,100 @@
+/*
+* 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.mocca.id;
+
+import at.gv.egiz.bku.slcommands.SLCommand;
+import at.gv.egiz.bku.slcommands.SLCommandContext;
+import at.gv.egiz.bku.slcommands.SLResult;
+import at.gv.egiz.bku.slcommands.impl.ErrorResultImpl;
+import at.gv.egiz.bku.slexceptions.SLCommandException;
+
+public class SLCommandBrocker {
+
+ private Sync<SLCommand> commandSync = new Sync<SLCommand>();
+
+ private Sync<SLResult> resultSync = new Sync<SLResult>();
+
+ public SLResult execute(SLCommand command, SLCommandContext context, long timeout) throws InterruptedException {
+ try {
+ commandSync.put(command, timeout);
+ if (command != null) {
+ return resultSync.get(timeout);
+ } else {
+ return null;
+ }
+ } catch (SLCommandException e) {
+ return new ErrorResultImpl(e, context.getLocale());
+ }
+ }
+
+ public SLCommand nextCommand(SLResult result, long timeout) throws SLCommandException, InterruptedException {
+ if (result != null) {
+ resultSync.put(result, timeout);
+ }
+ return commandSync.get(timeout);
+ }
+
+ public class Sync<R> {
+
+ private boolean available;
+
+ private R r;
+
+ public synchronized R get(long timeout) throws SLCommandException, InterruptedException {
+
+ long t0 = System.currentTimeMillis();
+ long elapsed = 0;
+
+ while (!available) {
+ wait(timeout - elapsed);
+ elapsed = System.currentTimeMillis() - t0;
+ if (elapsed > timeout) {
+ notifyAll();
+ throw new SLCommandException(6000);
+ }
+ }
+
+ R r = this.r;
+ this.r = null;
+ available = false;
+ notifyAll();
+ return r;
+ }
+
+ public synchronized void put(R r, long timeout) throws SLCommandException, InterruptedException {
+
+ long t0 = System.currentTimeMillis();
+ long elapsed = 0;
+
+ while (available) {
+ wait(timeout - elapsed);
+ elapsed = System.currentTimeMillis() - t0;
+ if (elapsed > timeout) {
+ notifyAll();
+ throw new SLCommandException(6000);
+ }
+ }
+
+ this.r = r;
+ available = true;
+ notifyAll();
+ }
+
+ }
+
+}