summaryrefslogtreecommitdiff
path: root/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder
diff options
context:
space:
mode:
Diffstat (limited to 'eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder')
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/AbstractAuthenticationDataBuilder.java467
-rw-r--r--eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/BPKBuilder.java302
2 files changed, 769 insertions, 0 deletions
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/AbstractAuthenticationDataBuilder.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/AbstractAuthenticationDataBuilder.java
new file mode 100644
index 00000000..561f6f32
--- /dev/null
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/AbstractAuthenticationDataBuilder.java
@@ -0,0 +1,467 @@
+/*******************************************************************************
+ *******************************************************************************/
+package at.gv.egiz.eaaf.core.impl.idp.auth.builder;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.util.Base64Utils;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import at.gv.egiz.eaaf.core.api.IRequest;
+import at.gv.egiz.eaaf.core.api.data.EAAFConstants;
+import at.gv.egiz.eaaf.core.api.data.PVPAttributeDefinitions;
+import at.gv.egiz.eaaf.core.api.idp.IAuthenticationDataBuilder;
+import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
+import at.gv.egiz.eaaf.core.api.idp.ISPConfiguration;
+import at.gv.egiz.eaaf.core.api.idp.auth.data.IAuthProcessDataContainer;
+import at.gv.egiz.eaaf.core.api.idp.auth.data.IIdentityLink;
+import at.gv.egiz.eaaf.core.exceptions.EAAFBuilderException;
+import at.gv.egiz.eaaf.core.exceptions.EAAFConfigurationException;
+import at.gv.egiz.eaaf.core.exceptions.EAAFParserException;
+import at.gv.egiz.eaaf.core.exceptions.XPathException;
+import at.gv.egiz.eaaf.core.impl.data.Pair;
+import at.gv.egiz.eaaf.core.impl.idp.AuthenticationData;
+import at.gv.egiz.eaaf.core.impl.idp.auth.data.SimpleIdentityLinkAssertionParser;
+import at.gv.egiz.eaaf.core.impl.utils.XPathUtils;
+
+
+public abstract class AbstractAuthenticationDataBuilder implements IAuthenticationDataBuilder {
+ private static final Logger log = LoggerFactory.getLogger(AbstractAuthenticationDataBuilder.class);
+ protected Collection<String> includedToGenericAuthData = null;
+ @Autowired protected IConfiguration basicConfig;
+
+ protected void generateBasicAuthData(AuthenticationData authData, IRequest pendingReq,
+ IAuthProcessDataContainer authProcessData) throws EAAFBuilderException, EAAFConfigurationException, XPathException, DOMException, EAAFParserException {
+
+ if (authProcessData.getGenericSessionDataStorage() != null &&
+ !authProcessData.getGenericSessionDataStorage().isEmpty())
+ includedToGenericAuthData = authProcessData.getGenericSessionDataStorage().keySet();
+ else
+ includedToGenericAuthData = new ArrayList<String>();
+
+ //####################################################
+ //set general authData info's
+ authData.setAuthenticationIssuer(pendingReq.getAuthURL());
+ authData.setSsoSession(pendingReq.needSingleSignOnFunctionality());
+ authData.setBaseIDTransferRestrication(pendingReq.getServiceProviderConfiguration().hasBaseIdTransferRestriction());
+
+
+ //####################################################
+ //parse user info's from identityLink
+ IIdentityLink idlFromPVPAttr = null;
+ IIdentityLink identityLink = authProcessData.getIdentityLink();
+ if (identityLink != null) {
+ parseBasicUserInfosFromIDL(authData, identityLink, includedToGenericAuthData);
+
+ } else {
+ // identityLink is not direct in MOASession
+ String pvpAttrIDL = authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_IDENTITY_LINK_NAME, String.class);
+ //find PVP-Attr. which contains the IdentityLink
+ if (StringUtils.isNotEmpty(pvpAttrIDL)) {
+ log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.EID_IDENTITY_LINK_FRIENDLY_NAME
+ + " --> Parse basic user info's from that attribute.");
+ InputStream idlStream = null;
+ try {
+ idlStream = new ByteArrayInputStream(Base64Utils.decodeFromString(pvpAttrIDL));
+ idlFromPVPAttr = new SimpleIdentityLinkAssertionParser(idlStream).parseIdentityLink();
+ parseBasicUserInfosFromIDL(authData, idlFromPVPAttr, includedToGenericAuthData);
+
+ //set identitylink into AuthProcessData
+ authProcessData.setIdentityLink(idlFromPVPAttr);;
+
+ } catch (EAAFParserException e) {
+ log.warn("Received IdentityLink is not valid", e);
+
+ } catch (Exception e) {
+ log.warn("Received IdentityLink is not valid", e);
+
+ } finally {
+ try {
+ includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_IDENTITY_LINK_NAME);
+ if (idlStream != null)
+ idlStream.close();
+
+ } catch (IOException e) {
+ log.warn("Close InputStream FAILED.", e);
+
+ }
+ }
+ }
+
+ //if no basic user info's are set yet, parse info's single PVP-Attributes
+ if (StringUtils.isEmpty(authData.getFamilyName())) {
+ log.debug("No IdentityLink found or not parseable --> Parse basic user info's from single PVP-Attributes.");
+ authData.setFamilyName(authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.PRINCIPAL_NAME_NAME, String.class));
+ authData.setGivenName(authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.GIVEN_NAME_NAME, String.class));
+ authData.setDateOfBirth(authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.BIRTHDATE_NAME, String.class));
+ authData.setIdentificationValue(authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_SOURCE_PIN_NAME, String.class));
+ authData.setIdentificationType(authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_SOURCE_PIN_TYPE_NAME, String.class));
+
+ //remove corresponding keys from genericSessionData if exists
+ includedToGenericAuthData.remove(PVPAttributeDefinitions.PRINCIPAL_NAME_NAME);
+ includedToGenericAuthData.remove(PVPAttributeDefinitions.GIVEN_NAME_NAME);
+ includedToGenericAuthData.remove(PVPAttributeDefinitions.BIRTHDATE_NAME);
+ includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_NAME);
+ includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_TYPE_NAME);
+ }
+
+ }
+
+ if (authData.getIdentificationType() != null &&
+ !authData.getIdentificationType().equals(EAAFConstants.URN_PREFIX_BASEID)) {
+ log.trace("IdentificationType is not a baseID --> clear it. ");
+ authData.setBPK(authData.getIdentificationValue());
+ authData.setBPKType(authData.getIdentificationType());
+
+ authData.setIdentificationValue(null);
+ authData.setIdentificationType(null);
+ }
+
+ //####################################################
+ //set QAA level
+ includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_NAME);
+ String currentLoA = null;
+ if (StringUtils.isNotEmpty(authProcessData.getQAALevel()))
+ currentLoA = authProcessData.getQAALevel();
+ else {
+ currentLoA = authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_NAME, String.class);
+ if (StringUtils.isNotEmpty(currentLoA)) {
+ log.debug("Find PVP-Attr '" + PVPAttributeDefinitions.EID_CITIZEN_EIDAS_QAA_LEVEL_FRIENDLY_NAME + "':" + currentLoA
+ + " --> Parse QAA-Level from that attribute.");
+
+ }
+ }
+ if (StringUtils.isNotEmpty(currentLoA)) {
+ if (currentLoA.startsWith(EAAFConstants.EIDAS_QAA_PREFIX)) {
+ authData.seteIDASLoA(currentLoA);
+
+ } else
+ log.info("Only eIDAS LoAs are supported by this implementation");
+
+ } else {
+ log.info("No QAA level found. Set to default level " + EAAFConstants.EIDAS_QAA_LOW);
+ authData.seteIDASLoA(EAAFConstants.EIDAS_QAA_LOW);
+
+ }
+
+ //####################################################
+ //set isForeigner flag
+ //TODO: change to new eIDAS-token attribute identifier
+ if (authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_STORK_TOKEN_NAME) != null) {
+ log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.EID_STORK_TOKEN_FRIENDLY_NAME
+ + " --> Set 'isForeigner' flag to TRUE");
+ authData.setForeigner(true);
+
+ } else {
+ authData.setForeigner(authProcessData.isForeigner());
+
+ }
+
+ //####################################################
+ //set citizen country-code
+ includedToGenericAuthData.remove(PVPAttributeDefinitions.EID_ISSUING_NATION_NAME);
+ String pvpCCCAttr = authProcessData.getGenericDataFromSession(PVPAttributeDefinitions.EID_ISSUING_NATION_NAME, String.class);
+ if (StringUtils.isNotEmpty(pvpCCCAttr)) {
+ authData.setCiticenCountryCode(pvpCCCAttr);
+ log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.EID_ISSUING_NATION_FRIENDLY_NAME);
+
+ } else {
+ if (authData.isForeigner()) {
+ //TODO!!!!
+
+ } else {
+ authData.setCiticenCountryCode(basicConfig.getBasicConfiguration(
+ IConfiguration.CONFIG_PROPS_AUTH_DEFAULT_COUNTRYCODE,
+ EAAFConstants.COUNTRYCODE_AUSTRIA));
+
+ }
+ }
+
+
+ //####################################################
+ // set bPK and IdentityLink
+ String pvpbPKValue = getbPKValueFromPVPAttribute(authProcessData);
+ String pvpbPKTypeAttr = getbPKTypeFromPVPAttribute(authProcessData);
+ Pair<String, String> pvpEncbPKAttr = getEncryptedbPKFromPVPAttribute(authProcessData, authData, pendingReq.getServiceProviderConfiguration());
+
+ //check if a unique ID for this citizen exists
+ if (StringUtils.isEmpty(authData.getIdentificationValue()) &&
+ StringUtils.isEmpty(pvpbPKValue) && StringUtils.isEmpty(authData.getBPK()) &&
+ pvpEncbPKAttr == null) {
+ log.info("Can not build authData, because moaSession include no bPK, encrypted bPK or baseID");
+ throw new EAAFBuilderException("builder.08", new Object[]{"No " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME
+ + " or " + PVPAttributeDefinitions.EID_SOURCE_PIN_FRIENDLY_NAME
+ + " or " + PVPAttributeDefinitions.ENC_BPK_LIST_FRIENDLY_NAME},
+ "No " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME
+ + " or " + PVPAttributeDefinitions.EID_SOURCE_PIN_FRIENDLY_NAME
+ + " or " + PVPAttributeDefinitions.ENC_BPK_LIST_FRIENDLY_NAME);
+
+ }
+
+ // baseID is in MOASesson --> calculate bPK directly
+ if (StringUtils.isNotEmpty(authData.getIdentificationValue())) {
+ log.debug("Citizen baseID is in MOASession --> calculate bPK from this.");
+ Pair<String, String> result = buildOAspecificbPK(pendingReq, authData);
+ authData.setBPK(result.getFirst());
+ authData.setBPKType(result.getSecond());
+
+ //check if bPK already added to AuthData matches OA
+ } else if (StringUtils.isNotEmpty(authData.getBPK())
+ && matchsReceivedbPKToOnlineApplication(pendingReq.getServiceProviderConfiguration(), authData.getBPKType()) ) {
+ log.debug("Correct bPK is already included in AuthData.");
+
+ //check if bPK received by PVP-Attribute matches OA
+ } else if (StringUtils.isNotEmpty(pvpbPKValue) &&
+ matchsReceivedbPKToOnlineApplication(pendingReq.getServiceProviderConfiguration(), pvpbPKTypeAttr)) {
+ log.debug("Receive correct bPK from PVP-Attribute");
+ authData.setBPK(pvpbPKValue);
+ authData.setBPKType(pvpbPKTypeAttr);
+
+ //check if decrypted bPK exists
+ } else if (pvpEncbPKAttr != null) {
+ log.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 (StringUtils.isEmpty(notValidbPK) &&
+ StringUtils.isEmpty(notValidbPKType)) {
+ notValidbPK = pvpbPKValue;
+ notValidbPKType = pvpbPKTypeAttr;
+
+ if (StringUtils.isEmpty(notValidbPK) &&
+ StringUtils.isEmpty(notValidbPKType)) {
+ log.error("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<String, String> baseIDFromSZR = getbaseIDFromSZR(authData, notValidbPK, notValidbPKType);
+ if (baseIDFromSZR != null) {
+ log.info("Receive citizen baseID from SRZ. Authentication can be completed");
+ authData.setIdentificationValue(baseIDFromSZR.getFirst());
+ authData.setIdentificationType(baseIDFromSZR.getSecond());
+ Pair<String, String> result = buildOAspecificbPK(pendingReq, authData);
+ authData.setBPK(result.getFirst());
+ authData.setBPKType(result.getSecond());
+
+ } else {
+ log.warn("Can not build authData, because moaSession include no valid bPK, encrypted bPK or baseID");
+ throw new EAAFBuilderException("builder.08", new Object[]{"No valid " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME
+ + " or " + PVPAttributeDefinitions.EID_SOURCE_PIN_FRIENDLY_NAME
+ + " or " + PVPAttributeDefinitions.ENC_BPK_LIST_FRIENDLY_NAME},
+ "No valid " + PVPAttributeDefinitions.BPK_FRIENDLY_NAME
+ + " or " + PVPAttributeDefinitions.EID_SOURCE_PIN_FRIENDLY_NAME
+ + " or " + PVPAttributeDefinitions.ENC_BPK_LIST_FRIENDLY_NAME);
+
+ }
+ }
+
+ //build IdentityLink
+ if (authProcessData.getIdentityLink() != null)
+ authData.setIdentityLink(buildOAspecificIdentityLink(
+ pendingReq.getServiceProviderConfiguration(),
+ authProcessData.getIdentityLink(),
+ authData.getBPK(),
+ authData.getBPKType()));
+ else
+ log.info("Can NOT set IdentityLink. Msg: No IdentityLink found");
+
+ }
+
+ //extract a encrypted bPK from PVP attrobute
+ protected abstract Pair<String, String> getEncryptedbPKFromPVPAttribute(IAuthProcessDataContainer authProcessDataContainer,
+ AuthenticationData authData, ISPConfiguration spConfig) throws EAAFBuilderException;
+
+ //request baseId from SRZ
+ protected abstract Pair<String, String> getbaseIDFromSZR(AuthenticationData authData, String notValidbPK,
+ String notValidbPKType);
+
+
+ protected Pair<String, String> buildOAspecificbPK(IRequest pendingReq, AuthenticationData authData) throws EAAFBuilderException {
+ ISPConfiguration oaParam = pendingReq.getServiceProviderConfiguration();
+
+ String baseID = authData.getIdentificationValue();
+ String baseIDType = authData.getIdentificationType();
+ Pair<String, String> sectorSpecId = null;
+
+ if (EAAFConstants.URN_PREFIX_BASEID.equals(baseIDType)) {
+ //SAML1 legacy target parameter work-around
+ String spTargetId = oaParam.getAreaSpecificTargetIdentifier();
+ log.debug("Use OA target identifier '" + spTargetId + "' from configuration");
+
+ //calculate sector specific unique identifier
+ sectorSpecId = new BPKBuilder().generateAreaSpecificPersonIdentifier(baseID, spTargetId);
+
+ } else {
+ log.error("!!!baseID-element does not include a baseID. This should not be happen any more!!!");
+ sectorSpecId = Pair.newInstance(baseID, baseIDType);
+
+ }
+
+ log.trace("Authenticate user with bPK:" + sectorSpecId.getFirst() + " Type:" + sectorSpecId.getSecond());
+ return sectorSpecId;
+
+ }
+
+ protected IIdentityLink buildOAspecificIdentityLink(ISPConfiguration spConfig, IIdentityLink idl, String bPK, String bPKType) throws EAAFConfigurationException, XPathException, DOMException, EAAFParserException {
+ if (spConfig.hasBaseIdTransferRestriction()) {
+ log.debug("SP: " + spConfig.getUniqueIdentifier() + " has baseId transfer restriction. Remove baseId from IDL ...");
+ Element idlassertion = idl.getSamlAssertion();
+ //set bpk/wpbk;
+ Node prIdentification = XPathUtils.selectSingleNode(idlassertion, SimpleIdentityLinkAssertionParser.PERSON_IDENT_VALUE_XPATH);
+ prIdentification.getFirstChild().setNodeValue(bPK);
+ //set bkp/wpbk type
+ Node prIdentificationType = XPathUtils.selectSingleNode(idlassertion, SimpleIdentityLinkAssertionParser.PERSON_IDENT_TYPE_XPATH);
+ prIdentificationType.getFirstChild().setNodeValue(bPKType);
+
+ SimpleIdentityLinkAssertionParser idlparser = new SimpleIdentityLinkAssertionParser(idlassertion);
+ return idlparser.parseIdentityLink();
+
+ } else
+ return idl;
+
+ }
+
+ /**
+ * Check a bPK-Type against a Service-Provider configuration <br>
+ * If bPK-Type is <code>null</code> the result is <code>false</code>.
+ *
+ * @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
+ */
+ private boolean matchsReceivedbPKToOnlineApplication(ISPConfiguration oaParam, String bPKType) {
+ return oaParam.getAreaSpecificTargetIdentifier().equals(bPKType);
+
+ }
+
+ /**
+ * Parse information from an IdentityLink into AuthData object
+ *
+ * @param authData
+ * @param identityLink
+ * @param includedGenericSessionData
+ */
+ private void parseBasicUserInfosFromIDL(AuthenticationData authData, IIdentityLink identityLink, Collection<String> includedGenericSessionData) {
+ 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(PVPAttributeDefinitions.PRINCIPAL_NAME_NAME);
+ includedGenericSessionData.remove(PVPAttributeDefinitions.GIVEN_NAME_NAME);
+ includedGenericSessionData.remove(PVPAttributeDefinitions.BIRTHDATE_NAME);
+ includedGenericSessionData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_NAME);
+ includedGenericSessionData.remove(PVPAttributeDefinitions.EID_SOURCE_PIN_TYPE_NAME);
+
+ }
+
+ /**
+ * Get bPK from PVP Attribute 'BPK_NAME', which could be exist in
+ * MOASession as 'GenericData' <br> <pre><code>session.getGenericDataFromSession(PVPConstants.BPK_NAME, String.class)</code></pre>
+ *
+ * @param session MOASession, but never null
+ * @return bPK, which was received by PVP-Attribute, or <code>null</code> if no attribute exists
+ */
+ private String getbPKValueFromPVPAttribute(IAuthProcessDataContainer session) {
+ String pvpbPKValueAttr = session.getGenericDataFromSession(PVPAttributeDefinitions.BPK_NAME, String.class);
+ if (StringUtils.isNotEmpty(pvpbPKValueAttr)) {
+
+ //fix a wrong bPK-value prefix, which was used in some PVP Standardportal implementations
+ if (pvpbPKValueAttr.startsWith("bPK:")) {
+ log.warn("Attribute " + PVPAttributeDefinitions.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) {
+ log.warn("Attribute " + PVPAttributeDefinitions.BPK_NAME + " has a wrong encoding and can NOT be USED!"
+ + " Value:" + pvpbPKValueAttr);
+ return null;
+
+ }
+ log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.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' <br> <pre><code>session.getGenericDataFromSession(PVPConstants.EID_SECTOR_FOR_IDENTIFIER_NAME, String.class)</code></pre>
+ *
+ * @param session MOASession, but never null
+ * @return bPKType, which was received by PVP-Attribute, or <code>null</code> if no attribute exists
+ */
+ private String getbPKTypeFromPVPAttribute(IAuthProcessDataContainer session) {
+ String pvpbPKTypeAttr = session.getGenericDataFromSession(PVPAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME, String.class);
+ if (StringUtils.isNotEmpty(pvpbPKTypeAttr)) {
+
+ //fix a wrong bPK-Type encoding, which was used in some PVP Standardportal implementations
+ if (pvpbPKTypeAttr.startsWith(EAAFConstants.URN_PREFIX_CDID) &&
+ !pvpbPKTypeAttr.substring(EAAFConstants.URN_PREFIX_CDID.length(),
+ EAAFConstants.URN_PREFIX_CDID.length() + 1).equals("+")) {
+ log.warn("Receive uncorrect encoded bBKType attribute " + pvpbPKTypeAttr + " Starting attribute value correction ... ");
+ pvpbPKTypeAttr = EAAFConstants.URN_PREFIX_CDID + "+" + pvpbPKTypeAttr.substring(EAAFConstants.URN_PREFIX_CDID.length() + 1);
+
+ }
+ log.debug("Find PVP-Attr: " + PVPAttributeDefinitions.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());
+//
+// }
+// }
+
+ }
+
+}
diff --git a/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/BPKBuilder.java b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/BPKBuilder.java
new file mode 100644
index 00000000..62a57dd1
--- /dev/null
+++ b/eaaf_core/src/main/java/at/gv/egiz/eaaf/core/impl/idp/auth/builder/BPKBuilder.java
@@ -0,0 +1,302 @@
+/*******************************************************************************
+ * 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.
+ ******************************************************************************/
+/*
+ * Copyright 2003 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.egiz.eaaf.core.impl.idp.auth.builder;
+
+import java.security.InvalidKeyException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.Base64Utils;
+
+import at.gv.egiz.eaaf.core.api.data.EAAFConstants;
+import at.gv.egiz.eaaf.core.exceptions.EAAFBuilderException;
+import at.gv.egiz.eaaf.core.impl.data.Pair;
+
+/**
+ * Builder for the bPK, as defined in
+ * <code>&quot;Ableitung f&uml;r die bereichsspezifische Personenkennzeichnung&quot;</code>
+ * version <code>1.0.1</code> from <code>&quot;reference.e-government.gv.at&quot;</code>.
+ *
+ */
+public class BPKBuilder {
+ private static final Logger log = LoggerFactory.getLogger(BPKBuilder.class);
+
+ /**
+ * Calculates an area specific unique person-identifier from a baseID
+ *
+ * @param baseID baseId from user but never null
+ * @param targetIdentifier target identifier for area specific identifier calculation but never null
+ * @return Pair<unique person identifier for this target, targetArea> but never null
+ * @throws EAAFBuilderException if some input data are not valid
+ */
+ public Pair<String, String> generateAreaSpecificPersonIdentifier(String baseID, String targetIdentifier) throws EAAFBuilderException {
+ return generateAreaSpecificPersonIdentifier(baseID, EAAFConstants.URN_PREFIX_BASEID, targetIdentifier);
+
+ }
+
+ /**
+ * Calculates an area specific unique person-identifier from an unique identifier with a specific type
+ *
+ * @param baseID baseId from user but never null
+ * @param baseIdType Type of the baseID but never null
+ * @param targetIdentifier target identifier for area specific identifier calculation but never null
+ * @return Pair<unique person identifier for this target, targetArea> but never null
+ * @throws EAAFBuilderException if some input data are not valid
+ */
+ public Pair<String, String> generateAreaSpecificPersonIdentifier(String baseID, String baseIdType, String targetIdentifier) throws EAAFBuilderException{
+ if (StringUtils.isEmpty(baseID))
+ throw new EAAFBuilderException("builder.00", new Object[]{"baseID is empty or null"},
+ "BaseId is empty or null");
+
+ if (StringUtils.isEmpty(baseIdType))
+ throw new EAAFBuilderException("builder.00", new Object[]{"the type of baseID is empty or null"},
+ "Type of baseId is empty or null");
+
+ if (StringUtils.isEmpty(targetIdentifier))
+ throw new EAAFBuilderException("builder.00", new Object[]{"SP specific target identifier is empty or null"},
+ "SP specific target identifier is empty or null");
+
+ if (baseIdType.equals(EAAFConstants.URN_PREFIX_BASEID)) {
+ log.trace("Find baseID. Starting unique identifier caluclation for this target");
+
+ if (targetIdentifier.startsWith(EAAFConstants.URN_PREFIX_CDID) ||
+ targetIdentifier.startsWith(EAAFConstants.URN_PREFIX_WBPK)) {
+ log.trace("Calculate bPK, wbPK, or STORK identifier for target: " + targetIdentifier);
+ return Pair.newInstance(calculatebPKwbPK(baseID + "+" + targetIdentifier), targetIdentifier);
+
+ } else if (targetIdentifier.startsWith(EAAFConstants.URN_PREFIX_EIDAS)) {
+ log.trace("Calculate eIDAS identifier for target: " + targetIdentifier);
+ String[] splittedTarget = targetIdentifier.split("\\+");
+ String cititzenCountryCode = splittedTarget[1];
+ String eIDASOutboundCountry = splittedTarget[2];
+
+ if (cititzenCountryCode.equalsIgnoreCase(eIDASOutboundCountry)) {
+ log.warn("Suspect configuration FOUND!!! CitizenCountry equals DestinationCountry");
+
+ }
+ return buildeIDASIdentifer(baseID, baseIdType, cititzenCountryCode, eIDASOutboundCountry);
+
+
+ } else
+ throw new EAAFBuilderException("builder.00",
+ new Object[]{"Target identifier: " + targetIdentifier + " is NOT allowed or unknown"},
+ "Target identifier: " + targetIdentifier + " is NOT allowed or unknown");
+
+ } else {
+ log.trace("BaseID is not of type " + EAAFConstants.URN_PREFIX_BASEID + ". Check type against requested target ...");
+ if (baseIdType.equals(targetIdentifier)) {
+ log.debug("Unique identifier is already area specific. Is nothing todo");
+ return Pair.newInstance(baseID, targetIdentifier);
+
+ } else {
+ log.warn("Get unique identifier for target: " + baseIdType + " but target: " + targetIdentifier + " is required!");
+ throw new EAAFBuilderException("builder.00",
+ new Object[]{"Get unique identifier for target: " + baseIdType + " but target: " + targetIdentifier + " is required"},
+ "Get unique identifier for target: " + baseIdType + " but target: " + targetIdentifier + " is required");
+
+ }
+ }
+ }
+
+
+ /**
+ * Builds the eIDAS from the given parameters.
+ *
+ * @param baseID baseID of the citizen
+ * @param baseIDType Type of the baseID
+ * @param sourceCountry CountryCode of that country, which build the eIDAs ID
+ * @param destinationCountry CountryCode of that country, which receives the eIDAs ID
+ *
+ * @return Pair<eIDAs, bPKType> in a BASE64 encoding
+ * @throws EAAFBuilderException if some input data are not valid
+ */
+ private Pair<String, String> buildeIDASIdentifer(String baseID, String baseIDType, String sourceCountry, String destinationCountry)
+ throws EAAFBuilderException {
+ String bPK = null;
+ String bPKType = null;
+
+ // check if we have been called by public sector application
+ if (baseIDType.startsWith(EAAFConstants.URN_PREFIX_BASEID)) {
+ bPKType = EAAFConstants.URN_PREFIX_EIDAS + "+" + sourceCountry + "+" + destinationCountry;
+ log.debug("Building eIDAS identification from: [identValue]+" + bPKType);
+ bPK = calculatebPKwbPK(baseID + "+" + bPKType);
+
+ } else { // if not, sector identification value is already calculated by BKU
+ log.debug("eIDAS eIdentifier already provided by BKU");
+ bPK = baseID;
+ }
+
+ if ((StringUtils.isEmpty(bPK) ||
+ StringUtils.isEmpty(sourceCountry) ||
+ StringUtils.isEmpty(destinationCountry))) {
+ throw new EAAFBuilderException("builder.00",
+ new Object[]{"eIDAS-ID", "Unvollständige Parameterangaben: identificationValue=" +
+ bPK + ", Zielland=" + destinationCountry + ", Ursprungsland=" + sourceCountry}
+ ,"eIDAS-ID: Unvollständige Parameterangaben: identificationValue=" +
+ bPK + ", Zielland=" + destinationCountry + ", Ursprungsland=" + sourceCountry);
+ }
+
+ log.debug("Building eIDAS identification from: " + sourceCountry+"/"+destinationCountry+"/" + "[identValue]");
+ String eIdentifier = sourceCountry + "/" + destinationCountry + "/" + bPK;
+
+ return Pair.newInstance(eIdentifier, bPKType);
+ }
+
+ public static String encryptBPK(String bpk, String target, PublicKey publicKey) throws EAAFBuilderException {
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
+ if (target.startsWith(EAAFConstants.URN_PREFIX_CDID + "+"))
+ target = target.substring((EAAFConstants.URN_PREFIX_CDID + "+").length());
+
+ String input = "V1::urn:publicid:gv.at:cdid+" + target + "::"
+ + bpk + "::"
+ + sdf.format(new Date());
+ System.out.println(input);
+ byte[] result;
+ try {
+ byte[] inputBytes = input.getBytes("ISO-8859-1");
+ result = encrypt(inputBytes, publicKey);
+ return new String(Base64Utils.encode(result), "ISO-8859-1").replaceAll("\r\n", "");
+ //return new String(Base64Utils.encode(result, "ISO-8859-1")).replaceAll("\r\n", "");
+
+
+ } catch (Exception e) {
+ throw new EAAFBuilderException("bPK encryption FAILED", null,
+ e.getMessage(), e);
+
+ }
+ }
+
+ public static String decryptBPK(String encryptedBpk, String target, PrivateKey privateKey) throws EAAFBuilderException {
+ String decryptedString;
+ try {
+ //byte[] encryptedBytes = Base64Utils.decode(encryptedBpk, false, "ISO-8859-1");
+ byte[] encryptedBytes = Base64Utils.decode(encryptedBpk.getBytes("ISO-8859-1"));
+ byte[] decryptedBytes = decrypt(encryptedBytes, privateKey);
+ decryptedString = new String(decryptedBytes, "ISO-8859-1");
+
+ } catch (Exception e) {
+ throw new EAAFBuilderException("bPK decryption FAILED", null,
+ e.getMessage(), e);
+
+ }
+
+ String tmp = decryptedString.substring(decryptedString.indexOf('+') + 1);
+ String sector = tmp.substring(0, tmp.indexOf("::"));
+ tmp = tmp.substring(tmp.indexOf("::") + 2);
+ String bPK = tmp.substring(0, tmp.indexOf("::"));
+
+ if (target.startsWith(EAAFConstants.URN_PREFIX_CDID + "+"))
+ target = target.substring((EAAFConstants.URN_PREFIX_CDID + "+").length());
+
+ if (target.equals(sector))
+ return bPK;
+
+ else {
+ log.error("Decrypted bPK does not match to request bPK target.");
+ return null;
+ }
+ }
+
+ private String calculatebPKwbPK(String basisbegriff) throws EAAFBuilderException {
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ byte[] hash = md.digest(basisbegriff.getBytes("ISO-8859-1"));
+ String hashBase64 = new String(Base64Utils.encode(hash), "ISO-8859-1").replaceAll("\r\n", ""); //Base64Utils.encode(hash);
+ return hashBase64;
+
+ } catch (Exception ex) {
+ throw new EAAFBuilderException("builder.00", new Object[]{"bPK/wbPK", ex.toString()},
+ ex.getMessage(), ex);
+
+ }
+
+ }
+
+ private static byte[] encrypt(byte[] inputBytes, PublicKey publicKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
+ byte[] result;
+ Cipher cipher = null;
+ try {
+ cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); // try with bouncycastle
+
+ } catch(NoSuchAlgorithmException e) {
+ cipher = Cipher.getInstance("RSA/ECB/OAEP"); // try with iaik provider
+ }
+ cipher.init(Cipher.ENCRYPT_MODE, publicKey);
+ result = cipher.doFinal(inputBytes);
+
+ return result;
+ }
+
+ private static byte[] decrypt(byte[] encryptedBytes, PrivateKey privateKey)
+ throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException{
+ byte[] result;
+ Cipher cipher = null;
+ try {
+ cipher = Cipher.getInstance("RSA/ECB/OAEPPadding"); // try with bouncycastle
+
+ } catch(NoSuchAlgorithmException e) {
+ cipher = Cipher.getInstance("RSA/ECB/OAEP"); // try with iaik provider
+
+ }
+ cipher.init(Cipher.DECRYPT_MODE, privateKey);
+ result = cipher.doFinal(encryptedBytes);
+ return result;
+
+ }
+}