/* * Copyright 2014 Federal Chancellery Austria * MOA-ID has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, and Graz University of Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. */ package at.gv.egovernment.moa.id.auth.builder; import iaik.x509.X509Certificate; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Date; import java.util.GregorianCalendar; import java.util.List; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import org.opensaml.saml2.core.Assertion; import org.opensaml.saml2.core.Attribute; import org.opensaml.saml2.core.AttributeQuery; import org.opensaml.saml2.core.AttributeStatement; import org.opensaml.saml2.core.Response; import org.opensaml.ws.soap.common.SOAPException; import org.opensaml.xml.XMLObject; import org.opensaml.xml.security.SecurityException; import org.w3c.dom.Element; import org.w3c.dom.Node; import eu.stork.peps.auth.commons.PersonalAttribute; import eu.stork.peps.auth.commons.PersonalAttributeList; import at.gv.egovernment.moa.id.auth.MOAIDAuthConstants; import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; import at.gv.egovernment.moa.id.auth.data.IdentityLink; import at.gv.egovernment.moa.id.auth.data.VerifyXMLSignatureResponse; import at.gv.egovernment.moa.id.auth.exception.BuildException; import at.gv.egovernment.moa.id.auth.exception.DynamicOABuildException; import at.gv.egovernment.moa.id.auth.exception.ParseException; import at.gv.egovernment.moa.id.auth.exception.WrongParametersException; import at.gv.egovernment.moa.id.auth.parser.IdentityLinkAssertionParser; import at.gv.egovernment.moa.id.commons.db.MOASessionDBUtils; import at.gv.egovernment.moa.id.commons.db.dao.session.InterfederationSessionStore; import at.gv.egovernment.moa.id.commons.db.dao.session.OASessionStore; import at.gv.egovernment.moa.id.commons.db.ex.MOADatabaseException; import at.gv.egovernment.moa.id.config.ConfigurationException; import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider; import at.gv.egovernment.moa.id.config.auth.IOAAuthParameters; import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; import at.gv.egovernment.moa.id.data.AuthenticationData; import at.gv.egovernment.moa.id.data.IAuthData; import at.gv.egovernment.moa.id.moduls.IRequest; import at.gv.egovernment.moa.id.protocols.pvp2x.PVPConstants; import at.gv.egovernment.moa.id.protocols.pvp2x.PVPTargetConfiguration; import at.gv.egovernment.moa.id.protocols.pvp2x.builder.AttributQueryBuilder; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.AssertionAttributeExtractorExeption; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.AssertionValidationExeption; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.AttributQueryException; import at.gv.egovernment.moa.id.protocols.pvp2x.messages.MOARequest; import at.gv.egovernment.moa.id.protocols.pvp2x.utils.MOASAMLSOAPClient; import at.gv.egovernment.moa.id.protocols.pvp2x.verification.SAMLVerificationEngine; import at.gv.egovernment.moa.id.protocols.pvp2x.verification.TrustEngineFactory; import at.gv.egovernment.moa.id.protocols.saml1.SAML1AuthenticationData; import at.gv.egovernment.moa.id.protocols.saml1.SAML1RequestImpl; import at.gv.egovernment.moa.id.storage.AuthenticationSessionStoreage; import at.gv.egovernment.moa.id.util.IdentityLinkReSigner; import at.gv.egovernment.moa.id.util.ParamValidatorUtils; import at.gv.egovernment.moa.id.util.client.mis.simple.MISMandate; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.Base64Utils; import at.gv.egovernment.moa.util.Constants; import at.gv.egovernment.moa.util.MiscUtil; import at.gv.egovernment.moa.util.XPathUtils; /** * @author tlenz * */ public class AuthenticationDataBuilder implements MOAIDAuthConstants { public static IAuthData buildAuthenticationData(IRequest protocolRequest, AuthenticationSession session, List reqAttributes) throws ConfigurationException, BuildException, WrongParametersException, DynamicOABuildException { String oaID = protocolRequest.getOAURL(); if (oaID == null) { throw new WrongParametersException("StartAuthentication", PARAM_OA, "auth.12"); } // check parameter if (!ParamValidatorUtils.isValidOA(oaID)) throw new WrongParametersException("StartAuthentication", PARAM_OA, "auth.12"); AuthenticationData authdata = null; if (protocolRequest instanceof SAML1RequestImpl) { //request is SAML1 SAML1AuthenticationData saml1authdata = new SAML1AuthenticationData(); saml1authdata.setExtendedSAMLAttributesOA(session.getExtendedSAMLAttributesOA()); authdata = saml1authdata; } else { authdata = new AuthenticationData(); } //reuse some parameters if it is a reauthentication OASessionStore activeOA = AuthenticationSessionStoreage.searchActiveOASSOSession(session, oaID, protocolRequest.requestedModule()); if (activeOA != null) { authdata.setSessionIndex(activeOA.getAssertionSessionID()); authdata.setNameID(activeOA.getUserNameID()); authdata.setNameIDFormat(activeOA.getUserNameIDFormat()); //mark AttributeQuery as used if ( protocolRequest instanceof PVPTargetConfiguration && ((PVPTargetConfiguration) protocolRequest).getRequest() instanceof MOARequest && ((PVPTargetConfiguration) protocolRequest).getRequest().getInboundMessage() instanceof AttributeQuery) { try { activeOA.setAttributeQueryUsed(true); MOASessionDBUtils.saveOrUpdate(activeOA); } catch (MOADatabaseException e) { Logger.error("MOASession interfederation information can not stored to database.", e); } } } InterfederationSessionStore interfIDP = AuthenticationSessionStoreage.searchInterfederatedIDPFORAttributeQueryWithSessionID(session); IOAAuthParameters oaParam = null; if (reqAttributes == null) { //get OnlineApplication from MOA-ID-Auth configuration oaParam = AuthConfigurationProvider.getInstance() .getOnlineApplicationParameter(oaID); } else { //build OnlineApplication dynamic from requested attributes oaParam = DynamicOAAuthParameterBuilder.buildFromAttributeQuery(reqAttributes, interfIDP); } if (interfIDP != null ) { //IDP is a chained interfederated IDP and Authentication is requested if (oaParam.isInderfederationIDP() && protocolRequest instanceof PVPTargetConfiguration && !(((PVPTargetConfiguration)protocolRequest).getRequest() instanceof AttributeQuery)) { //only set minimal response attributes authdata.setQAALevel(interfIDP.getQAALevel()); authdata.setBPK(interfIDP.getUserNameID()); } else { //get attributes from interfederated IDP OAAuthParameter idp = AuthConfigurationProvider.getInstance().getOnlineApplicationParameter(interfIDP.getIdpurlprefix()); getAuthDataFromInterfederation(authdata, session, oaParam, protocolRequest, interfIDP, idp, reqAttributes); //mark attribute request as used try { interfIDP.setAttributesRequested(true); MOASessionDBUtils.saveOrUpdate(interfIDP); } catch (MOADatabaseException e) { Logger.error("MOASession interfederation information can not stored to database.", e); } } } else { //build AuthenticationData from MOASession buildAuthDataFormMOASession(authdata, session, oaParam); } return authdata; } /** * @param req * @param session * @param reqAttributes * @return * @throws WrongParametersException * @throws ConfigurationException * @throws BuildException * @throws DynamicOABuildException */ public static IAuthData buildAuthenticationData(IRequest req, AuthenticationSession session) throws WrongParametersException, ConfigurationException, BuildException, DynamicOABuildException { return buildAuthenticationData(req, session, null); } /** * @param authdata * @param session * @param oaParam * @param protocolRequest * @param interfIDP * @param idp * @param reqQueryAttr * @throws ConfigurationException */ private static void getAuthDataFromInterfederation( AuthenticationData authdata, AuthenticationSession session, IOAAuthParameters oaParam, IRequest req, InterfederationSessionStore interfIDP, OAAuthParameter idp, List reqQueryAttr) throws BuildException, ConfigurationException{ try { List attributs = null; //IDP is a chained interfederated IDP and request is of type AttributQuery if (oaParam.isInderfederationIDP() && req instanceof PVPTargetConfiguration && (((PVPTargetConfiguration)req).getRequest() instanceof AttributeQuery) && reqQueryAttr != null) { attributs = reqQueryAttr; //IDP is a service provider IDP and request interfederated IDP to collect attributes } else { //TODO: check if response include attributes and map this attributes to requested attributes //get PVP 2.1 attributes from protocol specific requested attributes attributs = req.getRequestedAttributes(); } //collect attributes by using BackChannel communication String endpoint = idp.getIDPAttributQueryServiceURL(); if (MiscUtil.isEmpty(endpoint)) { Logger.error("No AttributeQueryURL for interfederationIDP " + oaParam.getPublicURLPrefix()); throw new ConfigurationException("No AttributeQueryURL for interfederationIDP " + oaParam.getPublicURLPrefix(), null); } //build attributQuery request AttributeQuery query = AttributQueryBuilder.buildAttributQueryRequest(interfIDP.getUserNameID(), endpoint, attributs); //build SOAP request List xmlObjects = MOASAMLSOAPClient.send(endpoint, query); if (xmlObjects.size() == 0) { Logger.error("Receive emptry AttributeQuery response-body."); throw new AttributQueryException("Receive emptry AttributeQuery response-body.", null); } if (xmlObjects.get(0) instanceof Response) { Response intfResp = (Response) xmlObjects.get(0); //validate PVP 2.1 response try { SAMLVerificationEngine engine = new SAMLVerificationEngine(); engine.verifyResponse(intfResp, TrustEngineFactory.getSignatureKnownKeysTrustEngine()); SAMLVerificationEngine.validateAssertion(intfResp, false); } catch (Exception e) { Logger.warn("PVP 2.1 assertion validation FAILED.", e); throw new AssertionValidationExeption("PVP 2.1 assertion validation FAILED.", null, e); } //parse response information to authData buildAuthDataFormInterfederationResponse(authdata, session, intfResp); } else { Logger.error("Receive AttributeQuery response-body include no PVP 2.1 response"); throw new AttributQueryException("Receive AttributeQuery response-body include no PVP 2.1 response.", null); } } catch (SOAPException e) { throw new BuildException("builder.06", null, e); } catch (SecurityException e) { throw new BuildException("builder.06", null, e); } catch (AttributQueryException e) { throw new BuildException("builder.06", null, e); } catch (BuildException e) { throw new BuildException("builder.06", null, e); } catch (AssertionValidationExeption e) { throw new BuildException("builder.06", null, e); } catch (AssertionAttributeExtractorExeption e) { throw new BuildException("builder.06", null, e); } } private static void buildAuthDataFormInterfederationResponse(AuthenticationData authData, AuthenticationSession session, Response intfResp) throws BuildException, AssertionAttributeExtractorExeption { Logger.debug("Build AuthData from assertion starts ...."); Assertion assertion = intfResp.getAssertions().get(0); if (assertion.getAttributeStatements().size() == 0) { Logger.warn("Can not build AuthData from Assertion. NO Attributes included."); throw new AssertionAttributeExtractorExeption("Can not build AuthData from Assertion. NO Attributes included.", null); } AttributeStatement attrStat = assertion.getAttributeStatements().get(0); for (Attribute attr : attrStat.getAttributes()) { if (attr.getName().equals(PVPConstants.PRINCIPAL_NAME_NAME)) authData.setFamilyName(attr.getAttributeValues().get(0).getDOM().getTextContent()); if (attr.getName().equals(PVPConstants.GIVEN_NAME_NAME)) authData.setGivenName(attr.getAttributeValues().get(0).getDOM().getTextContent()); if (attr.getName().equals(PVPConstants.BIRTHDATE_NAME)) authData.setDateOfBirth(attr.getAttributeValues().get(0).getDOM().getTextContent()); if (attr.getName().equals(PVPConstants.BPK_NAME)) { String pvpbPK = attr.getAttributeValues().get(0).getDOM().getTextContent(); authData.setBPK(pvpbPK.split(":")[1]); } if (attr.getName().equals(PVPConstants.EID_SECTOR_FOR_IDENTIFIER_NAME)) authData.setBPKType(attr.getAttributeValues().get(0).getDOM().getTextContent()); if (attr.getName().equals(PVPConstants.EID_CITIZEN_QAA_LEVEL_NAME)) authData.setQAALevel(PVPConstants.STORK_QAA_PREFIX + attr.getAttributeValues().get(0).getDOM().getTextContent()); if (attr.getName().equals(PVPConstants.EID_ISSUING_NATION_NAME)) authData.setCcc(attr.getAttributeValues().get(0).getDOM().getTextContent()); if (attr.getName().equals(PVPConstants.EID_CCS_URL_NAME)) authData.setBkuURL(attr.getAttributeValues().get(0).getDOM().getTextContent()); if (attr.getName().equals(PVPConstants.EID_AUTH_BLOCK_NAME)) { try { byte[] authBlock = Base64Utils.decode(attr.getAttributeValues().get(0).getDOM().getTextContent(), false); authData.setAuthBlock(new String(authBlock, "UTF-8")); } catch (IOException e) { Logger.error("Received AuthBlock is not valid", e); } } if (attr.getName().equals(PVPConstants.EID_SIGNER_CERTIFICATE_NAME)) { try { authData.setSignerCertificate(Base64Utils.decode( attr.getAttributeValues().get(0).getDOM().getTextContent(), false)); } catch (IOException e) { Logger.error("Received SignerCertificate is not valid", e); } } if (attr.getName().equals(PVPConstants.EID_SOURCE_PIN_NAME)) authData.setIdentificationValue(attr.getAttributeValues().get(0).getDOM().getTextContent()); if (attr.getName().equals(PVPConstants.EID_SOURCE_PIN_TYPE_NAME)) authData.setIdentificationType(attr.getAttributeValues().get(0).getDOM().getTextContent()); if (attr.getName().equals(PVPConstants.EID_IDENTITY_LINK_NAME)) { try { InputStream idlStream = Base64Utils.decodeToStream(attr.getAttributeValues().get(0).getDOM().getTextContent(), false); IdentityLink idl = new IdentityLinkAssertionParser(idlStream).parseIdentityLink(); authData.setIdentityLink(idl); } catch (ParseException e) { Logger.error("Received IdentityLink is not valid", e); } catch (Exception e) { Logger.error("Received IdentityLink is not valid", e); } } if (attr.getName().equals(PVPConstants.MANDATE_REFERENCE_VALUE_NAME)) authData.setMandateReferenceValue(attr.getAttributeValues().get(0).getDOM().getTextContent()); if (attr.getName().equals(PVPConstants.MANDATE_FULL_MANDATE_NAME)) { try { byte[] mandate = Base64Utils.decode( attr.getAttributeValues().get(0).getDOM().getTextContent(), false); if (authData.getMISMandate() == null) authData.setMISMandate(new MISMandate()); authData.getMISMandate().setMandate(mandate); authData.setUseMandate(true); } catch (Exception e) { Logger.error("Received Mandate is not valid", e); throw new AssertionAttributeExtractorExeption(PVPConstants.MANDATE_FULL_MANDATE_NAME); } } if (attr.getName().equals(PVPConstants.MANDATE_PROF_REP_OID_NAME)) { if (authData.getMISMandate() == null) authData.setMISMandate(new MISMandate()); authData.getMISMandate().setProfRep( attr.getAttributeValues().get(0).getDOM().getTextContent()); } if (attr.getName().equals(PVPConstants.EID_STORK_TOKEN_NAME)) { authData.setStorkAuthnResponse(attr.getAttributeValues().get(0).getDOM().getTextContent()); authData.setForeigner(true); } if (attr.getName().startsWith(PVPConstants.STORK_ATTRIBUTE_PREFIX)) { if (authData.getStorkAttributes() == null) authData.setStorkAttributes(new PersonalAttributeList()); List storkAttrValues = new ArrayList(); storkAttrValues.add(attr.getAttributeValues().get(0).getDOM().getTextContent()); PersonalAttribute storkAttr = new PersonalAttribute(attr.getName(), false, storkAttrValues , "Available"); authData.getStorkAttributes().put(attr.getName(), storkAttr ); authData.setForeigner(true); } } authData.setSsoSession(true); if (assertion.getConditions() != null && assertion.getConditions().getNotOnOrAfter() != null) authData.setSsoSessionValidTo(assertion.getConditions().getNotOnOrAfter().toDate()); //only for SAML1 if (PVPConstants.STORK_QAA_1_4.equals(authData.getQAALevel())) authData.setQualifiedCertificate(true); else authData.setQualifiedCertificate(false); authData.setPublicAuthority(false); } private static void buildAuthDataFormMOASession(AuthenticationData authData, AuthenticationSession session, IOAAuthParameters oaParam) throws BuildException, ConfigurationException { String target = oaParam.getTarget(); IdentityLink identityLink = session.getIdentityLink(); VerifyXMLSignatureResponse verifyXMLSigResp = session.getXMLVerifySignatureResponse(); boolean businessService = oaParam.getBusinessService(); authData.setIssuer(session.getAuthURL()); //baseID or wbpk in case of BusinessService without SSO or BusinessService SSO authData.setIdentificationValue(identityLink.getIdentificationValue()); authData.setIdentificationType(identityLink.getIdentificationType()); authData.setGivenName(identityLink.getGivenName()); authData.setFamilyName(identityLink.getFamilyName()); authData.setDateOfBirth(identityLink.getDateOfBirth()); if (verifyXMLSigResp != null) { authData.setQualifiedCertificate(verifyXMLSigResp .isQualifiedCertificate()); authData.setPublicAuthority(verifyXMLSigResp.isPublicAuthority()); authData.setPublicAuthorityCode(verifyXMLSigResp .getPublicAuthorityCode()); } else { Logger.warn("No signature verfication response found!"); } authData.setBkuURL(session.getBkuURL()); authData.setStorkAttributes(session.getStorkAttributes()); authData.setStorkAuthnResponse(session.getStorkAuthnResponse()); authData.setStorkRequest(session.getStorkAuthnRequest()); authData.setSignerCertificate(session.getEncodedSignerCertificate()); authData.setAuthBlock(session.getAuthBlock()); authData.setForeigner(session.isForeigner()); authData.setQAALevel(session.getQAALevel()); if (session.isForeigner()) { if (authData.getStorkAuthnRequest() != null) { authData.setCcc(authData.getStorkAuthnRequest() .getCitizenCountryCode()); } else { try { //TODO: replace with TSL lookup when TSL is ready! X509Certificate certificate = new X509Certificate(authData.getSignerCertificate()); if (certificate != null) { LdapName ln = new LdapName(certificate.getIssuerDN() .getName()); for (Rdn rdn : ln.getRdns()) { if (rdn.getType().equalsIgnoreCase("C")) { Logger.info("C is: " + rdn.getValue()); authData.setCcc(rdn.getValue().toString()); break; } } } } catch (Exception e) { Logger.error("Failed to extract country code from certificate", e); } } } else { authData.setCcc("AT"); } try { authData.setSsoSession(AuthenticationSessionStoreage.isSSOSession(session.getSessionID())); //set max. SSO session time if (authData.isSsoSession()) { long maxSSOSessionTime = AuthConfigurationProvider.getInstance().getTimeOuts().getMOASessionCreated().longValue() * 1000; Date ssoSessionValidTo = new Date(session.getSessionCreated().getTime() + maxSSOSessionTime); authData.setSsoSessionValidTo(ssoSessionValidTo); } else { //set valid to 5 min Date ssoSessionValidTo = new Date(new Date().getTime() + 5 * 60 * 1000); authData.setSsoSessionValidTo(ssoSessionValidTo); } /* TODO: Support SSO Mandate MODE! * Insert functionality to translate mandates in case of SSO */ MISMandate mandate = session.getMISMandate(); authData.setMISMandate(mandate); authData.setUseMandate(session.getUseMandate()); authData.setMandateReferenceValue(session.getMandateReferenceValue()); if (session.getUseMandate() && session.isOW() && mandate != null && MiscUtil.isNotEmpty(mandate.getOWbPK())) { authData.setBPK(mandate.getOWbPK()); authData.setBPKType(Constants.URN_PREFIX_CDID + "+" + "OW"); //TODO: check in case of mandates for business services authData.setIdentityLink(identityLink); Logger.trace("Authenticated User is OW: " + mandate.getOWbPK()); } else { if (businessService) { //since we have foreigner, wbPK is not calculated in BKU if (identityLink.getIdentificationType().equals(Constants.URN_PREFIX_BASEID)) { String registerAndOrdNr = oaParam.getIdentityLinkDomainIdentifier(); if (registerAndOrdNr.startsWith(AuthenticationSession.REGISTERANDORDNR_PREFIX_)) { // If domainIdentifier starts with prefix // "urn:publicid:gv.at:wbpk+"; remove this prefix registerAndOrdNr = registerAndOrdNr .substring(AuthenticationSession.REGISTERANDORDNR_PREFIX_.length()); Logger.debug("Register and ordernumber prefix stripped off; resulting register string: " + registerAndOrdNr); } String wbpkBase64 = new BPKBuilder().buildWBPK(identityLink.getIdentificationValue(), registerAndOrdNr); authData.setBPK(wbpkBase64); authData.setBPKType(Constants.URN_PREFIX_WBPK + "+" + registerAndOrdNr); } else { authData.setBPK(identityLink.getIdentificationValue()); authData.setBPKType(identityLink.getIdentificationType()); } Logger.trace("Authenticate user with wbPK " + authData.getBPK()); Element idlassertion = session.getIdentityLink().getSamlAssertion(); //set bpk/wpbk; Node prIdentification = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_IDENT_VALUE_XPATH); prIdentification.getFirstChild().setNodeValue(authData.getBPK()); //set bkp/wpbk type Node prIdentificationType = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_IDENT_TYPE_XPATH); prIdentificationType.getFirstChild().setNodeValue(authData.getBPKType()); IdentityLinkAssertionParser idlparser = new IdentityLinkAssertionParser(idlassertion); IdentityLink idl = idlparser.parseIdentityLink(); //resign IDL IdentityLinkReSigner identitylinkresigner = IdentityLinkReSigner.getInstance(); Element resignedilAssertion; resignedilAssertion = identitylinkresigner.resignIdentityLink(idl.getSamlAssertion()); IdentityLinkAssertionParser resignedIDLParser = new IdentityLinkAssertionParser(resignedilAssertion); IdentityLink resignedIDL = resignedIDLParser.parseIdentityLink(); authData.setIdentityLink(resignedIDL); } else { if (identityLink.getIdentificationType().equals(Constants.URN_PREFIX_BASEID)) { // only compute bPK if online application is a public service and we have the Stammzahl String bpkBase64 = new BPKBuilder().buildBPK(identityLink.getIdentificationValue(), target); authData.setBPK(bpkBase64); authData.setBPKType(Constants.URN_PREFIX_CDID + "+" + oaParam.getTarget()); } Logger.trace("Authenticate user with bPK " + authData.getBPK()); authData.setIdentityLink(identityLink); } } } catch (Throwable ex) { throw new BuildException("builder.00", new Object[]{ "AuthenticationData", ex.toString()}, ex); } } }