From b1c8641a63a67e3c64d948f9e8dce5c01e11e2dd Mon Sep 17 00:00:00 2001 From: mcentner Date: Wed, 5 May 2010 15:29:01 +0000 Subject: Merged feature branch mocca-1.2.13-id@r724 back to trunk. git-svn-id: https://joinup.ec.europa.eu/svn/mocca/trunk@725 8a26b1a7-26f0-462f-b9ef-d0e30c41f5a4 --- .../AbstractCommandSequenceBindingProcessor.java | 123 +++++++ .../at/gv/egiz/mocca/id/DataURLServerServlet.java | 225 +++++++++++++ .../src/main/java/at/gv/egiz/mocca/id/IdLink.java | 346 ++++++++++++++++++++ .../java/at/gv/egiz/mocca/id/IdLinkException.java | 43 +++ .../java/at/gv/egiz/mocca/id/IdLinkFactory.java | 154 +++++++++ .../at/gv/egiz/mocca/id/IdLinkKeySelector.java | 88 +++++ .../java/at/gv/egiz/mocca/id/IdLinkPersonData.java | 76 +++++ .../at/gv/egiz/mocca/id/PersonalIdentifier.java | 81 +++++ .../java/at/gv/egiz/mocca/id/QESTemplates.java | 115 +++++++ .../egiz/mocca/id/SAMLBindingProcessorFactory.java | 44 +++ .../gv/egiz/mocca/id/SAMLBindingProcessorImpl.java | 357 +++++++++++++++++++++ .../at/gv/egiz/mocca/id/SAMLRequestHandler.java | 33 ++ .../java/at/gv/egiz/mocca/id/SLCommandBrocker.java | 100 ++++++ 13 files changed, 1785 insertions(+) create mode 100644 BKUOnline/src/main/java/at/gv/egiz/mocca/id/AbstractCommandSequenceBindingProcessor.java create mode 100644 BKUOnline/src/main/java/at/gv/egiz/mocca/id/DataURLServerServlet.java create mode 100644 BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLink.java create mode 100644 BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkException.java create mode 100644 BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkFactory.java create mode 100644 BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkKeySelector.java create mode 100644 BKUOnline/src/main/java/at/gv/egiz/mocca/id/IdLinkPersonData.java create mode 100644 BKUOnline/src/main/java/at/gv/egiz/mocca/id/PersonalIdentifier.java create mode 100644 BKUOnline/src/main/java/at/gv/egiz/mocca/id/QESTemplates.java create mode 100644 BKUOnline/src/main/java/at/gv/egiz/mocca/id/SAMLBindingProcessorFactory.java create mode 100644 BKUOnline/src/main/java/at/gv/egiz/mocca/id/SAMLBindingProcessorImpl.java create mode 100644 BKUOnline/src/main/java/at/gv/egiz/mocca/id/SAMLRequestHandler.java create mode 100644 BKUOnline/src/main/java/at/gv/egiz/mocca/id/SLCommandBrocker.java (limited to 'BKUOnline/src/main/java/at/gv/egiz/mocca/id') 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 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 Assertion (root element) of the IdLink. + */ + protected AssertionType assertion; + + /** + * The citizen's asserted public keys. + */ + protected List 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 getCitizenPublicKeys() throws MarshalException { + if (citizenPublicKeys == null) { + + citizenPublicKeys = new ArrayList(); + + AttributeStatementType attributeStatement = getAttributeStatement(); + if (attributeStatement != null) { + List attributes = attributeStatement.getAttribute(); + for (AttributeType attribute : attributes) { + if ("urn:publicid:gv.at:namespaces:identitylink:1.2".equals(attribute.getAttributeNamespace()) + && "CitizenPublicKey".equals(attribute.getAttributeName())) { + List 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 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 objects = signature.getObjects(); + for (XMLObject object : objects) { + List content = object.getContent(); + if (content.get(0) instanceof Manifest) { + Manifest manifest = (Manifest) content.get(0); + List 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 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 templatesMap = Collections.synchronizedMap(new HashMap()); + + 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 supportedProtocols = Collections.singleton(Protocol.SAML); + + @Override + public Set 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 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 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 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 identityLinkDomainIdentifier = factory + .createIdentityLinkDomainIdentifier(domainIdentifier); + AnyChildrenType anyChildrenType = factory.createAnyChildrenType(); + anyChildrenType.getAny().add(identityLinkDomainIdentifier); + + infoboxReadRequestType.setBoxSpecificParameters(anyChildrenType); + } + + return factory.createInfoboxReadRequest(infoboxReadRequestType); + + } + + protected JAXBElement 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 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 commandSync = new Sync(); + + private Sync resultSync = new Sync(); + + 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 { + + 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(); + } + + } + +} -- cgit v1.2.3