/* * 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 java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.security.PrivateKey; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.Iterator; import java.util.List; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import org.opensaml.saml2.core.Attribute; import org.opensaml.saml2.core.AttributeQuery; import org.opensaml.saml2.core.Response; import org.opensaml.ws.soap.common.SOAPException; import org.opensaml.xml.XMLObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import at.gv.egovernment.moa.id.auth.data.AuthenticationSessionStorageConstants; 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.MOAIDAuthConstants; import at.gv.egovernment.moa.id.commons.api.AuthConfiguration; import at.gv.egovernment.moa.id.commons.api.IOAAuthParameters; import at.gv.egovernment.moa.id.commons.api.IRequest; import at.gv.egovernment.moa.id.commons.api.data.ExtendedSAMLAttribute; import at.gv.egovernment.moa.id.commons.api.data.IAuthenticationSession; import at.gv.egovernment.moa.id.commons.api.data.IIdentityLink; import at.gv.egovernment.moa.id.commons.api.data.IMISMandate; import at.gv.egovernment.moa.id.commons.api.data.IVerifiyXMLSignatureResponse; import at.gv.egovernment.moa.id.commons.api.exceptions.ConfigurationException; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.commons.api.exceptions.SessionDataStorageException; import at.gv.egovernment.moa.id.commons.db.dao.session.OASessionStore; import at.gv.egovernment.moa.id.data.AuthenticationData; import at.gv.egovernment.moa.id.data.AuthenticationRoleFactory; import at.gv.egovernment.moa.id.data.IAuthData; import at.gv.egovernment.moa.id.data.MISMandate; import at.gv.egovernment.moa.id.data.Pair; 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.AssertionValidationExeption; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.AttributQueryException; import at.gv.egovernment.moa.id.protocols.pvp2x.metadata.MOAMetadataProvider; import at.gv.egovernment.moa.id.protocols.pvp2x.utils.AssertionAttributeExtractor; import at.gv.egovernment.moa.id.protocols.pvp2x.utils.MOASAMLSOAPClient; import at.gv.egovernment.moa.id.protocols.pvp2x.verification.SAMLVerificationEngineSP; import at.gv.egovernment.moa.id.protocols.pvp2x.verification.TrustEngineFactory; import at.gv.egovernment.moa.id.storage.IAuthenticationSessionStoreage; import at.gv.egovernment.moa.id.util.IdentityLinkReSigner; import at.gv.egovernment.moa.id.util.PVPtoSTORKMapper; 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; import at.gv.util.client.szr.SZRClient; import at.gv.util.config.EgovUtilPropertiesConfiguration; import at.gv.util.wsdl.szr.SZRException; import at.gv.util.xsd.szr.PersonInfoType; import iaik.x509.X509Certificate; /** * @author tlenz * */ @Service("AuthenticationDataBuilder") public class AuthenticationDataBuilder extends MOAIDAuthConstants { @Autowired private IAuthenticationSessionStoreage authenticatedSessionStorage; @Autowired protected AuthConfiguration authConfig; @Autowired private AttributQueryBuilder attributQueryBuilder; @Autowired private SAMLVerificationEngineSP samlVerificationEngine; @Autowired(required=true) private MOAMetadataProvider metadataProvider; public IAuthData buildAuthenticationData(IRequest pendingReq, IAuthenticationSession session) throws ConfigurationException, BuildException, WrongParametersException, DynamicOABuildException { return buildAuthenticationData(pendingReq, session, pendingReq.getOnlineApplicationConfiguration()); } public IAuthData buildAuthenticationData(IRequest pendingReq, IAuthenticationSession session, IOAAuthParameters oaParam) throws ConfigurationException, BuildException, WrongParametersException, DynamicOABuildException { AuthenticationData authdata = null; //only needed for SAML1 legacy support try { //check if SAML1 authentication module is in Classpath Class saml1RequstTemplate = Class.forName("at.gv.egovernment.moa.id.protocols.saml1.SAML1RequestImpl"); IAuthData saml1authdata = (IAuthData) Class.forName("at.gv.egovernment.moa.id.protocols.saml1.SAML1AuthenticationData").newInstance(); if (saml1RequstTemplate != null && saml1RequstTemplate.isInstance(pendingReq)) { //request is SAML1 --> invoke SAML1 protocol specific methods if (session.getExtendedSAMLAttributesOA() == null) { saml1authdata.getClass().getMethod("setExtendedSAMLAttributesOA", List.class).invoke(saml1authdata, new ArrayList()); } else { saml1authdata.getClass().getMethod("setExtendedSAMLAttributesOA", List.class).invoke(saml1authdata, session.getExtendedSAMLAttributesOA()); } authdata = (AuthenticationData) saml1authdata; } else { authdata = new AuthenticationData(); } } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | java.lang.SecurityException ex) { authdata = new AuthenticationData(); } OASessionStore activeOA = authenticatedSessionStorage.searchActiveOASSOSession(session, pendingReq.getOAURL(), pendingReq.requestedModule()); //reuse authentication information in case of service-provider reauthentication if (activeOA != null) { authdata.setSessionIndex(activeOA.getAssertionSessionID()); authdata.setNameID(activeOA.getUserNameID()); authdata.setNameIDFormat(activeOA.getUserNameIDFormat()); } //TODO: move to eIDAS-Code in case of ISA1.18 action is enabled for eIDAS //build OA dynamically from STROK request if this OA is used as STORK<->PVP gateway if (oaParam.isSTORKPVPGateway()) oaParam = DynamicOAAuthParameterBuilder.buildFromAuthnRequest(oaParam, pendingReq); Boolean isMinimalFrontChannelResp = pendingReq.getGenericData( PVPTargetConfiguration.DATAID_INTERFEDERATION_MINIMAL_FRONTCHANNEL_RESP, Boolean.class); if (isMinimalFrontChannelResp != null && isMinimalFrontChannelResp) { //only set minimal response attributes authdata.setQAALevel( pendingReq.getGenericData(PVPTargetConfiguration.DATAID_INTERFEDERATION_QAALEVEL, String.class)); authdata.setBPK( pendingReq.getGenericData(PVPTargetConfiguration.DATAID_INTERFEDERATION_NAMEID, String.class)); } else { //build AuthenticationData from MOASession buildAuthDataFormMOASession(authdata, session, oaParam, pendingReq); } return authdata; } /** * Get PVP authentication attributes by using a SAML2 AttributeQuery * * @param reqQueryAttr List of PVP attributes which are requested * @param userNameID SAML2 UserNameID of the user for which attributes are requested * @param idpConfig Configuration of the IDP, which is requested * @return * @return PVP attribute DAO, which contains all received information * @throws MOAIDException */ public AssertionAttributeExtractor getAuthDataFromAttributeQuery(List reqQueryAttr, String userNameID, IOAAuthParameters idpConfig ) throws MOAIDException{ String idpEnityID = idpConfig.getPublicURLPrefix(); try { Logger.debug("Starting AttributeQuery process ..."); //collect attributes by using BackChannel communication String endpoint = idpConfig.getIDPAttributQueryServiceURL(); if (MiscUtil.isEmpty(endpoint)) { Logger.error("No AttributeQueryURL for interfederationIDP " + idpEnityID); throw new ConfigurationException("config.26", new Object[]{idpEnityID}); } //build attributQuery request AttributeQuery query = attributQueryBuilder.buildAttributQueryRequest(userNameID, endpoint, reqQueryAttr); //build SOAP request List xmlObjects = MOASAMLSOAPClient.send(endpoint, query); if (xmlObjects.size() == 0) { Logger.error("Receive emptry AttributeQuery response-body."); throw new AttributQueryException("auth.27", new Object[]{idpEnityID, "Receive emptry AttributeQuery response-body."}); } Response intfResp; if (xmlObjects.get(0) instanceof Response) { intfResp = (Response) xmlObjects.get(0); //validate PVP 2.1 response try { samlVerificationEngine.verifyIDPResponse(intfResp, TrustEngineFactory.getSignatureKnownKeysTrustEngine( metadataProvider)); //create assertion attribute extractor from AttributeQuery response return new AssertionAttributeExtractor(intfResp); } catch (Exception e) { Logger.warn("PVP 2.1 assertion validation FAILED.", e); throw new AssertionValidationExeption("auth.27", new Object[]{idpEnityID, e.getMessage()}, e); } } else { Logger.error("Receive AttributeQuery response-body include no PVP 2.1 response"); throw new AttributQueryException("auth.27", new Object[]{idpEnityID, "Receive AttributeQuery response-body include no PVP 2.1 response"}); } } catch (SOAPException e) { throw new BuildException("builder.06", null, e); } catch (SecurityException e) { throw new BuildException("builder.06", null, e); } catch (org.opensaml.xml.security.SecurityException e1) { throw new BuildException("builder.06", null, e1); } } private void buildAuthDataFormMOASession(AuthenticationData authData, IAuthenticationSession session, IOAAuthParameters oaParam, IRequest protocolRequest) throws BuildException, ConfigurationException { Collection includedToGenericAuthData = null; if (session.getGenericSessionDataStorage() != null && !session.getGenericSessionDataStorage().isEmpty()) includedToGenericAuthData = session.getGenericSessionDataStorage().keySet(); else includedToGenericAuthData = new ArrayList(); try { //#################################################### //set general authData info's authData.setIssuer(protocolRequest.getAuthURL()); authData.setSsoSession(protocolRequest.needSingleSignOnFunctionality()); authData.setBaseIDTransferRestrication(oaParam.hasBaseIdTransferRestriction()); //#################################################### //parse user info's from identityLink IIdentityLink idlFromPVPAttr = null; IIdentityLink identityLink = session.getIdentityLink(); if (identityLink != null) { parseBasicUserInfosFromIDL(authData, identityLink, includedToGenericAuthData); } else { // identityLink is not direct in MOASession String pvpAttrIDL = session.getGenericDataFromSession(PVPConstants.EID_IDENTITY_LINK_NAME, String.class); //find PVP-Attr. which contains the IdentityLink if (MiscUtil.isNotEmpty(pvpAttrIDL)) { Logger.debug("Find PVP-Attr: " + PVPConstants.EID_IDENTITY_LINK_FRIENDLY_NAME + " --> Parse basic user info's from that attribute."); InputStream idlStream = null; try { idlStream = Base64Utils.decodeToStream(pvpAttrIDL, false); idlFromPVPAttr = new IdentityLinkAssertionParser(idlStream).parseIdentityLink(); parseBasicUserInfosFromIDL(authData, idlFromPVPAttr, includedToGenericAuthData); } catch (ParseException e) { Logger.error("Received IdentityLink is not valid", e); } catch (Exception e) { Logger.error("Received IdentityLink is not valid", e); } finally { try { includedToGenericAuthData.remove(PVPConstants.EID_IDENTITY_LINK_NAME); if (idlStream != null) idlStream.close(); } catch (IOException e) { Logger.fatal("Close InputStream FAILED.", e); } } } //if no basic user info's are set yet, parse info's single PVP-Attributes if (MiscUtil.isEmpty(authData.getFamilyName())) { Logger.debug("No IdentityLink found or not parseable --> Parse basic user info's from single PVP-Attributes."); authData.setFamilyName(session.getGenericDataFromSession(PVPConstants.PRINCIPAL_NAME_NAME, String.class)); authData.setGivenName(session.getGenericDataFromSession(PVPConstants.GIVEN_NAME_NAME, String.class)); authData.setDateOfBirth(session.getGenericDataFromSession(PVPConstants.BIRTHDATE_NAME, String.class)); authData.setIdentificationValue(session.getGenericDataFromSession(PVPConstants.EID_SOURCE_PIN_NAME, String.class)); authData.setIdentificationType(session.getGenericDataFromSession(PVPConstants.EID_SOURCE_PIN_TYPE_NAME, String.class)); //remove corresponding keys from genericSessionData if exists includedToGenericAuthData.remove(PVPConstants.PRINCIPAL_NAME_NAME); includedToGenericAuthData.remove(PVPConstants.GIVEN_NAME_NAME); includedToGenericAuthData.remove(PVPConstants.BIRTHDATE_NAME); includedToGenericAuthData.remove(PVPConstants.EID_SOURCE_PIN_NAME); includedToGenericAuthData.remove(PVPConstants.EID_SOURCE_PIN_TYPE_NAME); } } if (authData.getIdentificationType() != null && !authData.getIdentificationType().equals(Constants.URN_PREFIX_BASEID)) { Logger.trace("IdentificationType is not a baseID --> clear it. "); authData.setBPK(authData.getIdentificationValue()); authData.setBPKType(authData.getIdentificationType()); authData.setIdentificationValue(null); authData.setIdentificationType(null); } //#################################################### //set BKU URL includedToGenericAuthData.remove(PVPConstants.EID_CCS_URL_NAME); if (MiscUtil.isNotEmpty(session.getBkuURL())) authData.setBkuURL(session.getBkuURL()); else authData.setBkuURL(session.getGenericDataFromSession(PVPConstants.EID_CCS_URL_NAME, String.class)); //TODO: fully switch from STORK QAA to eIDAS LoA //#################################################### //set QAA level includedToGenericAuthData.remove(PVPConstants.EID_CITIZEN_QAA_LEVEL_NAME); if (MiscUtil.isNotEmpty(session.getQAALevel())) authData.setQAALevel(session.getQAALevel()); else { String qaaLevel = session.getGenericDataFromSession(PVPConstants.EID_CITIZEN_QAA_LEVEL_NAME, String.class); if (MiscUtil.isNotEmpty(qaaLevel)) { Logger.debug("Find PVP-Attr: " + PVPConstants.EID_CITIZEN_QAA_LEVEL_FRIENDLY_NAME + " --> Parse QAA-Level from that attribute."); if (qaaLevel.startsWith(PVPConstants.STORK_QAA_PREFIX)) { authData.setQAALevel(qaaLevel); } else { Logger.debug("Found PVP QAA level. QAA mapping process starts ... "); String mappedQAA = PVPtoSTORKMapper.getInstance().mapToQAALevel(qaaLevel); if (MiscUtil.isNotEmpty(mappedQAA)) authData.setQAALevel(mappedQAA); } } } //if no QAA level is set in MOASession then set default QAA level if (MiscUtil.isEmpty(authData.getQAALevel())) { Logger.info("No QAA level found. Set to default level " + PVPConstants.STORK_QAA_PREFIX + "1"); authData.setQAALevel(PVPConstants.STORK_QAA_PREFIX + "1"); } //#################################################### //set signer certificate includedToGenericAuthData.remove(PVPConstants.EID_SIGNER_CERTIFICATE_NAME); if (session.getEncodedSignerCertificate() != null) authData.setSignerCertificate(session.getEncodedSignerCertificate()); else { String pvpAttrSignerCert = session.getGenericDataFromSession(PVPConstants.EID_SIGNER_CERTIFICATE_NAME, String.class); if (MiscUtil.isNotEmpty(pvpAttrSignerCert)) { Logger.debug("Find PVP-Attr: " + PVPConstants.EID_SIGNER_CERTIFICATE_FRIENDLY_NAME); try { authData.setSignerCertificate(Base64Utils.decode(pvpAttrSignerCert, false)); } catch (IOException e) { Logger.error("SignerCertificate received via federated IDP is NOT valid", e); } } else Logger.info("NO SignerCertificate in MOASession."); } //#################################################### //set authBlock includedToGenericAuthData.remove(PVPConstants.EID_AUTH_BLOCK_NAME); if (MiscUtil.isNotEmpty(session.getAuthBlock())) { authData.setAuthBlock(session.getAuthBlock()); } else { String pvpAttrAuthBlock = session.getGenericDataFromSession(PVPConstants.EID_AUTH_BLOCK_NAME, String.class); if (MiscUtil.isNotEmpty(pvpAttrAuthBlock)) { Logger.debug("Find PVP-Attr: " + PVPConstants.EID_AUTH_BLOCK_FRIENDLY_NAME); try { byte[] authBlock = Base64Utils.decode(pvpAttrAuthBlock, false); authData.setAuthBlock(new String(authBlock, "UTF-8")); } catch (IOException e) { Logger.error("AuthBlock received via federated IDP is NOT valid", e); } } else Logger.info("NO AuthBlock in MOASession."); } //#################################################### //set isForeigner flag //TODO: change to new eIDAS-token attribute identifier if (session.getGenericDataFromSession(PVPConstants.EID_STORK_TOKEN_NAME) != null) { Logger.debug("Find PVP-Attr: " + PVPConstants.EID_STORK_TOKEN_FRIENDLY_NAME + " --> Set 'isForeigner' flag to TRUE"); authData.setForeigner(true); } else { authData.setForeigner(session.isForeigner()); } //#################################################### //set citizen country-code includedToGenericAuthData.remove(PVPConstants.EID_ISSUING_NATION_NAME); String pvpCCCAttr = session.getGenericDataFromSession(PVPConstants.EID_ISSUING_NATION_NAME, String.class); if (MiscUtil.isNotEmpty(pvpCCCAttr)) { authData.setCcc(pvpCCCAttr); Logger.debug("Find PVP-Attr: " + PVPConstants.EID_ISSUING_NATION_FRIENDLY_NAME); } else { if (authData.isForeigner()) { try { if (authData.getSignerCertificate() != null) { //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; } } } } else Logger.warn("NO PVP-Attr: " + PVPConstants.EID_ISSUING_NATION_NAME + " and NO SignerCertificate in MOASession -->" + " Can NOT extract citizen-country of foreign person."); } catch (Exception e) { Logger.error("Failed to extract country code from certificate with message: " + e.getMessage()); } } else { authData.setCcc(COUNTRYCODE_AUSTRIA); } } //#################################################### //set max. SSO session time includedToGenericAuthData.remove(AuthenticationSessionStorageConstants.FEDERATION_RESPONSE_VALIDE_TO); Date validToFromFederatedIDP = session.getGenericDataFromSession( AuthenticationSessionStorageConstants.FEDERATION_RESPONSE_VALIDE_TO, Date.class); if (validToFromFederatedIDP != null) { authData.setSsoSessionValidTo(validToFromFederatedIDP); Logger.debug("Use idToken validTo periode from federated IDP response."); } else { if (authData.isSsoSession()) { long maxSSOSessionTime = authConfig.getSSOCreatedTimeOut() * 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); } } //mandate functionality IMISMandate misMandate = null; if (session.isMandateUsed()) { //#################################################### //set Mandate reference value includedToGenericAuthData.remove(PVPConstants.MANDATE_REFERENCE_VALUE_NAME); if (MiscUtil.isNotEmpty(session.getMandateReferenceValue())) authData.setMandateReferenceValue(session.getMandateReferenceValue()); else { String pvpMandateRefAttr = session.getGenericDataFromSession(PVPConstants.MANDATE_REFERENCE_VALUE_NAME, String.class); if (MiscUtil.isNotEmpty(pvpMandateRefAttr)) { authData.setMandateReferenceValue(pvpMandateRefAttr); Logger.debug("Find PVP-Attr: " + PVPConstants.MANDATE_REFERENCE_VALUE_FRIENDLY_NAME); } } /* TODO: Support SSO Mandate MODE! * Insert functionality to translate mandates in case of SSO */ //#################################################### //set Full-mandate misMandate = session.getMISMandate(); if (misMandate != null ) { //set MIS mandate to authdata authData.setMISMandate(misMandate); authData.setUseMandate(session.isMandateUsed()); } else { String pvpFullMandateAttr = session.getGenericDataFromSession( PVPConstants.MANDATE_FULL_MANDATE_NAME, String.class); //check if full-mandate is available as PVP attribute if (MiscUtil.isNotEmpty(pvpFullMandateAttr)) { Logger.debug("Find PVP-Attr: " + PVPConstants.MANDATE_FULL_MANDATE_FRIENDLY_NAME); try { byte[] mandate = Base64Utils.decode(pvpFullMandateAttr, false); misMandate = new MISMandate(); misMandate.setMandate(mandate); //read Organwalter OID String pvpRepOIDAttr = session.getGenericDataFromSession(PVPConstants.MANDATE_PROF_REP_OID_NAME, String.class); if (MiscUtil.isNotEmpty(pvpRepOIDAttr)) { misMandate.setProfRep(pvpRepOIDAttr); Logger.debug("Find PVP-Attr: " + PVPConstants.MANDATE_PROF_REP_OID_NAME); } //read Organwalter bPK from full-mandate NodeList mandateElements = misMandate.getMandateDOM().getChildNodes(); for (int i=0; i Use single PVP attributes for mandate information."); //check if ELGA mandates exists String mandateType = session.getGenericDataFromSession(PVPConstants.MANDATE_TYPE_NAME, String.class); if (MiscUtil.isNotEmpty(mandateType)) { //switch to mandate-mode for authdata generation, because mandate-information // is directly included in MOA-Session as PVP attributes Logger.info("AuthDataBuilder find directly included 'MandateType' PVP-attribute." + " --> Switch to mandate-mode for authdata generation."); authData.setUseMandate(true); } } } //remove PVP attributes with mandate information, because full-mandate exists if (authData.getMISMandate() != null) { includedToGenericAuthData.remove(PVPConstants.MANDATE_FULL_MANDATE_NAME); includedToGenericAuthData.remove(PVPConstants.MANDATE_TYPE_NAME); includedToGenericAuthData.remove(PVPConstants.MANDATE_LEG_PER_FULL_NAME_NAME); includedToGenericAuthData.remove(PVPConstants.MANDATE_LEG_PER_SOURCE_PIN_NAME); includedToGenericAuthData.remove(PVPConstants.MANDATE_LEG_PER_SOURCE_PIN_TYPE_NAME); includedToGenericAuthData.remove(PVPConstants.MANDATE_NAT_PER_FAMILY_NAME_NAME); includedToGenericAuthData.remove(PVPConstants.MANDATE_NAT_PER_GIVEN_NAME_NAME); includedToGenericAuthData.remove(PVPConstants.MANDATE_NAT_PER_BIRTHDATE_NAME); includedToGenericAuthData.remove(PVPConstants.MANDATE_NAT_PER_BPK_NAME); includedToGenericAuthData.remove(PVPConstants.MANDATE_NAT_PER_SOURCE_PIN_NAME); includedToGenericAuthData.remove(PVPConstants.MANDATE_NAT_PER_SOURCE_PIN_TYPE_NAME); includedToGenericAuthData.remove(PVPConstants.MANDATE_PROF_REP_DESC_NAME); includedToGenericAuthData.remove(PVPConstants.MANDATE_PROF_REP_OID_NAME); } } //#################################################### // set bPK and IdentityLink for Organwalter --> // Organwalter has a special bPK is received from MIS if (authData.isUseMandate() && session.isOW() && misMandate != null && MiscUtil.isNotEmpty(misMandate.getOWbPK())) { //TODO: if full-mandate is removed in OPB --> OWbPK functionality needs an update!!! authData.setBPK(misMandate.getOWbPK()); authData.setBPKType(Constants.URN_PREFIX_CDID + "+" + "OW"); Logger.trace("Authenticated User is OW: " + misMandate.getOWbPK()); //TODO: check in case of mandates for business services if (identityLink != null) authData.setIdentityLink(identityLink); else if (idlFromPVPAttr != null){ authData.setIdentityLink(idlFromPVPAttr); Logger.debug("Set IdentityLink received from federated IDP for Organwalter"); } else Logger.info("Can NOT set Organwalter IdentityLink. Msg: No IdentityLink found"); //set bPK and IdenityLink for all other } else { //build bPK String pvpbPKValue = getbPKValueFromPVPAttribute(session); String pvpbPKTypeAttr = getbPKTypeFromPVPAttribute(session); Pair pvpEncbPKAttr = getEncryptedbPKFromPVPAttribute(session, authData, oaParam); //check if a unique ID for this citizen exists if (MiscUtil.isEmpty(authData.getIdentificationValue()) && MiscUtil.isEmpty(pvpbPKValue) && MiscUtil.isEmpty(authData.getBPK()) && pvpEncbPKAttr == null) { Logger.info("Can not build authData, because moaSession include no bPK, encrypted bPK or baseID"); throw new MOAIDException("builder.08", new Object[]{"No " + PVPConstants.BPK_FRIENDLY_NAME + " or " + PVPConstants.EID_SOURCE_PIN_FRIENDLY_NAME + " or " + PVPConstants.ENC_BPK_LIST_FRIENDLY_NAME}); } // baseID is in MOASesson --> calculate bPK directly if (MiscUtil.isNotEmpty(authData.getIdentificationValue())) { Logger.debug("Citizen baseID is in MOASession --> calculate bPK from this."); Pair result = buildOAspecificbPK(protocolRequest, oaParam, authData); authData.setBPK(result.getFirst()); authData.setBPKType(result.getSecond()); //check if bPK already added to AuthData matches OA } else if (MiscUtil.isNotEmpty(authData.getBPK()) && matchsReceivedbPKToOnlineApplication(oaParam, authData.getBPKType()) ) { Logger.debug("Correct bPK is already included in AuthData."); //check if bPK received by PVP-Attribute matches OA } else if (MiscUtil.isNotEmpty(pvpbPKValue) && matchsReceivedbPKToOnlineApplication(oaParam, pvpbPKTypeAttr)) { Logger.debug("Receive correct bPK from PVP-Attribute"); authData.setBPK(pvpbPKValue); authData.setBPKType(pvpbPKTypeAttr); //check if decrypted bPK exists } else if (pvpEncbPKAttr != null) { Logger.debug("Receive bPK as encrypted bPK and decryption was possible."); authData.setBPK(pvpEncbPKAttr.getFirst()); authData.setBPKType(pvpEncbPKAttr.getSecond()); //ask SZR to get bPK } else { String notValidbPK = authData.getBPK(); String notValidbPKType = authData.getBPKType(); if (MiscUtil.isEmpty(notValidbPK) && MiscUtil.isEmpty(notValidbPKType)) { notValidbPK = pvpbPKValue; notValidbPKType = pvpbPKTypeAttr; if (MiscUtil.isEmpty(notValidbPK) && MiscUtil.isEmpty(notValidbPKType)) { Logger.fatal("No bPK in MOASession. THIS error should not occur any more."); throw new NullPointerException("No bPK in MOASession. THIS error should not occur any more."); } } Pair baseIDFromSZR = getbaseIDFromSZR(authData, notValidbPK, notValidbPKType); if (baseIDFromSZR != null) { Logger.info("Receive citizen baseID from SRZ. Authentication can be completed"); authData.setIdentificationValue(baseIDFromSZR.getFirst()); authData.setIdentificationType(baseIDFromSZR.getSecond()); Pair result = buildOAspecificbPK(protocolRequest, oaParam, authData); authData.setBPK(result.getFirst()); authData.setBPKType(result.getSecond()); } else { Logger.warn("Can not build authData, because moaSession include no valid bPK, encrypted bPK or baseID"); throw new MOAIDException("builder.08", new Object[]{"No valid " + PVPConstants.BPK_FRIENDLY_NAME + " or " + PVPConstants.EID_SOURCE_PIN_FRIENDLY_NAME + " or " + PVPConstants.ENC_BPK_LIST_FRIENDLY_NAME}); } } //build IdentityLink if (identityLink != null) authData.setIdentityLink(buildOAspecificIdentityLink(oaParam, identityLink, authData.getBPK(), authData.getBPKType())); else if (idlFromPVPAttr != null) { authData.setIdentityLink(buildOAspecificIdentityLink(oaParam, idlFromPVPAttr, authData.getBPK(), authData.getBPKType())); Logger.debug("Set IdentityLink received from federated IDP"); } else { Logger.info("Can NOT set IdentityLink. Msg: No IdentityLink found"); } } //################################################################### //set PVP role attribute (implemented for ISA 1.18 action) includedToGenericAuthData.remove(PVPConstants.ROLES_NAME); String pvpAttrRoles = session.getGenericDataFromSession(PVPConstants.ROLES_NAME, String.class); if (MiscUtil.isNotEmpty(pvpAttrRoles)) { List roles = Arrays.asList(pvpAttrRoles.split(";")); for (String role : roles) { authData.addAuthenticationRole(AuthenticationRoleFactory.buildFormPVPole(role)); } } //################################################################### //set PVP OU attribute (implemented for ISA 1.18 action) includedToGenericAuthData.remove(PVPConstants.OU_NAME); String pvpAttrOUName = session.getGenericDataFromSession(PVPConstants.OU_NAME, String.class); if (MiscUtil.isNotEmpty(pvpAttrOUName)) { authData.setPvpAttribute_OU(pvpAttrOUName); Logger.debug("Found PVP 'OU' attribute in response -> " + authData.getPvpAttribute_OU()); } //#################################################################### //parse AuthBlock signature-verification response //INFO: this parameters are only required for SAML1 auth. protocol IVerifiyXMLSignatureResponse verifyXMLSigResp = session.getXMLVerifySignatureResponse(); if (verifyXMLSigResp != null) { authData.setQualifiedCertificate(verifyXMLSigResp .isQualifiedCertificate()); authData.setPublicAuthority(verifyXMLSigResp.isPublicAuthority()); authData.setPublicAuthorityCode(verifyXMLSigResp .getPublicAuthorityCode()); } else { //set parameters in respect to QAA level Logger.info("No authBlock signature-verfication response found. Maybe IDP federation is in use."); if (PVPConstants.STORK_QAA_1_4.equals(authData.getQAALevel())) authData.setQualifiedCertificate(true); else authData.setQualifiedCertificate(false); authData.setPublicAuthority(false); } //#################################################################### //copy all generic authentication information, which are not processed before to authData Iterator copyInterator = includedToGenericAuthData.iterator(); while (copyInterator.hasNext()) { String elementKey = copyInterator.next(); try { authData.setGenericData(elementKey, session.getGenericDataFromSession(elementKey)); } catch (SessionDataStorageException e) { Logger.warn("Can not add generic authData with key:" + elementKey, e); } } } catch (BuildException e) { throw e; } catch (Throwable ex) { throw new BuildException("builder.00", new Object[]{ "AuthenticationData", ex.toString()}, ex); } } /** * Check a bPK-Type against a Service-Provider configuration
* If bPK-Type is null the result is false. * * @param oaParam Service-Provider configuration, never null * @param bPKType bPK-Type to check * @return true, if bPK-Type matchs to Service-Provider configuration, otherwise false * @throws ConfigurationException */ private boolean matchsReceivedbPKToOnlineApplication(IOAAuthParameters oaParam, String bPKType) throws ConfigurationException { return oaParam.getAreaSpecificTargetIdentifier().equals(bPKType); } private void parseBasicUserInfosFromIDL(AuthenticationData authData, IIdentityLink identityLink, Collection includedGenericSessionData) { //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()); //remove corresponding keys from genericSessionData if exists includedGenericSessionData.remove(PVPConstants.PRINCIPAL_NAME_NAME); includedGenericSessionData.remove(PVPConstants.GIVEN_NAME_NAME); includedGenericSessionData.remove(PVPConstants.BIRTHDATE_NAME); includedGenericSessionData.remove(PVPConstants.EID_SOURCE_PIN_NAME); includedGenericSessionData.remove(PVPConstants.EID_SOURCE_PIN_TYPE_NAME); } /** * @param authData * @param notValidbPK * @param notValidbPKType * @return */ private Pair getbaseIDFromSZR(AuthenticationData authData, String notValidbPK, String notValidbPKType) { try { EgovUtilPropertiesConfiguration eGovClientsConfig = authConfig.geteGovUtilsConfig(); if (eGovClientsConfig != null) { Logger.info("bPK in MOASession (bPK-Type:" + notValidbPKType + " does no match to Service-Provider configuration. --> Request SZR to get correct bPK."); SZRClient szrclient = new SZRClient(eGovClientsConfig); Logger.debug("Create SZR request to get baseID ... "); PersonInfoType personInfo = new PersonInfoType(); at.gv.util.xsd.szr.persondata.PhysicalPersonType person = new at.gv.util.xsd.szr.persondata.PhysicalPersonType(); personInfo.setPerson(person); at.gv.util.xsd.szr.persondata.PersonNameType name = new at.gv.util.xsd.szr.persondata.PersonNameType(); person.setName(name); at.gv.util.xsd.szr.persondata.IdentificationType idValue = new at.gv.util.xsd.szr.persondata.IdentificationType(); person.setIdentification(idValue); //set bPK or wbPK idValue.setValue(authData.getBPK()); idValue.setType(authData.getBPKType()); //set person information name.setGivenName(authData.getGivenName()); name.setFamilyName(authData.getFamilyName()); if (authData.getDateOfBirth() != null) person.setDateOfBirth(authData.getFormatedDateOfBirth()); //request szr and store baseID return Pair.newInstance(szrclient.getStammzahl(personInfo), Constants.URN_PREFIX_BASEID); } else { Logger.debug("No SZR clieht configuration found."); return null; } } catch (SZRException e) { Logger.warn("SZR connection FAILED. Interfederation SSO login not possible.", e); } catch (at.gv.util.ex.EgovUtilException e) { Logger.warn("SZR connection FAILED. Interfederation SSO login not possible.", e); } return null; } /** * Add encrypted bPKs from PVP Attribute 'ENC_BPK_LIST_NAME', which could be exist in * MOASession as 'GenericData'
session.getGenericDataFromSession(PVPConstants.ENC_BPK_LIST_NAME, String.class)
* to authData * * @param session MOASession, but never null * @param authData AuthenticationData DAO * @param spConfig Service-Provider configuration * * @return Pair which was received by PVP-Attribute and could be decrypted for this Service Provider, * or null if no attribute exists or can not decrypted * @throws ConfigurationException */ private Pair getEncryptedbPKFromPVPAttribute(IAuthenticationSession session, AuthenticationData authData, IOAAuthParameters spConfig) throws ConfigurationException { //set List of encrypted bPKs to authData DAO String pvpEncbPKListAttr = session.getGenericDataFromSession(PVPConstants.ENC_BPK_LIST_NAME, String.class); if (MiscUtil.isNotEmpty(pvpEncbPKListAttr)) { List encbPKList = Arrays.asList(pvpEncbPKListAttr.split(";")); authData.setEncbPKList(encbPKList); //check if one of this encrypted bPK could be decrypt for this Service-Provider for (String fullEncbPK : encbPKList) { int index = fullEncbPK.indexOf("|"); if (index >= 0) { String encbPK = fullEncbPK.substring(index+1); String second = fullEncbPK.substring(0, index); int secIndex = second.indexOf("+"); if (secIndex >= 0) { String oaTargetId = spConfig.getAreaSpecificTargetIdentifier(); if (oaTargetId.startsWith(MOAIDAuthConstants.PREFIX_CDID)) { String publicServiceShortTarget = oaTargetId.substring(MOAIDAuthConstants.PREFIX_CDID.length()); if (publicServiceShortTarget.equals(second.substring(secIndex+1))) { Logger.debug("Found encrypted bPK for online-application " + spConfig.getPublicURLPrefix() + " Start decryption process ..."); PrivateKey privKey = spConfig.getBPKDecBpkDecryptionKey(); if (privKey != null) { try { String bPK = BPKBuilder.decryptBPK(encbPK, publicServiceShortTarget, privKey); if (MiscUtil.isNotEmpty(bPK)) { Logger.info("bPK decryption process finished successfully."); return Pair.newInstance(bPK, oaTargetId); } else { Logger.error("bPK decryption FAILED."); } } catch (BuildException e) { Logger.error("bPK decryption FAILED.", e); } } else { Logger.info("bPK decryption FAILED, because no valid decryption key is found."); } } else { Logger.info("Found encrypted bPK but " + "encrypted bPK target does not match to online-application target"); } } else { Logger.info("Encrypted bPKs are only allowed for public services with prefix: " + MOAIDAuthConstants.PREFIX_CDID + " BUT oaTarget is " + oaTargetId); } } } } } return null; } /** * Get bPK from PVP Attribute 'BPK_NAME', which could be exist in * MOASession as 'GenericData'
session.getGenericDataFromSession(PVPConstants.BPK_NAME, String.class)
* * @param session MOASession, but never null * @return bPK, which was received by PVP-Attribute, or null if no attribute exists */ private String getbPKValueFromPVPAttribute(IAuthenticationSession session) { String pvpbPKValueAttr = session.getGenericDataFromSession(PVPConstants.BPK_NAME, String.class); if (MiscUtil.isNotEmpty(pvpbPKValueAttr)) { //fix a wrong bPK-value prefix, which was used in some PVP Standardportal implementations if (pvpbPKValueAttr.startsWith("bPK:")) { Logger.warn("Attribute " + PVPConstants.BPK_NAME + " contains a not standardize prefix! Staring attribute value correction process ..."); pvpbPKValueAttr = pvpbPKValueAttr.substring("bPK:".length()); } String[] spitted = pvpbPKValueAttr.split(":"); if (spitted.length != 2) { Logger.warn("Attribute " + PVPConstants.BPK_NAME + " has a wrong encoding and can NOT be USED!" + " Value:" + pvpbPKValueAttr); return null; } Logger.debug("Find PVP-Attr: " + PVPConstants.BPK_FRIENDLY_NAME); return spitted[1]; } return null; } /** * Get bPK-Type from PVP Attribute 'EID_SECTOR_FOR_IDENTIFIER_NAME', which could be exist in * MOASession as 'GenericData'
session.getGenericDataFromSession(PVPConstants.EID_SECTOR_FOR_IDENTIFIER_NAME, String.class)
* * @param session MOASession, but never null * @return bPKType, which was received by PVP-Attribute, or null if no attribute exists */ private String getbPKTypeFromPVPAttribute(IAuthenticationSession session) { String pvpbPKTypeAttr = session.getGenericDataFromSession(PVPConstants.EID_SECTOR_FOR_IDENTIFIER_NAME, String.class); if (MiscUtil.isNotEmpty(pvpbPKTypeAttr)) { //fix a wrong bPK-Type encoding, which was used in some PVP Standardportal implementations if (pvpbPKTypeAttr.startsWith(Constants.URN_PREFIX_CDID) && !pvpbPKTypeAttr.substring(Constants.URN_PREFIX_CDID.length(), Constants.URN_PREFIX_CDID.length() + 1).equals("+")) { Logger.warn("Receive uncorrect encoded bBKType attribute " + pvpbPKTypeAttr + " Starting attribute value correction ... "); pvpbPKTypeAttr = Constants.URN_PREFIX_CDID + "+" + pvpbPKTypeAttr.substring(Constants.URN_PREFIX_CDID.length() + 1); } Logger.debug("Find PVP-Attr: " + PVPConstants.EID_SECTOR_FOR_IDENTIFIER_FRIENDLY_NAME); return pvpbPKTypeAttr; } return null; /* * INFO: This code could be used to extract the bPKType from 'PVPConstants.BPK_NAME', * because the prefix of BPK_NAME attribute contains the postfix of the bPKType * * Now, all PVP Standardportals should be able to send 'EID_SECTOR_FOR_IDENTIFIER' * PVP attributes */ // String pvpbPKValueAttr = session.getGenericDataFromSession(PVPConstants.BPK_NAME, String.class); // String[] spitted = pvpbPKValueAttr.split(":"); // if (MiscUtil.isEmpty(authData.getBPKType())) { // Logger.debug("PVP assertion contains NO bPK/wbPK target attribute. " + // "Starting target extraction from bPK/wbPK prefix ..."); // //exract bPK/wbPK type from bpk attribute value prefix if type is // //not transmitted as single attribute // Pattern pattern = Pattern.compile("[a-zA-Z]{2}(-[a-zA-Z]+)?"); // Matcher matcher = pattern.matcher(spitted[0]); // if (matcher.matches()) { // //find public service bPK // authData.setBPKType(Constants.URN_PREFIX_CDID + "+" + spitted[0]); // Logger.debug("Found bPK prefix. Set target to " + authData.getBPKType()); // // } else { // //find business service wbPK // authData.setBPKType(Constants.URN_PREFIX_WBPK+ "+" + spitted[0]); // Logger.debug("Found wbPK prefix. Set target to " + authData.getBPKType()); // // } // } } private IIdentityLink buildOAspecificIdentityLink(IOAAuthParameters oaParam, IIdentityLink idl, String bPK, String bPKType) throws MOAIDException { if (oaParam.hasBaseIdTransferRestriction()) { Element idlassertion = idl.getSamlAssertion(); //set bpk/wpbk; Node prIdentification = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_IDENT_VALUE_XPATH); prIdentification.getFirstChild().setNodeValue(bPK); //set bkp/wpbk type Node prIdentificationType = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_IDENT_TYPE_XPATH); prIdentificationType.getFirstChild().setNodeValue(bPKType); IdentityLinkAssertionParser idlparser = new IdentityLinkAssertionParser(idlassertion); IIdentityLink businessServiceIdl = idlparser.parseIdentityLink(); //resign IDL IdentityLinkReSigner identitylinkresigner = IdentityLinkReSigner.getInstance(); Element resignedilAssertion; if (authConfig.isIdentityLinkResigning()) { resignedilAssertion = identitylinkresigner.resignIdentityLink(businessServiceIdl.getSamlAssertion(), authConfig.getIdentityLinkResigningKey()); } else { resignedilAssertion = businessServiceIdl.getSamlAssertion(); } IdentityLinkAssertionParser resignedIDLParser = new IdentityLinkAssertionParser(resignedilAssertion); return resignedIDLParser.parseIdentityLink(); } else return idl; } private Pair buildOAspecificbPK(IRequest pendingReq, IOAAuthParameters oaParam, AuthenticationData authData) throws BuildException, ConfigurationException { String baseID = authData.getIdentificationValue(); String baseIDType = authData.getIdentificationType(); Pair sectorSpecId = null; if (Constants.URN_PREFIX_BASEID.equals(baseIDType)) { //SAML1 legacy target parameter work-around String oaTargetId = null; Class saml1RequstTemplate = null; try { saml1RequstTemplate = Class.forName("at.gv.egovernment.moa.id.protocols.saml1.SAML1RequestImpl"); if (saml1RequstTemplate != null && saml1RequstTemplate.isInstance(pendingReq)) { oaTargetId = (String) pendingReq.getClass().getMethod("getTarget").invoke(pendingReq); } } catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException | java.lang.SecurityException | InvocationTargetException | NoSuchMethodException ex) { } if (MiscUtil.isEmpty(oaTargetId)) { oaTargetId = oaParam.getAreaSpecificTargetIdentifier(); Logger.debug("Use OA target identifier '" + oaTargetId + "' from configuration"); } else Logger.info("Use OA target identifier '" + oaTargetId + "' from SAML1 request for bPK calculation"); //calculate sector specific unique identifier sectorSpecId = new BPKBuilder().generateAreaSpecificPersonIdentifier(baseID, oaTargetId); } else { Logger.fatal("!!!baseID-element does not include a baseID. This should not be happen any more!!!"); sectorSpecId = Pair.newInstance(baseID, baseIDType); } Logger.trace("Authenticate user with bPK:" + sectorSpecId.getFirst() + " Type:" + sectorSpecId.getSecond()); return sectorSpecId; } }