diff options
Diffstat (limited to 'id/server')
18 files changed, 354 insertions, 91 deletions
diff --git a/id/server/doc/handbook/additional/additional.html b/id/server/doc/handbook/additional/additional.html index ec57a74c6..9e3cdf11e 100644 --- a/id/server/doc/handbook/additional/additional.html +++ b/id/server/doc/handbook/additional/additional.html @@ -361,6 +361,21 @@ <td width="1127" valign="top"><p>SAML1 StartAuthentication Request</p></td> </tr> <tr> + <td valign="top">3400</td> + <td valign="top"> </td> + <td valign="top">eIDAS Metadata Request</td> + </tr> + <tr> + <td valign="top">3401</td> + <td valign="top">Request ID</td> + <td valign="top">Eingehender eIDAS Authentication Request</td> + </tr> + <tr> + <td valign="top">3402</td> + <td valign="top">Response ID</td> + <td valign="top">eIDAS Authentication Response erstellt</td> + </tr> + <tr> <td width="165" valign="top"><p align="center">4000</p></td> <td width="312" valign="top"><p align="left"> </p></td> <td width="1127" valign="top"><p>Identifizierungs- und Authentifizierungsprozess wurde gestartet</p></td> diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/AuthenticationDataBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/AuthenticationDataBuilder.java index 3264fc3bd..cad3354f5 100644 --- a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/AuthenticationDataBuilder.java +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/AuthenticationDataBuilder.java @@ -71,7 +71,6 @@ 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.moduls.RequestImpl; 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; @@ -1105,16 +1104,20 @@ public class AuthenticationDataBuilder extends MOAIDAuthConstants { String baseID = authData.getIdentificationValue(); String baseIDType = authData.getIdentificationType(); - - String eIDASOutboundCountry = pendingReq.getGenericData(RequestImpl.eIDAS_GENERIC_REQ_DATA_COUNTRY, String.class); - - //TODO: maybe find a better solution - String cititzenCountryCode = - authConfig.getBasicMOAIDConfiguration("moa.id.protocols.eIDAS.node.countrycode", - MOAIDAuthConstants.COUNTRYCODE_AUSTRIA); - - if (Constants.URN_PREFIX_BASEID.equals(baseIDType)) { - if (MiscUtil.isNotEmpty(eIDASOutboundCountry) && !cititzenCountryCode.equals(eIDASOutboundCountry)) { + + if (Constants.URN_PREFIX_BASEID.equals(baseIDType)) { + //Calculate eIDAS identifier + if (oaParam.getBusinessService() && + oaParam.getIdentityLinkDomainIdentifier().startsWith(Constants.URN_PREFIX_EIDAS)) { + String[] splittedTarget = oaParam.getIdentityLinkDomainIdentifier().split("\\+"); + String cititzenCountryCode = splittedTarget[1]; + String eIDASOutboundCountry = splittedTarget[2]; + + if (cititzenCountryCode.equalsIgnoreCase(eIDASOutboundCountry)) { + Logger.warn("Suspect configuration FOUND!!! CitizenCountry equals DestinationCountry"); + + } + Pair<String, String> eIDASID = new BPKBuilder().buildeIDASIdentifer(baseIDType, baseID, cititzenCountryCode, eIDASOutboundCountry); Logger.debug("Authenticate user with bPK:" + eIDASID.getFirst() + " Type:" + eIDASID.getSecond()); diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/BPKBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/BPKBuilder.java index 9e4e36fec..32ac8ad68 100644 --- a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/BPKBuilder.java +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/builder/BPKBuilder.java @@ -238,7 +238,7 @@ public class BPKBuilder { Logger.debug("Building eIDAS identification from: " + sourceCountry+"/"+destinationCountry+"/" + "[identValue]"); String eIdentifier = sourceCountry + "/" + destinationCountry + "/" + bPK; - return Pair.newInstance(eIdentifier, baseIDType); + return Pair.newInstance(eIdentifier, bPKType); } private String calculatebPKwbPK(String basisbegriff) throws BuildException { diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/stork/STORKConfig.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/stork/STORKConfig.java index 99e4b4cce..b85938bb7 100644 --- a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/stork/STORKConfig.java +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/config/stork/STORKConfig.java @@ -96,7 +96,7 @@ public class STORKConfig implements IStorkConfig { new CPEPS(storkCPEPSProps.get(listCounter + "." + MOAIDConfigurationConstants.GENERAL_AUTH_STORK_CPEPS_LIST_COUNTRY),
new URL(storkCPEPSProps.get(listCounter + "." + MOAIDConfigurationConstants.GENERAL_AUTH_STORK_CPEPS_LIST_URL)),
enableAssertionEncryption);
- cpepsMap.put(moacpep.getCountryCode(), moacpep);
+ cpepsMap.put(moacpep.getFullCountryCode(), moacpep);
} catch (MalformedURLException e) {
Logger.warn("CPEPS URL " +
@@ -167,7 +167,7 @@ public class STORKConfig implements IStorkConfig { if (StringUtils.isEmpty(ccc) || this.cpepsMap.isEmpty())
return false;
- if (this.cpepsMap.containsKey(ccc.toUpperCase()))
+ if (this.cpepsMap.containsKey(ccc))
return true;
else
return false;
diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/moduls/RequestImpl.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/moduls/RequestImpl.java index b612352c6..b87574d52 100644 --- a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/moduls/RequestImpl.java +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/moduls/RequestImpl.java @@ -61,7 +61,7 @@ public abstract class RequestImpl implements IRequest, Serializable{ public static final String DATAID_REQUESTER_IP_ADDRESS = "requesterIP"; - public static final String eIDAS_GENERIC_REQ_DATA_COUNTRY = "country"; +// public static final String eIDAS_GENERIC_REQ_DATA_COUNTRY = "country"; public static final String eIDAS_GENERIC_REQ_DATA_LEVELOFASSURENCE = "eIDAS_LoA"; diff --git a/id/server/idserverlib/src/main/resources/resources/properties/id_messages_de.properties b/id/server/idserverlib/src/main/resources/resources/properties/id_messages_de.properties index b88df0b9d..1a2f0d1d3 100644 --- a/id/server/idserverlib/src/main/resources/resources/properties/id_messages_de.properties +++ b/id/server/idserverlib/src/main/resources/resources/properties/id_messages_de.properties @@ -271,6 +271,8 @@ eIDAS.11=Received eIDAS Error-Response. Reason:{0} eIDAS.12=Received eIDAS AuthnRequest is not valid. Reason:{0}
eIDAS.13=Generation of eIDAS Response FAILED. Reason:{0}
eIDAS.14=eIDAS Response validation FAILED: LevelOfAssurance {0} is to low.
+eIDAS.15=Generation of eIDAS Response FAILED. Required attribute: {0} is NOT available.
+eIDAS.16=eIDAS Response attribute-validation FAILED. Attribute:{0} Reason: {1}.
pvp2.01=Fehler beim kodieren der PVP2 Antwort
pvp2.02=Ungueltiges Datumsformat
diff --git a/id/server/idserverlib/src/main/resources/resources/properties/protocol_response_statuscodes_de.properties b/id/server/idserverlib/src/main/resources/resources/properties/protocol_response_statuscodes_de.properties index e72a28046..c6d0844ce 100644 --- a/id/server/idserverlib/src/main/resources/resources/properties/protocol_response_statuscodes_de.properties +++ b/id/server/idserverlib/src/main/resources/resources/properties/protocol_response_statuscodes_de.properties @@ -228,6 +228,8 @@ eIDAS.11=1302 eIDAS.12=1305 eIDAS.13=1307 eIDAS.14=1301 +eIDAS.15=1307 +eIDAS.16=1301 pvp2.01=6100 pvp2.06=6100 diff --git a/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/id/commons/api/data/CPEPS.java b/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/id/commons/api/data/CPEPS.java index a88aa2171..525a660b4 100644 --- a/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/id/commons/api/data/CPEPS.java +++ b/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/id/commons/api/data/CPEPS.java @@ -62,11 +62,23 @@ public class CPEPS { this.isXMLSignatureSupported = isXMLSignatureSupported;
}
+
+ public String getFullCountryCode() {
+ return countryCode;
+ }
+
/**
* Gets the country code of this C-PEPS
* @return ISO country code
*/
public String getCountryCode() {
+ if (countryCode != null &&
+ countryCode.contains("-")) {
+ //remove trailing information to country code
+ return countryCode.substring(0, countryCode.indexOf("-"));
+
+ }
+
return countryCode;
}
diff --git a/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/id/commons/config/ConfigurationMigrationUtils.java b/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/id/commons/config/ConfigurationMigrationUtils.java index 61b08f3a6..b8284c8f9 100644 --- a/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/id/commons/config/ConfigurationMigrationUtils.java +++ b/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/id/commons/config/ConfigurationMigrationUtils.java @@ -981,13 +981,17 @@ public class ConfigurationMigrationUtils { } // transfer the incoming data to the database model stork.setStorkLogonEnabled(Boolean.parseBoolean(oa.get(MOAIDConfigurationConstants.SERVICE_AUTH_STORK_ENABLED))); - if (MiscUtil.isNotEmpty(oa.get(MOAIDConfigurationConstants.SERVICE_AUTH_STORK_MINQAALEVEL))) + if (MiscUtil.isNotEmpty(oa.get(MOAIDConfigurationConstants.SERVICE_AUTH_STORK_MINQAALEVEL))) { try { + stork.seteIDAS_LOA(oa.get(MOAIDConfigurationConstants.SERVICE_AUTH_STORK_MINQAALEVEL)); stork.setQaa(Integer.valueOf(oa.get(MOAIDConfigurationConstants.SERVICE_AUTH_STORK_MINQAALEVEL))); + } catch (NumberFormatException e) { Logger.info("Downgraded OA config found -> change eIDAS LoA to STORK QAA"); stork.setQaa(4); + } + } if (MiscUtil.isNotEmpty(oa.get(MOAIDConfigurationConstants.PREFIX_MOAID_SERVICES)) && oa.get(MOAIDConfigurationConstants.PREFIX_MOAID_SERVICES).equals(MOAIDConfigurationConstants.PREFIX_VIDP)) diff --git a/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/util/Constants.java b/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/util/Constants.java index 260b2ecb1..129478270 100644 --- a/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/util/Constants.java +++ b/id/server/moa-id-commons/src/main/java/at/gv/egovernment/moa/util/Constants.java @@ -449,7 +449,7 @@ public interface Constants { //TODO: update to eIDAS prefix /** URN prefix for context dependent id (eIDAS). */ - public static final String URN_PREFIX_EIDAS = URN_PREFIX + ":storkid"; + public static final String URN_PREFIX_EIDAS = URN_PREFIX + ":eidasid"; /** URN prefix for context dependent id. */ public static final String URN_PREFIX_BASEID = URN_PREFIX + ":baseid"; diff --git a/id/server/moa-id-frontend-resources/src/main/java/at/gv/egovernment/moa/id/auth/frontend/builder/ServiceProviderSpecificGUIFormBuilderConfiguration.java b/id/server/moa-id-frontend-resources/src/main/java/at/gv/egovernment/moa/id/auth/frontend/builder/ServiceProviderSpecificGUIFormBuilderConfiguration.java index 0a5cdaf3e..63df81b3c 100644 --- a/id/server/moa-id-frontend-resources/src/main/java/at/gv/egovernment/moa/id/auth/frontend/builder/ServiceProviderSpecificGUIFormBuilderConfiguration.java +++ b/id/server/moa-id-frontend-resources/src/main/java/at/gv/egovernment/moa/id/auth/frontend/builder/ServiceProviderSpecificGUIFormBuilderConfiguration.java @@ -126,12 +126,12 @@ public class ServiceProviderSpecificGUIFormBuilderConfiguration extends Abstract try { for (CPEPS current : oaParam.getPepsList()) { String countryName = null; - if (MiscUtil.isNotEmpty(MOAIDAuthConstants.COUNTRYCODE_XX_TO_NAME.get(current.getCountryCode().toUpperCase()))) - countryName = MOAIDAuthConstants.COUNTRYCODE_XX_TO_NAME.get(current.getCountryCode().toUpperCase()); + if (MiscUtil.isNotEmpty(MOAIDAuthConstants.COUNTRYCODE_XX_TO_NAME.get(current.getFullCountryCode().toUpperCase()))) + countryName = MOAIDAuthConstants.COUNTRYCODE_XX_TO_NAME.get(current.getFullCountryCode().toUpperCase()); else - countryName = current.getCountryCode().toUpperCase(); + countryName = current.getFullCountryCode().toUpperCase(); - pepslist += "<option value=" + current.getCountryCode() + ">" + pepslist += "<option value=" + current.getFullCountryCode() + ">" + countryName + "</option>\n"; diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/Constants.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/Constants.java index 6cb4d6add..369d77863 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/Constants.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/Constants.java @@ -98,8 +98,6 @@ public class Constants { public static final int eIDAS_REVERSIONSLOG_METADATA = 3400; public static final int eIDAS_REVERSIONSLOG_IDP_AUTHREQUEST = 3401; public static final int eIDAS_REVERSIONSLOG_IDP_AUTHRESPONSE = 3402; - public static final int eIDAS_REVERSIONSLOG_SP_AUTHREQUEST= 3403; - public static final int eIDAS_REVERSIONSLOG_SP_AUTHRESPONSE= 3404; //metadata constants // public final static Map<String, EidasAttributesTypes> METADATA_POSSIBLE_ATTRIBUTES = Collections.unmodifiableMap( diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java index 0eb067c5a..7155040c6 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java @@ -22,13 +22,10 @@ */ package at.gv.egovernment.moa.id.auth.modules.eidas.tasks; -import java.io.ByteArrayOutputStream; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.zip.Deflater; -import java.util.zip.DeflaterOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -42,7 +39,6 @@ import org.opensaml.common.xml.SAMLConstants; import org.opensaml.saml2.metadata.EntityDescriptor; import org.opensaml.saml2.metadata.SingleSignOnService; import org.opensaml.saml2.metadata.provider.MetadataProviderException; -import org.opensaml.xml.util.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -197,7 +193,17 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { } } - //build requested attribute set + //request + if (reqAttrList.isEmpty()) { + Logger.info("No attributes requested by OA:" + pendingReq.getOnlineApplicationConfiguration().getPublicURLPrefix() + + " --> Request attr:" + Constants.eIDAS_ATTR_PERSONALIDENTIFIER + " by default"); + AttributeDefinition<?> newAttribute = SAMLEngineUtils.getMapOfAllAvailableAttributes().get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER); + Builder<?> attrBuilder = AttributeDefinition.builder(newAttribute).required(true); + reqAttrList.add(attrBuilder.build()); + + } + + //build requested attribute set ImmutableAttributeMap reqAttrMap = new ImmutableAttributeMap.Builder().putAll(reqAttrList).build(); //build eIDAS AuthnRequest @@ -274,40 +280,6 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { } } - - /** - * Encode the eIDAS request with Redirect binding - * - * @param pendingReq - * @param authnReqEndpoint - * @param token - * @param authnRequest - * @param response - * @throws MOAIDException - */ - private void buildRedirecttBindingRequest(IRequest pendingReq, SingleSignOnService authnReqEndpoint, - byte[] token, IRequestMessage authnRequest, HttpServletResponse response) - throws MOAIDException { - - //FIXME: implement correct deflat encoding accodring to SAML2 Redirect Binding specification - - try { - ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); - Deflater deflater = new Deflater(Deflater.DEFLATED, true); - DeflaterOutputStream deflaterStream = new DeflaterOutputStream(bytesOut, deflater); - deflaterStream.write(token); - deflaterStream.finish(); - String samlReqBase64 = Base64.encodeBytes(bytesOut.toByteArray(), Base64.DONT_BREAK_LINES); - - - - } catch (Exception e) { - Logger.error("eIDAS Redirect-Binding request encoding error: " + e.getMessage()); - throw new MOAIDException("eIDAS.02", new Object[]{e.getMessage()}, e); - - } - - } /** * Encode the eIDAS request with POST binding diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/ReceiveAuthnResponseTask.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/ReceiveAuthnResponseTask.java index c4b2bfeae..45ba3d64e 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/ReceiveAuthnResponseTask.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/ReceiveAuthnResponseTask.java @@ -19,12 +19,12 @@ import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.commons.db.ex.MOADatabaseException; import at.gv.egovernment.moa.id.process.api.ExecutionContext; +import at.gv.egovernment.moa.id.protocols.eidas.validator.eIDASResponseValidator; import at.gv.egovernment.moa.id.protocols.pvp2x.PVPConstants; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MiscUtil; import eu.eidas.auth.commons.EidasStringUtil; import eu.eidas.auth.commons.protocol.IAuthenticationResponse; -import eu.eidas.auth.commons.protocol.eidas.LevelOfAssurance; import eu.eidas.auth.engine.ProtocolEngineI; import eu.eidas.engine.exceptions.EIDASSAMLEngineException; @@ -78,16 +78,8 @@ public class ReceiveAuthnResponseTask extends AbstractAuthServletTask { // ********************************************************** // ******* MOA-ID specific response validation ********** // ********************************************************** - - //validate received LoA against minimum required LoA - LevelOfAssurance reqLoA = LevelOfAssurance.fromString(pendingReq.getOnlineApplicationConfiguration().getQaaLevel()); - LevelOfAssurance respLoA = LevelOfAssurance.fromString(samlResp.getLevelOfAssurance()); - if (respLoA.numericValue() < reqLoA.numericValue()) { - Logger.error("eIDAS Response LevelOfAssurance is lower than the required! " - + "(Resp-LoA:" + respLoA.getValue() + " Req-LoA:" + reqLoA.getValue() + ")"); - throw new MOAIDException("eIDAS.14", new Object[]{respLoA.getValue()}); - - } + String spCountry = authConfig.getBasicMOAIDConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_COUNTRYCODE, "AT"); + eIDASResponseValidator.validateResponse(pendingReq, samlResp, spCountry); // ********************************************************** diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/eIDASAttributeProcessingUtils.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/eIDASAttributeProcessingUtils.java new file mode 100644 index 000000000..30e1e4505 --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/eIDASAttributeProcessingUtils.java @@ -0,0 +1,75 @@ +/* + * 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.modules.eidas.utils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; +import at.gv.egovernment.moa.id.data.Trible; +import at.gv.egovernment.moa.logging.Logger; + +/** + * @author tlenz + * + */ +public class eIDASAttributeProcessingUtils { + public static final String PERSONALIDENIFIER_VALIDATION_PATTERN = "^[A-Z,a-z]{2}/[A-Z,a-z]{2}/.*"; + + /** + * Validate a eIDAS PersonalIdentifier attribute value + * This validation is done according to eIDAS SAML Attribute Profile - Section 2.2.3 Unique Identifier + * + * @param uniqueID eIDAS attribute value of a unique identifier + * @return true if the uniqueID matches to eIDAS to Unique Identifier specification, otherwise false + */ + public static boolean validateEidasPersonalIdentifier(String uniqueID) { + Pattern pattern = Pattern.compile(PERSONALIDENIFIER_VALIDATION_PATTERN ); + Matcher matcher = pattern.matcher(uniqueID); + return matcher.matches(); + + } + + + /** + * Parse an eIDAS PersonalIdentifier attribute value into it components. + * This processing is done according to eIDAS SAML Attribute Profile - Section 2.2.3 Unique Identifier + * + * @param uniqueID eIDAS attribute value of a unique identifier + * @return {@link Trible} that contains: + * <br> First : citizen country + * <br> Second: destination country + * <br> Third : unique identifier + * <br> or null if the attribute value has a wrong format + */ + public static Trible<String, String, String> parseEidasPersonalIdentifier(String uniqueID) { + if (!validateEidasPersonalIdentifier(uniqueID)) { + Logger.error("eIDAS attribute value for " + Constants.eIDAS_ATTR_PERSONALIDENTIFIER + + " looks wrong formated. Value:" + ((String)uniqueID)); + return null; + + } + return Trible.newInstance(uniqueID.substring(0, 2), uniqueID.substring(3, 5), uniqueID.substring(6)); + + } +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java index aefae939b..388d65963 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java @@ -25,6 +25,8 @@ package at.gv.egovernment.moa.id.protocols.eidas; import java.io.IOException; import java.io.StringWriter; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -149,7 +151,8 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { //preProcess eIDAS request preProcess(req, resp, pendingReq); - revisionsLogger.logEvent(pendingReq, Constants.eIDAS_REVERSIONSLOG_IDP_AUTHREQUEST); + revisionsLogger.logEvent(pendingReq, Constants.eIDAS_REVERSIONSLOG_IDP_AUTHREQUEST, + pendingReq.getEidasRequest().getId()); //AuthnRequest needs authentication pendingReq.setNeedAuthentication(true); @@ -213,6 +216,11 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { } + //check eIDAS node configuration + IOAAuthParameters oaConfig = authConfig.getOnlineApplicationParameter(samlReq.getIssuer()); + if (oaConfig == null) + throw new EIDASAuthnRequestProcessingException("eIDAS.08", new Object[]{samlReq.getIssuer()}); + //validate AssertionConsumerServiceURL against metadata EntityDescriptor eIDASNodeEntityDesc = new MOAeIDASMetadataProviderDecorator(eIDASMetadataProvider) .getEntityDescriptor(eIDASSamlReq.getIssuer(), SAMLEngineUtils.getMetadataSigner()); @@ -258,8 +266,33 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { } + //validate request country-code against eIDAS node config + String reqCC = samlReq.getOriginCountryCode(); + String eIDASTarget = oaConfig.getIdentityLinkDomainIdentifier(); + + //validate eIDAS target + Pattern pattern = Pattern.compile("^" + at.gv.egovernment.moa.util.Constants.URN_PREFIX_EIDAS + + "\\+[A-Z,a-z]{2}\\+[A-Z,a-z]{2}$"); + Matcher matcher = pattern.matcher(eIDASTarget); + if (MiscUtil.isEmpty(eIDASTarget) || !matcher.matches()) { + Logger.error("Configuration for eIDAS-node:" + samlReq.getIssuer() + + " contains wrong formated eIDAS target:" + eIDASTarget); + throw new MOAIDException("config.08", new Object[]{samlReq.getIssuer()}); + + } else { + String[] splittedTarget = eIDASTarget.split("\\+"); + if (!splittedTarget[2].equalsIgnoreCase(reqCC)) { + Logger.error("Configuration for eIDAS-node:" + samlReq.getIssuer() + + " Destination Country from request (" + reqCC + + ") does not match to configuration:" + eIDASTarget); + throw new MOAIDException("eIDAS.01", + new Object[]{"Destination Country from request does not match to configuration"}); + + } + Logger.debug("CountryCode from request matches eIDAS-node configuration target"); + } - + //************************************************* //***** store eIDAS request information ********* //************************************************* @@ -269,10 +302,6 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { // - memorize relaystate String relayState = request.getParameter("RelayState"); pendingReq.setRemoteRelayState(relayState); - - // - memorize country code of target country - pendingReq.setGenericDataToSession( - RequestImpl.eIDAS_GENERIC_REQ_DATA_COUNTRY, samlReq.getOriginCountryCode()); //store level of assurance pendingReq.setGenericDataToSession(RequestImpl.eIDAS_GENERIC_REQ_DATA_LEVELOFASSURENCE, @@ -288,10 +317,6 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { pendingReq.setOAURL(samlReq.getIssuer()); // - memorize OA config - IOAAuthParameters oaConfig = authConfig.getOnlineApplicationParameter(pendingReq.getOAURL()); - if (oaConfig == null) - throw new EIDASAuthnRequestProcessingException("eIDAS.08", new Object[]{pendingReq.getOAURL()}); - pendingReq.setOnlineApplicationConfiguration(oaConfig); // - memorize service-provider type from eIDAS request @@ -302,7 +327,7 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { if (MiscUtil.isEmpty(spType)) spType = MetadataUtil.getSPTypeFromMetadata(eIDASNodeEntityDesc); - if (MiscUtil.isEmpty(spType)) + if (MiscUtil.isNotEmpty(spType)) Logger.debug("eIDAS request has SPType:" + spType); else Logger.info("eIDAS request and eIDAS metadata contains NO 'SPType' element."); diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/eIDASAuthenticationRequest.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/eIDASAuthenticationRequest.java index 97241af6a..2fe52bb4f 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/eIDASAuthenticationRequest.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/eIDASAuthenticationRequest.java @@ -43,12 +43,14 @@ import at.gv.egovernment.moa.id.auth.frontend.velocity.VelocityProvider; import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDASChainingMetadataProvider; import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SimpleEidasAttributeGenerator; +import at.gv.egovernment.moa.id.auth.modules.eidas.utils.eIDASAttributeProcessingUtils; import at.gv.egovernment.moa.id.commons.MOAIDConstants; import at.gv.egovernment.moa.id.commons.api.IRequest; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.data.IAuthData; import at.gv.egovernment.moa.id.data.SLOInformationImpl; import at.gv.egovernment.moa.id.data.SLOInformationInterface; +import at.gv.egovernment.moa.id.data.Trible; import at.gv.egovernment.moa.id.moduls.IAction; import at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator; import at.gv.egovernment.moa.id.protocols.builder.attributes.MandateLegalPersonFullNameAttributeBuilder; @@ -121,12 +123,28 @@ public class eIDASAuthenticationRequest implements IAction { newValue = authData.getBPK(); isUniqueID = true; + //generate eIDAS conform 'PersonalIdentifier' attribute + if (!eIDASAttributeProcessingUtils.validateEidasPersonalIdentifier(newValue)) { + Logger.debug("preCalculated PersonalIdentifier does not include eIDAS conform prefixes ... add prefix now"); + if (MiscUtil.isEmpty(authData.getBPKType()) + || !authData.getBPKType().startsWith(at.gv.egovernment.moa.util.Constants.URN_PREFIX_EIDAS)) { + Logger.error("BPKType is empty or does not start with eIDAS bPKType prefix! bPKType:" + authData.getBPKType()); + throw new MOAIDException("builder.08", new Object[]{"Suspect bPKType for eIDAS identifier generation"}); + + } + + String prefix = authData.getBPKType().substring(at.gv.egovernment.moa.util.Constants.URN_PREFIX_EIDAS.length() + 1); + newValue = prefix.replaceAll("\\+", "/") + "/" + newValue; + + } + //generate a transient unique identifier if it is requested String reqNameIDFormat = eidasRequest.getEidasRequest().getNameIdFormat(); if (MiscUtil.isNotEmpty(reqNameIDFormat) && reqNameIDFormat.equals(SamlNameIdFormat.TRANSIENT.getNameIdFormat())) newValue = generateTransientNameID(newValue); - + + subjectNameID = newValue; break; case Constants.eIDAS_ATTR_LEGALPERSONIDENTIFIER: @@ -145,8 +163,14 @@ public class eIDASAuthenticationRequest implements IAction { } - if(MiscUtil.isEmpty(newValue)) { - Logger.info("eIDAS Attr:" + attr.getNameUri() + " is not available."); + if(MiscUtil.isEmpty(newValue)) { + if (attr.isRequired()) { + Logger.info("eIDAS Attr:" + attr.getNameUri() + " is marked as 'Required' but not available."); + throw new MOAIDException("eIDAS.15", new Object[]{attr.getFriendlyName()}); + + } else + Logger.info("eIDAS Attr:" + attr.getNameUri() + " is not available."); + } else { //set uniqueIdentifier attribute, because eIDAS SAMLEngine use this flag to select the @@ -228,7 +252,8 @@ public class eIDASAuthenticationRequest implements IAction { } - revisionsLogger.logEvent(req, Constants.eIDAS_REVERSIONSLOG_IDP_AUTHREQUEST); + revisionsLogger.logEvent(req, Constants.eIDAS_REVERSIONSLOG_IDP_AUTHRESPONSE, + eIDASRespMsg.getResponse().getId()); // send the response try { @@ -295,12 +320,20 @@ public class eIDASAuthenticationRequest implements IAction { private String generateTransientNameID(String nameID) { - String random = Random.nextLongRandom(); + //extract source-country and destination country from persistent identifier + Trible<String, String, String> split = eIDASAttributeProcessingUtils.parseEidasPersonalIdentifier(nameID); + if (split == null) { + Logger.error("eIDAS 'PersonalIdentifier' has a wrong format. There had to be a ERROR in implementation!!!!"); + throw new IllegalStateException("eIDAS 'PersonalIdentifier' has a wrong format. There had to be a ERROR in implementation!!!!"); + + } + //build correct formated transient identifier + String random = Random.nextLongRandom(); try { MessageDigest md = MessageDigest.getInstance("SHA-1"); - byte[] hash = md.digest((nameID + random).getBytes("ISO-8859-1")); - return Base64Utils.encode(hash); + byte[] hash = md.digest((split.getThird() + random).getBytes("ISO-8859-1")); + return split.getFirst() + "/" + split.getSecond() + "/" + Base64Utils.encode(hash); } catch (Exception e) { Logger.error("Can not generate transient personal identifier!", e); diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/validator/eIDASResponseValidator.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/validator/eIDASResponseValidator.java new file mode 100644 index 000000000..f0527bc5e --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/validator/eIDASResponseValidator.java @@ -0,0 +1,130 @@ +/* + * 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.protocols.eidas.validator; + +import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; +import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils; +import at.gv.egovernment.moa.id.auth.modules.eidas.utils.eIDASAttributeProcessingUtils; +import at.gv.egovernment.moa.id.commons.api.IRequest; +import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; +import at.gv.egovernment.moa.id.data.Trible; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.MiscUtil; +import eu.eidas.auth.commons.protocol.IAuthenticationResponse; +import eu.eidas.auth.commons.protocol.eidas.LevelOfAssurance; + +/** + * @author tlenz + * + */ +public class eIDASResponseValidator { + + + public static void validateResponse(IRequest pendingReq, IAuthenticationResponse samlResp, String spCountry) throws MOAIDException { + + /*-----------------------------------------------------| + * validate received LoA against minimum required LoA | + *_____________________________________________________| + */ + LevelOfAssurance reqLoA = LevelOfAssurance.fromString(pendingReq.getOnlineApplicationConfiguration().getQaaLevel()); + LevelOfAssurance respLoA = LevelOfAssurance.fromString(samlResp.getLevelOfAssurance()); + if (respLoA.numericValue() < reqLoA.numericValue()) { + Logger.error("eIDAS Response LevelOfAssurance is lower than the required! " + + "(Resp-LoA:" + respLoA.getValue() + " Req-LoA:" + reqLoA.getValue() + ")"); + throw new MOAIDException("eIDAS.14", new Object[]{respLoA.getValue()}); + + } + + /*-----------------------------------------------------| + * validate 'PersonalIdentifier' attribute | + *_____________________________________________________| + */ + String respCC = samlResp.getCountry(); + Object personalIdObj = samlResp.getAttributes().getFirstValue( + SAMLEngineUtils.getMapOfAllAvailableAttributes().get( + Constants.eIDAS_ATTR_PERSONALIDENTIFIER)); + + //check attribute type + if (personalIdObj == null || !(personalIdObj instanceof String)) + Logger.warn("eIDAS Response include NO 'PersonalIdentifier' attriubte " + + ".... That can be a BIG problem in further processing steps"); + + else { + //validate attribute value format + Trible<String, String, String> split = + eIDASAttributeProcessingUtils.parseEidasPersonalIdentifier((String)personalIdObj); + if (split == null) { + throw new MOAIDException("eIDAS.16", + new Object[]{ + Constants.eIDAS_ATTR_PERSONALIDENTIFIER, + "Wrong identifier format"}); + + } else { + //validation according to eIDAS SAML Attribute Profile, Section 2.2.3 + if (MiscUtil.isEmpty(split.getSecond())) { + Logger.error("eIDAS attribute value for " + Constants.eIDAS_ATTR_PERSONALIDENTIFIER + + " includes NO destination country. Value:" + ((String)personalIdObj)); + throw new MOAIDException("eIDAS.16", + new Object[]{ + Constants.eIDAS_ATTR_PERSONALIDENTIFIER, + "No or empty destination country"}); + + } + if (!split.getSecond().equalsIgnoreCase(spCountry)) { + Logger.error("eIDAS attribute value for " + Constants.eIDAS_ATTR_PERSONALIDENTIFIER + + " includes wrong destination country. Value:" + ((String)personalIdObj) + + " SP-Country:" + spCountry); + throw new MOAIDException("eIDAS.16", + new Object[]{ + Constants.eIDAS_ATTR_PERSONALIDENTIFIER, + "Destination country does not match to SP country"}); + + } + + if (MiscUtil.isEmpty(split.getFirst())) { + Logger.error("eIDAS attribute value for " + Constants.eIDAS_ATTR_PERSONALIDENTIFIER + + " includes NO citizen country. Value:" + ((String)personalIdObj)); + throw new MOAIDException("eIDAS.16", + new Object[]{ + Constants.eIDAS_ATTR_PERSONALIDENTIFIER, + "No or empty citizen country"}); + + } + if (!split.getFirst().equalsIgnoreCase(respCC)) { + Logger.error("eIDAS attribute value for " + Constants.eIDAS_ATTR_PERSONALIDENTIFIER + + " includes a citizen country that does not match to eIDAS Response node. " + + " Value:" + ((String)personalIdObj) + + " Response-Node Country:" + respCC); + throw new MOAIDException("eIDAS.16", + new Object[]{ + Constants.eIDAS_ATTR_PERSONALIDENTIFIER, + "Citizen country does not match to eIDAS-node country that generates the response"}); + + } + } + } + + + + } +} |