/** * */ package at.gv.egovernment.moa.id.auth.stork; import java.util.List; import java.util.Vector; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.xml.namespace.QName; import org.opensaml.common.binding.BasicSAMLMessageContext; import org.opensaml.saml2.binding.decoding.HTTPPostDecoder; import org.opensaml.saml2.core.Assertion; import org.opensaml.saml2.core.Attribute; import org.opensaml.saml2.metadata.RequestedAttribute; import org.opensaml.ws.transport.http.HTTPInTransport; import org.opensaml.ws.transport.http.HTTPOutTransport; import org.opensaml.ws.transport.http.HttpServletRequestAdapter; import org.opensaml.ws.transport.http.HttpServletResponseAdapter; import org.opensaml.xml.XMLObject; import org.opensaml.xml.schema.XSAny; import org.opensaml.xml.schema.XSString; import org.opensaml.xml.util.Base64; import org.opensaml.xml.util.XMLHelper; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import at.gv.egovernment.moa.id.auth.AuthenticationServer; import at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute; import at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttributeImpl; import at.gv.egovernment.moa.id.auth.data.IdentityLink; import at.gv.egovernment.moa.id.auth.exception.ParseException; import at.gv.egovernment.moa.id.auth.parser.IdentityLinkAssertionParser; import at.gv.egovernment.moa.id.auth.validator.parep.client.szrgw.CreateIdentityLinkResponse; import at.gv.egovernment.moa.id.auth.validator.parep.client.szrgw.SZRGWClientException; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.Constants; import at.gv.egovernment.moa.util.DateTimeUtils; import at.gv.egovernment.moa.util.StringUtils; import eu.stork.mw.messages.saml.STORKResponse; import eu.stork.vidp.messages.common.STORKConstants; import eu.stork.vidp.messages.util.SAMLUtil; import eu.stork.vidp.messages.util.XMLUtil; /** * * Handles all functionality for the processing of a STORK response * @author bzwattendorfer * */ public class STORKResponseProcessor { /** OASIS DSS Namespace */ public static final String OASIS_DSS_NS = "urn:oasis:names:tc:dss:1.0:core:schema"; /** OASIS DSS Success Message */ public static final String OASIS_DSS_SUCCESS_MSG = "urn:oasis:names:tc:dss:1.0:resultmajor:Success"; /** * Extracts a STORK response from a HTTP message * @param request HttpServletRequest * @param response HttpServletResponse * @return STORK Response * @throws STORKException */ public static STORKResponse receiveSTORKRepsonse(HttpServletRequest request, HttpServletResponse response) throws STORKException { HTTPInTransport httpInTransport = new HttpServletRequestAdapter(request); HTTPOutTransport httpOutTransport = new HttpServletResponseAdapter(response, request.isSecure()); httpInTransport.getPeerAddress(); String samlResponseString = request.getParameter("SAMLResponse"); if (StringUtils.isEmpty(samlResponseString)) { Logger.error("SAMLResponse not found in request."); throw new STORKException("SAMLResponse not found in request."); } BasicSAMLMessageContext samlMessageContext = new BasicSAMLMessageContext(); samlMessageContext.setInboundMessageTransport(httpInTransport); samlMessageContext.setOutboundMessageTransport(httpOutTransport); HTTPPostDecoder postDecoder = new HTTPPostDecoder(); try { postDecoder.decode(samlMessageContext); } catch (Exception e) { Logger.error("Error decoding SAMLResponse message", e); throw new STORKException("Error decoding SAMLResponse message", e); } if (!(samlMessageContext.getInboundSAMLMessage() instanceof STORKResponse)) { Logger.error("Message received is not a SAMLResponse message"); throw new STORKException("Message received is not a SAMLResponse message"); } STORKResponse samlResponse = (STORKResponse) samlMessageContext.getInboundSAMLMessage(); return samlResponse; } /** * Verifies a STORK response according STORK specification * @param storkResponse STORK Response to verify * @throws STORKException if validation fails */ public static void verifySTORKResponse(STORKResponse storkResponse) throws STORKException { ResponseVerifier responseVerifier = new PEPSConnectorResponseVerifier(); try { responseVerifier.verify(storkResponse); } catch (SecurityException e) { Logger.error("Error validating response message from PEPS.", e); throw new STORKException("Error validating response message from PEPS."); } } /** * Verifies a STORK assertion * @param assertion STORK assertion * @param ipAddress Client IP address * @param authnRequestID ID of the AuthnRequest * @param recipient recipient for verification * @param audience audience for verification * @param reqAttributeList RequestedAttribute list for verification * @throws STORKException */ public static void verifySTORKAssertion( Assertion assertion, String ipAddress, String authnRequestID, String recipient, String audience, List reqAttributeList) throws STORKException { //validate Assertion AssertionVerifier assertionVerifier = new PEPSConnectorAssertionVerifier(); try { assertionVerifier.verify(assertion, ipAddress, authnRequestID, recipient, audience, reqAttributeList); //verify if all required attributes are present PEPSConnectorAssertionVerifier.validateRequiredAttributes(reqAttributeList, assertion.getAttributeStatements().get(0).getAttributes()); } catch (SecurityException e) { Logger.error("Error verifying assertion from PEPS", e); throw new STORKException("Error validating assertion received from PEPS."); } } /** * Extracts the citizen signature from the signedDoc element present in the STORK assertion * @param storkAssertion STORK assertion * @return citizen signature as XML * @throws STORKException */ public static Element extractCitizenSignature(Assertion storkAssertion) throws STORKException { Logger.debug("Processing DSS signature response from PEPS"); Element signatureResponse = getSignedDocAttributeValue(storkAssertion); if (signatureResponse == null) { String msg = "Could not find DSS signature response in SAML assertion"; Logger.error(msg); throw new STORKException(msg); } Logger.debug("Found DSS signature in SAML assertion"); Logger.debug("DSS Signature creation response received from PEPS (pretty print):"); Logger.debug(XMLHelper.prettyPrintXML(signatureResponse)); Logger.trace("DSS Signature creation response received from PEPS (original):"); Logger.trace(XMLUtil.printXML(signatureResponse)); Element signature = getSignature(signatureResponse); if (signature == null) { String msg = "Could not find citizen signature in SAML assertion"; Logger.error(msg); throw new STORKException(msg); } Logger.debug("Found foreign citizen signature in SAML assertion (pretty print):"); Logger.debug(XMLHelper.prettyPrintXML(signature)); Logger.trace("Found foreign citizen signature in SAML assertion (original):"); Logger.trace(XMLUtil.printXML(signature)); return signature; } /** * Extracts the signedDoc attribute from a STORK assertion as XML * @param storkAssertion STORK assertion * @return Value of signedDoc attribute * @throws STORKException */ private static Element getSignedDocAttributeValue(Assertion storkAssertion) throws STORKException { XMLObject xmlObj = SAMLUtil.getAttributeValue(storkAssertion.getAttributeStatements().get(0).getAttributes(), STORKConstants.STORK_ATTRIBUTE_SIGNEDDOC); if (xmlObj instanceof XSAny) return getSignedDocAttributeValueFromAny((XSAny) xmlObj); else if (xmlObj instanceof XSString) return getSignedDocAttributValueFromString((XSString) xmlObj); else return null; } /** * Get signedDoc as XML if provided as anyType * @param any AttributeValue as anyType * @return signedDoc as XML */ private static Element getSignedDocAttributeValueFromAny(XSAny any) { if (!any.getUnknownXMLObjects(new QName(OASIS_DSS_NS, "SignResponse")).isEmpty()) { XMLObject xmlObj = any.getUnknownXMLObjects(new QName(OASIS_DSS_NS, "SignResponse")).get(0); return xmlObj.getDOM(); } else { return null; } } /** * Get signedDoc as XML if provided as String * @param string AttributeValue as String * @return signedDoc as XML * @throws STORKException */ private static Element getSignedDocAttributValueFromString(XSString string) throws STORKException { try { return XMLUtil.stringToDOM(string.getValue()); } catch (Exception e) { Logger.error("Error building DOM", e); throw new STORKException(e); } } /** * Extracts the signature value out of a DSS response * @param signatureResponse DSS signature response * @return signature * @throws STORKException */ private static Element getSignature(Element signatureResponse) throws STORKException { NodeList nList = signatureResponse.getElementsByTagNameNS(OASIS_DSS_NS, "ResultMajor"); String resultMajor = XMLUtil.getFirstTextValueFromNodeList(nList); if (StringUtils.isEmpty(resultMajor)) { String msg = "DSS response not correct, ResultMajor element missing."; Logger.error(msg); throw new STORKException(msg); } Logger.trace("ResultMajor of DSS response: " + resultMajor); if (!OASIS_DSS_SUCCESS_MSG.equals(resultMajor)) { String msg = "DSS response not correct, ResultMajor is " + resultMajor; Logger.error(msg); throw new STORKException(msg); } NodeList nList2 = signatureResponse.getElementsByTagNameNS(OASIS_DSS_NS, "Base64Signature");; String base64SigString = XMLUtil.getFirstTextValueFromNodeList(nList2); if (StringUtils.isEmpty(base64SigString)) { String msg = "DSS response not correct, Base64Signature element missing."; Logger.error(msg); throw new STORKException(msg); } Logger.trace("Base64Signature element of DSS response: " + base64SigString); String sigString = new String(Base64.decode(base64SigString)); try { return XMLUtil.stringToDOM(sigString); } catch (Exception e) { String msg = "Unable to extract signature from DSS response"; Logger.error(msg); throw new STORKException(msg); } } /** * Handels connection to SZR-GW and returns Identity Link on success * @param citizenSignature Citizen signature * @param attributeList Received attribute List in assertion * @return Identity Link * @throws STORKException */ public static IdentityLink connectToSZRGateway(Element citizenSignature, List attributeList) throws STORKException { Logger.trace("Calling SZR Gateway with the following attributes:"); String fiscalNumber = SAMLUtil.getAttributeStringValue(attributeList, STORKConstants.STORK_ATTRIBUTE_FISCALNUMBER); Logger.trace(STORKConstants.STORK_ATTRIBUTE_FISCALNUMBER + " : " + fiscalNumber); String givenName = SAMLUtil.getAttributeStringValue(attributeList, STORKConstants.STORK_ATTRIBUTE_GIVENNAME); Logger.trace(STORKConstants.STORK_ATTRIBUTE_GIVENNAME+ " : " + givenName); String lastName = SAMLUtil.getAttributeStringValue(attributeList, STORKConstants.STORK_ATTRIBUTE_SURNAME); Logger.trace(STORKConstants.STORK_ATTRIBUTE_SURNAME+ " : " + lastName); String dateOfBirth = SAMLUtil.getAttributeStringValue(attributeList, STORKConstants.STORK_ATTRIBUTE_DATEOFBIRTH); Logger.trace(STORKConstants.STORK_ATTRIBUTE_DATEOFBIRTH + " : " + dateOfBirth); if (!StringUtils.isEmpty(dateOfBirth)) { dateOfBirth = DateTimeUtils.formatPEPSDateToMOADate(dateOfBirth); } CreateIdentityLinkResponse response; IdentityLink identityLink = null; try { Logger.trace("Starting call..."); response = AuthenticationServer.getInstance().getIdentityLink(fiscalNumber, givenName, lastName, dateOfBirth, citizenSignature); if (response.isError()) { Logger.error("Receveid ErrorResponse from SZR Gateway."); throw new SZRGWClientException(response.getError()); } else { Logger.trace("Receveid Success Response from SZR Gateway."); Element samlAssertion = response.getAssertion(); IdentityLinkAssertionParser ilParser = new IdentityLinkAssertionParser(samlAssertion); identityLink = ilParser.parseIdentityLink(); Logger.debug("Received Identity Link from SZR Gateway"); //TODO: is this ok? // if (StringUtils.isEmpty(identityLink.getDateOfBirth())) { // identityLink.setDateOfBirth("9999-12-31"); // } } } catch (SZRGWClientException e) { Logger.error("Error connecting SZR-Gateway: ", e); throw new STORKException("Error connecting SZR-Gateway: ", e); } catch (ParseException e) { Logger.error("Error parsing IdentityLink received from SZR-Gateway: ", e); throw new STORKException("Error parsing IdentityLink received from SZR-Gateway: ", e); } catch (at.gv.egovernment.moa.id.client.SZRGWClientException e) { Logger.error("Error connecting SZR-Gateway: ", e); throw new STORKException("Error connecting SZR-Gateway: ", e); } return identityLink; } /** * Transforms additional STORK attributes to MOA Extended attributes * @param storkAttributeList STORK attribute list * @return */ public static List addAdditionalSTORKAttributes(List storkAttributeList) { List moaExtendedSAMLAttributeList = new Vector(); Logger.trace("Adding the following attributes to MOA assertion: "); int count = 0; //only add attributes different than eIdentifier, given name, surname, dateOfBirth, signedDoc for (Attribute attribute : storkAttributeList) { //attribute is not in default returned attribute set if (!STORKConstants.DEFAULT_STORK_RETURNED_ATTRIBUTE_SET.contains(attribute.getName())) { String attributeValue = null; if (!attribute.getAttributeValues().isEmpty()) { //we have attribute value attributeValue = SAMLUtil.getStringValueFromXMLObject(attribute.getAttributeValues().get(0)); } ExtendedSAMLAttribute extendedSAMLAttribute = new ExtendedSAMLAttributeImpl(attribute.getName(), attributeValue, Constants.STORK_NS_URI, 0); moaExtendedSAMLAttributeList.add(extendedSAMLAttribute); count++; Logger.trace("Additional attribute: " + attribute.getName()); } } Logger.debug("Added " + count + " STORK attribute(s) to the MOA assertion."); return moaExtendedSAMLAttributeList; } }