diff options
Diffstat (limited to 'id/server/modules/moa-id-module-eIDAS')
32 files changed, 2028 insertions, 806 deletions
diff --git a/id/server/modules/moa-id-module-eIDAS/pom.xml b/id/server/modules/moa-id-module-eIDAS/pom.xml index addf086d8..174ce40cb 100644 --- a/id/server/modules/moa-id-module-eIDAS/pom.xml +++ b/id/server/modules/moa-id-module-eIDAS/pom.xml @@ -12,10 +12,11 @@ <properties> <repositoryPath>${basedir}/../../../../repository</repositoryPath> - <eidas-commons.version>eidas.1.0</eidas-commons.version> - <eidas-saml-engine.version>eidas.1.0</eidas-saml-engine.version> - <eidas-encryption.version>eidas.1.0</eidas-encryption.version> - <eidas-configmodule.version>eidas.1.0</eidas-configmodule.version> + <eidas-commons.version>1.1.0</eidas-commons.version> + <eidas-light-commons.version>1.1.0</eidas-light-commons.version> + <eidas-saml-engine.version>1.1.0</eidas-saml-engine.version> + <eidas-encryption.version>1.1.0</eidas-encryption.version> + <eidas-configmodule.version>1.1.0</eidas-configmodule.version> </properties> @@ -44,6 +45,11 @@ <dependencies> <dependency> + <groupId>MOA.id.server</groupId> + <artifactId>moa-id-lib</artifactId> + </dependency> + + <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <scope>test</scope> @@ -75,6 +81,12 @@ <dependency> <groupId>eu.eidas</groupId> + <artifactId>eidas-light-commons</artifactId> + <version>${eidas-light-commons.version}</version> + </dependency> + + <dependency> + <groupId>eu.eidas</groupId> <artifactId>eidas-configmodule</artifactId> <version>${eidas-configmodule.version}</version> <exclusions> @@ -100,7 +112,7 @@ <!-- eidas SAML Engine --> <dependency> <groupId>eu.eidas</groupId> - <artifactId>saml-engine</artifactId> + <artifactId>eidas-saml-engine</artifactId> <version>${eidas-saml-engine.version}</version> <scope>compile</scope> <exclusions> 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 d93d739b1..f45b6ffa5 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 @@ -22,15 +22,12 @@ */ package at.gv.egovernment.moa.id.auth.modules.eidas; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - import org.opensaml.xml.encryption.EncryptionConstants; import org.opensaml.xml.signature.SignatureConstants; +//import eu.eidas.auth.engine.core.validator.eidas.EIDASAttributes; -import eu.eidas.auth.engine.core.eidas.EidasAttributesTypes; -import eu.eidas.auth.engine.core.validator.eidas.EIDASAttributes; +import eu.eidas.auth.commons.attribute.AttributeRegistries; +import eu.eidas.auth.commons.attribute.AttributeRegistry; /** * @author tlenz @@ -54,6 +51,7 @@ public class Constants { //configuration property keys public static final String CONIG_PROPS_EIDAS_PREFIX="moa.id.protocols.eIDAS"; public static final String CONIG_PROPS_EIDAS_SAMLENGINE="samlengine"; + public static final String CONIG_PROPS_EIDAS_NODE= CONIG_PROPS_EIDAS_PREFIX + ".node"; public static final String CONIG_PROPS_EIDAS_SAMLENGINE_PREFIX=CONIG_PROPS_EIDAS_PREFIX + "." + CONIG_PROPS_EIDAS_SAMLENGINE; public static final String CONIG_PROPS_EIDAS_SAMLENGINE_BASIC_CONFIGFILE = CONIG_PROPS_EIDAS_SAMLENGINE_PREFIX + ".config.file"; public static final String CONIG_PROPS_EIDAS_SAMLENGINE_SIGN="sign"; @@ -64,23 +62,33 @@ public class Constants { + CONIG_PROPS_EIDAS_SAMLENGINE_ENCRYPT + ".config.file"; public static final String CONIG_PROPS_EIDAS_METADATA_VALIDATION_TRUSTSTORE = CONIG_PROPS_EIDAS_PREFIX + ".metadata.validation.truststore"; + public static final String CONIG_PROPS_EIDAS_NODE_COUNTRYCODE = CONIG_PROPS_EIDAS_NODE + ".countrycode"; + public static final String CONIG_PROPS_EIDAS_NODE_COUNTRY = CONIG_PROPS_EIDAS_NODE + ".country"; + public static final String CONIG_PROPS_EIDAS_NODE_LoA = CONIG_PROPS_EIDAS_NODE + ".LoA"; + + //timeouts and clock skews - public static final long CONFIG_PROPS_SKEWTIME = 2 * 60 * 1000; //2 minutes skew time for response validation + public static final int CONFIG_PROPS_SKEWTIME = 2 * 60 * 1000; //2 minutes skew time for response validation public static final int CONFIG_PROPS_METADATA_SOCKED_TIMEOUT = 20 * 1000; //20 seconds metadata socked timeout public static final long CONFIG_PROPS_METADATA_GARBAGE_TIMEOUT = 7 * 24 * 60 * 60 * 1000; //remove unused eIDAS metadata after 7 days - - //eIDAS attribute names - public static final String eIDAS_ATTR_PERSONALIDENTIFIER = EIDASAttributes.ATTRIBUTE_NAME_SUFFIX_PERSONIDENTIFIER; - public static final String eIDAS_ATTR_DATEOFBIRTH = EIDASAttributes.ATTRIBUTE_NAME_SUFFIX_DATEOFBIRTH; - public static final String eIDAS_ATTR_CURRENTGIVENNAME = EIDASAttributes.ATTRIBUTE_NAME_SUFFIX_FIRSTNAME; - public static final String eIDAS_ATTR_CURRENTFAMILYNAME = EIDASAttributes.ATTRIBUTE_NAME_SUFFIX_GIVENNAME; + + //eIDAS request parameters + public static final String eIDAS_REQ_NAMEID_FORMAT = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"; + + //eIDAS attribute names + public static final String eIDAS_ATTR_PERSONALIDENTIFIER = "PersonIdentifier"; + public static final String eIDAS_ATTR_DATEOFBIRTH = "DateOfBirth"; + public static final String eIDAS_ATTR_CURRENTGIVENNAME = "FirstName"; + public static final String eIDAS_ATTR_CURRENTFAMILYNAME = "FamilyName"; + public static final String eIDAS_ATTR_LEGALPERSONIDENTIFIER = "LegalPersonIdentifier"; + public static final String eIDAS_ATTR_LEGALNAME = "LegalName"; //http endpoint descriptions public static final String eIDAS_HTTP_ENDPOINT_SP_POST = "/eidas/sp/post"; public static final String eIDAS_HTTP_ENDPOINT_SP_REDIRECT = "/eidas/sp/redirect"; - public static final String eIDAS_HTTP_ENDPOINT_IDP_POST = "/eidas/idp/post"; + //public static final String eIDAS_HTTP_ENDPOINT_IDP_POST = "/eidas/idp/post"; + //public static final String eIDAS_HTTP_ENDPOINT_IDP_REDIRECT = "/eidas/idp/redirect"; public static final String eIDAS_HTTP_ENDPOINT_IDP_COLLEAGUEREQUEST = "/eidas/ColleagueRequest"; - public static final String eIDAS_HTTP_ENDPOINT_IDP_REDIRECT = "/eidas/idp/redirect"; public static final String eIDAS_HTTP_ENDPOINT_METADATA = "/eidas/metadata"; @@ -92,22 +100,38 @@ public class Constants { public static final int eIDAS_REVERSIONSLOG_SP_AUTHRESPONSE= 3404; //metadata constants - public final static Map<String, EidasAttributesTypes> METADATA_POSSIBLE_ATTRIBUTES = Collections.unmodifiableMap( - new HashMap<String, EidasAttributesTypes>(){ - private static final long serialVersionUID = 1L; - { - put(EIDASAttributes.ATTRIBUTE_GIVENNAME, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); - put(EIDASAttributes.ATTRIBUTE_FIRSTNAME, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); - put(EIDASAttributes.ATTRIBUTE_DATEOFBIRTH, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); - put(EIDASAttributes.ATTRIBUTE_PERSONIDENTIFIER, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); - - //TODO: add additional attributes for eIDAS with mandates - //put(EIDASAttributes.ATTRIBUTE_LEGALIDENTIFIER, EidasAttributesTypes.LEGAL_PERSON_MANDATORY); - //put(EIDASAttributes.ATTRIBUTE_LEGALNAME, EidasAttributesTypes.LEGAL_PERSON_MANDATORY); - } - } - ); +// public final static Map<String, EidasAttributesTypes> METADATA_POSSIBLE_ATTRIBUTES = Collections.unmodifiableMap( +// new HashMap<String, EidasAttributesTypes>(){ +// private static final long serialVersionUID = 1L; +// { +// put(EIDASAttributes.ATTRIBUTE_GIVENNAME, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); +// put(EIDASAttributes.ATTRIBUTE_FIRSTNAME, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); +// put(EIDASAttributes.ATTRIBUTE_DATEOFBIRTH, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); +// put(EIDASAttributes.ATTRIBUTE_PERSONIDENTIFIER, EidasAttributesTypes.NATURAL_PERSON_MANDATORY); +// +// //TODO: add additional attributes for eIDAS with mandates +// //put(EIDASAttributes.ATTRIBUTE_LEGALIDENTIFIER, EidasAttributesTypes.LEGAL_PERSON_MANDATORY); +// //put(EIDASAttributes.ATTRIBUTE_LEGALNAME, EidasAttributesTypes.LEGAL_PERSON_MANDATORY); +// } +// } +// ); + public static final AttributeRegistry NAT_ATTR = + AttributeRegistries.of( eu.eidas.auth.engine.core.eidas.spec.NaturalPersonSpec.Definitions.PERSON_IDENTIFIER, + eu.eidas.auth.engine.core.eidas.spec.NaturalPersonSpec.Definitions.CURRENT_FAMILY_NAME, + eu.eidas.auth.engine.core.eidas.spec.NaturalPersonSpec.Definitions.CURRENT_GIVEN_NAME, + eu.eidas.auth.engine.core.eidas.spec.NaturalPersonSpec.Definitions.DATE_OF_BIRTH + ); + + public static final AttributeRegistry LEGAL_ATTR = + AttributeRegistries.of( eu.eidas.auth.engine.core.eidas.spec.LegalPersonSpec.Definitions.LEGAL_PERSON_IDENTIFIER, + eu.eidas.auth.engine.core.eidas.spec.LegalPersonSpec.Definitions.LEGAL_NAME + ); + + public static final AttributeRegistry MOA_IDP_ATTR_REGISTRY = + AttributeRegistries.copyOf(NAT_ATTR, LEGAL_ATTR); + + public static final String METADATA_ALLOWED_ALG_DIGIST = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256 + ";" + SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512 ; diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOASWSigner.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOASWSigner.java new file mode 100644 index 000000000..302c12aaa --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOASWSigner.java @@ -0,0 +1,56 @@ +/* + * 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.config; + +import java.util.Map; + +import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; +import eu.eidas.auth.engine.configuration.SamlEngineConfigurationException; +import eu.eidas.auth.engine.configuration.dom.ConfigurationAdapter; +import eu.eidas.auth.engine.configuration.dom.ConfigurationKey; +import eu.eidas.auth.engine.core.impl.KeyStoreProtocolSigner; +import eu.eidas.samlengineconfig.CertificateConfigurationManager; + +/** + * @author tlenz + * + */ +public class MOASWSigner extends KeyStoreProtocolSigner { + + public MOASWSigner(Map<String, String> properties) throws SamlEngineConfigurationException { + super(properties); + + } + + /** + * @param configManager + * @throws SamlEngineConfigurationException + */ + public MOASWSigner(CertificateConfigurationManager configManager) throws SamlEngineConfigurationException { + super(ConfigurationAdapter.adapt(configManager).getInstances().get(Constants.eIDAS_SAML_ENGINE_NAME).getConfigurationEntries().get(ConfigurationKey.SIGNATURE_CONFIGURATION.getKey()).getParameters()); + + } + + + +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLEngineConfigurationImpl.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLEngineConfigurationImpl.java index 5d1874157..78793d3fc 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLEngineConfigurationImpl.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLEngineConfigurationImpl.java @@ -42,9 +42,7 @@ import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.FileUtils; import at.gv.egovernment.moa.util.MiscUtil; - import eu.eidas.samlengineconfig.BinaryParameter; -import eu.eidas.samlengineconfig.ConfigurationParameter; import eu.eidas.samlengineconfig.EngineInstance; import eu.eidas.samlengineconfig.InstanceConfiguration; import eu.eidas.samlengineconfig.PropsParameter; @@ -57,10 +55,10 @@ import eu.eidas.samlengineconfig.SamlEngineConfiguration; public class MOAeIDASSAMLEngineConfigurationImpl extends SamlEngineConfiguration { - private static final String KEYSTORE_PATH="keystorePath"; - private static final String METADATA_KEYSTORE_PATH="metadata.keystorePath"; + private static final String KEYSTORE_PATH="keyStorePath"; + private static final String METADATA_KEYSTORE_PATH="metadata.keyStorePath"; private static final String ENCRYPTION_ACTIVATION="encryptionActivation"; - private static final String[] BINARY_PARAMETERS={KEYSTORE_PATH, ENCRYPTION_ACTIVATION,METADATA_KEYSTORE_PATH}; + public static final String[] BINARY_PARAMETERS={KEYSTORE_PATH, ENCRYPTION_ACTIVATION,METADATA_KEYSTORE_PATH}; public List<EngineInstance> getInstances(){ return super.getInstances(); @@ -95,7 +93,7 @@ public class MOAeIDASSAMLEngineConfigurationImpl extends //add basic eIDAS SAML-engine configuration MOAeIDASSAMLInstanceConfigurationImpl samlBaseConfig = new MOAeIDASSAMLInstanceConfigurationImpl(); samlBaseConfig.setName(Constants.eIDAS_SAML_ENGINE_NAME_ID_BASICCONFIG); - samlBaseConfig.addParameter(loadConfigurationFromExternalFile(Constants.CONIG_PROPS_EIDAS_SAMLENGINE_BASIC_CONFIGFILE)); + samlBaseConfig.addParameter(buildPropsParameter(Constants.CONIG_PROPS_EIDAS_SAMLENGINE_BASIC_CONFIGFILE)); engineConfigs.add(samlBaseConfig); //add signing eIDAS SAML-engine configuration @@ -103,7 +101,7 @@ public class MOAeIDASSAMLEngineConfigurationImpl extends samlSignConfig.setName(Constants.eIDAS_SAML_ENGINE_NAME_ID_SIGNATURECONFIG); samlSignConfig.addParameter(Constants.eIDAS_SAML_ENGINE_NAME_ID_CLASS, Constants.SAML_SIGNING_IMPLENTATION); - + //TODO: load signing keys directly from MOA-ID configuration in finale version samlSignConfig.addParameter(loadConfigurationFromExternalFile(Constants.CONIG_PROPS_EIDAS_SAMLENGINE_SIGN_CONFIGFILE)); engineConfigs.add(samlSignConfig); @@ -122,16 +120,16 @@ public class MOAeIDASSAMLEngineConfigurationImpl extends super.addInstance(engineInst); } - + /** * Load an external eIDAS SAML-engine configuration file, which is referenced from MOA-ID configuration * * @param key Configuration key, which is used in property based MOA-ID configuration file - * @return eIDAS SAML-engine configuration object + * @return eIDAS SAML-engine configuration Properties * @throws ConfigurationException */ - private ConfigurationParameter loadConfigurationFromExternalFile(String key) throws ConfigurationException { + private Properties loadConfigurationFromExternalFile(String key) throws ConfigurationException { String configFile = AuthConfigurationProviderFactory.getInstance().getBasicMOAIDConfiguration(key); if (MiscUtil.isEmpty(configFile)) { @@ -141,15 +139,21 @@ public class MOAeIDASSAMLEngineConfigurationImpl extends return null; } - Properties inputProps = loadPropsFromXml(configFile); - return buildPropsParameter(inputProps, configFile); + Properties inputProps = loadPropsFromXml(configFile); + return inputProps; + //return buildPropsParameter(inputProps, configFile); } - private PropsParameter buildPropsParameter(Properties inputProps, String fileName) throws EIDASEngineConfigurationException { + private PropsParameter buildPropsParameter(String configKey) throws ConfigurationException { + Properties inputProps = loadConfigurationFromExternalFile(configKey); + + String configFile = + AuthConfigurationProviderFactory.getInstance().getBasicMOAIDConfiguration(configKey); + PropsParameter outputProps = new PropsParameter(); - outputProps.setFileName(fileName); + outputProps.setFileName(configFile); //original eIDAS SAML-engine use this identifier outputProps.setName("fileConfiguration"); @@ -241,6 +245,8 @@ public class MOAeIDASSAMLEngineConfigurationImpl extends configFile, AuthConfigurationProviderFactory.getInstance().getRootConfigFileDir()); + Logger.debug("Load eIDAS configuration from file:" + absoluteConfigFile); + File file = new File(new URL(absoluteConfigFile).toURI()); is = new FileInputStream(file); props.loadFromXML(is); diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLInstanceConfigurationImpl.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLInstanceConfigurationImpl.java index dccd39905..384d6be0b 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLInstanceConfigurationImpl.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAeIDASSAMLInstanceConfigurationImpl.java @@ -22,9 +22,22 @@ */ package at.gv.egovernment.moa.id.auth.modules.eidas.config; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; import java.util.List; +import java.util.Map.Entry; +import java.util.Properties; +import at.gv.egovernment.moa.id.commons.api.exceptions.ConfigurationException; +import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.FileUtils; import eu.eidas.samlengineconfig.ConfigurationParameter; import eu.eidas.samlengineconfig.InstanceConfiguration; import eu.eidas.samlengineconfig.StringParameter; @@ -56,5 +69,49 @@ public class MOAeIDASSAMLInstanceConfigurationImpl extends addParameter(param); } + + public void addParameter(Properties parameters) { + Iterator<Entry<Object, Object>> paramInterator = parameters.entrySet().iterator(); + while (paramInterator.hasNext()) { + Entry<Object, Object> next = paramInterator.next(); + + StringParameter param = new StringParameter(); + String keyName = (String) next.getKey(); + param.setName(keyName); + + //make path to binary files absolute + if (Arrays.asList(MOAeIDASSAMLEngineConfigurationImpl.BINARY_PARAMETERS).contains(keyName)) + try { + String absoluteConfigFile = FileUtils.makeAbsoluteURL( + (String)next.getValue(), + AuthConfigurationProviderFactory.getInstance().getRootConfigFileDir()); + + URI uri = new URL(absoluteConfigFile).toURI(); + + File file = new File(uri); + if (file.exists()) + param.setValue(file.getCanonicalPath()); + + else { + Logger.error("eIDAS-configuration fileparameter with key:" + param.getName() + " and path:" + uri.toString() + " NOT exist!"); + param.setValue(null); + + } + + + } catch (ConfigurationException | URISyntaxException | IOException e) { + //TODO: make final!!!! + e.printStackTrace(); + param.setValue(next.getValue()); + + } + else + param.setValue(next.getValue()); + + addParameter(param); + + } + + } } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/ModifiedEncryptionSW.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/ModifiedEncryptionSW.java index 1ba344fd1..9ad5f0db3 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/ModifiedEncryptionSW.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/ModifiedEncryptionSW.java @@ -1,18 +1,95 @@ package at.gv.egovernment.moa.id.auth.modules.eidas.config; +import java.security.cert.X509Certificate; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; + +import com.google.common.collect.ImmutableMap; +import com.sun.istack.Nullable; + import at.gv.egovernment.moa.id.commons.api.AuthConfiguration; import at.gv.egovernment.moa.id.commons.api.exceptions.ConfigurationException; import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; import at.gv.egovernment.moa.logging.Logger; -import eu.eidas.auth.engine.core.impl.EncryptionSW; +import eu.eidas.auth.commons.EidasErrorKey; +import eu.eidas.auth.commons.io.ReloadableProperties; +import eu.eidas.auth.engine.configuration.SamlEngineConfigurationException; +import eu.eidas.auth.engine.configuration.dom.EncryptionKey; +import eu.eidas.auth.engine.core.impl.CertificateValidator; +import eu.eidas.auth.engine.core.impl.KeyStoreSamlEngineEncryption; +import eu.eidas.auth.engine.xml.opensaml.CertificateUtil; +import eu.eidas.engine.exceptions.EIDASSAMLEngineException; /** * This encryption module asks the moa configuration on whether to encrypt the response or not. In doubt, encryption is enforced. */ -public class ModifiedEncryptionSW extends EncryptionSW { +public class ModifiedEncryptionSW extends KeyStoreSamlEngineEncryption { + + private final ImmutableMap<String, String> properties; + + private final ReloadableProperties encryptionActivationProperties; + + private static ReloadableProperties initActivationConf(Map<String, String> properties) { + String activationConfigurationFile = EncryptionKey.ENCRYPTION_ACTIVATION.getAsString(properties); + Logger.debug("File containing encryption configuration: \"" + activationConfigurationFile + "\""); + return new ReloadableProperties(activationConfigurationFile); + } + + /** + * @param properties + * @throws SamlEngineConfigurationException + */ + public ModifiedEncryptionSW(Map<String, String> properties) throws SamlEngineConfigurationException { + super(properties); + this.properties = ImmutableMap.copyOf(properties); + encryptionActivationProperties = initActivationConf(properties); + } + + /* (non-Javadoc) + * @see eu.eidas.auth.engine.core.ProtocolEncrypterI#getEncryptionCertificate(java.lang.String) + */ + @Override + @Nullable + public X509Certificate getEncryptionCertificate(@Nullable String destinationCountryCode) + throws EIDASSAMLEngineException { + if (isEncryptionEnabled(destinationCountryCode)) { + String issuerKey = new StringBuilder(EncryptionKey.RESPONSE_TO_POINT_ISSUER_PREFIX.getKey()).append( + destinationCountryCode).toString(); + String serialNumberKey = + new StringBuilder(EncryptionKey.RESPONSE_TO_POINT_SERIAL_NUMBER_PREFIX.getKey()).append( + destinationCountryCode).toString(); + String serialNumber = properties.get(serialNumberKey); + String responseToPointIssuer = properties.get(issuerKey); + if (StringUtils.isNotBlank(responseToPointIssuer)) { + for (final X509Certificate certificate : getEncryptionCertificates()) { + if (CertificateUtil.matchesCertificate(serialNumber, responseToPointIssuer, certificate)) { + + if (isDisallowedSelfSignedCertificate()) { + CertificateValidator.checkCertificateIssuer(certificate); + } + if (isCheckedValidityPeriod()) { + CertificateValidator.checkCertificateValidityPeriod(certificate); + } + + return certificate; + } + } + throw new EIDASSAMLEngineException(EidasErrorKey.SAML_ENGINE_INVALID_CERTIFICATE.errorCode(), + EidasErrorKey.SAML_ENGINE_INVALID_CERTIFICATE.errorMessage()); + } else { + Logger.error("Encryption of SAML Response NOT done, because no \"" + issuerKey + + "\" configured!"); + } + } + return null; + } + /* (non-Javadoc) + * @see eu.eidas.auth.engine.core.ProtocolEncrypterI#isEncryptionEnabled(java.lang.String) + */ @Override - public boolean isEncryptionEnable(String countryCode) { + public boolean isEncryptionEnabled(String countryCode) { // - encrypt if so configured try { AuthConfiguration moaconfig = AuthConfigurationProviderFactory.getInstance(); diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDAsExtensionProcessor.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAEidasProtocolProcesser.java index 5837d7dbf..c24c5efca 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDAsExtensionProcessor.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAEidasProtocolProcesser.java @@ -22,27 +22,36 @@ */ package at.gv.egovernment.moa.id.auth.modules.eidas.engine; -import java.util.HashSet; -import java.util.Set; - -import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; -import eu.eidas.auth.engine.core.ExtensionProcessorI; -import eu.eidas.auth.engine.core.eidas.EidasExtensionProcessor; +import eu.eidas.auth.engine.core.eidas.EidasProtocolProcessor; +import eu.eidas.auth.engine.metadata.MetadataFetcherI; +import eu.eidas.auth.engine.metadata.MetadataSignerI; /** * @author tlenz * */ -public class MOAeIDAsExtensionProcessor extends EidasExtensionProcessor implements ExtensionProcessorI { +public class MOAEidasProtocolProcesser extends EidasProtocolProcessor { + private static final String OWN_EIDAS_RESPONSE_VALIDATOR_SUITE_ID = "moaEidasResponseValidatorSuiteId"; + + private final MetadataFetcherI metadataFetcher; + private final MetadataSignerI metadataSigner; + /** - * Add only eIDAS attributes which are supported by Austrian eIDAS node - * + * @param metadataFetcher + * @param metadataSigner */ + public MOAEidasProtocolProcesser(MetadataFetcherI metadataFetcher, MetadataSignerI metadataSigner) { + super(metadataFetcher, metadataSigner); + + this.metadataFetcher = metadataFetcher; + this.metadataSigner = metadataSigner; + + } + @Override - public Set<String> getSupportedAttributes(){ - Set<String> supportedAttributes=new HashSet<String>( Constants.METADATA_POSSIBLE_ATTRIBUTES.keySet()); - - return supportedAttributes; + public String getResponseValidatorId() { + return OWN_EIDAS_RESPONSE_VALIDATOR_SUITE_ID; } + } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASChainingMetadataProvider.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASChainingMetadataProvider.java index 80a2734f2..0cb6228a7 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASChainingMetadataProvider.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASChainingMetadataProvider.java @@ -18,58 +18,80 @@ import org.opensaml.saml2.metadata.EntitiesDescriptor; import org.opensaml.saml2.metadata.EntityDescriptor; import org.opensaml.saml2.metadata.RoleDescriptor; import org.opensaml.saml2.metadata.provider.ChainingMetadataProvider; -import org.opensaml.saml2.metadata.provider.FilterException; import org.opensaml.saml2.metadata.provider.HTTPMetadataProvider; import org.opensaml.saml2.metadata.provider.MetadataFilter; import org.opensaml.saml2.metadata.provider.MetadataProvider; import org.opensaml.saml2.metadata.provider.MetadataProviderException; import org.opensaml.saml2.metadata.provider.ObservableMetadataProvider; import org.opensaml.xml.XMLObject; +import org.springframework.stereotype.Service; +import at.gv.egovernment.moa.id.auth.IDestroyableObject; +import at.gv.egovernment.moa.id.auth.IGarbageCollectorProcessing; import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; import at.gv.egovernment.moa.id.commons.api.AuthConfiguration; import at.gv.egovernment.moa.id.commons.ex.MOAHttpProtocolSocketFactoryException; import at.gv.egovernment.moa.id.commons.utils.MOAHttpProtocolSocketFactory; import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProviderFactory; -import at.gv.egovernment.moa.id.config.auth.IGarbageCollectorProcessing; -import at.gv.egovernment.moa.id.config.auth.MOAGarbageCollector; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.filter.SchemaValidationException; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.filter.SignatureValidationException; import at.gv.egovernment.moa.id.protocols.pvp2x.verification.metadata.MOASPMetadataSignatureFilter; import at.gv.egovernment.moa.id.saml2.MetadataFilterChain; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MiscUtil; -import eu.eidas.auth.engine.AbstractSAMLEngine; +import eu.eidas.auth.engine.AbstractProtocolEngine; -public class MOAeIDASChainingMetadataProvider implements ObservableMetadataProvider, IGarbageCollectorProcessing { +@Service("eIDASMetadataProvider") +public class MOAeIDASChainingMetadataProvider implements ObservableMetadataProvider, + IGarbageCollectorProcessing, IDestroyableObject { - private static MOAeIDASChainingMetadataProvider instance = null; +// private static MOAeIDASChainingMetadataProvider instance = null; private static Object mutex = new Object(); private MetadataProvider internalProvider; private Map<String, Date> lastAccess = null; - public static MOAeIDASChainingMetadataProvider getInstance() { - if (instance == null) { - synchronized (mutex) { - if (instance == null) { - instance = new MOAeIDASChainingMetadataProvider(); - MOAGarbageCollector.addModulForGarbageCollection(instance); - } - } - } - return instance; - } +// public static MOAeIDASChainingMetadataProvider getInstance() { +// if (instance == null) { +// synchronized (mutex) { +// if (instance == null) { +// instance = new MOAeIDASChainingMetadataProvider(); +// MOAGarbageCollector.addModulForGarbageCollection(instance); +// } +// } +// } +// return instance; +// } - private MOAeIDASChainingMetadataProvider() { + public MOAeIDASChainingMetadataProvider() { internalProvider = new ChainingMetadataProvider(); lastAccess = new HashMap<String, Date>(); } /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.auth.IDestroyableObject#fullyDestroy() + */ + @Override + public void fullyDestroy() { + Map<String, HTTPMetadataProvider> loadedproviders = getAllActuallyLoadedProviders(); + if (loadedproviders != null) { + for (Entry<String, HTTPMetadataProvider> el : loadedproviders.entrySet()) { + try { + el.getValue().destroy(); + Logger.debug("Destroy eIDAS Matadataprovider: " + el.getKey() + " finished"); + + } catch (Exception e) { + Logger.warn("Destroy eIDAS Matadataprovider: " + el.getKey() + " FAILED"); + + } + } + } + } + + /* (non-Javadoc) * @see at.gv.egovernment.moa.id.config.auth.IGarbageCollectorProcessing#runGarbageCollector() */ @Override @@ -128,9 +150,11 @@ public class MOAeIDASChainingMetadataProvider implements ObservableMetadataProvi List<String> nonValidMetadataProvider = new ArrayList<String>(); for (HTTPMetadataProvider provider : loadedproviders.values()) { try { - provider.getMetadataFilter().doFilter(provider.getMetadata()); + provider.refresh(); - } catch (FilterException | MetadataProviderException e) { + //provider.getMetadataFilter().doFilter(provider.getMetadata()); + + } catch (MetadataProviderException e) { Logger.info("eIDAS MetadataProvider: " + provider.getMetadataURI() + " is not valid any more. Reason:" + e.getMessage()); if (Logger.isDebugEnabled()) @@ -182,11 +206,11 @@ public class MOAeIDASChainingMetadataProvider implements ObservableMetadataProvi try { MOAHttpProtocolSocketFactory protoSocketFactory = new MOAHttpProtocolSocketFactory( Constants.SSLSOCKETFACTORYNAME, - authConfig.getCertstoreDirectory(), authConfig.getTrustedCACertificates(), null, AuthConfiguration.DEFAULT_X509_CHAININGMODE, - authConfig.isTrustmanagerrevoationchecking()); + authConfig.isTrustmanagerrevoationchecking(), + authConfig.getRevocationMethodOrder()); httpClient.setCustomSSLTrustStore(metadataURL, protoSocketFactory); @@ -196,10 +220,10 @@ public class MOAeIDASChainingMetadataProvider implements ObservableMetadataProvi } } - timer = new Timer(); + timer = new Timer(true); httpProvider = new HTTPMetadataProvider(timer, httpClient, metadataURL); - httpProvider.setParserPool(AbstractSAMLEngine.getNewBasicSecuredParserPool()); + httpProvider.setParserPool(AbstractProtocolEngine.getSecuredParserPool()); httpProvider.setRequireValidMetadata(true); httpProvider.setMinRefreshDelay(1000*60*15); //15 minutes httpProvider.setMaxRefreshDelay(1000*60*60*24); //24 hours @@ -282,14 +306,17 @@ public class MOAeIDASChainingMetadataProvider implements ObservableMetadataProvi } else { //load new Metadata Provider ChainingMetadataProvider chainProvider = (ChainingMetadataProvider) internalProvider; - HTTPMetadataProvider newMetadataProvider = createNewHTTPMetaDataProvider(metadataURL); - chainProvider.addMetadataProvider(newMetadataProvider); + HTTPMetadataProvider newMetadataProvider = createNewHTTPMetaDataProvider(metadataURL); - emitChangeEvent(); - Logger.info("eIDAS metadata for " - + metadataURL + " is added."); - return true; - + if (newMetadataProvider != null) { + chainProvider.addMetadataProvider(newMetadataProvider); + + emitChangeEvent(); + Logger.info("eIDAS metadata for " + + metadataURL + " is added."); + return true; + + } } } else @@ -405,5 +432,4 @@ public class MOAeIDASChainingMetadataProvider implements ObservableMetadataProvi if (observer != null) observer.onEvent(this); } - } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASMetadataProviderDecorator.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASMetadataProviderDecorator.java index 7537c4d84..c5e56502b 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASMetadataProviderDecorator.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAeIDASMetadataProviderDecorator.java @@ -31,15 +31,17 @@ import org.opensaml.saml2.metadata.SPSSODescriptor; import org.opensaml.saml2.metadata.provider.MetadataProvider; import org.opensaml.saml2.metadata.provider.MetadataProviderException; -import eu.eidas.auth.engine.EIDASSAMLEngine; -import eu.eidas.auth.engine.metadata.MetadataProcessorI; +import eu.eidas.auth.engine.ProtocolEngineI; +import eu.eidas.auth.engine.metadata.MetadataFetcherI; +import eu.eidas.auth.engine.metadata.MetadataSignerI; +import eu.eidas.engine.exceptions.EIDASSAMLEngineException; import eu.eidas.engine.exceptions.SAMLEngineException; /** * @author tlenz * */ -public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { +public class MOAeIDASMetadataProviderDecorator implements MetadataFetcherI { private MetadataProvider metadataprovider = null; @@ -51,10 +53,31 @@ public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { } + /* (non-Javadoc) - * @see eu.eidas.auth.engine.metadata.MetadataProcessorI#getEntityDescriptor(java.lang.String) + * @see eu.eidas.auth.engine.metadata.MetadataFetcherI#getEntityDescriptor(java.lang.String, eu.eidas.auth.engine.metadata.MetadataSignerI) */ @Override + public EntityDescriptor getEntityDescriptor(String url, MetadataSignerI paramMetadataSignerI) + throws EIDASSAMLEngineException { + try { + /*TODO: maybe implement metadata signature validation on every request, + * but it is not needed in case of cached metadata provider, + * because signature must be only validated in case of cache reload operation + */ + return this.metadataprovider.getEntityDescriptor(url); + + } catch (MetadataProviderException e) { + throw new EIDASSAMLEngineException("eIDAS Metadata processing FAILED.", e); + + } + } + + + /* (non-Javadoc) + * @see eu.eidas.auth.engine.metadata.MetadataProcessorI#getEntityDescriptor(java.lang.String) + */ + @Deprecated public EntityDescriptor getEntityDescriptor(String url) throws SAMLEngineException { try { @@ -69,7 +92,7 @@ public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { /* (non-Javadoc) * @see eu.eidas.auth.engine.metadata.MetadataProcessorI#getSPSSODescriptor(java.lang.String) */ - @Override + @Deprecated public SPSSODescriptor getSPSSODescriptor(String url) throws SAMLEngineException { return getFirstRoleDescriptor(getEntityDescriptor(url), SPSSODescriptor.class); @@ -79,7 +102,7 @@ public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { /* (non-Javadoc) * @see eu.eidas.auth.engine.metadata.MetadataProcessorI#getIDPSSODescriptor(java.lang.String) */ - @Override + @Deprecated public IDPSSODescriptor getIDPSSODescriptor(String url) throws SAMLEngineException { return getFirstRoleDescriptor(getEntityDescriptor(url), IDPSSODescriptor.class); @@ -89,8 +112,8 @@ public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { /* (non-Javadoc) * @see eu.eidas.auth.engine.metadata.MetadataProcessorI#checkValidMetadataSignature(java.lang.String, eu.eidas.auth.engine.EIDASSAMLEngine) */ - @Override - public void checkValidMetadataSignature(String url, EIDASSAMLEngine engine) + @Deprecated + public void checkValidMetadataSignature(String url, ProtocolEngineI engine) throws SAMLEngineException { //Do nothing, because metadata signature is already validated during //metadata provider initialization @@ -102,7 +125,7 @@ public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { /* (non-Javadoc) * @see eu.eidas.auth.engine.metadata.MetadataProcessorI#checkValidMetadataSignature(java.lang.String, java.security.KeyStore) */ - @Override + @Deprecated public void checkValidMetadataSignature(String url, KeyStore trustStore) throws SAMLEngineException { //Do nothing, because metadata signature is already validated during @@ -110,6 +133,7 @@ public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { } + @Deprecated protected <T extends RoleDescriptor> T getFirstRoleDescriptor(EntityDescriptor entityDescriptor, final Class<T> clazz){ for(RoleDescriptor rd:entityDescriptor.getRoleDescriptors()){ if(clazz.isInstance(rd)){ @@ -119,4 +143,6 @@ public class MOAeIDASMetadataProviderDecorator implements MetadataProcessorI { return null; } + + } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/validation/MoaEidasConditionsValidator.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/validation/MoaEidasConditionsValidator.java new file mode 100644 index 000000000..d9453322f --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/validation/MoaEidasConditionsValidator.java @@ -0,0 +1,83 @@ +/* + * 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.engine.validation; + +import org.joda.time.DateTime; +import org.opensaml.saml2.core.Conditions; +import org.opensaml.saml2.core.validator.ConditionsSpecValidator; +import org.opensaml.xml.validation.ValidationException; + +import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; +import at.gv.egovernment.moa.logging.Logger; + +/** + * @author tlenz + * + * MOA-ID specific eIDAS Response Condition validator + * + * This validator allows time jitter in 'notBefore' validation + * + */ + +public class MoaEidasConditionsValidator extends ConditionsSpecValidator { + + + + @Override + public void validate(Conditions conditions) throws ValidationException { + Logger.debug("conditions.getNotBefore() "+ conditions.getNotBefore()); + Logger.debug("conditions.getNotOnOrAfter() "+ conditions.getNotOnOrAfter()); + Logger.debug("dateTime.now() "+ DateTime.now()); + + super.validate(conditions); + + if (conditions.getNotBefore() == null) { + + throw new ValidationException("NotBefore is required."); + } + + if (conditions.getNotBefore().minusMillis(Constants.CONFIG_PROPS_SKEWTIME).isAfterNow()) { + throw new ValidationException("Current time is before NotBefore condition"); + } + + if (conditions.getNotOnOrAfter() == null) { + + throw new ValidationException("NotOnOrAfter is required."); + } + if (conditions.getNotOnOrAfter().isBeforeNow()) { + + throw new ValidationException("Current time is after NotOnOrAfter condition"); + } + + if (conditions.getAudienceRestrictions() == null || conditions.getAudienceRestrictions().isEmpty()) { + + throw new ValidationException("AudienceRestriction is required."); + } + + if (conditions.getOneTimeUse() == null) { + + throw new ValidationException("OneTimeUse is required."); + } + + } +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAuthnRequestProcessingException.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASAuthnRequestProcessingException.java index c96af37ef..d51629d9e 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAuthnRequestProcessingException.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASAuthnRequestProcessingException.java @@ -30,7 +30,7 @@ import at.gv.egovernment.moa.util.MiscUtil; * @author tlenz * */ -public class eIDASAuthnRequestProcessingException extends eIDASException { +public class EIDASAuthnRequestProcessingException extends EIDASException { private String subStatusCode = null; @@ -43,20 +43,20 @@ public class eIDASAuthnRequestProcessingException extends eIDASException { * @param messageId * @param parameters */ - public eIDASAuthnRequestProcessingException(String messageId, Object[] parameters) { + public EIDASAuthnRequestProcessingException(String messageId, Object[] parameters) { super(messageId, parameters); } - public eIDASAuthnRequestProcessingException(String subStatusCode, String messageId, Object[] parameters) { + public EIDASAuthnRequestProcessingException(String subStatusCode, String messageId, Object[] parameters) { super(messageId, parameters); this.subStatusCode = subStatusCode; } - public eIDASAuthnRequestProcessingException(String messageId, Object[] parameters, Throwable e) { + public EIDASAuthnRequestProcessingException(String messageId, Object[] parameters, Throwable e) { super(messageId, parameters, e ); } - public eIDASAuthnRequestProcessingException(String subStatusCode, String messageId, Object[] parameters, Throwable e) { + public EIDASAuthnRequestProcessingException(String subStatusCode, String messageId, Object[] parameters, Throwable e) { super(messageId, parameters, e ); this.subStatusCode = subStatusCode; } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAuthnRequestValidationException.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASAuthnRequestValidationException.java index 2a15ee18a..a6da769b7 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAuthnRequestValidationException.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASAuthnRequestValidationException.java @@ -28,7 +28,7 @@ import org.opensaml.saml2.core.StatusCode; * @author tlenz * */ -public class eIDASAuthnRequestValidationException extends eIDASException { +public class EIDASAuthnRequestValidationException extends EIDASException { /** * @@ -39,7 +39,7 @@ public class eIDASAuthnRequestValidationException extends eIDASException { * @param messageId * @param parameters */ - public eIDASAuthnRequestValidationException(String messageId, Object[] parameters) { + public EIDASAuthnRequestValidationException(String messageId, Object[] parameters) { super(messageId, parameters); } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASEngineException.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASEngineException.java index 234c4e038..8bf7f7452 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASEngineException.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASEngineException.java @@ -28,7 +28,7 @@ import org.opensaml.saml2.core.StatusCode; * @author tlenz * */ -public class EIDASEngineException extends eIDASException { +public class EIDASEngineException extends EIDASException { /** * @param objects diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASException.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASException.java index f42004abc..e3d6c5a2e 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASException.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASException.java @@ -28,7 +28,7 @@ import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; * @author tlenz * */ -public abstract class eIDASException extends MOAIDException { +public abstract class EIDASException extends MOAIDException { /** * @@ -44,7 +44,7 @@ public abstract class eIDASException extends MOAIDException { * @param messageId * @param parameters */ - public eIDASException(String messageId, Object[] parameters) { + public EIDASException(String messageId, Object[] parameters) { super(messageId, parameters); } @@ -52,7 +52,7 @@ public abstract class eIDASException extends MOAIDException { * @param messageId * @param parameters */ - public eIDASException(String messageId, Object[] parameters, Throwable e) { + public EIDASException(String messageId, Object[] parameters, Throwable e) { super(messageId, parameters, e); } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASResponseBuildException.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASResponseBuildException.java index 0ffcf11ef..5e6b87b39 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASResponseBuildException.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASResponseBuildException.java @@ -28,7 +28,7 @@ import org.opensaml.saml2.core.StatusCode; * @author tlenz * */ -public class eIDASResponseBuildException extends eIDASException { +public class EIDASResponseBuildException extends EIDASException { /** * @@ -39,11 +39,11 @@ public class eIDASResponseBuildException extends eIDASException { * @param messageId * @param parameters */ - public eIDASResponseBuildException(String messageId, Object[] parameters) { + public EIDASResponseBuildException(String messageId, Object[] parameters) { super(messageId, parameters); } - public eIDASResponseBuildException(String messageId, Object[] parameters, Throwable e) { + public EIDASResponseBuildException(String messageId, Object[] parameters, Throwable e) { super(messageId, parameters, e); } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASResponseNotSuccessException.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASResponseNotSuccessException.java index d10ca1c88..460561eb3 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASResponseNotSuccessException.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/EIDASResponseNotSuccessException.java @@ -28,14 +28,14 @@ import org.opensaml.saml2.core.StatusCode; * @author tlenz * */ -public class eIDASResponseNotSuccessException extends eIDASException { +public class EIDASResponseNotSuccessException extends EIDASException { /** * */ private static final long serialVersionUID = 6145402939313568907L; - public eIDASResponseNotSuccessException(String messageId, Object[] parameters) { + public EIDASResponseNotSuccessException(String messageId, Object[] parameters) { super(messageId, parameters); } @@ -44,7 +44,7 @@ public class eIDASResponseNotSuccessException extends eIDASException { * @param parameters * @param e */ - public eIDASResponseNotSuccessException(String messageId, Object[] parameters, Throwable e) { + public EIDASResponseNotSuccessException(String messageId, Object[] parameters, Throwable e) { super(messageId, parameters, e); } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAttributeException.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAttributeException.java index b25895eca..17f0a9b72 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAttributeException.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/exceptions/eIDASAttributeException.java @@ -28,7 +28,7 @@ import org.opensaml.saml2.core.StatusCode; * @author tlenz * */ -public class eIDASAttributeException extends eIDASException { +public class eIDASAttributeException extends EIDASException { private static final long serialVersionUID = 1L; diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/CreateIdentityLinkTask.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/CreateIdentityLinkTask.java index 5d7430dd7..cf3a13e32 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/CreateIdentityLinkTask.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/CreateIdentityLinkTask.java @@ -28,18 +28,20 @@ import java.text.SimpleDateFormat; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.joda.time.DateTime; import org.springframework.stereotype.Component; import org.w3c.dom.Element; import org.w3c.dom.Node; import at.gv.egovernment.moa.id.advancedlogging.MOAIDEventConstants; import at.gv.egovernment.moa.id.auth.data.AuthenticationSessionStorageConstants; -import at.gv.egovernment.moa.id.auth.data.IdentityLink; import at.gv.egovernment.moa.id.auth.modules.AbstractAuthServletTask; import at.gv.egovernment.moa.id.auth.modules.TaskExecutionException; import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.eIDASAttributeException; +import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils; import at.gv.egovernment.moa.id.auth.parser.IdentityLinkAssertionParser; +import at.gv.egovernment.moa.id.commons.api.data.IIdentityLink; 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; @@ -47,7 +49,7 @@ import at.gv.egovernment.moa.id.util.IdentityLinkReSigner; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.DOMUtils; import at.gv.egovernment.moa.util.XPathUtils; -import eu.eidas.auth.commons.IPersonalAttributeList; +import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; /** * @author tlenz @@ -67,11 +69,11 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask { defaultTaskInitialization(request, executionContext); //get eIDAS attributes from MOA-Session - IPersonalAttributeList eIDASAttributes = moasession.getGenericDataFromSession( + ImmutableAttributeMap eIDASAttributes = moasession.getGenericDataFromSession( AuthenticationSessionStorageConstants.eIDAS_ATTRIBUTELIST, - IPersonalAttributeList.class); + ImmutableAttributeMap.class); - IdentityLink identityLink = null; + IIdentityLink identityLink = null; //connect SZR-Gateway //TODO: implement SZR-Gateway communication!!!! @@ -86,35 +88,51 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask { // replace data Element idlassertion = identityLink.getSamlAssertion(); - - // - set bpk/wpbk; + + // - set fake baseID; Node prIdentification = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_IDENT_VALUE_XPATH); - if(!eIDASAttributes.containsKey(Constants.eIDAS_ATTR_PERSONALIDENTIFIER)) - throw new eIDASAttributeException(Constants.eIDAS_ATTR_PERSONALIDENTIFIER); - String eIdentifier = eIDASAttributes.get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER).getValue().get(0); - prIdentification.getFirstChild().setNodeValue(eIdentifier); + + + Object eIdentifier = eIDASAttributes.getFirstValue( + SAMLEngineUtils.getMapOfAllAvailableAttributes().get( + Constants.eIDAS_ATTR_PERSONALIDENTIFIER)); + if (eIdentifier == null || !(eIdentifier instanceof String)) + throw new eIDASAttributeException(Constants.eIDAS_ATTR_PERSONALIDENTIFIER); + prIdentification.getFirstChild().setNodeValue((String) eIdentifier); + + //build personal identifier which looks like a baseID +// String fakeBaseID = new BPKBuilder().buildBPK(eIdentifier, "baseID"); +// Logger.info("Map eIDAS eIdentifier:" + eIdentifier + " to fake baseID:" + fakeBaseID); +// prIdentification.getFirstChild().setNodeValue(fakeBaseID); // - set last name - Node prFamilyName = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_FAMILY_NAME_XPATH); - if(!eIDASAttributes.containsKey(Constants.eIDAS_ATTR_CURRENTFAMILYNAME)) + Node prFamilyName = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_FAMILY_NAME_XPATH); + Object familyName = eIDASAttributes.getFirstValue( + SAMLEngineUtils.getMapOfAllAvailableAttributes().get( + Constants.eIDAS_ATTR_CURRENTFAMILYNAME)); + if (familyName == null || !(familyName instanceof String)) throw new eIDASAttributeException(Constants.eIDAS_ATTR_CURRENTFAMILYNAME); - String familyName = eIDASAttributes.get(Constants.eIDAS_ATTR_CURRENTFAMILYNAME).getValue().get(0); - prFamilyName.getFirstChild().setNodeValue(familyName); + prFamilyName.getFirstChild().setNodeValue((String) familyName); // - set first name Node prGivenName = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_GIVEN_NAME_XPATH); - if(!eIDASAttributes.containsKey(Constants.eIDAS_ATTR_CURRENTGIVENNAME)) + Object givenName = eIDASAttributes.getFirstValue( + SAMLEngineUtils.getMapOfAllAvailableAttributes().get( + Constants.eIDAS_ATTR_CURRENTGIVENNAME)); + if (givenName == null || !(givenName instanceof String)) throw new eIDASAttributeException(Constants.eIDAS_ATTR_CURRENTGIVENNAME); - String givenName = eIDASAttributes.get(Constants.eIDAS_ATTR_CURRENTGIVENNAME).getValue().get(0); - prGivenName.getFirstChild().setNodeValue(givenName); + prGivenName.getFirstChild().setNodeValue((String) givenName); // - set date of birth - Node prDateOfBirth = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_DATE_OF_BIRTH_XPATH); - if(!eIDASAttributes.containsKey(Constants.eIDAS_ATTR_DATEOFBIRTH)) + Node prDateOfBirth = XPathUtils.selectSingleNode(idlassertion, IdentityLinkAssertionParser.PERSON_DATE_OF_BIRTH_XPATH); + Object dateOfBirth = eIDASAttributes.getFirstValue( + SAMLEngineUtils.getMapOfAllAvailableAttributes().get( + Constants.eIDAS_ATTR_DATEOFBIRTH)); + if (dateOfBirth == null || !(dateOfBirth instanceof DateTime)) throw new eIDASAttributeException(Constants.eIDAS_ATTR_DATEOFBIRTH); - String dateOfBirth = eIDASAttributes.get(Constants.eIDAS_ATTR_DATEOFBIRTH).getValue().get(0); - dateOfBirth = new SimpleDateFormat("yyyy-MM-dd").format(new SimpleDateFormat("yyyyMMdd").parse(dateOfBirth)); - prDateOfBirth.getFirstChild().setNodeValue(dateOfBirth); + + String formatedDateOfBirth = new SimpleDateFormat("yyyy-MM-dd").format(((DateTime)dateOfBirth).toDate()); + prDateOfBirth.getFirstChild().setNodeValue(formatedDateOfBirth); identityLink = new IdentityLinkAssertionParser(idlassertion).parseIdentityLink(); @@ -144,7 +162,7 @@ public class CreateIdentityLinkTask extends AbstractAuthServletTask { moasession.setBkuURL("Not applicable (eIDASAuthentication)"); //store MOA-session to database - authenticatedSessionStorage.storeSession(moasession); + requestStoreage.storePendingRequest(pendingReq); } catch (eIDASAttributeException e) { throw new TaskExecutionException(pendingReq, "Minimum required eIDAS attributeset not found.", e); 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 84b0078b3..3522a16fd 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 @@ -23,7 +23,9 @@ package at.gv.egovernment.moa.id.auth.modules.eidas.tasks; import java.io.StringWriter; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -33,6 +35,11 @@ import org.apache.commons.lang3.StringUtils; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; +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.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.google.common.net.MediaType; @@ -43,6 +50,7 @@ import at.gv.egovernment.moa.id.auth.frontend.velocity.VelocityProvider; import at.gv.egovernment.moa.id.auth.modules.AbstractAuthServletTask; import at.gv.egovernment.moa.id.auth.modules.TaskExecutionException; 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.exceptions.EIDASEngineException; import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils; import at.gv.egovernment.moa.id.commons.MOAIDAuthConstants; @@ -52,15 +60,17 @@ import at.gv.egovernment.moa.id.commons.api.data.StorkAttribute; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.process.api.ExecutionContext; import at.gv.egovernment.moa.logging.Logger; -import eu.eidas.auth.commons.EIDASAuthnRequest; -import eu.eidas.auth.commons.EIDASUtil; -import eu.eidas.auth.commons.EidasLoaCompareType; -import eu.eidas.auth.commons.EidasLoaLevels; -import eu.eidas.auth.commons.IPersonalAttributeList; -import eu.eidas.auth.commons.PersonalAttribute; -import eu.eidas.auth.commons.PersonalAttributeList; -import eu.eidas.auth.engine.EIDASSAMLEngine; -import eu.eidas.auth.engine.core.eidas.SPType; +import at.gv.egovernment.moa.util.MiscUtil; +import eu.eidas.auth.commons.EidasStringUtil; +import eu.eidas.auth.commons.attribute.AttributeDefinition; +import eu.eidas.auth.commons.attribute.AttributeDefinition.Builder; +import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; +import eu.eidas.auth.commons.protocol.IRequestMessage; +import eu.eidas.auth.commons.protocol.eidas.LevelOfAssurance; +import eu.eidas.auth.commons.protocol.eidas.LevelOfAssuranceComparison; +import eu.eidas.auth.commons.protocol.eidas.SpType; +import eu.eidas.auth.commons.protocol.eidas.impl.EidasAuthenticationRequest; +import eu.eidas.auth.engine.ProtocolEngineI; import eu.eidas.engine.exceptions.EIDASSAMLEngineException; /** @@ -70,6 +80,8 @@ import eu.eidas.engine.exceptions.EIDASSAMLEngineException; @Component("GenerateAuthnRequestTask") public class GenerateAuthnRequestTask extends AbstractAuthServletTask { + @Autowired(required=true) MOAeIDASChainingMetadataProvider eIDASMetadataProvider; + /* (non-Javadoc) * @see at.gv.egovernment.moa.id.process.springweb.MoaIdTask#execute(at.gv.egovernment.moa.id.process.api.ExecutionContext, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) */ @@ -82,23 +94,61 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { //get service-provider configuration IOAAuthParameters oaConfig = pendingReq.getOnlineApplicationConfiguration(); - // get target country + // get target and validate citizen countryCode String citizenCountryCode = (String) executionContext.get(MOAIDAuthConstants.PARAM_CCC); if (StringUtils.isEmpty(citizenCountryCode)) { // illegal state; task should not have been executed without a selected country throw new AuthenticationException("eIDAS.03", new Object[] { "" }); + } - CPEPS cpeps = authConfig.getStorkConfig().getCPEPS(citizenCountryCode); if(null == cpeps) { Logger.error("PEPS unknown for country", new Object[] {citizenCountryCode}); throw new AuthenticationException("eIDAS.04", new Object[] {citizenCountryCode}); } Logger.debug("Found eIDaS Node/C-PEPS configuration for citizen of country: " + citizenCountryCode); - String destination = cpeps.getPepsURL().toString().split(";")[1].trim(); // FIXME convenience for metadata url and assertion destination + + + // select SingleSignOnService Endpoint from eIDAS-node metadata + String destination = null; String metadataUrl = cpeps.getPepsURL().toString().split(";")[0].trim(); + try { + EntityDescriptor eIDASNodeMetadata = eIDASMetadataProvider.getEntityDescriptor(metadataUrl); + if (eIDASNodeMetadata != null) { + SingleSignOnService ssoDescr = selectSingleSignOnServiceFromMetadata(eIDASNodeMetadata); + if (ssoDescr != null) { + destination = ssoDescr.getLocation(); + Logger.debug("Use destination URL:" + destination + " from eIDAS metadata:" + metadataUrl); + + } else + Logger.warn("eIDAS metadata for node:" + metadataUrl + " has no IDPSSODescriptor or no SingleSignOnService information."); + + } else + Logger.warn("No eIDAS metadata for node:" + metadataUrl + " "); + + } catch (MetadataProviderException e) { + Logger.warn("Load eIDAS metadata from node:" + metadataUrl + " FAILED with an error.", e); + + } + // load SingleSignOnService Endpoint from configuration, if Metadata contains no information + // FIXME convenience function for not standard conform metadata + if (MiscUtil.isEmpty(destination)) { + String[] splitString = cpeps.getPepsURL().toString().split(";"); + if (splitString.length > 1) + destination = cpeps.getPepsURL().toString().split(";")[1].trim(); + + if (MiscUtil.isNotEmpty(destination)) + Logger.debug("Use eIDAS node destination URL:" + destination + " from configuration"); + + else { + Logger.error("No eIDAS-node destination URL FOUND. Request eIDAS node not possible."); + throw new MOAIDException("eIDAS.02", new Object[]{"No eIDAS-node Destination-URL FOUND"}); + + } + + } //TODO: switch to entityID revisionsLogger.logEvent(oaConfig, pendingReq, @@ -109,50 +159,71 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { Collection<StorkAttribute> attributesFromConfig = oaConfig.getRequestedSTORKAttributes(); // - prepare attribute list - IPersonalAttributeList pAttList = new PersonalAttributeList(); - + ProtocolEngineI engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); + // - fill container - for (StorkAttribute current : attributesFromConfig) { - PersonalAttribute newAttribute = new PersonalAttribute(); - newAttribute.setName(current.getName()); - - boolean globallyMandatory = false; - for (StorkAttribute currentGlobalAttribute : authConfig.getStorkConfig().getStorkAttributes()) - if (current.getName().equals(currentGlobalAttribute.getName())) { - globallyMandatory = BooleanUtils.isTrue(currentGlobalAttribute.getMandatory()); - break; - } - - newAttribute.setIsRequired(current.getMandatory() || globallyMandatory); - pAttList.add(newAttribute); + List<AttributeDefinition<?>> reqAttrList = new ArrayList<AttributeDefinition<?>>(); + for (StorkAttribute current : attributesFromConfig) { + AttributeDefinition<?> newAttribute = SAMLEngineUtils.getMapOfAllAvailableAttributes().get(current.getName()); + + if (newAttribute == null) { + Logger.warn("eIDAS attribute with friendlyName:" + current.getName() + " is not supported."); + + } else { + boolean globallyMandatory = false; + for (StorkAttribute currentGlobalAttribute : authConfig.getStorkConfig().getStorkAttributes()) + if (current.getName().equals(currentGlobalAttribute.getName())) { + globallyMandatory = BooleanUtils.isTrue(currentGlobalAttribute.getMandatory()); + break; + } + + Builder<?> attrBuilder = AttributeDefinition.builder(newAttribute).required(current.getMandatory() || globallyMandatory); + reqAttrList.add(attrBuilder.build()); + + } } - - EIDASSAMLEngine engine = SAMLEngineUtils.createSAMLEngine(); - - //build eIDAS AuthnRequest - EIDASAuthnRequest authnRequest = new EIDASAuthnRequest(); - authnRequest.setProviderName(pendingReq.getAuthURL()); - authnRequest.setPersonalAttributeList(pAttList); - authnRequest.setIssuer(pendingReq.getAuthURL() + Constants.eIDAS_HTTP_ENDPOINT_METADATA); + //build requested attribute set + ImmutableAttributeMap reqAttrMap = new ImmutableAttributeMap.Builder().putAll(reqAttrList).build(); + + //build eIDAS AuthnRequest + EidasAuthenticationRequest.Builder authnRequestBuilder = new EidasAuthenticationRequest.Builder(); + + authnRequestBuilder.id(eu.eidas.auth.engine.xml.opensaml.SAMLEngineUtils.generateNCName()); + authnRequestBuilder.providerName(pendingReq.getAuthURL()); + String issur = pendingReq.getAuthURL() + Constants.eIDAS_HTTP_ENDPOINT_METADATA; + authnRequestBuilder.issuer(issur); + authnRequestBuilder.destination(destination); + + authnRequestBuilder.nameIdFormat(Constants.eIDAS_REQ_NAMEID_FORMAT); - authnRequest.setDestination(destination); - authnRequest.setEidasNameidFormat(EIDASAuthnRequest.NAMEID_FORMAT_UNSPECIFIED); - authnRequest.setEidasLoA(EidasLoaLevels.LOW.stringValue()); - authnRequest.setEidasLoACompareType(EidasLoaCompareType.MINIMUM.stringValue()); + //set minimum required eIDAS LoA from OA config + authnRequestBuilder.levelOfAssurance(LevelOfAssurance.fromString(oaConfig.getQaaLevel())); + authnRequestBuilder.levelOfAssuranceComparison(LevelOfAssuranceComparison.MINIMUM); //set correct SPType for this online application if (oaConfig.getBusinessService()) - authnRequest.setSPType("private"); + authnRequestBuilder.spType(SpType.PRIVATE); else - authnRequest.setSPType(SPType.DEFAULT_VALUE); - - engine.initRequestedAttributes(pAttList); - authnRequest = engine.generateEIDASAuthnRequest(authnRequest); + authnRequestBuilder.spType(SpType.PUBLIC); + + + //set service provider (eIDAS node) countryCode + authnRequestBuilder.serviceProviderCountryCode( + authConfig.getBasicMOAIDConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_COUNTRYCODE, "AT")); + + //set citizen country code for foreign uses + authnRequestBuilder.citizenCountryCode(cpeps.getCountryCode()); + + //add requested attributes + authnRequestBuilder.requestedAttributes(reqAttrMap); + + + IRequestMessage authnRequest = engine.generateRequestMessage(authnRequestBuilder.build(), issur); //encode AuthnRequest - byte[] token = authnRequest.getTokenSaml(); - String SAMLRequest = EIDASUtil.encodeSAMLToken(token); + byte[] token = authnRequest.getMessageBytes(); + String SAMLRequest = EidasStringUtil.encodeToBase64(token); //send @@ -187,7 +258,7 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { revisionsLogger.logEvent(oaConfig, pendingReq, MOAIDEventConstants.AUTHPROCESS_PEPS_REQUESTED, - authnRequest.getSamlId()); + authnRequest.getRequest().getId()); } catch (Exception e) { Logger.error("Velocity general error: " + e.getMessage()); @@ -209,4 +280,28 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { } } + private SingleSignOnService selectSingleSignOnServiceFromMetadata(EntityDescriptor idpEntity) { + //select SingleSignOn Service endpoint from IDP metadata + SingleSignOnService endpoint = null; + if (idpEntity.getIDPSSODescriptor(SAMLConstants.SAML20P_NS) == null) { + return null; + + } + + for (SingleSignOnService sss : + idpEntity.getIDPSSODescriptor(SAMLConstants.SAML20P_NS).getSingleSignOnServices()) { + + // use POST binding as default if it exists + if (sss.getBinding().equals(SAMLConstants.SAML2_POST_BINDING_URI)) + endpoint = sss; + +// else if ( sss.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI) +// && endpoint == null ) +// endpoint = sss; + + } + + return endpoint; + } + } 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 fae06031a..c4b2bfeae 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 @@ -4,6 +4,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.opensaml.saml2.core.StatusCode; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import at.gv.egovernment.moa.id.advancedlogging.MOAIDEventConstants; @@ -11,9 +12,9 @@ import at.gv.egovernment.moa.id.auth.data.AuthenticationSessionStorageConstants; import at.gv.egovernment.moa.id.auth.modules.AbstractAuthServletTask; import at.gv.egovernment.moa.id.auth.modules.TaskExecutionException; 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.exceptions.EIDASEngineException; -import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.eIDASResponseNotSuccessException; -import at.gv.egovernment.moa.id.auth.modules.eidas.utils.MOAPersonalAttributeList; +import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.EIDASResponseNotSuccessException; 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; @@ -21,14 +22,17 @@ import at.gv.egovernment.moa.id.process.api.ExecutionContext; 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.EIDASAuthnResponse; -import eu.eidas.auth.commons.EIDASUtil; -import eu.eidas.auth.engine.EIDASSAMLEngine; +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; @Component("ReceiveAuthnResponseTask") public class ReceiveAuthnResponseTask extends AbstractAuthServletTask { + @Autowired(required=true) MOAeIDASChainingMetadataProvider eIDASMetadataProvider; + @Override public void execute(ExecutionContext executionContext, HttpServletRequest request, HttpServletResponse response) throws TaskExecutionException { @@ -45,40 +49,59 @@ public class ReceiveAuthnResponseTask extends AbstractAuthServletTask { defaultTaskInitialization(request, executionContext); //decode SAML response - byte[] decSamlToken = EIDASUtil.decodeSAMLToken(base64SamlToken); + byte[] decSamlToken = EidasStringUtil.decodeBytesFromBase64(base64SamlToken); //get eIDAS SAML-engine - EIDASSAMLEngine engine = SAMLEngineUtils.createSAMLEngine(); - + ProtocolEngineI engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); + //validate SAML token - EIDASAuthnResponse samlResp = engine.validateEIDASAuthnResponse(decSamlToken, - request.getRemoteHost(), Constants.CONFIG_PROPS_SKEWTIME); - - boolean encryptedResponse=engine.isEncryptedSamlResponse(decSamlToken); - if (encryptedResponse) { + IAuthenticationResponse samlResp = engine.unmarshallResponseAndValidate(decSamlToken, + request.getRemoteHost(), + Constants.CONFIG_PROPS_SKEWTIME, + pendingReq.getAuthURL() + Constants.eIDAS_HTTP_ENDPOINT_METADATA); + + if (samlResp.isEncrypted()) { Logger.info("Received encrypted eIDAS SAML-Response."); //TODO: check if additional decryption operation is required } + //check response StatusCode if (!samlResp.getStatusCode().equals(StatusCode.SUCCESS_URI)) { Logger.info("Receice eIDAS Response with StatusCode:" + samlResp.getStatusCode() - + " Subcode:" + samlResp.getSubStatusCode() + " Msg:" + samlResp.getMessage()); - throw new eIDASResponseNotSuccessException("eIDAS.11", new Object[]{samlResp.getMessage()}); + + " Subcode:" + samlResp.getSubStatusCode() + " Msg:" + samlResp.getStatusMessage()); + throw new EIDASResponseNotSuccessException("eIDAS.11", new Object[]{samlResp.getStatusMessage()}); + + } + + // ********************************************************** + // ******* 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()}); } + - //MOA-ID specific response validation - //TODO: implement MOA-ID specific response validation + // ********************************************************** + // ******* Store resonse infos into session object ********** + // ********************************************************** //update MOA-Session data with received information Logger.debug("Store eIDAS response information into MOA-session."); - moasession.setQAALevel(samlResp.getAssuranceLevel()); + + moasession.setQAALevel(samlResp.getLevelOfAssurance()); moasession.setGenericDataToSession( AuthenticationSessionStorageConstants.eIDAS_ATTRIBUTELIST, - new MOAPersonalAttributeList(samlResp.getPersonalAttributeList())); + samlResp.getAttributes()); moasession.setGenericDataToSession( AuthenticationSessionStorageConstants.eIDAS_RESPONSE, @@ -88,17 +111,20 @@ public class ReceiveAuthnResponseTask extends AbstractAuthServletTask { moasession.setGenericDataToSession(PVPConstants.EID_ISSUING_NATION_NAME, samlResp.getCountry()); //store MOA-session to database - authenticatedSessionStorage.storeSession(moasession); + requestStoreage.storePendingRequest(pendingReq); revisionsLogger.logEvent(pendingReq.getOnlineApplicationConfiguration(), pendingReq, MOAIDEventConstants.AUTHPROCESS_PEPS_RECEIVED, - samlResp.getSamlId()); + samlResp.getId()); + + } catch (MOAIDException e) { + throw new TaskExecutionException(pendingReq, "eIDAS Response processing FAILED.", e); }catch (EIDASSAMLEngineException e) { Logger.error("eIDAS AuthnRequest generation FAILED.", e); revisionsLogger.logEvent(pendingReq.getOnlineApplicationConfiguration(), pendingReq, MOAIDEventConstants.AUTHPROCESS_PEPS_RECEIVED_ERROR); - throw new TaskExecutionException(pendingReq, "eIDAS AuthnRequest generation FAILED.", + throw new TaskExecutionException(pendingReq, "eIDAS Response processing FAILED.", new EIDASEngineException("eIDAS.09", new Object[]{e.getMessage()}, e)); } catch (MOADatabaseException e) { diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAOrderedAttributeIterator.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAOrderedAttributeIterator.java deleted file mode 100644 index 573163af0..000000000 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAOrderedAttributeIterator.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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.Iterator; -import java.util.NoSuchElementException; - -import at.gv.egovernment.moa.logging.Logger; - -import eu.eidas.auth.commons.PersonalAttribute; -import eu.eidas.auth.commons.PersonalAttributeList; - - -/** - * @author tlenz - * - */ -public class MOAOrderedAttributeIterator implements Iterator<PersonalAttribute> { - - private MOAPersonalAttributeList pal; - private Iterator<String> keyIterator; - - public MOAOrderedAttributeIterator(MOAPersonalAttributeList palArg) { - this.pal = palArg; - keyIterator = palArg.getInsertOrder().iterator(); - } - - @Override - public boolean hasNext() { - return keyIterator.hasNext(); - } - - @Override - public PersonalAttribute next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - return pal.get(keyIterator.next()); - } - - @Override - public void remove() { - Logger.error("Not implemented"); - } - -} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAPersonalAttributeList.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAPersonalAttributeList.java deleted file mode 100644 index 5cc100b70..000000000 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAPersonalAttributeList.java +++ /dev/null @@ -1,343 +0,0 @@ -/* - * 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.io.IOException; -import java.io.ObjectInputStream; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.StringTokenizer; -import java.util.concurrent.ConcurrentHashMap; - -import org.apache.commons.lang.StringUtils; - -import at.gv.egovernment.moa.logging.Logger; - -import eu.eidas.auth.commons.AttributeConstants; -import eu.eidas.auth.commons.AttributeUtil; -import eu.eidas.auth.commons.EIDASErrors; -import eu.eidas.auth.commons.EIDASParameters; -import eu.eidas.auth.commons.EIDASUtil; -import eu.eidas.auth.commons.EIDASValues; -import eu.eidas.auth.commons.IPersonalAttributeList; -import eu.eidas.auth.commons.PersonalAttribute; -import eu.eidas.auth.commons.exceptions.InternalErrorEIDASException; - -/** - * @author tlenz - * - */ -public final class MOAPersonalAttributeList extends - ConcurrentHashMap<String, PersonalAttribute> implements IPersonalAttributeList { - - /** - * - */ - private static final long serialVersionUID = -4488124133022713089L; - - public MOAPersonalAttributeList(IPersonalAttributeList eIDASAttributeList) { - super(); - Iterator<PersonalAttribute> element = eIDASAttributeList.iterator(); - while(element.hasNext()) - add(element.next()); - - } - - /** - * Hash with the latest fetched attribute name alias. - */ - private Map<String, Integer> latestAttrAlias = - new HashMap<String, Integer>(); - - /** - * Hash with mapping number of alias or the attribute name. - */ - private Map<String, Integer> attrAliasNumber = - new HashMap<String, Integer>(); - private List<String> insertOrder = new ArrayList<String>(); - - /** - * Obtain the insertOrder Collection - * - * @return defensive copy of the collection - */ - List<String> getInsertOrder() { - return Collections.unmodifiableList(this.insertOrder); - } - - /** - * Default constructor. - */ - public MOAPersonalAttributeList() { - super(); - - } - - /** - * Constructor with initial capacity for the PersonalAttributeList size. - * - * @param capacity The initial capacity for the PersonalAttributeList. - */ - public MOAPersonalAttributeList(final int capacity) { - super(capacity); - } - - /** - * {@inheritDoc} - */ - public Iterator<PersonalAttribute> iterator() { - return new MOAOrderedAttributeIterator(this); - } - - /** - * {@inheritDoc} - */ - public PersonalAttribute get(final Object key) { - String attrName = (String) key; - - if (this.latestAttrAlias == null) - this.latestAttrAlias = new HashMap<String, Integer>(); - - if (this.attrAliasNumber == null) - this.attrAliasNumber = new HashMap<String, Integer>(); - - if (this.latestAttrAlias.containsKey(key)) { - attrName = attrName + this.latestAttrAlias.get(key); - } else { - if (this.attrAliasNumber.containsKey(key)) { - this.latestAttrAlias.put(attrName, this.attrAliasNumber.get(key)); - } - } - return super.get(attrName); - } - - /** - * {@inheritDoc} - */ - public void add(final PersonalAttribute value) { - if (value != null) { - this.put(value.getName(), value); - } - } - - /** - * {@inheritDoc} - */ - public PersonalAttribute put(final String key, final PersonalAttribute val) { - if (StringUtils.isNotEmpty(key) && val != null) { - // Validate if attribute name already exists! - String attrAlias = key; - if (this.containsKey(attrAlias)) { - if (this.attrAliasNumber == null) - this.attrAliasNumber = new HashMap<String, Integer>(); - if (!val.isEmptyValue() && StringUtils.isNumeric(val.getValue().get(0))) { - final String attrValue = val.getValue().get(0); - attrAlias = key + attrValue; - this.attrAliasNumber.put(key, Integer.valueOf(attrValue)); - } else { - final PersonalAttribute attr = super.get(key); - if (!attr.isEmptyValue() - && StringUtils.isNumeric(attr.getValue().get(0))) { - attrAlias = key + attr.getValue().get(0); - super.put(key, (PersonalAttribute) attr); - this.attrAliasNumber.put(key, null); - } - } - } else { - if (insertOrder == null) - insertOrder = new ArrayList<String>(); - - insertOrder.add(key); - } - return super.put(attrAlias, val); - } else { - return null; - } - } - - @Override - public PersonalAttribute remove(Object key) { - insertOrder.remove(key); - return super.remove(key); - } - - /** - * {@inheritDoc} - */ - public void populate(final String attrList) { - final StringTokenizer strToken = - new StringTokenizer(attrList, EIDASValues.ATTRIBUTE_SEP.toString()); - - while (strToken.hasMoreTokens()) { - final PersonalAttribute persAttr = new PersonalAttribute(); - String[] tuples = - strToken.nextToken().split(EIDASValues.ATTRIBUTE_TUPLE_SEP.toString(), - AttributeConstants.NUMBER_TUPLES.intValue()); - - // Convert to the new format if needed! - tuples = convertFormat(tuples); - - if (AttributeUtil.hasValidTuples(tuples)) { - final int attrValueIndex = - AttributeConstants.ATTR_VALUE_INDEX.intValue(); - final String tmpAttrValue = - tuples[attrValueIndex].substring(1, - tuples[attrValueIndex].length() - 1); - final String[] vals = - tmpAttrValue.split(EIDASValues.ATTRIBUTE_VALUE_SEP.toString()); - - persAttr.setName(tuples[AttributeConstants.ATTR_NAME_INDEX.intValue()]); - persAttr.setIsRequired(Boolean - .valueOf(tuples[AttributeConstants.ATTR_TYPE_INDEX.intValue()])); - // check if it is a complex value - if (tuples[AttributeConstants.ATTR_NAME_INDEX.intValue()] - .equals(EIDASParameters.COMPLEX_ADDRESS_VALUE.toString())) { - persAttr.setComplexValue(createComplexValue(vals)); - } else { - persAttr.setValue(createValues(vals)); - } - - if (tuples.length == AttributeConstants.NUMBER_TUPLES.intValue()) { - persAttr.setStatus(tuples[AttributeConstants.ATTR_STATUS_INDEX - .intValue()]); - } - this.put(tuples[AttributeConstants.ATTR_NAME_INDEX.intValue()], - persAttr); - - } else { - Logger.info("BUSINESS EXCEPTION : Invalid personal attribute list tuples"); - } - - } - } - - /** - * Returns a copy of this <tt>IPersonalAttributeList</tt> instance. - * - * @return The copy of this IPersonalAttributeList. - */ - public Object clone() { - try { - MOAPersonalAttributeList theClone= (MOAPersonalAttributeList)super.clone(); - theClone.insertOrder=new ArrayList<String>(insertOrder); - return theClone; - - } catch (CloneNotSupportedException e) { - throw new InternalErrorEIDASException( - EIDASUtil.getConfig(EIDASErrors.INTERNAL_ERROR.errorCode()), - EIDASUtil.getConfig(EIDASErrors.INTERNAL_ERROR.errorMessage()), e); - } - } - - /** - * Creates a string in the following format. - * - * attrName:attrType:[attrValue1,attrValue2=attrComplexValue]:attrStatus; - * - * @return {@inheritDoc} - */ - @Override - public String toString() { - final StringBuilder strBuilder = new StringBuilder(); - final Iterator<String> iteratorInsertOrder = insertOrder.iterator(); - while (iteratorInsertOrder.hasNext()) { - String key = iteratorInsertOrder.next(); - final PersonalAttribute attr = get(key); - strBuilder.append(attr.toString()); - if (isNumberAlias(key)) { - strBuilder.append(get(key).toString()); - } - } - return strBuilder.toString(); - } - - /** - * Validates and creates the attribute's complex values. - * - * @param values The complex values. - * @return The {@link Map} with the complex values. - * @see Map - */ - private Map<String, String> createComplexValue(final String[] values) { - final Map<String, String> complexValue = new HashMap<String, String>(); - for (final String val : values) { - final String[] tVal = val.split("="); - if (StringUtils.isNotEmpty(val) && tVal.length == 2) { - complexValue.put(tVal[0], AttributeUtil.unescape(tVal[1])); - } - } - return complexValue; - } - - /** - * Validates and creates the attribute values. - * - * @param vals The attribute values. - * @return The {@link List} with the attribute values. - * @see List - */ - private List<String> createValues(final String[] vals) { - final List<String> values = new ArrayList<String>(); - for (final String val : vals) { - if (StringUtils.isNotEmpty(val)) { - values.add(AttributeUtil.unescape(val)); - } - } - return values; - } - - ////////////////// - /** - * Converts the attribute tuple (attrName:attrType...) to the new format. - * - * @param tuples The attribute tuples to convert. - * @return The attribute tuples in the new format. - */ - private String[] convertFormat(final String[] tuples) { - final String[] newFormatTuples = - new String[AttributeConstants.NUMBER_TUPLES.intValue()]; - if (tuples != null) { - System.arraycopy(tuples, 0, newFormatTuples, 0, tuples.length); - - for (int i = tuples.length; i < newFormatTuples.length; i++) { - if (i == AttributeConstants.ATTR_VALUE_INDEX.intValue()) { - newFormatTuples[i] = "[]"; - } else { - newFormatTuples[i] = ""; - } - } - } - return newFormatTuples; - } - - public boolean isNumberAlias(String key) { - return this.attrAliasNumber.containsKey(key); - } - - - -} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAProtocolEngineFactory.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAProtocolEngineFactory.java new file mode 100644 index 000000000..f29d2bb65 --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAProtocolEngineFactory.java @@ -0,0 +1,99 @@ +/* + * 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 at.gv.egovernment.moa.id.auth.modules.eidas.config.MOAIDCertificateManagerConfigurationImpl; +import at.gv.egovernment.moa.logging.Logger; +import eu.eidas.auth.engine.ProtocolEngineFactory; +import eu.eidas.auth.engine.configuration.SamlEngineConfigurationException; +import eu.eidas.auth.engine.configuration.dom.ProtocolEngineConfigurationFactory; +import eu.eidas.samlengineconfig.CertificateConfigurationManager; + +/** + * @author tlenz + * + */ +public class MOAProtocolEngineFactory extends ProtocolEngineFactory { + + /** + * Initialization-on-demand holder idiom. + * <p/> + * See item 71 of Effective Java 2nd Edition. + * <p/> + * See http://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom. + */ + private static final class LazyHolder { + + private static final MOAProtocolEngineFactory DEFAULT_SAML_ENGINE_FACTORY; + + private static final Exception INITIALIZATION_EXCEPTION; + + static { + Exception initializationException = null; + MOAProtocolEngineFactory defaultProtocolEngineFactory = null; + try { + //get eIDAS SAMLengine configuration from MOA-ID configuration + CertificateConfigurationManager configManager = new MOAIDCertificateManagerConfigurationImpl(); + + ProtocolEngineConfigurationFactory engineConfigurationFactory = new ProtocolEngineConfigurationFactory(configManager); + defaultProtocolEngineFactory = new MOAProtocolEngineFactory(engineConfigurationFactory); + + } catch (Exception ex) { + initializationException = ex; + Logger.error("Unable to instantiate default SAML engines: " + ex, ex); + + } + + DEFAULT_SAML_ENGINE_FACTORY = defaultProtocolEngineFactory; + INITIALIZATION_EXCEPTION = initializationException; + } + + static MOAProtocolEngineFactory getDefaultSamlEngineFactory() { + if (null == INITIALIZATION_EXCEPTION) { + return DEFAULT_SAML_ENGINE_FACTORY; + + } else { + throw new IllegalStateException(INITIALIZATION_EXCEPTION); + + } + } + } + + + public static MOAProtocolEngineFactory getInstance() { + return LazyHolder.getDefaultSamlEngineFactory(); + + } + + /** + * @param configurationFactory + * @throws SamlEngineConfigurationException + */ + private MOAProtocolEngineFactory(ProtocolEngineConfigurationFactory configurationFactory) + throws SamlEngineConfigurationException { + super(configurationFactory); + + } + + +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAeIDASMetadataGenerator.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAeIDASMetadataGenerator.java new file mode 100644 index 000000000..09c3dff38 --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAeIDASMetadataGenerator.java @@ -0,0 +1,621 @@ +/* + * 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.security.cert.X509Certificate; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.joda.time.DateTime; +import org.joda.time.DurationFieldType; +import org.opensaml.Configuration; +import org.opensaml.common.xml.SAMLConstants; +import org.opensaml.saml2.common.Extensions; +import org.opensaml.saml2.core.Attribute; +import org.opensaml.saml2.core.AttributeValue; +import org.opensaml.saml2.metadata.AssertionConsumerService; +import org.opensaml.saml2.metadata.Company; +import org.opensaml.saml2.metadata.ContactPerson; +import org.opensaml.saml2.metadata.ContactPersonTypeEnumeration; +import org.opensaml.saml2.metadata.EmailAddress; +import org.opensaml.saml2.metadata.EncryptionMethod; +import org.opensaml.saml2.metadata.EntitiesDescriptor; +import org.opensaml.saml2.metadata.EntityDescriptor; +import org.opensaml.saml2.metadata.GivenName; +import org.opensaml.saml2.metadata.IDPSSODescriptor; +import org.opensaml.saml2.metadata.KeyDescriptor; +import org.opensaml.saml2.metadata.LocalizedString; +import org.opensaml.saml2.metadata.NameIDFormat; +import org.opensaml.saml2.metadata.Organization; +import org.opensaml.saml2.metadata.OrganizationDisplayName; +import org.opensaml.saml2.metadata.OrganizationURL; +import org.opensaml.saml2.metadata.SPSSODescriptor; +import org.opensaml.saml2.metadata.SSODescriptor; +import org.opensaml.saml2.metadata.SingleSignOnService; +import org.opensaml.saml2.metadata.SurName; +import org.opensaml.saml2.metadata.TelephoneNumber; +import org.opensaml.samlext.saml2mdattr.EntityAttributes; +import org.opensaml.xml.XMLObject; +import org.opensaml.xml.XMLObjectBuilderFactory; +import org.opensaml.xml.schema.XSString; +import org.opensaml.xml.schema.impl.XSStringBuilder; +import org.opensaml.xml.security.SecurityException; +import org.opensaml.xml.security.credential.Credential; +import org.opensaml.xml.security.credential.UsageType; +import org.opensaml.xml.security.keyinfo.KeyInfoGenerator; +import org.opensaml.xml.security.x509.X509KeyInfoGeneratorFactory; +import org.opensaml.xml.signature.KeyInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.collect.ImmutableSortedSet; +import com.google.common.collect.Ordering; + +import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; +import eu.eidas.auth.commons.EIDASUtil; +import eu.eidas.auth.commons.EidasStringUtil; +import eu.eidas.auth.commons.attribute.AttributeDefinition; +import eu.eidas.auth.commons.protocol.impl.SamlNameIdFormat; +import eu.eidas.auth.commons.xml.opensaml.OpenSamlHelper; +import eu.eidas.auth.engine.ProtocolEngineI; +import eu.eidas.auth.engine.core.SAMLExtensionFormat; +import eu.eidas.auth.engine.core.eidas.DigestMethod; +import eu.eidas.auth.engine.core.eidas.EidasConstants; +import eu.eidas.auth.engine.core.eidas.SPType; +import eu.eidas.auth.engine.core.eidas.SigningMethod; +import eu.eidas.auth.engine.metadata.Contact; +import eu.eidas.auth.engine.metadata.EntityDescriptorContainer; +import eu.eidas.auth.engine.metadata.MetadataConfigParams; +import eu.eidas.auth.engine.metadata.MetadataGenerator; +import eu.eidas.auth.engine.metadata.MetadataSignerI; +import eu.eidas.auth.engine.xml.opensaml.BuilderFactoryUtil; +import eu.eidas.auth.engine.xml.opensaml.CertificateUtil; +import eu.eidas.encryption.exception.UnmarshallException; +import eu.eidas.engine.exceptions.EIDASSAMLEngineException; +import eu.eidas.engine.exceptions.SAMLEngineException; + +/** + * @author tlenz + * + */ +public class MOAeIDASMetadataGenerator extends MetadataGenerator { + private static final Logger LOGGER = LoggerFactory.getLogger(MetadataGenerator.class.getName()); + + MetadataConfigParams params; + + XMLObjectBuilderFactory builderFactory = Configuration.getBuilderFactory(); + + SPSSODescriptor spSSODescriptor = null; + + IDPSSODescriptor idpSSODescriptor = null; + + private String ssoLocation; + + /** + * @return a String representation of the entityDescriptr built based on the attributes previously set + */ + public String generateMetadata() throws EIDASSAMLEngineException { + EntityDescriptor entityDescriptor; + try { + entityDescriptor = (EntityDescriptor) builderFactory.getBuilder(EntityDescriptor.DEFAULT_ELEMENT_NAME) + .buildObject(EntityDescriptor.DEFAULT_ELEMENT_NAME); + + entityDescriptor.setEntityID(params.getEntityID()); + entityDescriptor.setOrganization(buildOrganization()); + entityDescriptor.getContactPersons().add(buildContact(ContactPersonTypeEnumeration.SUPPORT)); + entityDescriptor.getContactPersons().add(buildContact(ContactPersonTypeEnumeration.TECHNICAL)); + entityDescriptor.setValidUntil(getExpireDate()); + + X509KeyInfoGeneratorFactory keyInfoGeneratorFactory = new X509KeyInfoGeneratorFactory(); + keyInfoGeneratorFactory.setEmitEntityCertificate(true); + Extensions e = generateExtensions(); + if (!e.getUnknownXMLObjects().isEmpty()) { + entityDescriptor.setExtensions(e); + } + if (spSSODescriptor != null) { + generateSPSSODescriptor(entityDescriptor, keyInfoGeneratorFactory); + } + if (idpSSODescriptor != null) { + generateIDPSSODescriptor(entityDescriptor, keyInfoGeneratorFactory); + } + if (params.getSpEngine() != null) { + ProtocolEngineI spEngine = params.getSpEngine(); + ((MetadataSignerI) spEngine.getSigner()).signMetadata(entityDescriptor); + } else if (params.getIdpEngine() != null) { + ProtocolEngineI idpEngine = params.getIdpEngine(); + ((MetadataSignerI) idpEngine.getSigner()).signMetadata(entityDescriptor); + } + return EidasStringUtil.toString(OpenSamlHelper.marshall(entityDescriptor, false)); + } catch (Exception ex) { + LOGGER.info("ERROR : SAMLException ", ex.getMessage()); + LOGGER.debug("ERROR : SAMLException ", ex); + throw new IllegalStateException(ex); + } + } + + private void generateSPSSODescriptor(final EntityDescriptor entityDescriptor, + final X509KeyInfoGeneratorFactory keyInfoGeneratorFactory) + throws org.opensaml.xml.security.SecurityException, IllegalAccessException, NoSuchFieldException, + SAMLEngineException, EIDASSAMLEngineException { + //the node has SP role + spSSODescriptor.setWantAssertionsSigned(params.isWantAssertionsSigned()); + spSSODescriptor.setAuthnRequestsSigned(true); + spSSODescriptor.setID(idpSSODescriptor == null ? params.getEntityID() + : ("SP" + params.getEntityID())); + if (params.getSPSignature() != null) { + spSSODescriptor.setSignature(params.getSPSignature()); + } + if (params.getSpSigningCredential() != null) { + spSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getSpSigningCredential(), UsageType.SIGNING)); + + } else if (params.getSigningCredential() != null) { + spSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getSigningCredential(), UsageType.SIGNING)); + } + + if (params.getSpEncryptionCredential() != null) { + spSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getSpEncryptionCredential(), + UsageType.ENCRYPTION)); + } else if (params.getEncryptionCredential() != null) { + spSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getEncryptionCredential(), UsageType.ENCRYPTION)); + } + spSSODescriptor.addSupportedProtocol(params.getSpSamlProtocol()); + if (!StringUtils.isEmpty(params.getAssertionConsumerUrl())) { + addAssertionConsumerService(); + } + fillNameIDFormat(spSSODescriptor); + if (params.getSpEngine() != null) { + ProtocolEngineI spEngine = params.getSpEngine(); + ((MetadataSignerI) spEngine.getSigner()).signMetadata(spSSODescriptor); + } + entityDescriptor.getRoleDescriptors().add(spSSODescriptor); + + } + + private void fillNameIDFormat(SSODescriptor ssoDescriptor) throws EIDASSAMLEngineException { + NameIDFormat persistentFormat = + (NameIDFormat) BuilderFactoryUtil.buildXmlObject(NameIDFormat.DEFAULT_ELEMENT_NAME); + persistentFormat.setFormat(SamlNameIdFormat.PERSISTENT.getNameIdFormat()); + ssoDescriptor.getNameIDFormats().add(persistentFormat); + NameIDFormat transientFormat = + (NameIDFormat) BuilderFactoryUtil.buildXmlObject(NameIDFormat.DEFAULT_ELEMENT_NAME); + transientFormat.setFormat(SamlNameIdFormat.TRANSIENT.getNameIdFormat()); + ssoDescriptor.getNameIDFormats().add(transientFormat); + NameIDFormat unspecifiedFormat = + (NameIDFormat) BuilderFactoryUtil.buildXmlObject(NameIDFormat.DEFAULT_ELEMENT_NAME); + unspecifiedFormat.setFormat(SamlNameIdFormat.UNSPECIFIED.getNameIdFormat()); + ssoDescriptor.getNameIDFormats().add(unspecifiedFormat); + } + + private void generateIDPSSODescriptor(final EntityDescriptor entityDescriptor, + final X509KeyInfoGeneratorFactory keyInfoGeneratorFactory) + throws org.opensaml.xml.security.SecurityException, IllegalAccessException, NoSuchFieldException, + SAMLEngineException, EIDASSAMLEngineException { + //the node has IDP role + idpSSODescriptor.setWantAuthnRequestsSigned(true); + idpSSODescriptor.setID(spSSODescriptor == null ? params.getEntityID() + : ("IDP" + params.getEntityID())); + if (params.getIDPSignature() != null) { + idpSSODescriptor.setSignature(params.getIDPSignature()); + } + if (params.getIdpSigningCredential() != null) { + idpSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getIdpSigningCredential(), UsageType.SIGNING)); + } else if (params.getSigningCredential() != null) { + idpSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getSigningCredential(), UsageType.SIGNING)); + } + if (params.getIdpEncryptionCredential() != null) { + idpSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getIdpEncryptionCredential(), + UsageType.ENCRYPTION)); + } else if (params.getEncryptionCredential() != null) { + idpSSODescriptor.getKeyDescriptors() + .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getEncryptionCredential(), UsageType.ENCRYPTION)); + } + idpSSODescriptor.addSupportedProtocol(params.getIdpSamlProtocol()); + fillNameIDFormat(idpSSODescriptor); + if (params.getIdpEngine() != null) { + if (params.getIdpEngine().getProtocolProcessor() != null + && params.getIdpEngine().getProtocolProcessor().getFormat() == SAMLExtensionFormat.EIDAS10) { + + /*TODO: Only a work-around to add eIDAS attributes, which could be provided from MOA-ID, to IDP metadata + * If we restrict the eIDAS Engine attribute definitions then also additional incoming attributes can not processed any more. + * + * INFO: Maybe, this code can be removed in a future version of the eIDAS engine + */ + generateSupportedAttributes(idpSSODescriptor, getAllSupportedAttributes()); + } + ProtocolEngineI idpEngine = params.getIdpEngine(); + ((MetadataSignerI) idpEngine.getSigner()).signMetadata(idpSSODescriptor); + } + + idpSSODescriptor.getSingleSignOnServices().addAll(buildSingleSignOnServicesBindingLocations()); + + entityDescriptor.getRoleDescriptors().add(idpSSODescriptor); + + } + + /*TODO: Only a work-around to add eIDAS attributes, which could be provided from MOA-ID, to IDP metadata + * If we restrict the eIDAS Engine attribute definitions then also additional incoming attributes can not processed any more. + */ + public ImmutableSortedSet<AttributeDefinition<?>> getAllSupportedAttributes() { + ImmutableSortedSet.Builder<AttributeDefinition<?>> builder = + new ImmutableSortedSet.Builder<>(Ordering.<AttributeDefinition<?>>natural()); + builder.addAll(Constants.MOA_IDP_ATTR_REGISTRY.getAttributes()); + return builder.build(); + } + + private ArrayList<SingleSignOnService> buildSingleSignOnServicesBindingLocations() + throws NoSuchFieldException, IllegalAccessException { + ArrayList<SingleSignOnService> singleSignOnServices = new ArrayList<SingleSignOnService>(); + + HashMap<String, String> bindingLocations = params.getProtocolBindingLocation(); + for (String binding : bindingLocations.keySet()) { + SingleSignOnService ssos = BuilderFactoryUtil.buildXmlObject(SingleSignOnService.class); + ssos.setBinding(binding); + ssos.setLocation(bindingLocations.get(binding)); + singleSignOnServices.add(ssos); + } + + return singleSignOnServices; + } + + /** + * @param metadata + * @return an EntityDescriptor parsed from the given String or null + */ + // TODO (commented by donydgr) Move to a eu.eidas.auth.engine.metadata.MetadataUtil ? Throw an exception if the metadata is invalid instead of returning null ? + public static EntityDescriptorContainer deserializeEntityDescriptor(String metadata) { + EntityDescriptorContainer result = new EntityDescriptorContainer(); + try { + byte[] metaDataBytes = EidasStringUtil.getBytes(metadata); + XMLObject obj = OpenSamlHelper.unmarshall(metaDataBytes); + if (obj instanceof EntityDescriptor) { + result.addEntityDescriptor((EntityDescriptor) obj, metaDataBytes); + } else if (obj instanceof EntitiesDescriptor) { + EntitiesDescriptor ed = (EntitiesDescriptor) obj; + result.setEntitiesDescriptor(ed); + result.getEntityDescriptors().addAll(((EntitiesDescriptor) obj).getEntityDescriptors()); + result.setSerializedEntitesDescriptor(metaDataBytes); + } + } catch (UnmarshallException ue) { + LOGGER.info("ERROR : unmarshalling error", ue.getMessage()); + LOGGER.debug("ERROR : unmarshalling error", ue); + } + return result; + } + + private KeyDescriptor getKeyDescriptor(X509KeyInfoGeneratorFactory keyInfoGeneratorFactory, + Credential credential, + UsageType usage) + throws NoSuchFieldException, IllegalAccessException, SecurityException, EIDASSAMLEngineException { + KeyDescriptor keyDescriptor = null; + if (credential != null) { + keyDescriptor = BuilderFactoryUtil.buildXmlObject(KeyDescriptor.class); + KeyInfoGenerator keyInfoGenerator = keyInfoGeneratorFactory.newInstance(); + + KeyInfo keyInfo = keyInfoGenerator.generate(credential); + keyDescriptor.setUse(usage); + keyDescriptor.setKeyInfo(keyInfo); + if (usage == UsageType.ENCRYPTION && params.getEncryptionAlgorithms() != null) { + Set<String> encryptionAlgos = EIDASUtil.parseSemicolonSeparatedList(params.getEncryptionAlgorithms()); + for (String encryptionAlgo : encryptionAlgos) { + EncryptionMethod em = + (EncryptionMethod) BuilderFactoryUtil.buildXmlObject(EncryptionMethod.DEFAULT_ELEMENT_NAME); + em.setAlgorithm(encryptionAlgo); + keyDescriptor.getEncryptionMethods().add(em); + } + } + + } + return keyDescriptor; + } + + private Organization buildOrganization() { + Organization organization = null; + try { + organization = BuilderFactoryUtil.buildXmlObject(Organization.class); + OrganizationDisplayName odn = BuilderFactoryUtil.buildXmlObject(OrganizationDisplayName.class); + odn.setName(new LocalizedString(params.getCountryName(), "en")); + organization.getDisplayNames().add(odn); + OrganizationURL url = BuilderFactoryUtil.buildXmlObject(OrganizationURL.class); + url.setURL(new LocalizedString(params.getNodeUrl(), "en")); + organization.getURLs().add(url); + } catch (IllegalAccessException iae) { + LOGGER.info("ERROR : error generating the Organization: {}", iae.getMessage()); + LOGGER.debug("ERROR : error generating the Organization: {}", iae); + } catch (NoSuchFieldException nfe) { + LOGGER.info("ERROR : error generating the Organization: {}", nfe.getMessage()); + LOGGER.debug("ERROR : error generating the Organization: {}", nfe); + } + return organization; + } + + private ContactPerson buildContact(ContactPersonTypeEnumeration contactType) { + ContactPerson contact = null; + try { + Contact currentContact = null; + if (contactType == ContactPersonTypeEnumeration.SUPPORT) { + currentContact = params.getSupportContact(); + } else if (contactType == ContactPersonTypeEnumeration.TECHNICAL) { + currentContact = params.getTechnicalContact(); + } else { + LOGGER.error("ERROR: unsupported contact type"); + } + contact = BuilderFactoryUtil.buildXmlObject(ContactPerson.class); + if (currentContact == null) { + LOGGER.error("ERROR: cannot retrieve contact from the configuration"); + return contact; + } + + EmailAddress emailAddressObj = BuilderFactoryUtil.buildXmlObject(EmailAddress.class); + Company company = BuilderFactoryUtil.buildXmlObject(Company.class); + GivenName givenName = BuilderFactoryUtil.buildXmlObject(GivenName.class); + SurName surName = BuilderFactoryUtil.buildXmlObject(SurName.class); + TelephoneNumber phoneNumber = BuilderFactoryUtil.buildXmlObject(TelephoneNumber.class); + contact.setType(contactType); + emailAddressObj.setAddress(currentContact.getEmail()); + company.setName(currentContact.getCompany()); + givenName.setName(currentContact.getGivenName()); + surName.setName(currentContact.getSurName()); + phoneNumber.setNumber(currentContact.getPhone()); + + populateContact(contact, currentContact, emailAddressObj, company, givenName, surName, phoneNumber); + + } catch (IllegalAccessException iae) { + LOGGER.info("ERROR : error generating the Organization: {}", iae.getMessage()); + LOGGER.debug("ERROR : error generating the Organization: {}", iae); + } catch (NoSuchFieldException nfe) { + LOGGER.info("ERROR : error generating the Organization: {}", nfe.getMessage()); + LOGGER.debug("ERROR : error generating the Organization: {}", nfe); + } + return contact; + } + + private void populateContact(ContactPerson contact, + Contact currentContact, + EmailAddress emailAddressObj, + Company company, + GivenName givenName, + SurName surName, + TelephoneNumber phoneNumber) { + if (!StringUtils.isEmpty(currentContact.getEmail())) { + contact.getEmailAddresses().add(emailAddressObj); + } + if (!StringUtils.isEmpty(currentContact.getCompany())) { + contact.setCompany(company); + } + if (!StringUtils.isEmpty(currentContact.getGivenName())) { + contact.setGivenName(givenName); + } + if (!StringUtils.isEmpty(currentContact.getSurName())) { + contact.setSurName(surName); + } + if (!StringUtils.isEmpty(currentContact.getPhone())) { + contact.getTelephoneNumbers().add(phoneNumber); + } + + } + + /** + * @param engine a EIDASSamlEngine from which signing and encryption information is extracted + */ + + public void initialize(ProtocolEngineI engine) throws EIDASSAMLEngineException { + + X509Certificate decryptionCertificate = engine.getDecryptionCertificate(); + if (null != decryptionCertificate) { + params.setEncryptionCredential(CertificateUtil.toCredential(decryptionCertificate)); + } + params.setSigningCredential(CertificateUtil.toCredential(engine.getSigningCertificate())); + params.setIdpEngine(engine); + params.setSpEngine(engine); + } + + /** + * @param spEngine a EIDASSamlEngine for the + */ + + public void initialize(ProtocolEngineI spEngine, ProtocolEngineI idpEngine) throws EIDASSAMLEngineException { + if (idpEngine != null) { + idpEngine.getProtocolProcessor().configure(); + params.setIdpSigningCredential(CertificateUtil.toCredential(idpEngine.getSigningCertificate())); + + final X509Certificate idpEngineDecryptionCertificate = idpEngine.getDecryptionCertificate(); + if (idpEngineDecryptionCertificate != null) { + params.setIdpEncryptionCredential(CertificateUtil.toCredential(idpEngineDecryptionCertificate)); + } + + } + if (spEngine != null) { + spEngine.getProtocolProcessor().configure(); + params.setSpSigningCredential(CertificateUtil.toCredential(spEngine.getSigningCertificate())); + + final X509Certificate spEngineDecryptionCertificate = spEngine.getDecryptionCertificate(); + if (spEngineDecryptionCertificate != null) { + params.setSpEncryptionCredential(CertificateUtil.toCredential(spEngineDecryptionCertificate)); + } + } + + params.setIdpEngine(idpEngine); + params.setSpEngine(spEngine); + } + + public void addSPRole() throws EIDASSAMLEngineException { + try { + if (spSSODescriptor == null) { + spSSODescriptor = BuilderFactoryUtil.buildXmlObject(SPSSODescriptor.class); + } + } catch (IllegalAccessException iae) { + throw new EIDASSAMLEngineException(iae); + } catch (NoSuchFieldException nsfe) { + throw new EIDASSAMLEngineException(nsfe); + } + } + + public void addIDPRole() throws EIDASSAMLEngineException { + try { + if (idpSSODescriptor == null) { + idpSSODescriptor = BuilderFactoryUtil.buildXmlObject(IDPSSODescriptor.class); + } + } catch (IllegalAccessException iae) { + throw new EIDASSAMLEngineException(iae); + } catch (NoSuchFieldException nsfe) { + throw new EIDASSAMLEngineException(nsfe); + } + } + + private void generateDigest(Extensions eidasExtensions) throws EIDASSAMLEngineException { + if (!StringUtils.isEmpty(params.getDigestMethods())) { + Set<String> signatureMethods = EIDASUtil.parseSemicolonSeparatedList(params.getDigestMethods()); + Set<String> digestMethods = new HashSet<String>(); + for (String signatureMethod : signatureMethods) { + digestMethods.add(CertificateUtil.validateDigestAlgorithm(signatureMethod)); + } + for (String digestMethod : digestMethods) { + final DigestMethod dm = (DigestMethod) BuilderFactoryUtil.buildXmlObject(DigestMethod.DEF_ELEMENT_NAME); + if (dm != null) { + dm.setAlgorithm(digestMethod); + eidasExtensions.getUnknownXMLObjects().add(dm); + } else { + LOGGER.info("BUSINESS EXCEPTION error adding DigestMethod extension"); + } + } + } + + } + + private Extensions generateExtensions() throws EIDASSAMLEngineException { + Extensions eidasExtensions = BuilderFactoryUtil.generateExtension(); + if (params.getAssuranceLevel() != null) { + generateLoA(eidasExtensions); + } + if (!StringUtils.isEmpty(params.getSpType())) { + final SPType spTypeObj = (SPType) BuilderFactoryUtil.buildXmlObject(SPType.DEF_ELEMENT_NAME); + if (spTypeObj != null) { + spTypeObj.setSPType(params.getSpType()); + eidasExtensions.getUnknownXMLObjects().add(spTypeObj); + } else { + LOGGER.info("BUSINESS EXCEPTION error adding SPType extension"); + } + } + generateDigest(eidasExtensions); + + if (!StringUtils.isEmpty(params.getSigningMethods())) { + Set<String> signMethods = EIDASUtil.parseSemicolonSeparatedList(params.getDigestMethods()); + for (String signMethod : signMethods) { + final SigningMethod sm = + (SigningMethod) BuilderFactoryUtil.buildXmlObject(SigningMethod.DEF_ELEMENT_NAME); + if (sm != null) { + sm.setAlgorithm(signMethod); + eidasExtensions.getUnknownXMLObjects().add(sm); + } else { + LOGGER.info("BUSINESS EXCEPTION error adding SigningMethod extension"); + } + } + } + return eidasExtensions; + } + + private void generateLoA(Extensions eidasExtensions) throws EIDASSAMLEngineException { + EntityAttributes loa = + (EntityAttributes) BuilderFactoryUtil.buildXmlObject(EntityAttributes.DEFAULT_ELEMENT_NAME); + Attribute loaAttrib = (Attribute) BuilderFactoryUtil.buildXmlObject(Attribute.DEFAULT_ELEMENT_NAME); + loaAttrib.setName(EidasConstants.LEVEL_OF_ASSURANCE_NAME); + loaAttrib.setNameFormat(Attribute.URI_REFERENCE); + XSStringBuilder stringBuilder = + (XSStringBuilder) Configuration.getBuilderFactory().getBuilder(XSString.TYPE_NAME); + XSString stringValue = stringBuilder.buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, XSString.TYPE_NAME); + stringValue.setValue(params.getAssuranceLevel()); + loaAttrib.getAttributeValues().add(stringValue); + loa.getAttributes().add(loaAttrib); + eidasExtensions.getUnknownXMLObjects().add(loa); + + } + + private static final Set<String> DEFAULT_BINDING = new HashSet<String>() {{ + this.add(SAMLConstants.SAML2_POST_BINDING_URI); + }}; + + private void addAssertionConsumerService() throws EIDASSAMLEngineException { + int index = 0; + Set<String> bindings = params.getProtocolBinding().isEmpty() ? DEFAULT_BINDING : params.getProtocolBinding(); + for (String binding : bindings) { + AssertionConsumerService asc = (AssertionConsumerService) BuilderFactoryUtil.buildXmlObject( + AssertionConsumerService.DEFAULT_ELEMENT_NAME); + asc.setLocation(params.getAssertionConsumerUrl()); + asc.setBinding(checkBinding(binding)); + asc.setIndex(index); + if (index == 0) { + asc.setIsDefault(true); + } + index++; + spSSODescriptor.getAssertionConsumerServices().add(asc); + } + } + + private String checkBinding(String binding) { + if (binding != null && (binding.equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI) || binding.equals( + SAMLConstants.SAML2_POST_BINDING_URI))) { + return binding; + } + return SAMLConstants.SAML2_POST_BINDING_URI; + } + + private DateTime getExpireDate() { + DateTime expiryDate = DateTime.now(); + expiryDate = + expiryDate.withFieldAdded(DurationFieldType.seconds(), (int) (getConfigParams().getValidityDuration())); + return expiryDate; + } + + private void generateSupportedAttributes(IDPSSODescriptor idpssoDescriptor, + ImmutableSortedSet<AttributeDefinition<?>> attributeDefinitions) + throws EIDASSAMLEngineException { + List<Attribute> attributes = idpssoDescriptor.getAttributes(); + for (AttributeDefinition<?> attributeDefinition : attributeDefinitions) { + Attribute a = (Attribute) BuilderFactoryUtil.buildXmlObject(Attribute.DEFAULT_ELEMENT_NAME); + a.setName(attributeDefinition.getNameUri().toASCIIString()); + a.setFriendlyName(attributeDefinition.getFriendlyName()); + a.setNameFormat(Attribute.URI_REFERENCE); + attributes.add(a); + } + } + + public MetadataConfigParams getConfigParams() { + return params; + } + + public void setConfigParams(MetadataConfigParams params) { + this.params = params; + } + +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/SAMLEngineUtils.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/SAMLEngineUtils.java index eeb8305cf..eb50c113f 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/SAMLEngineUtils.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/SAMLEngineUtils.java @@ -22,15 +22,27 @@ */ package at.gv.egovernment.moa.id.auth.modules.eidas.utils; +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import org.opensaml.xml.ConfigurationException; +import org.opensaml.xml.XMLConfigurator; + import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; import at.gv.egovernment.moa.id.auth.modules.eidas.config.MOAIDCertificateManagerConfigurationImpl; +import at.gv.egovernment.moa.id.auth.modules.eidas.config.MOASWSigner; +import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAEidasProtocolProcesser; import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDASChainingMetadataProvider; import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDASMetadataProviderDecorator; -import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDAsExtensionProcessor; import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.EIDASEngineException; import at.gv.egovernment.moa.logging.Logger; -import eu.eidas.auth.engine.EIDASSAMLEngine; -import eu.eidas.auth.engine.core.ExtensionProcessorI; +import eu.eidas.auth.commons.attribute.AttributeDefinition; +import eu.eidas.auth.engine.ProtocolEngineI; +import eu.eidas.auth.engine.SamlEngineSystemClock; +import eu.eidas.auth.engine.metadata.MetadataFetcherI; +import eu.eidas.auth.engine.metadata.MetadataSignerI; +import eu.eidas.auth.engine.xml.opensaml.SAMLBootstrap; import eu.eidas.engine.exceptions.EIDASSAMLEngineException; import eu.eidas.samlengineconfig.CertificateConfigurationManager; @@ -40,31 +52,44 @@ import eu.eidas.samlengineconfig.CertificateConfigurationManager; */ public class SAMLEngineUtils { - private static EIDASSAMLEngine eIDASEngine = null; + private static ProtocolEngineI eIDASEngine = null; + private static MetadataSignerI metadataSigner = null; + private static MetadataFetcherI metadataFetcher = null; + private static Map<String, AttributeDefinition<?>> allSupportedAttributeMap = + new HashMap<String, AttributeDefinition<?>>(); - public static synchronized EIDASSAMLEngine createSAMLEngine() throws EIDASEngineException{ + public static synchronized ProtocolEngineI createSAMLEngine(MOAeIDASChainingMetadataProvider moaeIDASMetadataProvider) throws EIDASEngineException{ if (eIDASEngine == null) { try { //get eIDAS SAMLengine configuration from MOA-ID configuration CertificateConfigurationManager configManager = new MOAIDCertificateManagerConfigurationImpl(); + + //set metadata management to eIDAS SAMLengine + metadataFetcher = new MOAeIDASMetadataProviderDecorator(moaeIDASMetadataProvider); + + //set metadata signer + metadataSigner = new MOASWSigner(configManager); + + //build eIDAS SAML eninge + ProtocolEngineI engine = MOAProtocolEngineFactory.createProtocolEngine( + Constants.eIDAS_SAML_ENGINE_NAME, + configManager, + new MOAEidasProtocolProcesser(metadataFetcher, metadataSigner), + new SamlEngineSystemClock()); + + //build a map with all actually supported attributes + for (AttributeDefinition<?> el : engine.getProtocolProcessor().getAllSupportedAttributes()) + allSupportedAttributeMap.put(el.getFriendlyName(), el); + + //TODO: check if bug is fixed in next eIDAS SAML-engine version + //overwrite eIDAS response validator suite because Condition-Valitator has not time jitter + initOpenSAMLConfig("own-saml-eidasnode-config.xml"); - //initial eIDAS SAMLengine - EIDASSAMLEngine engine = EIDASSAMLEngine.createSAMLEngine(Constants.eIDAS_SAML_ENGINE_NAME, - configManager); - - //set metadata management to eIDAS SAMLengine - engine.setMetadataProcessor( - new MOAeIDASMetadataProviderDecorator( - MOAeIDASChainingMetadataProvider.getInstance())); - - //set MOA specific extension processor - ExtensionProcessorI extensionProcessor = new MOAeIDAsExtensionProcessor(); - engine.setExtensionProcessor(extensionProcessor); eIDASEngine = engine; - } catch (EIDASSAMLEngineException e) { + } catch (EIDASSAMLEngineException | ConfigurationException e) { Logger.error("eIDAS SAMLengine initialization FAILED!", e); throw new EIDASEngineException("eIDAS.00", new Object[]{e.getMessage()}, e); @@ -73,5 +98,51 @@ public class SAMLEngineUtils { return eIDASEngine; } + + /** + * Get a map of all eIDAS attributes, which are actually supported by eIDAS engine + * + * @return Map<Attr. FriendlyName, AttributeDefinition> + */ + public static Map<String, AttributeDefinition<?>> getMapOfAllAvailableAttributes() { + return allSupportedAttributeMap; + + } + + /** + * @return the metadataSigner + */ + public static MetadataSignerI getMetadataSigner() { + if (eIDASEngine != null) + return metadataSigner; + + else { + Logger.error("eIDAS SAMLEngine is not initialized."); + return null; + + } + } + + /** + * @return the metadataFetcher + */ + public static MetadataFetcherI getMetadataFetcher() { + if (eIDASEngine != null) + return metadataFetcher; + + else { + Logger.error("eIDAS SAMLEngine is not initialized."); + return null; + + } + } + + private static void initOpenSAMLConfig(String xmlConfig) throws ConfigurationException { + XMLConfigurator configurator = new XMLConfigurator(); + InputStream is = SAMLBootstrap.class.getClassLoader().getResourceAsStream(xmlConfig); + configurator.load(is); + + } + } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/SimpleEidasAttributeGenerator.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/SimpleEidasAttributeGenerator.java new file mode 100644 index 000000000..d43fa1622 --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/SimpleEidasAttributeGenerator.java @@ -0,0 +1,68 @@ +/* + * 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 at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator; + +/** + * @author tlenz + * + */ +public class SimpleEidasAttributeGenerator implements IAttributeGenerator<String> { + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator#buildStringAttribute(java.lang.String, java.lang.String, java.lang.String) + */ + @Override + public String buildStringAttribute(String friendlyName, String name, String value) { + return value; + + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator#buildIntegerAttribute(java.lang.String, java.lang.String, int) + */ + @Override + public String buildIntegerAttribute(String friendlyName, String name, int value) { + return String.valueOf(value); + + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator#buildLongAttribute(java.lang.String, java.lang.String, long) + */ + @Override + public String buildLongAttribute(String friendlyName, String name, long value) { + return String.valueOf(value); + + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator#buildEmptyAttribute(java.lang.String, java.lang.String) + */ + @Override + public String buildEmptyAttribute(String friendlyName, String name) { + return null; + } + +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASData.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASData.java index 563c3a18c..7647b4cab 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASData.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASData.java @@ -2,13 +2,14 @@ package at.gv.egovernment.moa.id.protocols.eidas; import java.util.Collection; +import org.opensaml.saml2.metadata.provider.MetadataProvider; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; -import at.gv.egovernment.moa.id.auth.modules.eidas.utils.MOAPersonalAttributeList; import at.gv.egovernment.moa.id.moduls.RequestImpl; -import eu.eidas.auth.commons.EIDASAuthnRequest; +import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; +import eu.eidas.auth.commons.protocol.IAuthenticationRequest; @Component("EIDASData") @Scope(value = BeanDefinition.SCOPE_PROTOTYPE) @@ -18,10 +19,10 @@ public class EIDASData extends RequestImpl { private static final long serialVersionUID = 8765755670214923910L; /** The attributes requested by the eIDaS. */ - private MOAPersonalAttributeList attributes; + private ImmutableAttributeMap attributes; /** The incoming eIDaS SAML2 AuthnRequest. */ - private EIDASAuthnRequest authnRequest; + private IAuthenticationRequest authnRequest; /** The ip address of the requester. */ private String remoteIPAddress; @@ -29,7 +30,7 @@ public class EIDASData extends RequestImpl { private String remoteRelayState; @Override - public Collection<String> getRequestedAttributes() { + public Collection<String> getRequestedAttributes(MetadataProvider metadataProvider) { // TODO Auto-generated method stub return null; } @@ -39,17 +40,17 @@ public class EIDASData extends RequestImpl { * * @return the requested attributes */ - public MOAPersonalAttributeList getEidasRequestedAttributes() { - return (MOAPersonalAttributeList) attributes.clone(); + public ImmutableAttributeMap getEidasRequestedAttributes() { + return attributes; } /** * Sets the eidas requested attributes. * - * @param personalAttributeList the requested attributes + * @param immutableAttributeMap the requested attributes */ - public void setEidasRequestedAttributes(MOAPersonalAttributeList personalAttributeList) { - attributes = personalAttributeList; + public void setEidasRequestedAttributes(ImmutableAttributeMap immutableAttributeMap) { + attributes = immutableAttributeMap; } /** @@ -57,7 +58,7 @@ public class EIDASData extends RequestImpl { * * @return the eidas request */ - public EIDASAuthnRequest getEidasRequest() { + public IAuthenticationRequest getEidasRequest() { return authnRequest; } @@ -66,7 +67,7 @@ public class EIDASData extends RequestImpl { * * @param request the new eidas request */ - public void setEidasRequest(EIDASAuthnRequest request) { + public void setEidasRequest(IAuthenticationRequest request) { authnRequest = request; } 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 779d898be..13e64cdd0 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 @@ -34,6 +34,8 @@ import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; import org.opensaml.saml2.core.StatusCode; import org.opensaml.saml2.metadata.AssertionConsumerService; +import org.opensaml.saml2.metadata.EntityDescriptor; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @@ -44,11 +46,11 @@ 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.engine.MOAeIDASMetadataProviderDecorator; -import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.eIDASAuthnRequestProcessingException; -import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.eIDASAuthnRequestValidationException; -import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.eIDASException; -import at.gv.egovernment.moa.id.auth.modules.eidas.utils.MOAPersonalAttributeList; +import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.EIDASAuthnRequestProcessingException; +import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.EIDASAuthnRequestValidationException; +import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.EIDASException; import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils; +import at.gv.egovernment.moa.id.commons.MOAIDAuthConstants; 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.exceptions.MOAIDException; @@ -56,10 +58,14 @@ import at.gv.egovernment.moa.id.moduls.RequestImpl; import at.gv.egovernment.moa.id.protocols.AbstractAuthProtocolModulController; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MiscUtil; -import eu.eidas.auth.commons.EIDASAuthnRequest; -import eu.eidas.auth.commons.EIDASAuthnResponse; -import eu.eidas.auth.commons.EIDASUtil; -import eu.eidas.auth.engine.EIDASSAMLEngine; +import eu.eidas.auth.commons.EidasStringUtil; +import eu.eidas.auth.commons.protocol.IAuthenticationRequest; +import eu.eidas.auth.commons.protocol.IResponseMessage; +import eu.eidas.auth.commons.protocol.eidas.IEidasAuthenticationRequest; +import eu.eidas.auth.commons.protocol.eidas.impl.EidasAuthenticationRequest; +import eu.eidas.auth.commons.protocol.impl.AuthenticationResponse; +import eu.eidas.auth.commons.protocol.impl.AuthenticationResponse.Builder; +import eu.eidas.auth.engine.ProtocolEngineI; import eu.eidas.auth.engine.metadata.MetadataUtil; import eu.eidas.engine.exceptions.EIDASSAMLEngineException; @@ -72,14 +78,17 @@ import eu.eidas.engine.exceptions.EIDASSAMLEngineException; public class EIDASProtocol extends AbstractAuthProtocolModulController { public static final String NAME = EIDASProtocol.class.getName(); - public static final String PATH = "eidas"; + public static final String PATH = "eidas"; + @Autowired(required=true) MOAeIDASChainingMetadataProvider eIDASMetadataProvider; + public EIDASProtocol() { super(); Logger.debug("Registering servlet " + getClass().getName() + " with mappings '" + Constants.eIDAS_HTTP_ENDPOINT_METADATA + "' and '" + Constants.eIDAS_HTTP_ENDPOINT_IDP_COLLEAGUEREQUEST + - "' and '" + Constants.eIDAS_HTTP_ENDPOINT_IDP_POST +"'."); + //"' and '" + Constants.eIDAS_HTTP_ENDPOINT_IDP_POST + + "'."); } @@ -164,75 +173,139 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { String base64SamlToken = request.getParameter("SAMLRequest"); if (MiscUtil.isEmpty(base64SamlToken)) { Logger.warn("No eIDAS SAMLRequest found in http request."); - throw new MOAIDException("HTTP request includes no eIDAS SAML-Request element.", null); + throw new MOAIDException("eIDAS.06", new Object[]{"HTTP request includes no eIDAS SAML-Request element."}); } - byte[] decSamlToken = EIDASUtil.decodeSAMLToken(base64SamlToken); - + try { + //decode SAML2 token + byte[] decSamlToken = EidasStringUtil.decodeBytesFromBase64(base64SamlToken); + //get eIDAS SAML-engine - EIDASSAMLEngine engine = SAMLEngineUtils.createSAMLEngine(); - - //validate SAML token - EIDASAuthnRequest samlReq = engine.validateEIDASAuthnRequest(decSamlToken); - - // - memorize remote ip - pendingReq.setRemoteAddress(request.getRemoteAddr()); + ProtocolEngineI engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); - // - 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.getCountry()); + String cititzenCountryCode = + authConfig.getBasicMOAIDConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_COUNTRYCODE, + MOAIDAuthConstants.COUNTRYCODE_AUSTRIA); - // - memorize requested attributes - pendingReq.setEidasRequestedAttributes(new MOAPersonalAttributeList(samlReq.getPersonalAttributeList())); + + //**************************************** + //***** validate eIDAS request ********* + //**************************************** + //validate SAML token + IAuthenticationRequest samlReq = engine.unmarshallRequestAndValidate(decSamlToken, cititzenCountryCode ); - // - memorize whole request - samlReq.setPersonalAttributeList(pendingReq.getEidasRequestedAttributes()); // circumvent non-serializable eidas personal attribute list - pendingReq.setEidasRequest(samlReq); + //validate internal JAVA class type + if (!(samlReq instanceof IEidasAuthenticationRequest)) { + Logger.error("eIDAS AuthnRequst from node:" + samlReq.getIssuer() + + " is NOT from Type:" + IEidasAuthenticationRequest.class.getName()); + throw new MOAIDException("eIDAS.06", new Object[]{"eIDAS AuthnRequest maps to an wrong internal Type."}); + + } + IEidasAuthenticationRequest eIDASSamlReq = (IEidasAuthenticationRequest) samlReq; + + //validate Destination against MOA-ID-Auth configuration + String reqDestination = eIDASSamlReq.getDestination(); + if (MiscUtil.isEmpty(reqDestination) || + !reqDestination.startsWith(pendingReq.getAuthURL())) { + Logger.info("eIDAS AuthnRequest contains a not valid 'Destination' attribute"); + throw new EIDASAuthnRequestValidationException("stork.01", + new Object[]{"eIDAS AuthnRequest contains a not valid 'Destination' attribute"}); + + } + + //validate AssertionConsumerServiceURL against metadata + EntityDescriptor eIDASNodeEntityDesc = new MOAeIDASMetadataProviderDecorator(eIDASMetadataProvider) + .getEntityDescriptor(eIDASSamlReq.getIssuer(), SAMLEngineUtils.getMetadataSigner()); - //validate destination against metadata - String reqDestination = samlReq.getDestination(); - if (MiscUtil.isNotEmpty(reqDestination)) { - boolean isValid = false; - List<AssertionConsumerService> allowedAssertionConsumerUrl = new MOAeIDASMetadataProviderDecorator(MOAeIDASChainingMetadataProvider.getInstance()) - .getSPSSODescriptor(samlReq.getIssuer()).getAssertionConsumerServices(); + String reqAssertionConsumerServiceURL = eIDASSamlReq.getAssertionConsumerServiceURL(); + if (MiscUtil.isNotEmpty(reqAssertionConsumerServiceURL)) { + boolean isValid = false; + List<AssertionConsumerService> allowedAssertionConsumerUrl = + MetadataUtil.getSPSSODescriptor(eIDASNodeEntityDesc).getAssertionConsumerServices(); for (AssertionConsumerService el : allowedAssertionConsumerUrl) { - if (reqDestination.equals(el.getLocation())) + if (reqAssertionConsumerServiceURL.equals(el.getLocation())) isValid = true; } if (!isValid) { - Logger.info("eIDAS AuthnRequest contains a not valid 'Destination' attribute"); - throw new eIDASAuthnRequestValidationException("stork.01", - new Object[]{"eIDAS AuthnRequest contains a not valid 'Destination' attribute"}); + Logger.info("eIDAS AuthnRequest contains a not valid 'AssertionConsumerServiceURL' attribute"); + throw new EIDASAuthnRequestValidationException("eIDAS.12", + new Object[]{"eIDAS AuthnRequest contains a not valid 'AssertionConsumerServiceURL' attribute"}); } - } + } else { + /*TODO: eIDAS SAMLEngine 1.1.0 does not validate and set AssertionConsumerServiceURL in a correct form + * + * Actually, this step is required because EidasProtocolProcesser.class only use the AssertionConsumerServiceURL + * from AuthnRequest to set the 'Destination' attribute in eIDAS Response. However, the AssertionConsumerServiceURL + * could be empty in Request, which break the Response building process. + */ + String assertionConsumerServiceURL = MetadataUtil.getAssertionConsumerUrlFromMetadata( + SAMLEngineUtils.getMetadataFetcher(), SAMLEngineUtils.getMetadataSigner(), eIDASSamlReq); + if (MiscUtil.isEmpty(assertionConsumerServiceURL)) { + Logger.error("eIDAS metadata for node:" + eIDASSamlReq.getIssuer() + + " contains NO 'AssertionConsumerServiceURL' element!"); + throw new EIDASSAMLEngineException("eIDAS metadata for node:" + eIDASSamlReq.getIssuer() + + " contains NO 'AssertionConsumerServiceURL' element!"); + + } + + EidasAuthenticationRequest.Builder test = EidasAuthenticationRequest.builder(eIDASSamlReq); + test.assertionConsumerServiceURL(assertionConsumerServiceURL); + eIDASSamlReq = test.build(); + + } + + //************************************************* + //***** store eIDAS request information ********* + //************************************************* + // - memorize remote ip + pendingReq.setRemoteAddress(request.getRemoteAddr()); + + // - 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, + eIDASSamlReq.getEidasLevelOfAssurance().stringValue()); + + // - memorize requested attributes + pendingReq.setEidasRequestedAttributes(eIDASSamlReq.getRequestedAttributes()); + + // - memorize whole request + pendingReq.setEidasRequest(eIDASSamlReq); + // - memorize OA url 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()}); + throw new EIDASAuthnRequestProcessingException("eIDAS.08", new Object[]{pendingReq.getOAURL()}); pendingReq.setOnlineApplicationConfiguration(oaConfig); - - String spType = samlReq.getSPType(); - if (MiscUtil.isEmpty(spType)) { - Logger.info("Load SPType from metadata ... IS NOT IMPLEMENTED YET!!!"); - //TODO: maybe implement this if required + + // - memorize service-provider type from eIDAS request + String spType = null; + if (eIDASSamlReq.getSpType() != null) + spType = eIDASSamlReq.getSpType().getValue(); + + if (MiscUtil.isEmpty(spType)) + spType = MetadataUtil.getSPTypeFromMetadata(eIDASNodeEntityDesc); - } - - Logger.debug("eIDAS request has SPType:" + spType); + if (MiscUtil.isEmpty(spType)) + Logger.debug("eIDAS request has SPType:" + spType); + else + Logger.info("eIDAS request and eIDAS metadata contains NO 'SPType' element."); } catch (MOAIDException e) { Logger.info("eIDAS AuthnRequest preProcessing FAILED. Msg:" + e.getMessage()); @@ -240,11 +313,11 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { } catch (EIDASSAMLEngineException e) { Logger.info("eIDAS AuthnRequest preProcessing FAILED. Msg:" + e.getMessage()); - throw new eIDASAuthnRequestProcessingException("eIDAS.06", new Object[]{e.getMessage()}, e); + throw new EIDASAuthnRequestProcessingException("eIDAS.06", new Object[]{e.getMessage()}, e); } catch(Exception e) { Logger.warn("eIDAS AuthnRequest preProcessing FAILED. Msg:" + e.getMessage(), e); - throw new eIDASAuthnRequestProcessingException("eIDAS.06", new Object[]{e.getMessage()}, e); + throw new EIDASAuthnRequestProcessingException("eIDAS.06", new Object[]{e.getMessage()}, e); } } @@ -258,43 +331,40 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { } try { - EIDASAuthnResponse eIDASResp = new EIDASAuthnResponse(); - eIDASResp.setIssuer(pendingReq.getAuthURL() + Constants.eIDAS_HTTP_ENDPOINT_METADATA); - - if (e instanceof eIDASException) { - eIDASResp.setStatusCode(((eIDASException) e).getStatusCodeFirstLevel()); - eIDASResp.setSubStatusCode(((eIDASException) e).getStatusCodeSecondLevel()); - eIDASResp.setMessage(e.getMessage()); + Builder eIDASRespBuilder = new AuthenticationResponse.Builder(); + eIDASRespBuilder.issuer(pendingReq.getAuthURL() + Constants.eIDAS_HTTP_ENDPOINT_METADATA); + + if (e instanceof EIDASException) { + eIDASRespBuilder.statusCode(((EIDASException) e).getStatusCodeFirstLevel()); + eIDASRespBuilder.subStatusCode(((EIDASException) e).getStatusCodeSecondLevel()); + eIDASRespBuilder.statusMessage(e.getMessage()); } else if (e instanceof MOAIDException ) { - eIDASResp.setStatusCode(StatusCode.RESPONDER_URI); - eIDASResp.setSubStatusCode(StatusCode.AUTHN_FAILED_URI); - eIDASResp.setMessage(e.getMessage()); + eIDASRespBuilder.statusCode(StatusCode.RESPONDER_URI); + eIDASRespBuilder.subStatusCode(StatusCode.AUTHN_FAILED_URI); + eIDASRespBuilder.statusMessage(e.getMessage()); } else { - eIDASResp.setStatusCode(StatusCode.RESPONDER_URI); - eIDASResp.setSubStatusCode(StatusCode.AUTHN_FAILED_URI); - eIDASResp.setMessage(e.getMessage()); + eIDASRespBuilder.statusCode(StatusCode.RESPONDER_URI); + eIDASRespBuilder.subStatusCode(StatusCode.AUTHN_FAILED_URI); + eIDASRespBuilder.statusMessage(e.getMessage()); } - + + eIDASRespBuilder.id(eu.eidas.auth.engine.xml.opensaml.SAMLEngineUtils.generateNCName()); + eIDASRespBuilder.inResponseTo(eidasReq.getEidasRequest().getId()); - EIDASSAMLEngine engine = SAMLEngineUtils.createSAMLEngine(); + //build response + AuthenticationResponse eIDASResp = eIDASRespBuilder.build(); - if(null == eidasReq.getEidasRequest().getAssertionConsumerServiceURL()) { - String assertionConsumerUrl = MetadataUtil.getAssertionUrlFromMetadata( - new MOAeIDASMetadataProviderDecorator(MOAeIDASChainingMetadataProvider.getInstance()), - engine, - eidasReq.getEidasRequest()); - eidasReq.getEidasRequest().setAssertionConsumerServiceURL(assertionConsumerUrl); - - } //get eIDAS SAML-engine + ProtocolEngineI engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); + + //build response message + IResponseMessage eIDASRespMsg = engine.generateResponseErrorMessage(eidasReq.getEidasRequest(),eIDASResp, eidasReq.getRemoteAddress()); - eIDASResp = engine.generateEIDASAuthnResponseFail(eidasReq.getEidasRequest(), eIDASResp, - eidasReq.getRemoteAddress(), true); - String token = EIDASUtil.encodeSAMLToken(eIDASResp.getTokenSaml()); + String token = EidasStringUtil.encodeToBase64(eIDASRespMsg.getMessageBytes()); VelocityEngine velocityEngine = VelocityProvider.getClassPathVelocityEngine(); Template template = velocityEngine.getTemplate("/resources/templates/stork2_postbinding_template.html"); diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EidasMetaDataRequest.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EidasMetaDataRequest.java index b4db5c83d..174fa2c17 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EidasMetaDataRequest.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EidasMetaDataRequest.java @@ -21,14 +21,20 @@ import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.opensaml.common.xml.SAMLConstants; import org.opensaml.saml2.metadata.ContactPerson; import org.opensaml.saml2.metadata.Organization; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; 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.exceptions.EIDASEngineException; +import at.gv.egovernment.moa.id.auth.modules.eidas.utils.MOAeIDASMetadataGenerator; import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils; +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.IRequest; import at.gv.egovernment.moa.id.commons.api.exceptions.ConfigurationException; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; @@ -37,11 +43,10 @@ import at.gv.egovernment.moa.id.data.SLOInformationInterface; import at.gv.egovernment.moa.id.moduls.IAction; import at.gv.egovernment.moa.id.protocols.pvp2x.config.PVPConfiguration; import at.gv.egovernment.moa.logging.Logger; -import eu.eidas.auth.engine.EIDASSAMLEngine; +import eu.eidas.auth.engine.ProtocolEngineI; import eu.eidas.auth.engine.metadata.Contact; import eu.eidas.auth.engine.metadata.MetadataConfigParams; -import eu.eidas.auth.engine.metadata.MetadataGenerator; -import eu.eidas.engine.exceptions.SAMLEngineException; +import eu.eidas.engine.exceptions.EIDASSAMLEngineException; /** @@ -50,6 +55,9 @@ import eu.eidas.engine.exceptions.SAMLEngineException; @Service("EidasMetaDataRequest") public class EidasMetaDataRequest implements IAction { + @Autowired(required=true) MOAeIDASChainingMetadataProvider eIDASMetadataProvider; + @Autowired(required=true) AuthConfiguration authConfig; + /* (non-Javadoc) * @see at.gv.egovernment.moa.id.moduls.IAction#processRequest(at.gv.egovernment.moa.id.moduls.IRequest, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, at.gv.egovernment.moa.id.data.IAuthData) */ @@ -61,10 +69,10 @@ public class EidasMetaDataRequest implements IAction { try { String pubURLPrefix = req.getAuthURL(); - String metadata_url = pubURLPrefix + Constants.eIDAS_HTTP_ENDPOINT_METADATA; + String metadata_url = pubURLPrefix + Constants.eIDAS_HTTP_ENDPOINT_METADATA; + String sp_return_url = pubURLPrefix + Constants.eIDAS_HTTP_ENDPOINT_SP_POST; - String sp_return_url = pubURLPrefix + Constants.eIDAS_HTTP_ENDPOINT_SP_POST; - String metaData = generateMetadata(metadata_url, sp_return_url); + String metaData = generateMetadata(req, metadata_url, sp_return_url); Logger.trace(metaData); @@ -100,24 +108,30 @@ public class EidasMetaDataRequest implements IAction { } - public String generateMetadata(String metadata_url, String sp_return_url) throws SAMLEngineException, EIDASEngineException{ + public String generateMetadata(IRequest pendingReq, String metadata_url, String sp_return_url) throws EIDASSAMLEngineException, EIDASEngineException{ String metadata="invalid metadata"; - EIDASSAMLEngine engine = SAMLEngineUtils.createSAMLEngine(); + ProtocolEngineI engine = SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); - MetadataGenerator generator = new MetadataGenerator(); + MOAeIDASMetadataGenerator generator = new MOAeIDASMetadataGenerator(); MetadataConfigParams mcp=new MetadataConfigParams(); generator.setConfigParams(mcp); generator.initialize(engine); mcp.setEntityID(metadata_url); mcp.setAssertionConsumerUrl(sp_return_url); + mcp.getProtocolBindingLocation().put( + SAMLConstants.SAML2_POST_BINDING_URI, + pendingReq.getAuthURL() + Constants.eIDAS_HTTP_ENDPOINT_IDP_COLLEAGUEREQUEST); //TODO: make it configurable mcp.setAuthnRequestsSigned(true); mcp.setWantAssertionsSigned(true); - mcp.setAssuranceLevel("http://eidas.europa.eu/LoA/substantial"); + mcp.setAssuranceLevel( + authConfig.getBasicMOAIDConfiguration( + Constants.CONIG_PROPS_EIDAS_NODE_LoA, + MOAIDAuthConstants.eIDAS_LOA_HIGH)); //must be set in request, because it could be different for every online-application //mcp.setSpType(SPType.DEFAULT_VALUE); @@ -133,18 +147,24 @@ public class EidasMetaDataRequest implements IAction { Contact technicalContact = new Contact(); List<ContactPerson> contacts = PVPConfiguration.getInstance().getIDPContacts(); - if (contacts != null && contacts.size() >= 1) { - technicalContact.setEmail(contacts.get(0).getEmailAddresses().get(0).getAddress()); - technicalContact.setGivenName(contacts.get(0).getGivenName().getName()); - technicalContact.setSurName(contacts.get(0).getSurName().getName()); - technicalContact.setPhone(contacts.get(0).getTelephoneNumbers().get(0).getNumber()); + if (contacts != null && contacts.size() >= 1) { + ContactPerson contact = contacts.get(0); + technicalContact.setGivenName(contact.getGivenName().getName()); + technicalContact.setSurName(contact.getSurName().getName()); + + if (!contact.getEmailAddresses().isEmpty()) + technicalContact.setEmail(contact.getEmailAddresses().get(0).getAddress()); + + if (!contact.getTelephoneNumbers().isEmpty()) + technicalContact.setPhone(contact.getTelephoneNumbers().get(0).getNumber()); + mcp.setTechnicalContact(technicalContact ); } if (pvpOrganisation != null) { mcp.setNodeUrl(pvpOrganisation.getURLs().get(0).getURL().getLocalString()); - mcp.setCountryName("Austria"); + mcp.setCountryName(authConfig.getBasicMOAIDConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_COUNTRY, "Austria")); technicalContact.setCompany(pvpOrganisation.getDisplayNames().get(0).getName().getLocalString()); } 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 ebd4e1e6d..22ac37604 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 @@ -23,8 +23,8 @@ package at.gv.egovernment.moa.id.protocols.eidas; import java.io.StringWriter; +import java.security.MessageDigest; import java.text.SimpleDateFormat; -import java.util.Map.Entry; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -32,29 +32,44 @@ import javax.servlet.http.HttpServletResponse; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; +import org.opensaml.saml2.core.StatusCode; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.stereotype.Service; +import com.google.common.collect.ImmutableSet; + import at.gv.egovernment.moa.id.advancedlogging.MOAReversionLogger; 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.engine.MOAeIDASMetadataProviderDecorator; -import at.gv.egovernment.moa.id.auth.modules.eidas.utils.MOAPersonalAttributeList; -import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils; +import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SimpleEidasAttributeGenerator; 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.moduls.IAction; +import at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator; +import at.gv.egovernment.moa.id.protocols.builder.attributes.MandateLegalPersonFullNameAttributeBuilder; +import at.gv.egovernment.moa.id.protocols.builder.attributes.MandateLegalPersonSourcePinAttributeBuilder; +import at.gv.egovernment.moa.id.protocols.pvp2x.builder.attributes.exceptions.AttributeException; +import at.gv.egovernment.moa.id.util.Random; import at.gv.egovernment.moa.logging.Logger; -import eu.eidas.auth.commons.EIDASAuthnResponse; -import eu.eidas.auth.commons.EIDASStatusCode; -import eu.eidas.auth.commons.EIDASUtil; -import eu.eidas.auth.commons.PersonalAttribute; -import eu.eidas.auth.engine.EIDASSAMLEngine; -import eu.eidas.auth.engine.metadata.MetadataUtil; +import at.gv.egovernment.moa.util.Base64Utils; +import at.gv.egovernment.moa.util.MiscUtil; +import eu.eidas.auth.commons.EidasStringUtil; +import eu.eidas.auth.commons.attribute.AttributeDefinition; +import eu.eidas.auth.commons.attribute.AttributeDefinition.Builder; +import eu.eidas.auth.commons.attribute.AttributeValue; +import eu.eidas.auth.commons.attribute.AttributeValueMarshaller; +import eu.eidas.auth.commons.attribute.AttributeValueMarshallingException; +import eu.eidas.auth.commons.attribute.ImmutableAttributeMap; +import eu.eidas.auth.commons.protocol.IResponseMessage; +import eu.eidas.auth.commons.protocol.impl.AuthenticationResponse; +import eu.eidas.auth.commons.protocol.impl.SamlNameIdFormat; +import eu.eidas.auth.engine.ProtocolEngineI; +import eu.eidas.auth.engine.xml.opensaml.SAMLEngineUtils; /** @@ -67,7 +82,10 @@ import eu.eidas.auth.engine.metadata.MetadataUtil; @Service("eIDASAuthenticationRequest") public class eIDASAuthenticationRequest implements IAction { + private static IAttributeGenerator<String> generator = new SimpleEidasAttributeGenerator(); + @Autowired protected MOAReversionLogger revisionsLogger; + @Autowired(required=true) MOAeIDASChainingMetadataProvider eIDASMetadataProvider; @Override public SLOInformationInterface processRequest(IRequest req, HttpServletRequest httpReq, HttpServletResponse httpResp, IAuthData authData) throws MOAIDException { @@ -78,67 +96,136 @@ public class eIDASAuthenticationRequest implements IAction { throw new MOAIDException("got wrong IRequest type. is: {}, should be: {}", new String[] {req.getClass().toString(), EIDASData.class.toString()}); - // gather attributes - MOAPersonalAttributeList resultingAttributeList = (MOAPersonalAttributeList) eidasRequest.getEidasRequestedAttributes().clone(); + String subjectNameID = null; - for(Entry<String, PersonalAttribute> current : resultingAttributeList.entrySet()) { + //gather attributes + ImmutableAttributeMap reqAttributeList = (ImmutableAttributeMap) eidasRequest.getEidasRequestedAttributes(); + ImmutableAttributeMap.Builder attrMapBuilder = ImmutableAttributeMap.builder(); + + //TODO: if we support more then this minimum required attributes -> redesign to a smoother attribute builder selector + for(AttributeDefinition<?> attr : reqAttributeList.getDefinitions()) { String newValue = ""; - - // TODO make use of proper builder - switch(current.getKey()) { - case Constants.eIDAS_ATTR_DATEOFBIRTH: newValue = new SimpleDateFormat("YYYY-MM-dd").format(authData.getDateOfBirth()); break; - case Constants.eIDAS_ATTR_CURRENTFAMILYNAME: newValue = authData.getFamilyName();break; - case Constants.eIDAS_ATTR_CURRENTGIVENNAME: newValue = authData.getGivenName();break; - - //TODO: change bPK builder !!!!!! - case Constants.eIDAS_ATTR_PERSONALIDENTIFIER: newValue = authData.getBPK(); break; + boolean isUniqueID = false; + try { + switch(attr.getFriendlyName()) { + case Constants.eIDAS_ATTR_DATEOFBIRTH: + newValue = new SimpleDateFormat("YYYY-MM-dd").format(authData.getDateOfBirth()); + break; + case Constants.eIDAS_ATTR_CURRENTFAMILYNAME: + newValue = authData.getFamilyName(); + break; + case Constants.eIDAS_ATTR_CURRENTGIVENNAME: + newValue = authData.getGivenName(); + break; + case Constants.eIDAS_ATTR_PERSONALIDENTIFIER: + newValue = authData.getBPK(); + isUniqueID = true; + + //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: + newValue = new MandateLegalPersonSourcePinAttributeBuilder().build( + req.getOnlineApplicationConfiguration(), authData, generator); + break; + case Constants.eIDAS_ATTR_LEGALNAME: + newValue = new MandateLegalPersonFullNameAttributeBuilder().build( + req.getOnlineApplicationConfiguration(), authData, generator); + break; + + } + + } catch (AttributeException e) { + Logger.debug("Attribute can not generate requested attribute:" + attr.getFriendlyName() + " Reason:" + e.getMessage()); + } - - if("".equals(newValue)) - current.getValue().setStatus(EIDASStatusCode.STATUS_NOT_AVAILABLE.toString()); - else { - current.getValue().getValue().clear(); - current.getValue().getValue().add(newValue); - current.getValue().setStatus(EIDASStatusCode.STATUS_AVAILABLE.toString()); + + if(MiscUtil.isEmpty(newValue)) { + Logger.info("eIDAS Attr:" + attr.getNameUri() + " is not available."); + + } else { + //set uniqueIdentifier attribute, because eIDAS SAMLEngine use this flag to select the + // Subject->NameID value from this attribute + Builder<?> attrBuilder = AttributeDefinition.builder(attr); + attrBuilder.uniqueIdentifier(isUniqueID); + AttributeDefinition<?> returnAttr = attrBuilder.build(); + + //unmarshal attribute value into eIDAS attribute + AttributeValueMarshaller<?> attributeValueMarshaller = returnAttr.getAttributeValueMarshaller(); + ImmutableSet.Builder<AttributeValue<?>> builder = ImmutableSet.builder(); + + AttributeValue<?> attributeValue = null; + try { + attributeValue = attributeValueMarshaller.unmarshal(newValue, false); + builder.add(attributeValue); + + } catch (AttributeValueMarshallingException e) { + throw new IllegalStateException(e); + + } + + //add attribute to Map + attrMapBuilder.put((AttributeDefinition)returnAttr, (ImmutableSet) builder.build()); + } } // construct eIDaS response - EIDASAuthnResponse response = new EIDASAuthnResponse(); - response.setPersonalAttributeList(resultingAttributeList); + AuthenticationResponse.Builder responseBuilder = new AuthenticationResponse.Builder(); + + responseBuilder.id(SAMLEngineUtils.generateNCName()); + responseBuilder.inResponseTo(eidasRequest.getEidasRequest().getId()); - // - create metadata url - String pubURLPrefix = req.getAuthURL(); + String pubURLPrefix = req.getAuthURL(); String metadata_url = pubURLPrefix + Constants.eIDAS_HTTP_ENDPOINT_METADATA; - response.setIssuer(metadata_url); - - response.setAssuranceLevel(authData.getEIDASQAALevel()); + responseBuilder.issuer(metadata_url); + + responseBuilder.levelOfAssurance(authData.getEIDASQAALevel()); + + //add attributes + responseBuilder.attributes(attrMapBuilder.build()); + + //set success statuscode + responseBuilder.statusCode(StatusCode.SUCCESS_URI); + + //build response + AuthenticationResponse response = responseBuilder.build(); String token = null; + IResponseMessage eIDASRespMsg = null; try { - EIDASSAMLEngine engine = SAMLEngineUtils.createSAMLEngine(); + ProtocolEngineI engine = at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils.createSAMLEngine(eIDASMetadataProvider); // encryption is done by the SamlEngine, i.e. by the module we provide in the config // but we need to set the appropriate request issuer - engine.setRequestIssuer(eidasRequest.getEidasRequest().getIssuer()); - + //engine.setRequestIssuer(eidasRequest.getEidasRequest().getIssuer()); - if(null == eidasRequest.getEidasRequest().getAssertionConsumerServiceURL()) { - String assertionConsumerUrl = MetadataUtil.getAssertionUrlFromMetadata( - new MOAeIDASMetadataProviderDecorator(MOAeIDASChainingMetadataProvider.getInstance()), - engine, - eidasRequest.getEidasRequest()); - eidasRequest.getEidasRequest().setAssertionConsumerServiceURL(assertionConsumerUrl); - - } + eIDASRespMsg = engine.generateResponseMessage(eidasRequest.getEidasRequest(), + response, true, eidasRequest.getRemoteAddress()); + +// if(null == eidasRequest.getEidasRequest().getAssertionConsumerServiceURL()) { +// String assertionConsumerUrl = MetadataUtil.getAssertionUrlFromMetadata( +// new MOAeIDASMetadataProviderDecorator(eIDASMetadataProvider), +// engine, +// eidasRequest.getEidasRequest()); +// eidasRequest.getEidasRequest().setAssertionConsumerServiceURL(assertionConsumerUrl); +// +// } - response = engine.generateEIDASAuthnResponse(eidasRequest.getEidasRequest(), response, eidasRequest.getRemoteAddress(), true); +// response = engine.generateEIDASAuthnResponse(eidasRequest.getEidasRequest(), response, eidasRequest.getRemoteAddress(), true); - token = EIDASUtil.encodeSAMLToken(response.getTokenSaml()); + token = EidasStringUtil.encodeToBase64(eIDASRespMsg.getMessageBytes()); + + } catch(Exception e) { + Logger.error("eIDAS Response encoding error." , e); + throw new MOAIDException("eIDAS.13", new Object[]{e.getMessage()}, e); - } catch(Exception e) { - e.printStackTrace(); } revisionsLogger.logEvent(req, Constants.eIDAS_REVERSIONSLOG_IDP_AUTHREQUEST); @@ -172,10 +259,28 @@ public class eIDASAuthenticationRequest implements IAction { } catch (Exception e) { - Logger.error("Velocity error: " + e.getMessage()); + Logger.error("Velocity error: " + e.getMessage()); + throw new MOAIDException("eIDAS.13", new Object[]{e.getMessage()}, e); + } - - return null; + + SLOInformationInterface ssoContainer = null; + try { + ssoContainer = new SLOInformationImpl( + req.getAuthURL(), + eidasRequest.getEidasRequest().getIssuer(), + null, + subjectNameID, + eidasRequest.getEidasRequest().getNameIdFormat(), + EIDASProtocol.NAME); + + } catch (Exception e) { + Logger.error("Can not generate container with SSO information!", e); + + } + + return ssoContainer; + } @Override @@ -189,4 +294,20 @@ public class eIDASAuthenticationRequest implements IAction { } + private String generateTransientNameID(String nameID) { + 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); + + } catch (Exception e) { + Logger.error("Can not generate transient personal identifier!", e); + return null; + + } + + } + } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/resources/moaid_eidas_auth.beans.xml b/id/server/modules/moa-id-module-eIDAS/src/main/resources/moaid_eidas_auth.beans.xml index 5d79d082a..20395f210 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/resources/moaid_eidas_auth.beans.xml +++ b/id/server/modules/moa-id-module-eIDAS/src/main/resources/moaid_eidas_auth.beans.xml @@ -14,6 +14,9 @@ <bean id="EIDASProtocol" class="at.gv.egovernment.moa.id.protocols.eidas.EIDASProtocol"/> + + <bean id="eIDASMetadataProvider" + class="at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDASChainingMetadataProvider"/> <!-- Authentication Process Tasks --> <bean id="GenerateAuthnRequestTask" diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/resources/own-saml-eidasnode-config.xml b/id/server/modules/moa-id-module-eIDAS/src/main/resources/own-saml-eidasnode-config.xml new file mode 100644 index 000000000..856ebd96a --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/resources/own-saml-eidasnode-config.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<XMLTooling xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://www.opensaml.org/xmltooling-config ../../src/schema/xmltooling-config.xsd" + xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" + xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" + xmlns:stork="urn:eu:stork:names:tc:STORK:1.0:assertion" + xmlns:storkp="urn:eu:stork:names:tc:STORK:1.0:protocol" + xmlns:eidas="http://eidas.europa.eu/saml-extensions" + xmlns="http://www.opensaml.org/xmltooling-config"> + +<!-- SAML 2.0 Protocol Object providers --> + <ValidatorSuites> + <!-- SAML 2.0 Schema Validation Rules --> + + <ValidatorSuite id="moaEidasResponseValidatorSuiteId"> + + <Validator qualifiedName="saml2p:Response" + className="eu.eidas.auth.engine.core.validator.eidas.EidasResponseOneAssertionValidator"/> + + <Validator qualifiedName="saml2p:Response" + className="eu.eidas.auth.engine.core.validator.eidas.EidasResponseValidator"/> + + <Validator qualifiedName="saml2:Assertion" + className="eu.eidas.auth.engine.core.validator.eidas.EidasAssertionValidator"/> + + + <Validator qualifiedName="saml2:Conditions" + className="at.gv.egovernment.moa.id.auth.modules.eidas.engine.validation.MoaEidasConditionsValidator"/> + + <Validator qualifiedName="saml2:AuthnStatement" + className="eu.eidas.auth.engine.core.validator.eidas.EidasAuthnStatementValidator"/> + + <Validator qualifiedName="saml2:Attribute" + className="eu.eidas.auth.engine.core.validator.eidas.EidasAttributeValidator"/> + + </ValidatorSuite> + + + </ValidatorSuites> + + +</XMLTooling>
\ No newline at end of file |