diff options
Diffstat (limited to 'id/server/modules/moa-id-module-eIDAS')
29 files changed, 1574 insertions, 265 deletions
diff --git a/id/server/modules/moa-id-module-eIDAS/pom.xml b/id/server/modules/moa-id-module-eIDAS/pom.xml index 174ce40cb..55d02e82a 100644 --- a/id/server/modules/moa-id-module-eIDAS/pom.xml +++ b/id/server/modules/moa-id-module-eIDAS/pom.xml @@ -12,11 +12,11 @@ <properties> <repositoryPath>${basedir}/../../../../repository</repositoryPath> - <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> + <eidas-commons.version>1.2.0</eidas-commons.version> + <eidas-light-commons.version>1.2.0</eidas-light-commons.version> + <eidas-saml-engine.version>1.2.0</eidas-saml-engine.version> + <eidas-encryption.version>1.2.0</eidas-encryption.version> + <eidas-configmodule.version>1.2.0</eidas-configmodule.version> </properties> @@ -166,6 +166,12 @@ <!-- <scope>provided</scope> --> </dependency> + <dependency> + <groupId>com.ibm.icu</groupId> + <artifactId>icu4j</artifactId> + <version>58.2</version> + </dependency> + </dependencies> 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 02c9a8f5d..36323f3a5 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,12 +22,9 @@ */ package at.gv.egovernment.moa.id.auth.modules.eidas; +import org.apache.xml.security.signature.XMLSignature; 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.commons.attribute.AttributeRegistries; -import eu.eidas.auth.commons.attribute.AttributeRegistry; /** * @author tlenz @@ -45,7 +42,8 @@ public class Constants { public static final String eIDAS_SAML_ENGINE_NAME_ID_CLASS = "class"; //default implementations for eIDAS SAML-engine functionality - public static final String SAML_SIGNING_IMPLENTATION = "at.gv.egovernment.moa.id.auth.modules.eidas.config.MOASWSigner"; + //public static final String SAML_SIGNING_IMPLENTATION = "at.gv.egovernment.moa.id.auth.modules.eidas.config.MOASWSigner"; + public static final String SAML_SIGNING_IMPLENTATION = "at.gv.egovernment.moa.id.auth.modules.eidas.config.MOAExtendedSWSigner"; public static final String SAML_ENCRYPTION_IMPLENTATION = "at.gv.egovernment.moa.id.auth.modules.eidas.config.ModifiedEncryptionSW"; //configuration property keys @@ -59,12 +57,16 @@ public class Constants { public static final String CONIG_PROPS_EIDAS_SAMLENGINE_SIGN_CONFIGFILE = CONIG_PROPS_EIDAS_SAMLENGINE_PREFIX + "." + CONIG_PROPS_EIDAS_SAMLENGINE_SIGN + ".config.file"; public static final String CONIG_PROPS_EIDAS_SAMLENGINE_ENC_CONFIGFILE = CONIG_PROPS_EIDAS_SAMLENGINE_PREFIX + "." - + CONIG_PROPS_EIDAS_SAMLENGINE_ENCRYPT + ".config.file"; + + CONIG_PROPS_EIDAS_SAMLENGINE_ENCRYPT + ".config.file"; + public static final String CONIG_PROPS_EIDAS_SAMLENGINE_ATTIONAL_ATTRIBUTE_DEFINITIONS = + CONIG_PROPS_EIDAS_SAMLENGINE_PREFIX + ".attributes.addition.config"; 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"; + public static final String CONIG_PROPS_EIDAS_NODE_LoA = CONIG_PROPS_EIDAS_NODE + ".LoA"; + //timeouts and clock skews @@ -86,8 +88,6 @@ public class Constants { //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_REDIRECT = "/eidas/idp/redirect"; public static final String eIDAS_HTTP_ENDPOINT_IDP_COLLEAGUEREQUEST = "/eidas/ColleagueRequest"; public static final String eIDAS_HTTP_ENDPOINT_METADATA = "/eidas/metadata"; @@ -96,53 +96,23 @@ public class Constants { public static final int eIDAS_REVERSIONSLOG_METADATA = 3400; public static final int eIDAS_REVERSIONSLOG_IDP_AUTHREQUEST = 3401; public static final int eIDAS_REVERSIONSLOG_IDP_AUTHRESPONSE = 3402; - public static final int eIDAS_REVERSIONSLOG_SP_AUTHREQUEST= 3403; - public static final int eIDAS_REVERSIONSLOG_SP_AUTHRESPONSE= 3404; - //metadata constants -// public final static Map<String, EidasAttributesTypes> METADATA_POSSIBLE_ATTRIBUTES = Collections.unmodifiableMap( -// 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 ; + SignatureConstants.ALGO_ID_DIGEST_SHA256 + ";" + + SignatureConstants.ALGO_ID_DIGEST_SHA512 ; public static final String METADATA_ALLOWED_ALG_SIGN = SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256 + ";" + - SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512; + SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512 + ";" + + XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256_MGF1 + ";" + + XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512_MGF1; public static final String METADATA_ALLOWED_ALG_ENCRYPT = EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128_GCM + ";" + EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES192_GCM + ";" + - EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM; + EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256_GCM + ";" + + EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES128 + ";" + + EncryptionConstants.ALGO_ID_BLOCKCIPHER_AES256; } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAExtendedSWSigner.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAExtendedSWSigner.java new file mode 100644 index 000000000..c872bcfb6 --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/config/MOAExtendedSWSigner.java @@ -0,0 +1,479 @@ +/* + * 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.security.KeyStore; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.StringUtils; +import org.apache.xml.security.signature.XMLSignature; +import org.opensaml.Configuration; +import org.opensaml.common.impl.SAMLObjectContentReference; +import org.opensaml.security.SAMLSignatureProfileValidator; +import org.opensaml.xml.io.MarshallingException; +import org.opensaml.xml.security.SecurityConfiguration; +import org.opensaml.xml.security.SecurityException; +import org.opensaml.xml.security.SecurityHelper; +import org.opensaml.xml.security.credential.Credential; +import org.opensaml.xml.security.keyinfo.KeyInfoGenerator; +import org.opensaml.xml.security.keyinfo.KeyInfoGeneratorFactory; +import org.opensaml.xml.security.keyinfo.KeyInfoGeneratorManager; +import org.opensaml.xml.security.keyinfo.NamedKeyInfoGeneratorManager; +import org.opensaml.xml.security.x509.X509Credential; +import org.opensaml.xml.signature.KeyInfo; +import org.opensaml.xml.signature.SignableXMLObject; +import org.opensaml.xml.signature.Signature; +import org.opensaml.xml.signature.SignatureException; +import org.opensaml.xml.signature.SignatureValidator; +import org.opensaml.xml.signature.Signer; +import org.opensaml.xml.validation.ValidationException; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.sun.istack.Nullable; + +import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; +import at.gv.egovernment.moa.id.auth.modules.eidas.utils.MOAWhiteListConfigurator; +import at.gv.egovernment.moa.logging.Logger; +import eu.eidas.auth.commons.EidasErrorKey; +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.configuration.dom.KeyStoreSignatureConfigurator; +import eu.eidas.auth.engine.configuration.dom.SignatureConfiguration; +import eu.eidas.auth.engine.core.ProtocolSignerI; +import eu.eidas.auth.engine.core.impl.BouncyCastleBootstrap; +import eu.eidas.auth.engine.core.impl.CertificateValidator; +import eu.eidas.auth.engine.metadata.MetadataSignerI; +import eu.eidas.auth.engine.xml.opensaml.CertificateUtil; +import eu.eidas.engine.exceptions.EIDASSAMLEngineException; +import eu.eidas.samlengineconfig.CertificateConfigurationManager; +import eu.eidas.util.Preconditions; + +/** + * @author tlenz + * + */ +public class MOAExtendedSWSigner implements ProtocolSignerI, MetadataSignerI { + + private static final String DEFAULT_SIGNATURE_ALGORITHM = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"; + private static final ImmutableSet<String> ALLOWED_ALGORITHMS_FOR_SIGNING; + private static final ImmutableSet<String> ALLOWED_ALGORITHMS_FOR_VERIFYING; + private static final ImmutableSet<String> DEFAULT_ALGORITHM_WHITE_LIST; + private static final String DEFAULT_DIGEST_ALGORITHM = "http://www.w3.org/2001/04/xmlenc#sha512"; + private static final ImmutableMap<Object, Object> SIGNATURE_TO_DIGEST_ALGORITHM_MAP; + private final boolean checkedValidityPeriod; + private final boolean disallowedSelfSignedCertificate; + private final boolean responseSignAssertions; + private final ImmutableSet<String> signatureAlgorithmWhiteList; + private final X509Credential privateSigningCredential; + private final X509Credential publicSigningCredential; + private final X509Credential privateMetadataSigningCredential; + private final X509Credential publicMetadataSigningCredential; + private final ImmutableList<X509Credential> trustedCredentials; + private final String signatureAlgorithm; + + public MOAExtendedSWSigner(Map<String, String> properties) throws SamlEngineConfigurationException { + this(new KeyStoreSignatureConfigurator().getSignatureConfiguration(properties)); + + } + + /** + * @param configManager + * @throws SamlEngineConfigurationException + */ + public MOAExtendedSWSigner(CertificateConfigurationManager configManager) throws SamlEngineConfigurationException { + this(new KeyStoreSignatureConfigurator().getSignatureConfiguration( + ConfigurationAdapter.adapt(configManager).getInstances().get(Constants.eIDAS_SAML_ENGINE_NAME).getConfigurationEntries().get(ConfigurationKey.SIGNATURE_CONFIGURATION.getKey()).getParameters())); + + } + + protected MOAExtendedSWSigner(SignatureConfiguration signatureConfiguration) + throws SamlEngineConfigurationException { + this(signatureConfiguration.isCheckedValidityPeriod(), + signatureConfiguration.isDisallowedSelfSignedCertificate(), + signatureConfiguration.isResponseSignAssertions(), + signatureConfiguration.getSignatureKeyAndCertificate(), signatureConfiguration.getTrustedCertificates(), + signatureConfiguration.getSignatureAlgorithm(), signatureConfiguration.getSignatureAlgorithmWhiteList(), + signatureConfiguration.getMetadataSigningKeyAndCertificate()); + } + + protected MOAExtendedSWSigner(boolean checkedValidityPeriod, boolean disallowedSelfSignedCertificate, + boolean responseSignAssertions, KeyStore.PrivateKeyEntry signatureKeyAndCertificate, + ImmutableSet<X509Certificate> trustedCertificates, String signatureAlgorithmVal, + String signatureAlgorithmWhiteListStr, + KeyStore.PrivateKeyEntry metadataSigningKeyAndCertificate) + throws SamlEngineConfigurationException { + Preconditions.checkNotNull(signatureKeyAndCertificate, "signatureKeyAndCertificate"); + Preconditions.checkNotNull(trustedCertificates, "trustedCertificates"); + String signatureAlg = signatureAlgorithmVal; + + if (StringUtils.isBlank(signatureAlg)) + signatureAlg = DEFAULT_SIGNATURE_ALGORITHM; + + else { + signatureAlg = validateSigningAlgorithm(signatureAlg); + + } + + ImmutableSet signatureAlgorithmWhiteSet = MOAWhiteListConfigurator.getAllowedAlgorithms( + DEFAULT_ALGORITHM_WHITE_LIST, ALLOWED_ALGORITHMS_FOR_VERIFYING, signatureAlgorithmWhiteListStr); + + this.checkedValidityPeriod = checkedValidityPeriod; + this.disallowedSelfSignedCertificate = disallowedSelfSignedCertificate; + this.responseSignAssertions = responseSignAssertions; + this.trustedCredentials = CertificateUtil.getListOfCredential(trustedCertificates); + this.signatureAlgorithmWhiteList = signatureAlgorithmWhiteSet; + this.signatureAlgorithm = signatureAlg; + this.privateSigningCredential = CertificateUtil.createCredential(signatureKeyAndCertificate); + this.publicSigningCredential = CertificateUtil + .toCredential((X509Certificate) signatureKeyAndCertificate.getCertificate()); + + if (null != metadataSigningKeyAndCertificate) { + this.privateMetadataSigningCredential = CertificateUtil.createCredential(metadataSigningKeyAndCertificate); + this.publicMetadataSigningCredential = CertificateUtil + .toCredential((X509Certificate) metadataSigningKeyAndCertificate.getCertificate()); + } else { + this.privateMetadataSigningCredential = null; + this.publicMetadataSigningCredential = null; + } + } + + private static X509Certificate getSignatureCertificate(Signature signature) throws EIDASSAMLEngineException { + KeyInfo keyInfo = signature.getKeyInfo(); + return CertificateUtil.toCertificate(keyInfo); + } + + public static String validateDigestAlgorithm(String signatureAlgorithmName) + throws SamlEngineConfigurationException { + + if (StringUtils.isBlank(signatureAlgorithmName)) { + return DEFAULT_DIGEST_ALGORITHM; + + } + + //BUGFIX: toLowerCase from original eIDAS SAML-engine produce problems with MGF1 signature algorithms + String canonicalAlgorithm = signatureAlgorithmName.trim(); + String digestAlgorithm = (String) SIGNATURE_TO_DIGEST_ALGORITHM_MAP.get(canonicalAlgorithm); + if (null != digestAlgorithm) { + return digestAlgorithm; + + } + String msg = "Signing algorithm \"" + signatureAlgorithmName + + "\" does not contain an allowed digest algorithm"; + + Logger.error(msg); + throw new SamlEngineConfigurationException(msg); + + } + + public static String validateSigningAlgorithm(@Nullable String signatureAlgorithmName) + throws SamlEngineConfigurationException { + if ((signatureAlgorithmName == null) || (StringUtils.isBlank(signatureAlgorithmName))) { + return DEFAULT_SIGNATURE_ALGORITHM; + + } + + //BUGFIX:: toLowerCase from original eIDAS SAML-engine produce problems with MGF1 signature algorithms + String canonicalAlgorithm = signatureAlgorithmName.trim(); + if (ALLOWED_ALGORITHMS_FOR_SIGNING.contains(canonicalAlgorithm)) { + return canonicalAlgorithm; + + } + String msg = "Signing algorithm \"" + signatureAlgorithmName + "\" is not allowed"; + Logger.error(msg); + throw new SamlEngineConfigurationException(msg); + + } + + protected void checkCertificateIssuer(X509Certificate certificate) throws EIDASSAMLEngineException { + CertificateValidator.checkCertificateIssuer(this.disallowedSelfSignedCertificate, certificate); + + } + + protected void checkCertificateValidityPeriod(X509Certificate certificate) throws EIDASSAMLEngineException { + CertificateValidator.checkCertificateValidityPeriod(this.checkedValidityPeriod, certificate); + + } + + protected Signature createSignature(X509Credential credential) throws EIDASSAMLEngineException { + checkCertificateValidityPeriod(credential.getEntityCertificate()); + checkCertificateIssuer(credential.getEntityCertificate()); + Signature signature; + try { + Logger.trace("Creating an OpenSAML signature object"); + + signature = (Signature) Configuration.getBuilderFactory().getBuilder(Signature.DEFAULT_ELEMENT_NAME) + .buildObject(Signature.DEFAULT_ELEMENT_NAME); + + signature.setSigningCredential(credential); + + signature.setSignatureAlgorithm(getSignatureAlgorithm()); + + SecurityConfiguration secConfiguration = Configuration.getGlobalSecurityConfiguration(); + NamedKeyInfoGeneratorManager keyInfoManager = secConfiguration.getKeyInfoGeneratorManager(); + KeyInfoGeneratorManager keyInfoGenManager = keyInfoManager.getDefaultManager(); + KeyInfoGeneratorFactory keyInfoGenFac = keyInfoGenManager.getFactory(credential); + KeyInfoGenerator keyInfoGenerator = keyInfoGenFac.newInstance(); + + KeyInfo keyInfo = keyInfoGenerator.generate(credential); + + signature.setKeyInfo(keyInfo); + signature.setCanonicalizationAlgorithm("http://www.w3.org/2001/10/xml-exc-c14n#"); + } catch (SecurityException e) { + Logger.error("ERROR : Security exception: " + e, e); + throw new EIDASSAMLEngineException(e); + + } + return signature; + } + + public X509Credential getPublicMetadataSigningCredential() { + return this.publicMetadataSigningCredential; + + } + + public X509Credential getPublicSigningCredential() { + return this.publicSigningCredential; + + } + + protected String getSignatureAlgorithm() { + return this.signatureAlgorithm; + + } + + protected ImmutableSet<String> getSignatureAlgorithmWhiteList() { + return this.signatureAlgorithmWhiteList; + + } + + private X509Credential getTrustedCertificate(Signature signature, + List<? extends Credential> trustedCredentialList) throws EIDASSAMLEngineException { + X509Certificate cert = getSignatureCertificate(signature); + + X509Credential entityX509Cred = CertificateUtil.toCredential(cert); + + CertificateUtil.checkTrust(entityX509Cred, trustedCredentialList); + checkCertificateValidityPeriod(cert); + checkCertificateIssuer(cert); + return entityX509Cred; + + } + + protected ImmutableList<X509Credential> getTrustedCredentials() { + return this.trustedCredentials; + + } + + private boolean isAlgorithmAllowedForVerifying(String signatureAlgorithm) { + return ((StringUtils.isNotBlank(signatureAlgorithm)) + && (getSignatureAlgorithmWhiteList().contains(signatureAlgorithm.trim()))); + + } + + public <T extends SignableXMLObject> T sign(T signableObject) throws EIDASSAMLEngineException { + return sign(signableObject, this.privateSigningCredential); + + } + + protected <T extends SignableXMLObject> T sign(T signableObject, X509Credential signingCredential) + throws EIDASSAMLEngineException { + Logger.trace("Start Sign process."); + try { + Signature signature = createSignature(signingCredential); + signableObject.setSignature(signature); + + String digestAlgorithm = validateDigestAlgorithm(getSignatureAlgorithm()); + List contentReferences = signature.getContentReferences(); + if (CollectionUtils.isNotEmpty(contentReferences)) + ((SAMLObjectContentReference) contentReferences.get(0)).setDigestAlgorithm(digestAlgorithm); + + else { + Logger.error("Unable to set DigestMethodAlgorithm - algorithm " + digestAlgorithm + " not set"); + + } + + Logger.trace("Marshall samlToken."); + Configuration.getMarshallerFactory().getMarshaller(signableObject).marshall(signableObject); + + Logger.trace("Sign samlToken."); + Signer.signObject(signature); + + } catch (MarshallingException e) { + Logger.error("ERROR : MarshallingException: " + e, e); + throw new EIDASSAMLEngineException(e); + + } catch (SignatureException e) { + Logger.error("ERROR : Signature exception: " + e, e); + throw new EIDASSAMLEngineException(e); + + } + + return signableObject; + } + + public <T extends SignableXMLObject> T signMetadata(T signableObject) throws EIDASSAMLEngineException { + if (null == this.privateMetadataSigningCredential) { + throw new SamlEngineConfigurationException("No metadataSigningCredential configured"); + + } + return sign(signableObject, this.privateMetadataSigningCredential); + } + + public <T extends SignableXMLObject> T validateMetadataSignature(T signedMetadata) + throws EIDASSAMLEngineException { + return validateSignature(signedMetadata, null); + + } + + private SAMLSignatureProfileValidator validateSamlSignatureStructure(SignableXMLObject signableObject) + throws EIDASSAMLEngineException { + SAMLSignatureProfileValidator sigProfValidator = new SAMLSignatureProfileValidator(); + try { + sigProfValidator.validate(signableObject.getSignature()); + + } catch (ValidationException e) { + Logger.error("ERROR : ValidationException: signature isn't conform to SAML Signature profile: " + e, e); + throw new EIDASSAMLEngineException(e); + + } + return sigProfValidator; + } + + public <T extends SignableXMLObject> T validateSignature(T signedObject, + Collection<X509Certificate> trustedCertificateCollection) throws EIDASSAMLEngineException { + List trustedCreds; + if (CollectionUtils.isEmpty(trustedCertificateCollection)) + trustedCreds = getTrustedCredentials(); + + else { + trustedCreds = CertificateUtil.getListOfCredential(trustedCertificateCollection); + + } + return (T) validateSignatureWithCredentials(signedObject, trustedCreds); + } + + private <T extends SignableXMLObject> T validateSignatureWithCredentials(T signedObject, + List<? extends Credential> trustedCredentialList) throws EIDASSAMLEngineException { + Logger.debug("Start signature validation."); + + validateSamlSignatureStructure(signedObject); + + verifyCryptographicSignature(signedObject.getSignature(), trustedCredentialList); + + return signedObject; + + } + + private void verifyCryptographicSignature(Signature signature, + List<? extends Credential> trustedCredentialList) throws EIDASSAMLEngineException { + String signatureAlgorithmVal = signature.getSignatureAlgorithm(); + Logger.trace("Key algorithm " + SecurityHelper.getKeyAlgorithmFromURI(signatureAlgorithmVal)); + + if (!(isAlgorithmAllowedForVerifying(signatureAlgorithmVal))) { + Logger.error("ERROR : the algorithm " + signatureAlgorithmVal + " used by the signature is not allowed"); + throw new EIDASSAMLEngineException(EidasErrorKey.INVALID_SIGNATURE_ALGORITHM.errorCode()); + + } + + X509Credential entityX509Cred = getTrustedCertificate(signature, trustedCredentialList); + SignatureValidator sigValidator = new SignatureValidator(entityX509Cred); + + try { + sigValidator.validate(signature); + + } catch (ValidationException e) { + Logger.error("ERROR : Signature Validation Exception: " + e, e); + throw new EIDASSAMLEngineException(e); + + } + } + + public boolean isResponseSignAssertions() { + return this.responseSignAssertions; + } + + static { + BouncyCastleBootstrap.bootstrap(); + + ALLOWED_ALGORITHMS_FOR_SIGNING = ImmutableSet.of("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", + "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384", + "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", + "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256", + "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384", + "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512", + + //Set other algorithms which are not supported by openSAML in default + XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256_MGF1, + XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512_MGF1); + + ALLOWED_ALGORITHMS_FOR_VERIFYING = ImmutableSet.of("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", + "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384", + "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", + "http://www.w3.org/2001/04/xmldsig-more#rsa-ripemd160", + "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256", + "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384", + "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512", + + //Set other algorithms which are not supported by openSAML in default + XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA1_MGF1, + XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA224_MGF1, + XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256_MGF1, + XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA384_MGF1, + XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512_MGF1); + + DEFAULT_ALGORITHM_WHITE_LIST = ImmutableSet.of("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", + "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384", + "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", + "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256", + "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384", + "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512", + + //Set other algorithms which are not supported by openSAML in default + XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256_MGF1); + + SIGNATURE_TO_DIGEST_ALGORITHM_MAP = ImmutableMap.builder() + .put("http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "http://www.w3.org/2001/04/xmlenc#sha256") + .put("http://www.w3.org/2001/04/xmldsig-more#rsa-sha384", + "http://www.w3.org/2001/04/xmldsig-more#sha384") + .put("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", "http://www.w3.org/2001/04/xmlenc#sha512") + .put("http://www.w3.org/2001/04/xmldsig-more#rsa-ripemd160", + "http://www.w3.org/2001/04/xmlenc#ripemd160") + .put("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256", "http://www.w3.org/2001/04/xmlenc#sha256") + .put("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384", + "http://www.w3.org/2001/04/xmldsig-more#sha384") + .put("http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512", "http://www.w3.org/2001/04/xmlenc#sha512") + + //Set other algorithms which are not supported by openSAML in default + .put(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256_MGF1, "http://www.w3.org/2001/04/xmlenc#sha256") + .put(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA512_MGF1, "http://www.w3.org/2001/04/xmlenc#sha512") + + .build(); + } +} 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 9ad5f0db3..de4f3fc9c 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 @@ -90,17 +90,21 @@ public class ModifiedEncryptionSW extends KeyStoreSamlEngineEncryption { */ @Override public boolean isEncryptionEnabled(String countryCode) { - // - encrypt if so configured + //encryption is enabled by default in MOA-ID configuration object try { AuthConfiguration moaconfig = AuthConfigurationProviderFactory.getInstance(); Boolean useEncryption = moaconfig.getStorkConfig().getCPEPS(countryCode).isXMLSignatureSupported(); - Logger.info(useEncryption ? "using encryption" : "do not use encrpytion"); + String logResult = useEncryption ? " using encryption" : " do not use encrpytion"; + Logger.debug("eIDAS respone for country " + countryCode + logResult); return useEncryption; + } catch(NullPointerException | ConfigurationException e) { Logger.warn("failed to gather information about encryption for countryCode " + countryCode + " - thus, enabling encryption"); if(Logger.isDebugEnabled()) e.printStackTrace(); return true; + } + } } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAEidasProtocolProcesser.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAEidasProtocolProcesser.java index c24c5efca..8abf29703 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAEidasProtocolProcesser.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAEidasProtocolProcesser.java @@ -22,7 +22,9 @@ */ package at.gv.egovernment.moa.id.auth.modules.eidas.engine; +import eu.eidas.auth.commons.attribute.AttributeRegistry; import eu.eidas.auth.engine.core.eidas.EidasProtocolProcessor; +import eu.eidas.auth.engine.core.eidas.spec.EidasSpec; import eu.eidas.auth.engine.metadata.MetadataFetcherI; import eu.eidas.auth.engine.metadata.MetadataSignerI; @@ -38,11 +40,14 @@ public class MOAEidasProtocolProcesser extends EidasProtocolProcessor { private final MetadataSignerI metadataSigner; /** - * @param metadataFetcher - * @param metadataSigner + * Build a MOA specific eIDAS-engine protocol processor + * + * @param metadataFetcher eIDAS-engine Metadata fetcher implementation + * @param metadataSigner eIDAS-engine Signer implementation + * @param addAttrDefinitions additinal eIDAS attributes */ - public MOAEidasProtocolProcesser(MetadataFetcherI metadataFetcher, MetadataSignerI metadataSigner) { - super(metadataFetcher, metadataSigner); + public MOAEidasProtocolProcesser(MetadataFetcherI metadataFetcher, MetadataSignerI metadataSigner, AttributeRegistry addAttrDefinitions) { + super(EidasSpec.REGISTRY, addAttrDefinitions, metadataFetcher, metadataSigner); this.metadataFetcher = metadataFetcher; this.metadataSigner = metadataSigner; diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAProtocolEngine.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAProtocolEngine.java new file mode 100644 index 000000000..d8fcd1694 --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/engine/MOAProtocolEngine.java @@ -0,0 +1,68 @@ +package at.gv.egovernment.moa.id.auth.modules.eidas.engine; + +import java.security.cert.X509Certificate; + +import org.apache.commons.lang3.StringUtils; +import org.opensaml.saml2.core.Response; + +import at.gv.egovernment.moa.logging.Logger; +import eu.eidas.auth.commons.EidasErrorKey; +import eu.eidas.auth.commons.protocol.IAuthenticationRequest; +import eu.eidas.auth.engine.ProtocolEngine; +import eu.eidas.auth.engine.configuration.ProtocolConfigurationAccessor; +import eu.eidas.auth.engine.xml.opensaml.SAMLEngineUtils; +import eu.eidas.engine.exceptions.EIDASSAMLEngineException; + +public class MOAProtocolEngine extends ProtocolEngine { + + public MOAProtocolEngine(ProtocolConfigurationAccessor configurationAccessor) { + super(configurationAccessor); + + } + +// @Override +// protected X509Certificate getEncryptionCertificate(String requestIssuer, +// String destinationCountryCode) throws EIDASSAMLEngineException { +// if ((StringUtils.isNotBlank(destinationCountryCode)) && (null != getProtocolEncrypter()) +// && (getProtocolEncrypter().isEncryptionEnabled(destinationCountryCode))) { +// X509Certificate encryptionCertificate = getProtocolProcessor().getEncryptionCertificate(requestIssuer); +// +// if (null == encryptionCertificate) { +// return getProtocolEncrypter().getEncryptionCertificate(destinationCountryCode); +// +// } +// return encryptionCertificate; +// } +// return null; +// } +// +// @Override +// protected Response signResponse(IAuthenticationRequest request, Response response) +// throws EIDASSAMLEngineException { +// Response responseToSign = response; +// +// if ((null != getProtocolEncrypter()) && (!(SAMLEngineUtils.isErrorSamlResponse(responseToSign)))) { +// X509Certificate destinationCertificate = getEncryptionCertificate(request.getIssuer(), +// request.getOriginCountryCode()); +// +// if (null != destinationCertificate) { +// responseToSign = getProtocolEncrypter().encryptSamlResponse(responseToSign, destinationCertificate); +// +// } else if (getProtocolEncrypter().isEncryptionEnabled(request.getOriginCountryCode())) { +//// Logger.error(SAML_EXCHANGE, +//// "BUSINESS EXCEPTION : encryption cannot be performed, no matching certificate for issuer=" +//// + request.getIssuer() + " and country=" + request.getOriginCountryCode()); +// +// throw new EIDASSAMLEngineException(EidasErrorKey.SAML_ENGINE_INVALID_CERTIFICATE.errorCode(), +// EidasErrorKey.SAML_ENGINE_INVALID_CERTIFICATE.errorMessage()); +// } +// +// } else if (!(SAMLEngineUtils.isErrorSamlResponse(responseToSign))) { +// checkSendingUnencryptedResponsesAllowed(); +// +// } +// +// Logger.debug("Signing SAML Response."); +// return ((Response) getSigner().sign(responseToSign)); +// } +} 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 a9c4d5d3a..7155040c6 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/GenerateAuthnRequestTask.java @@ -22,13 +22,10 @@ */ package at.gv.egovernment.moa.id.auth.modules.eidas.tasks; -import java.io.ByteArrayOutputStream; import java.io.StringWriter; import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.zip.Deflater; -import java.util.zip.DeflaterOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -42,7 +39,6 @@ import org.opensaml.common.xml.SAMLConstants; import org.opensaml.saml2.metadata.EntityDescriptor; import org.opensaml.saml2.metadata.SingleSignOnService; import org.opensaml.saml2.metadata.provider.MetadataProviderException; -import org.opensaml.xml.util.Base64; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -197,7 +193,17 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { } } - //build requested attribute set + //request + if (reqAttrList.isEmpty()) { + Logger.info("No attributes requested by OA:" + pendingReq.getOnlineApplicationConfiguration().getPublicURLPrefix() + + " --> Request attr:" + Constants.eIDAS_ATTR_PERSONALIDENTIFIER + " by default"); + AttributeDefinition<?> newAttribute = SAMLEngineUtils.getMapOfAllAvailableAttributes().get(Constants.eIDAS_ATTR_PERSONALIDENTIFIER); + Builder<?> attrBuilder = AttributeDefinition.builder(newAttribute).required(true); + reqAttrList.add(attrBuilder.build()); + + } + + //build requested attribute set ImmutableAttributeMap reqAttrMap = new ImmutableAttributeMap.Builder().putAll(reqAttrList).build(); //build eIDAS AuthnRequest @@ -211,8 +217,13 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { authnRequestBuilder.nameIdFormat(Constants.eIDAS_REQ_NAMEID_FORMAT); - //set minimum required eIDAS LoA from OA config - authnRequestBuilder.levelOfAssurance(LevelOfAssurance.fromString(oaConfig.getQaaLevel())); + //set minimum required eIDAS LoA from OA config + String LoA = oaConfig.getQaaLevel(); + if (MiscUtil.isNotEmpty(LoA)) + authnRequestBuilder.levelOfAssurance(LevelOfAssurance.fromString(oaConfig.getQaaLevel())); + else + authnRequestBuilder.levelOfAssurance(LevelOfAssurance.HIGH); + authnRequestBuilder.levelOfAssuranceComparison(LevelOfAssuranceComparison.MINIMUM); //set correct SPType for this online application @@ -234,7 +245,7 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { IRequestMessage authnRequest = engine.generateRequestMessage(authnRequestBuilder.build(), issur); - + //encode AuthnRequest byte[] token = authnRequest.getMessageBytes(); String SAMLRequest = EidasStringUtil.encodeToBase64(token); @@ -269,40 +280,6 @@ public class GenerateAuthnRequestTask extends AbstractAuthServletTask { } } - - /** - * Encode the eIDAS request with Redirect binding - * - * @param pendingReq - * @param authnReqEndpoint - * @param token - * @param authnRequest - * @param response - * @throws MOAIDException - */ - private void buildRedirecttBindingRequest(IRequest pendingReq, SingleSignOnService authnReqEndpoint, - byte[] token, IRequestMessage authnRequest, HttpServletResponse response) - throws MOAIDException { - - //FIXME: implement correct deflat encoding accodring to SAML2 Redirect Binding specification - - try { - ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(); - Deflater deflater = new Deflater(Deflater.DEFLATED, true); - DeflaterOutputStream deflaterStream = new DeflaterOutputStream(bytesOut, deflater); - deflaterStream.write(token); - deflaterStream.finish(); - String samlReqBase64 = Base64.encodeBytes(bytesOut.toByteArray(), Base64.DONT_BREAK_LINES); - - - - } catch (Exception e) { - Logger.error("eIDAS Redirect-Binding request encoding error: " + e.getMessage()); - throw new MOAIDException("eIDAS.02", new Object[]{e.getMessage()}, e); - - } - - } /** * Encode the eIDAS request with POST binding diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/ReceiveAuthnResponseTask.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/ReceiveAuthnResponseTask.java index c4b2bfeae..45ba3d64e 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/ReceiveAuthnResponseTask.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/tasks/ReceiveAuthnResponseTask.java @@ -19,12 +19,12 @@ import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.commons.db.ex.MOADatabaseException; import at.gv.egovernment.moa.id.process.api.ExecutionContext; +import at.gv.egovernment.moa.id.protocols.eidas.validator.eIDASResponseValidator; import at.gv.egovernment.moa.id.protocols.pvp2x.PVPConstants; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.MiscUtil; import eu.eidas.auth.commons.EidasStringUtil; import eu.eidas.auth.commons.protocol.IAuthenticationResponse; -import eu.eidas.auth.commons.protocol.eidas.LevelOfAssurance; import eu.eidas.auth.engine.ProtocolEngineI; import eu.eidas.engine.exceptions.EIDASSAMLEngineException; @@ -78,16 +78,8 @@ public class ReceiveAuthnResponseTask extends AbstractAuthServletTask { // ********************************************************** // ******* MOA-ID specific response validation ********** // ********************************************************** - - //validate received LoA against minimum required LoA - LevelOfAssurance reqLoA = LevelOfAssurance.fromString(pendingReq.getOnlineApplicationConfiguration().getQaaLevel()); - LevelOfAssurance respLoA = LevelOfAssurance.fromString(samlResp.getLevelOfAssurance()); - if (respLoA.numericValue() < reqLoA.numericValue()) { - Logger.error("eIDAS Response LevelOfAssurance is lower than the required! " - + "(Resp-LoA:" + respLoA.getValue() + " Req-LoA:" + reqLoA.getValue() + ")"); - throw new MOAIDException("eIDAS.14", new Object[]{respLoA.getValue()}); - - } + String spCountry = authConfig.getBasicMOAIDConfiguration(Constants.CONIG_PROPS_EIDAS_NODE_COUNTRYCODE, "AT"); + eIDASResponseValidator.validateResponse(pendingReq, samlResp, spCountry); // ********************************************************** diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAProtocolEngineFactory.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAProtocolEngineFactory.java index f29d2bb65..47cdb4ade 100644 --- 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 @@ -95,5 +95,22 @@ public class MOAProtocolEngineFactory extends ProtocolEngineFactory { } +// public static ProtocolEngineI createProtocolEngine(String instanceName, +// ProtocolEngineConfigurationFactory protocolEngineConfigurationFactory, +// ProtocolProcessorI protocolProcessor, SamlEngineClock samlEngineClock) +// throws SamlEngineConfigurationException { +// +// ProtocolEngineConfiguration preConfiguration = protocolEngineConfigurationFactory +// .getConfiguration(instanceName); +// +// protocolProcessor.configure(); +// +// ProtocolEngineConfiguration configuration = ProtocolEngineConfiguration.builder(preConfiguration) +// .protocolProcessor(protocolProcessor).clock(samlEngineClock).build(); +// +// ProtocolEngineI samlEngine = new MOAProtocolEngine(new FixedProtocolConfigurationAccessor(configuration)); +// +// return samlEngine; +// } } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAWhiteListConfigurator.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAWhiteListConfigurator.java index 7d647ff15..a2c6a3ad9 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAWhiteListConfigurator.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/MOAWhiteListConfigurator.java @@ -22,7 +22,6 @@ */ package at.gv.egovernment.moa.id.auth.modules.eidas.utils; -import java.util.Locale; import java.util.regex.Pattern; import org.apache.commons.collections.CollectionUtils; @@ -48,6 +47,11 @@ public class MOAWhiteListConfigurator { } ImmutableSet.Builder<String> allowed = ImmutableSet.builder(); String[] wlAlgorithms = WHITE_LIST_SPLITTER.split(algorithmWhiteListValue); + + //BugFix: remove newlines from configuration + for (int i=0; i<wlAlgorithms.length; i++) + wlAlgorithms[i] = StringUtils.trimToNull(KeyValueUtils.removeAllNewlineFromString(wlAlgorithms[i])); + if (null != wlAlgorithms && wlAlgorithms.length > 0) { return getAllowedAlgorithms(defaultWhiteList, allowedValues, ImmutableSet.<String>copyOf(wlAlgorithms)); } @@ -72,7 +76,11 @@ public class MOAWhiteListConfigurator { candidateValue = StringUtils.trimToNull( KeyValueUtils.removeAllNewlineFromString(candidateValue)); if (StringUtils.isNotBlank(candidateValue)) { - String candidateAlgorithm = StringUtils.lowerCase(candidateValue, Locale.ENGLISH); + + //BUGFIX: eIDAS SAML-engine MPF1 signature schemes problem + String candidateAlgorithm = candidateValue; + //String candidateAlgorithm = StringUtils.lowerCase(candidateValue, Locale.ENGLISH); + if (allowedValues.contains(candidateAlgorithm)) { allowed.add(candidateValue); if (!modified && !candidateAlgorithm.equals(candidateValue)) { 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 index dd14972e3..7b159c73d 100644 --- 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 @@ -77,7 +77,6 @@ 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 at.gv.egovernment.moa.id.protocols.pvp2x.utils.SAML2Utils; import eu.eidas.auth.commons.EIDASUtil; import eu.eidas.auth.commons.EidasStringUtil; @@ -209,16 +208,26 @@ public class MOAeIDASMetadataGenerator extends MetadataGenerator { if (!StringUtils.isEmpty(params.getAssertionConsumerUrl())) { addAssertionConsumerService(); } - fillNameIDFormat(spSSODescriptor); - if (params.getSpEngine() != null) { - ProtocolEngineI spEngine = params.getSpEngine(); - ((MetadataSignerI) spEngine.getSigner()).signMetadata(spSSODescriptor); - } + + //FIX: Austrian eIDAS node SP only needs persistent identifiers + NameIDFormat persistentFormat = + (NameIDFormat) BuilderFactoryUtil.buildXmlObject(NameIDFormat.DEFAULT_ELEMENT_NAME); + persistentFormat.setFormat(SamlNameIdFormat.PERSISTENT.getNameIdFormat()); + spSSODescriptor.getNameIDFormats().add(persistentFormat); + + /**FIXME: + * Double signing of SPSSODescribtor is not required + */ +// if (params.getSpEngine() != null) { +// ProtocolEngineI spEngine = params.getSpEngine(); +// ((MetadataSignerI) spEngine.getSigner()).signMetadata(spSSODescriptor); +// } + entityDescriptor.getRoleDescriptors().add(spSSODescriptor); } - - private void fillNameIDFormat(SSODescriptor ssoDescriptor) throws EIDASSAMLEngineException { + + private void fillIDPNameIDFormat(SSODescriptor ssoDescriptor) throws EIDASSAMLEngineException { NameIDFormat persistentFormat = (NameIDFormat) BuilderFactoryUtil.buildXmlObject(NameIDFormat.DEFAULT_ELEMENT_NAME); persistentFormat.setFormat(SamlNameIdFormat.PERSISTENT.getNameIdFormat()); @@ -265,7 +274,11 @@ public class MOAeIDASMetadataGenerator extends MetadataGenerator { .add(getKeyDescriptor(keyInfoGeneratorFactory, params.getEncryptionCredential(), UsageType.ENCRYPTION)); } idpSSODescriptor.addSupportedProtocol(params.getIdpSamlProtocol()); - fillNameIDFormat(idpSSODescriptor); + + //Austrian eIDAS node IDP can provided persistent, transient, and unspecified identifiers + fillIDPNameIDFormat(idpSSODescriptor); + + if (params.getIdpEngine() != null) { if (params.getIdpEngine().getProtocolProcessor() != null && params.getIdpEngine().getProtocolProcessor().getFormat() == SAMLExtensionFormat.EIDAS10) { @@ -277,8 +290,13 @@ public class MOAeIDASMetadataGenerator extends MetadataGenerator { */ generateSupportedAttributes(idpSSODescriptor, getAllSupportedAttributes()); } - ProtocolEngineI idpEngine = params.getIdpEngine(); - ((MetadataSignerI) idpEngine.getSigner()).signMetadata(idpSSODescriptor); + + + /**FIXME: + * Double signing of IDPSSODescribtor is not required + */ +// ProtocolEngineI idpEngine = params.getIdpEngine(); +// ((MetadataSignerI) idpEngine.getSigner()).signMetadata(idpSSODescriptor); } idpSSODescriptor.getSingleSignOnServices().addAll(buildSingleSignOnServicesBindingLocations()); @@ -287,13 +305,18 @@ public class MOAeIDASMetadataGenerator extends MetadataGenerator { } - /*TODO: Only a work-around to add eIDAS attributes, which could be provided from MOA-ID, to IDP metadata + /* FIX: 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()); + + for (String attr : eIDASAttributeBuilder.getAllProvideableeIDASAttributes()) { + AttributeDefinition<?> supAttr = params.getIdpEngine().getProtocolProcessor().getAttributeDefinitionNullable(attr); + builder.add(supAttr); + } + return builder.build(); } @@ -465,7 +488,7 @@ public class MOAeIDASMetadataGenerator extends MetadataGenerator { X509Certificate decryptionCertificate = engine.getDecryptionCertificate(); if (null != decryptionCertificate) { - params.setEncryptionCredential(CertificateUtil.toCredential(decryptionCertificate)); + params.setSpEncryptionCredential(CertificateUtil.toCredential(decryptionCertificate)); } params.setSigningCredential(CertificateUtil.toCredential(engine.getSigningCertificate())); params.setIdpEngine(engine); @@ -530,7 +553,10 @@ public class MOAeIDASMetadataGenerator extends MetadataGenerator { Set<String> signatureMethods = EIDASUtil.parseSemicolonSeparatedList(params.getDigestMethods()); Set<String> digestMethods = new HashSet<String>(); for (String signatureMethod : signatureMethods) { - digestMethods.add(CertificateUtil.validateDigestAlgorithm(signatureMethod)); + + //BUGFIX: eIDAS implementation does not allow MGF1 signature schemes + digestMethods.add(signatureMethod); + //digestMethods.add(CertificateUtil.validateDigestAlgorithm(signatureMethod)); } for (String digestMethod : digestMethods) { final DigestMethod dm = (DigestMethod) BuilderFactoryUtil.buildXmlObject(DigestMethod.DEF_ELEMENT_NAME); @@ -569,7 +595,7 @@ public class MOAeIDASMetadataGenerator extends MetadataGenerator { generateDigest(eidasExtensions); if (!StringUtils.isEmpty(params.getSigningMethods())) { - Set<String> signMethods = EIDASUtil.parseSemicolonSeparatedList(params.getDigestMethods()); + Set<String> signMethods = EIDASUtil.parseSemicolonSeparatedList(params.getSigningMethods()); for (String signMethod : signMethods) { final SigningMethod sm = (SigningMethod) BuilderFactoryUtil.buildXmlObject(SigningMethod.DEF_ELEMENT_NAME); 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 eb50c113f..edbecc4a0 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 @@ -23,6 +23,8 @@ package at.gv.egovernment.moa.id.auth.modules.eidas.utils; import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; import java.util.HashMap; import java.util.Map; @@ -30,14 +32,19 @@ 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.MOAExtendedSWSigner; 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.exceptions.EIDASEngineException; +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.auth.commons.attribute.AttributeDefinition; +import eu.eidas.auth.commons.attribute.AttributeRegistries; +import eu.eidas.auth.commons.attribute.AttributeRegistry; import eu.eidas.auth.engine.ProtocolEngineI; import eu.eidas.auth.engine.SamlEngineSystemClock; import eu.eidas.auth.engine.metadata.MetadataFetcherI; @@ -62,6 +69,7 @@ public class SAMLEngineUtils { if (eIDASEngine == null) { try { + //get eIDAS SAMLengine configuration from MOA-ID configuration CertificateConfigurationManager configManager = new MOAIDCertificateManagerConfigurationImpl(); @@ -69,13 +77,26 @@ public class SAMLEngineUtils { metadataFetcher = new MOAeIDASMetadataProviderDecorator(moaeIDASMetadataProvider); //set metadata signer - metadataSigner = new MOASWSigner(configManager); + metadataSigner = new MOAExtendedSWSigner(configManager); + + //load additional eIDAS attribute definitions + String additionalAttributeConfigFile = + AuthConfigurationProviderFactory.getInstance().getBasicMOAIDConfiguration( + Constants.CONIG_PROPS_EIDAS_SAMLENGINE_ATTIONAL_ATTRIBUTE_DEFINITIONS); + AttributeRegistry addAttrDefinitions = AttributeRegistries.empty(); + if (MiscUtil.isNotEmpty(additionalAttributeConfigFile)) { + URL addAttrConfigUrl = new URL(FileUtils.makeAbsoluteURL( + additionalAttributeConfigFile, + AuthConfigurationProviderFactory.getInstance().getRootConfigFileDir())); + addAttrDefinitions = AttributeRegistries.fromFile(addAttrConfigUrl.getPath()); + + } //build eIDAS SAML eninge ProtocolEngineI engine = MOAProtocolEngineFactory.createProtocolEngine( Constants.eIDAS_SAML_ENGINE_NAME, configManager, - new MOAEidasProtocolProcesser(metadataFetcher, metadataSigner), + new MOAEidasProtocolProcesser(metadataFetcher, metadataSigner, addAttrDefinitions), new SamlEngineSystemClock()); //build a map with all actually supported attributes @@ -93,6 +114,14 @@ public class SAMLEngineUtils { Logger.error("eIDAS SAMLengine initialization FAILED!", e); throw new EIDASEngineException("eIDAS.00", new Object[]{e.getMessage()}, e); + } catch (at.gv.egovernment.moa.id.commons.api.exceptions.ConfigurationException e) { + Logger.error("eIDAS SAMLengine initialization FAILED!", e); + throw new EIDASEngineException("eIDAS.00", new Object[]{e.getMessage()}, e); + + } catch (MalformedURLException e) { + Logger.error("eIDAS SAMLengine initialization FAILED!", e); + throw new EIDASEngineException("eIDAS.00", new Object[]{e.getMessage()}, e); + } } diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/eIDASAttributeBuilder.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/eIDASAttributeBuilder.java new file mode 100644 index 000000000..22b94178e --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/eIDASAttributeBuilder.java @@ -0,0 +1,174 @@ +/* + * 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.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.ServiceLoader; + +import com.google.common.collect.ImmutableSet; + +import at.gv.egovernment.moa.id.commons.api.IOAAuthParameters; +import at.gv.egovernment.moa.id.data.IAuthData; +import at.gv.egovernment.moa.id.data.Pair; +import at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeBuilder; +import at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator; +import at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.IeIDASAttribute; +import at.gv.egovernment.moa.id.protocols.pvp2x.builder.PVPAttributeBuilder; +import at.gv.egovernment.moa.id.protocols.pvp2x.builder.attributes.exceptions.AttributeException; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.MiscUtil; +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; + +/** + * @author tlenz + * + */ +public class eIDASAttributeBuilder extends PVPAttributeBuilder { + private static IAttributeGenerator<String> generator = new SimpleEidasAttributeGenerator(); + + private static List<String> listOfSupportedeIDASAttributes; + private static ServiceLoader<IeIDASAttribute> eIDASAttributLoader = + ServiceLoader.load(IeIDASAttribute.class); + + static { + List<String> supportAttrList = new ArrayList<String>(); + + Logger.info("Select eIDAS attributes that are corrently providable:"); + if (eIDASAttributLoader != null ) { + Iterator<IeIDASAttribute> moduleLoaderInterator = eIDASAttributLoader.iterator(); + while (moduleLoaderInterator.hasNext()) { + try { + IeIDASAttribute modul = moduleLoaderInterator.next(); + Logger.info("Loading eIDAS attribut-builder Modul Information: " + modul.getName()); + supportAttrList.add(modul.getName()); + + } catch(Throwable e) { + Logger.error("Check configuration! " + "Some attribute-builder modul" + + " is not a valid IAttributeBuilder", e); + } + } + } + + listOfSupportedeIDASAttributes = Collections.unmodifiableList(supportAttrList); + Logger.info("Selection of providable eIDAS attributes done"); + + } + + /** + * Get all eIDAS attribute names that can be generated by the Austrian eIDAS node. + * This list is dynamically generated from loaded eIDAS attribute builders that are found in Java Classpath + * + * @return {@link List} of {@link String} of eIDAS attribute names + */ + public static List<String> getAllProvideableeIDASAttributes() { + return listOfSupportedeIDASAttributes; + } + + /** + * This method build an eIDAS response attribute, by using a loaded eIDAS attribute builder. + * + * @param attr eIDAS attribute that should be generated + * @param onlineApplicationConfiguration SP configuration + * @param authData Authentication data that contains user information for attribute generation + * @return eIDAS attribute response {@link Pair} or null if the attribute generation FAILES + */ + public static Pair<AttributeDefinition<?>,ImmutableSet<AttributeValue<?>>> buildAttribute(AttributeDefinition<?> attr, IOAAuthParameters onlineApplicationConfiguration, + IAuthData authData) { + + String attrName = attr.getNameUri().toString(); + Logger.trace("Build eIDAS attribute: "+ attrName); + + + IAttributeBuilder attrBuilder = getAttributeBuilder(attrName); + if (attrBuilder != null) { + try { + String attrValue = attrBuilder.build(onlineApplicationConfiguration, authData, generator); + if (MiscUtil.isNotEmpty(attrValue)) { + //set uniqueIdentifier attribute, because eIDAS SAMLEngine use this flag to select the + // Subject->NameID value from this attribute + Builder<?> eIDASAttrBuilder = AttributeDefinition.builder(attr); + eIDASAttrBuilder.uniqueIdentifier(evaluateUniqueID(attrName, authData.isUseMandate())); + AttributeDefinition<?> returnAttr = eIDASAttrBuilder.build(); + + //unmarshal attribute value into eIDAS attribute + AttributeValueMarshaller<?> attributeValueMarshaller = returnAttr.getAttributeValueMarshaller(); + ImmutableSet.Builder<AttributeValue<?>> builder = ImmutableSet.builder(); + + AttributeValue<?> attributeValue = null; + try { + attributeValue = attributeValueMarshaller.unmarshal(attrValue, false); + builder.add(attributeValue); + + } catch (AttributeValueMarshallingException e) { + throw new IllegalStateException(e); + + } + + return Pair.newInstance(returnAttr, builder.build()); + + } + + } catch (AttributeException e) { + Logger.debug("Attribute can not generate requested attribute:" + attr.getNameUri().toString() + " Reason:" + e.getMessage()); + + } + + } else + Logger.warn("NO attribute builder FOUND for eIDAS attr: " + attrName); + + return null; + } + + /** + * This method use the information from authenticated session and + * evaluate the uniqueID flag according to eIDAS specification + * + * @param attrName eIDAS attribute name that is evaluated + * @param useMandate flag that indicates if the current authenticated session includes a mandate + * @return true if eIDAS attribute holds the unique ID, otherwise false + */ + private static boolean evaluateUniqueID(String attrName, boolean useMandate) { + //if no mandate is used the natural person identifier is the unique ID + if (!useMandate && + attrName.equals(eu.eidas.auth.engine.core.eidas.spec.NaturalPersonSpec.Definitions.PERSON_IDENTIFIER.getNameUri().toString())) + return true; + + //if mandates are used the the legal person identifier or the natural person identifier of the mandator is the unique ID + else if (useMandate && + attrName.equals(eu.eidas.auth.engine.core.eidas.spec.LegalPersonSpec.Definitions.LEGAL_PERSON_IDENTIFIER.getNameUri().toString())) + return true; + + //TODO: implement flag selector for mandates and natural persons + + + return false; + } + +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/eIDASAttributeProcessingUtils.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/eIDASAttributeProcessingUtils.java new file mode 100644 index 000000000..30e1e4505 --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/auth/modules/eidas/utils/eIDASAttributeProcessingUtils.java @@ -0,0 +1,75 @@ +/* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egovernment.moa.id.auth.modules.eidas.utils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; +import at.gv.egovernment.moa.id.data.Trible; +import at.gv.egovernment.moa.logging.Logger; + +/** + * @author tlenz + * + */ +public class eIDASAttributeProcessingUtils { + public static final String PERSONALIDENIFIER_VALIDATION_PATTERN = "^[A-Z,a-z]{2}/[A-Z,a-z]{2}/.*"; + + /** + * Validate a eIDAS PersonalIdentifier attribute value + * This validation is done according to eIDAS SAML Attribute Profile - Section 2.2.3 Unique Identifier + * + * @param uniqueID eIDAS attribute value of a unique identifier + * @return true if the uniqueID matches to eIDAS to Unique Identifier specification, otherwise false + */ + public static boolean validateEidasPersonalIdentifier(String uniqueID) { + Pattern pattern = Pattern.compile(PERSONALIDENIFIER_VALIDATION_PATTERN ); + Matcher matcher = pattern.matcher(uniqueID); + return matcher.matches(); + + } + + + /** + * Parse an eIDAS PersonalIdentifier attribute value into it components. + * This processing is done according to eIDAS SAML Attribute Profile - Section 2.2.3 Unique Identifier + * + * @param uniqueID eIDAS attribute value of a unique identifier + * @return {@link Trible} that contains: + * <br> First : citizen country + * <br> Second: destination country + * <br> Third : unique identifier + * <br> or null if the attribute value has a wrong format + */ + public static Trible<String, String, String> parseEidasPersonalIdentifier(String uniqueID) { + if (!validateEidasPersonalIdentifier(uniqueID)) { + Logger.error("eIDAS attribute value for " + Constants.eIDAS_ATTR_PERSONALIDENTIFIER + + " looks wrong formated. Value:" + ((String)uniqueID)); + return null; + + } + return Trible.newInstance(uniqueID.substring(0, 2), uniqueID.substring(3, 5), uniqueID.substring(6)); + + } +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASData.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASData.java index 7647b4cab..694efab80 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 @@ -15,6 +15,8 @@ import eu.eidas.auth.commons.protocol.IAuthenticationRequest; @Scope(value = BeanDefinition.SCOPE_PROTOTYPE) public class EIDASData extends RequestImpl { + public static final String REQ_PARAM_eIDAS_AUTHN_TRANSIENT_ID = "transiendIDRequested"; + /** The Constant serialVersionUID. */ private static final long serialVersionUID = 8765755670214923910L; @@ -28,7 +30,7 @@ public class EIDASData extends RequestImpl { private String remoteIPAddress; private String remoteRelayState; - + @Override public Collection<String> getRequestedAttributes(MetadataProvider metadataProvider) { // TODO Auto-generated method stub 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 13e64cdd0..5d13e26e2 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/EIDASProtocol.java @@ -25,6 +25,8 @@ package at.gv.egovernment.moa.id.protocols.eidas; import java.io.IOException; import java.io.StringWriter; import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -36,7 +38,6 @@ 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; import org.springframework.web.bind.annotation.RequestMethod; @@ -51,6 +52,7 @@ import at.gv.egovernment.moa.id.auth.modules.eidas.exceptions.EIDASAuthnRequestV 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.MOAIDConstants; 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; @@ -65,6 +67,7 @@ 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.commons.protocol.impl.SamlNameIdFormat; import eu.eidas.auth.engine.ProtocolEngineI; import eu.eidas.auth.engine.metadata.MetadataUtil; import eu.eidas.engine.exceptions.EIDASSAMLEngineException; @@ -149,7 +152,8 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { //preProcess eIDAS request preProcess(req, resp, pendingReq); - revisionsLogger.logEvent(pendingReq, Constants.eIDAS_REVERSIONSLOG_IDP_AUTHREQUEST); + revisionsLogger.logEvent(pendingReq, Constants.eIDAS_REVERSIONSLOG_IDP_AUTHREQUEST, + pendingReq.getEidasRequest().getId()); //AuthnRequest needs authentication pendingReq.setNeedAuthentication(true); @@ -213,6 +217,11 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { } + //check eIDAS node configuration + IOAAuthParameters oaConfig = authConfig.getOnlineApplicationParameter(samlReq.getIssuer()); + if (oaConfig == null) + throw new EIDASAuthnRequestProcessingException("eIDAS.08", new Object[]{samlReq.getIssuer()}); + //validate AssertionConsumerServiceURL against metadata EntityDescriptor eIDASNodeEntityDesc = new MOAeIDASMetadataProviderDecorator(eIDASMetadataProvider) .getEntityDescriptor(eIDASSamlReq.getIssuer(), SAMLEngineUtils.getMetadataSigner()); @@ -258,8 +267,33 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { } + //validate request country-code against eIDAS node config + String reqCC = samlReq.getOriginCountryCode(); + String eIDASTarget = oaConfig.getIdentityLinkDomainIdentifier(); + + //validate eIDAS target + Pattern pattern = Pattern.compile("^" + at.gv.egovernment.moa.util.Constants.URN_PREFIX_EIDAS + + "\\+[A-Z,a-z]{2}\\+[A-Z,a-z]{2}$"); + Matcher matcher = pattern.matcher(eIDASTarget); + if (MiscUtil.isEmpty(eIDASTarget) || !matcher.matches()) { + Logger.error("Configuration for eIDAS-node:" + samlReq.getIssuer() + + " contains wrong formated eIDAS target:" + eIDASTarget); + throw new MOAIDException("config.08", new Object[]{samlReq.getIssuer()}); + + } else { + String[] splittedTarget = eIDASTarget.split("\\+"); + if (!splittedTarget[2].equalsIgnoreCase(reqCC)) { + Logger.error("Configuration for eIDAS-node:" + samlReq.getIssuer() + + " Destination Country from request (" + reqCC + + ") does not match to configuration:" + eIDASTarget); + throw new MOAIDException("eIDAS.01", + new Object[]{"Destination Country from request does not match to configuration"}); + + } + Logger.debug("CountryCode from request matches eIDAS-node configuration target"); + } - + //************************************************* //***** store eIDAS request information ********* //************************************************* @@ -269,15 +303,18 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { // - memorize relaystate String relayState = request.getParameter("RelayState"); pendingReq.setRemoteRelayState(relayState); - - // - memorize country code of target country - pendingReq.setGenericDataToSession( - RequestImpl.eIDAS_GENERIC_REQ_DATA_COUNTRY, samlReq.getOriginCountryCode()); //store level of assurance pendingReq.setGenericDataToSession(RequestImpl.eIDAS_GENERIC_REQ_DATA_LEVELOFASSURENCE, eIDASSamlReq.getEidasLevelOfAssurance().stringValue()); + //set flag if transiend identifier is requested + if (MiscUtil.isNotEmpty(eIDASSamlReq.getNameIdFormat()) + && eIDASSamlReq.getNameIdFormat().equals(SamlNameIdFormat.TRANSIENT.getNameIdFormat())) + pendingReq.setGenericDataToSession(EIDASData.REQ_PARAM_eIDAS_AUTHN_TRANSIENT_ID, true); + else + pendingReq.setGenericDataToSession(EIDASData.REQ_PARAM_eIDAS_AUTHN_TRANSIENT_ID, false); + // - memorize requested attributes pendingReq.setEidasRequestedAttributes(eIDASSamlReq.getRequestedAttributes()); @@ -288,10 +325,6 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { pendingReq.setOAURL(samlReq.getIssuer()); // - memorize OA config - IOAAuthParameters oaConfig = authConfig.getOnlineApplicationParameter(pendingReq.getOAURL()); - if (oaConfig == null) - throw new EIDASAuthnRequestProcessingException("eIDAS.08", new Object[]{pendingReq.getOAURL()}); - pendingReq.setOnlineApplicationConfiguration(oaConfig); // - memorize service-provider type from eIDAS request @@ -302,7 +335,7 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { if (MiscUtil.isEmpty(spType)) spType = MetadataUtil.getSPTypeFromMetadata(eIDASNodeEntityDesc); - if (MiscUtil.isEmpty(spType)) + if (MiscUtil.isNotEmpty(spType)) Logger.debug("eIDAS request has SPType:" + spType); else Logger.info("eIDAS request and eIDAS metadata contains NO 'SPType' element."); @@ -367,7 +400,7 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { String token = EidasStringUtil.encodeToBase64(eIDASRespMsg.getMessageBytes()); VelocityEngine velocityEngine = VelocityProvider.getClassPathVelocityEngine(); - Template template = velocityEngine.getTemplate("/resources/templates/stork2_postbinding_template.html"); + Template template = velocityEngine.getTemplate("/resources/templates/eidas_postbinding_template.vm"); VelocityContext context = new VelocityContext(); context.put("RelayState", eidasReq.getRemoteRelayState()); @@ -387,7 +420,7 @@ public class EIDASProtocol extends AbstractAuthProtocolModulController { Logger.trace("Sending html content : " + new String(writer.getBuffer())); byte[] content = writer.getBuffer().toString().getBytes("UTF-8"); - response.setContentType(MediaType.TEXT_HTML.getType()); + response.setContentType(MOAIDConstants.DEFAULT_CONTENT_TYPE_HTML_UTF8); response.setContentLength(content.length); response.getOutputStream().write(content); 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 174fa2c17..df96bef12 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 @@ -72,13 +72,19 @@ public class EidasMetaDataRequest implements IAction { String metadata_url = pubURLPrefix + Constants.eIDAS_HTTP_ENDPOINT_METADATA; String sp_return_url = pubURLPrefix + Constants.eIDAS_HTTP_ENDPOINT_SP_POST; + //generate eIDAS metadata String metaData = generateMetadata(req, metadata_url, sp_return_url); - + + //write content to response + byte[] content = metaData.getBytes("UTF-8"); + httpResp.setStatus(HttpServletResponse.SC_OK); + httpResp.setContentType(MediaType.APPLICATION_XML.toString()); + httpResp.setContentLength(content.length); + httpResp.getOutputStream().write(content); + + //write log if required Logger.trace(metaData); - - httpResp.setContentType(MediaType.APPLICATION_XML.getType()); - httpResp.getWriter().print(metaData); - httpResp.flushBuffer(); + } catch (Exception e) { Logger.error("eIDAS Metadata generation FAILED.", e); throw new MOAIDException("eIDAS.05", new Object[]{e.getMessage()}, e); diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/IeIDASAttribute.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/IeIDASAttribute.java new file mode 100644 index 000000000..15060fb52 --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/IeIDASAttribute.java @@ -0,0 +1,33 @@ +/* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egovernment.moa.id.protocols.eidas.attributes.builder; + +import at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeBuilder; + +/** + * @author tlenz + * + */ +public interface IeIDASAttribute extends IAttributeBuilder{ + +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrDateOfBirth.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrDateOfBirth.java new file mode 100644 index 000000000..64e5ae770 --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrDateOfBirth.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egovernment.moa.id.protocols.eidas.attributes.builder; + +import at.gv.egovernment.moa.id.protocols.builder.attributes.BirthdateAttributeBuilder; + +/** + * @author tlenz + * + */ +public class eIDASAttrDateOfBirth extends BirthdateAttributeBuilder implements IeIDASAttribute { + + @Override + public String getName() { + return eu.eidas.auth.engine.core.eidas.spec.NaturalPersonSpec.Definitions.DATE_OF_BIRTH.getNameUri().toString(); + } +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrFamilyName.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrFamilyName.java new file mode 100644 index 000000000..4195eeeef --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrFamilyName.java @@ -0,0 +1,61 @@ +/* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egovernment.moa.id.protocols.eidas.attributes.builder; + +import at.gv.egovernment.moa.id.commons.api.IOAAuthParameters; +import at.gv.egovernment.moa.id.data.IAuthData; +import at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator; +import at.gv.egovernment.moa.id.protocols.pvp2x.builder.attributes.exceptions.AttributeException; + +/** + * @author tlenz + * + */ +public class eIDASAttrFamilyName implements IeIDASAttribute{ + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeBuilder#getName() + */ + @Override + public String getName() { + return eu.eidas.auth.engine.core.eidas.spec.NaturalPersonSpec.Definitions.CURRENT_FAMILY_NAME.getNameUri().toString(); + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeBuilder#build(at.gv.egovernment.moa.id.commons.api.IOAAuthParameters, at.gv.egovernment.moa.id.data.IAuthData, at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator) + */ + @Override + public <ATT> ATT build(IOAAuthParameters oaParam, IAuthData authData, IAttributeGenerator<ATT> g) + throws AttributeException { + return g.buildStringAttribute(null, getName(), authData.getFamilyName()); + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeBuilder#buildEmpty(at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator) + */ + @Override + public <ATT> ATT buildEmpty(IAttributeGenerator<ATT> g) { + return null; + } + +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrGivenName.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrGivenName.java new file mode 100644 index 000000000..2a654ac44 --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrGivenName.java @@ -0,0 +1,61 @@ +/* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egovernment.moa.id.protocols.eidas.attributes.builder; + +import at.gv.egovernment.moa.id.commons.api.IOAAuthParameters; +import at.gv.egovernment.moa.id.data.IAuthData; +import at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator; +import at.gv.egovernment.moa.id.protocols.pvp2x.builder.attributes.exceptions.AttributeException; + +/** + * @author tlenz + * + */ +public class eIDASAttrGivenName implements IeIDASAttribute{ + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeBuilder#getName() + */ + @Override + public String getName() { + return eu.eidas.auth.engine.core.eidas.spec.NaturalPersonSpec.Definitions.CURRENT_GIVEN_NAME.getNameUri().toString(); + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeBuilder#build(at.gv.egovernment.moa.id.commons.api.IOAAuthParameters, at.gv.egovernment.moa.id.data.IAuthData, at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator) + */ + @Override + public <ATT> ATT build(IOAAuthParameters oaParam, IAuthData authData, IAttributeGenerator<ATT> g) + throws AttributeException { + return g.buildStringAttribute(null, getName(), authData.getGivenName()); + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeBuilder#buildEmpty(at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator) + */ + @Override + public <ATT> ATT buildEmpty(IAttributeGenerator<ATT> g) { + return null; + } + +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrLegalName.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrLegalName.java new file mode 100644 index 000000000..51a2bd69b --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrLegalName.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egovernment.moa.id.protocols.eidas.attributes.builder; + +import at.gv.egovernment.moa.id.protocols.builder.attributes.MandateLegalPersonFullNameAttributeBuilder; + +/** + * @author tlenz + * + */ +public class eIDASAttrLegalName extends MandateLegalPersonFullNameAttributeBuilder implements IeIDASAttribute { + + @Override + public String getName() { + return eu.eidas.auth.engine.core.eidas.spec.LegalPersonSpec.Definitions.LEGAL_NAME.getNameUri().toString(); + } +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrLegalPersonIdentifier.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrLegalPersonIdentifier.java new file mode 100644 index 000000000..c008048cb --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrLegalPersonIdentifier.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egovernment.moa.id.protocols.eidas.attributes.builder; + +import at.gv.egovernment.moa.id.protocols.builder.attributes.MandateLegalPersonSourcePinAttributeBuilder; + +/** + * @author tlenz + * + */ +public class eIDASAttrLegalPersonIdentifier extends MandateLegalPersonSourcePinAttributeBuilder implements IeIDASAttribute { + + @Override + public String getName() { + return eu.eidas.auth.engine.core.eidas.spec.LegalPersonSpec.Definitions.LEGAL_PERSON_IDENTIFIER.getNameUri().toString(); + } +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrNaturalPersonalIdentifier.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrNaturalPersonalIdentifier.java new file mode 100644 index 000000000..cb659c2b1 --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/attributes/builder/eIDASAttrNaturalPersonalIdentifier.java @@ -0,0 +1,116 @@ +/* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egovernment.moa.id.protocols.eidas.attributes.builder; + +import java.security.MessageDigest; + +import at.gv.egovernment.moa.id.auth.modules.eidas.utils.eIDASAttributeProcessingUtils; +import at.gv.egovernment.moa.id.commons.api.IOAAuthParameters; +import at.gv.egovernment.moa.id.data.IAuthData; +import at.gv.egovernment.moa.id.data.Trible; +import at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator; +import at.gv.egovernment.moa.id.protocols.eidas.EIDASData; +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 at.gv.egovernment.moa.util.Base64Utils; +import at.gv.egovernment.moa.util.MiscUtil; + +/** + * @author tlenz + * + */ +public class eIDASAttrNaturalPersonalIdentifier implements IeIDASAttribute{ + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeBuilder#getName() + */ + @Override + public String getName() { + return eu.eidas.auth.engine.core.eidas.spec.NaturalPersonSpec.Definitions.PERSON_IDENTIFIER.getNameUri().toString(); + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeBuilder#build(at.gv.egovernment.moa.id.commons.api.IOAAuthParameters, at.gv.egovernment.moa.id.data.IAuthData, at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator) + */ + @Override + public <ATT> ATT build(IOAAuthParameters oaParam, IAuthData authData, IAttributeGenerator<ATT> g) + throws AttributeException { + String personalID = authData.getBPK(); + + //generate eIDAS conform 'PersonalIdentifier' attribute + if (!eIDASAttributeProcessingUtils.validateEidasPersonalIdentifier(personalID)) { + Logger.debug("preCalculated PersonalIdentifier does not include eIDAS conform prefixes ... add prefix now"); + if (MiscUtil.isEmpty(authData.getBPKType()) + || !authData.getBPKType().startsWith(at.gv.egovernment.moa.util.Constants.URN_PREFIX_EIDAS)) { + Logger.error("BPKType is empty or does not start with eIDAS bPKType prefix! bPKType:" + authData.getBPKType()); + throw new AttributeException("Suspect bPKType for eIDAS identifier generation"); + + } + + String prefix = authData.getBPKType().substring(at.gv.egovernment.moa.util.Constants.URN_PREFIX_EIDAS.length() + 1); + personalID = prefix.replaceAll("\\+", "/") + "/" + personalID; + + } + + //generate a transient unique identifier if it is requested + Boolean isTransiendIDRequested = + authData.getGenericData(EIDASData.REQ_PARAM_eIDAS_AUTHN_TRANSIENT_ID, Boolean.class); + if (isTransiendIDRequested != null && isTransiendIDRequested) + personalID = generateTransientNameID(personalID); + + return g.buildStringAttribute(null, getName(), personalID); + } + + /* (non-Javadoc) + * @see at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeBuilder#buildEmpty(at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeGenerator) + */ + @Override + public <ATT> ATT buildEmpty(IAttributeGenerator<ATT> g) { + return null; + } + + private String generateTransientNameID(String nameID) { + //extract source-country and destination country from persistent identifier + Trible<String, String, String> split = eIDASAttributeProcessingUtils.parseEidasPersonalIdentifier(nameID); + if (split == null) { + Logger.error("eIDAS 'PersonalIdentifier' has a wrong format. There had to be a ERROR in implementation!!!!"); + throw new IllegalStateException("eIDAS 'PersonalIdentifier' has a wrong format. There had to be a ERROR in implementation!!!!"); + + } + + //build correct formated transient identifier + String random = Random.nextLongRandom(); + try { + MessageDigest md = MessageDigest.getInstance("SHA-1"); + byte[] hash = md.digest((split.getThird() + random).getBytes("ISO-8859-1")); + return split.getFirst() + "/" + split.getSecond() + "/" + Base64Utils.encode(hash); + + } catch (Exception e) { + Logger.error("Can not generate transient personal identifier!", e); + return null; + + } + + } +} 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 22ac37604..d0cda38c7 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,6 @@ package at.gv.egovernment.moa.id.protocols.eidas; import java.io.StringWriter; -import java.security.MessageDigest; -import java.text.SimpleDateFormat; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -34,7 +32,6 @@ 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; @@ -44,30 +41,23 @@ import at.gv.egovernment.moa.id.auth.frontend.velocity.VelocityProvider; import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; import at.gv.egovernment.moa.id.auth.modules.eidas.engine.MOAeIDASChainingMetadataProvider; import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SimpleEidasAttributeGenerator; +import at.gv.egovernment.moa.id.auth.modules.eidas.utils.eIDASAttributeBuilder; +import at.gv.egovernment.moa.id.commons.MOAIDConstants; import at.gv.egovernment.moa.id.commons.api.IRequest; import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; import at.gv.egovernment.moa.id.data.IAuthData; +import at.gv.egovernment.moa.id.data.Pair; 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 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; @@ -95,82 +85,31 @@ public class eIDASAuthenticationRequest implements IAction { else throw new MOAIDException("got wrong IRequest type. is: {}, should be: {}", new String[] {req.getClass().toString(), EIDASData.class.toString()}); - + String subjectNameID = null; - + //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 + + //generate eIDAS attributes for(AttributeDefinition<?> attr : reqAttributeList.getDefinitions()) { - String newValue = ""; - 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; + Pair<AttributeDefinition<?>, ImmutableSet<AttributeValue<?>>> eIDASAttr = eIDASAttributeBuilder.buildAttribute( + attr, req.getOnlineApplicationConfiguration(), authData); - //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(MiscUtil.isEmpty(newValue)) { - Logger.info("eIDAS Attr:" + attr.getNameUri() + " is not available."); + if(eIDASAttr == null) { + if (attr.isRequired()) { + Logger.info("eIDAS Attr:" + attr.getNameUri() + " is marked as 'Required' but not available."); + throw new MOAIDException("eIDAS.15", new Object[]{attr.getFriendlyName()}); + + } else + Logger.info("eIDAS Attr:" + attr.getNameUri() + " is not available."); } else { - //set uniqueIdentifier attribute, because eIDAS SAMLEngine use this flag to select the - // 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()); + //add attribute to Map + attrMapBuilder.put( + (AttributeDefinition)eIDASAttr.getFirst(), + (ImmutableSet)eIDASAttr.getSecond()); } } @@ -207,19 +146,7 @@ public class eIDASAuthenticationRequest implements IAction { 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); - - + token = EidasStringUtil.encodeToBase64(eIDASRespMsg.getMessageBytes()); } catch(Exception e) { @@ -228,12 +155,13 @@ public class eIDASAuthenticationRequest implements IAction { } - revisionsLogger.logEvent(req, Constants.eIDAS_REVERSIONSLOG_IDP_AUTHREQUEST); + revisionsLogger.logEvent(req, Constants.eIDAS_REVERSIONSLOG_IDP_AUTHRESPONSE, + eIDASRespMsg.getResponse().getId()); // send the response try { VelocityEngine velocityEngine = VelocityProvider.getClassPathVelocityEngine(); - Template template = velocityEngine.getTemplate("/resources/templates/stork2_postbinding_template.html"); + Template template = velocityEngine.getTemplate("/resources/templates/eidas_postbinding_template.vm"); VelocityContext context = new VelocityContext(); context.put("RelayState", eidasRequest.getRemoteRelayState()); @@ -253,7 +181,7 @@ public class eIDASAuthenticationRequest implements IAction { Logger.trace("Sending html content : " + new String(writer.getBuffer())); byte[] content = writer.getBuffer().toString().getBytes("UTF-8"); - httpResp.setContentType(MediaType.TEXT_HTML.getType()); + httpResp.setContentType(MOAIDConstants.DEFAULT_CONTENT_TYPE_HTML_UTF8); httpResp.setContentLength(content.length); httpResp.getOutputStream().write(content); @@ -294,20 +222,6 @@ 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/java/at/gv/egovernment/moa/id/protocols/eidas/validator/eIDASResponseValidator.java b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/validator/eIDASResponseValidator.java new file mode 100644 index 000000000..f0527bc5e --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/java/at/gv/egovernment/moa/id/protocols/eidas/validator/eIDASResponseValidator.java @@ -0,0 +1,130 @@ +/* + * Copyright 2014 Federal Chancellery Austria + * MOA-ID has been developed in a cooperation between BRZ, the Federal + * Chancellery Austria - ICT staff unit, and Graz University of Technology. + * + * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by + * the European Commission - subsequent versions of the EUPL (the "Licence"); + * You may not use this work except in compliance with the Licence. + * You may obtain a copy of the Licence at: + * http://www.osor.eu/eupl/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the Licence is distributed on an "AS IS" basis, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the Licence for the specific language governing permissions and + * limitations under the Licence. + * + * This product combines work with different licenses. See the "NOTICE" text + * file for details on the various modules and licenses. + * The "NOTICE" text file is part of the distribution. Any derivative works + * that you distribute must include a readable copy of the "NOTICE" text file. + */ +package at.gv.egovernment.moa.id.protocols.eidas.validator; + +import at.gv.egovernment.moa.id.auth.modules.eidas.Constants; +import at.gv.egovernment.moa.id.auth.modules.eidas.utils.SAMLEngineUtils; +import at.gv.egovernment.moa.id.auth.modules.eidas.utils.eIDASAttributeProcessingUtils; +import at.gv.egovernment.moa.id.commons.api.IRequest; +import at.gv.egovernment.moa.id.commons.api.exceptions.MOAIDException; +import at.gv.egovernment.moa.id.data.Trible; +import at.gv.egovernment.moa.logging.Logger; +import at.gv.egovernment.moa.util.MiscUtil; +import eu.eidas.auth.commons.protocol.IAuthenticationResponse; +import eu.eidas.auth.commons.protocol.eidas.LevelOfAssurance; + +/** + * @author tlenz + * + */ +public class eIDASResponseValidator { + + + public static void validateResponse(IRequest pendingReq, IAuthenticationResponse samlResp, String spCountry) throws MOAIDException { + + /*-----------------------------------------------------| + * validate received LoA against minimum required LoA | + *_____________________________________________________| + */ + LevelOfAssurance reqLoA = LevelOfAssurance.fromString(pendingReq.getOnlineApplicationConfiguration().getQaaLevel()); + LevelOfAssurance respLoA = LevelOfAssurance.fromString(samlResp.getLevelOfAssurance()); + if (respLoA.numericValue() < reqLoA.numericValue()) { + Logger.error("eIDAS Response LevelOfAssurance is lower than the required! " + + "(Resp-LoA:" + respLoA.getValue() + " Req-LoA:" + reqLoA.getValue() + ")"); + throw new MOAIDException("eIDAS.14", new Object[]{respLoA.getValue()}); + + } + + /*-----------------------------------------------------| + * validate 'PersonalIdentifier' attribute | + *_____________________________________________________| + */ + String respCC = samlResp.getCountry(); + Object personalIdObj = samlResp.getAttributes().getFirstValue( + SAMLEngineUtils.getMapOfAllAvailableAttributes().get( + Constants.eIDAS_ATTR_PERSONALIDENTIFIER)); + + //check attribute type + if (personalIdObj == null || !(personalIdObj instanceof String)) + Logger.warn("eIDAS Response include NO 'PersonalIdentifier' attriubte " + + ".... That can be a BIG problem in further processing steps"); + + else { + //validate attribute value format + Trible<String, String, String> split = + eIDASAttributeProcessingUtils.parseEidasPersonalIdentifier((String)personalIdObj); + if (split == null) { + throw new MOAIDException("eIDAS.16", + new Object[]{ + Constants.eIDAS_ATTR_PERSONALIDENTIFIER, + "Wrong identifier format"}); + + } else { + //validation according to eIDAS SAML Attribute Profile, Section 2.2.3 + if (MiscUtil.isEmpty(split.getSecond())) { + Logger.error("eIDAS attribute value for " + Constants.eIDAS_ATTR_PERSONALIDENTIFIER + + " includes NO destination country. Value:" + ((String)personalIdObj)); + throw new MOAIDException("eIDAS.16", + new Object[]{ + Constants.eIDAS_ATTR_PERSONALIDENTIFIER, + "No or empty destination country"}); + + } + if (!split.getSecond().equalsIgnoreCase(spCountry)) { + Logger.error("eIDAS attribute value for " + Constants.eIDAS_ATTR_PERSONALIDENTIFIER + + " includes wrong destination country. Value:" + ((String)personalIdObj) + + " SP-Country:" + spCountry); + throw new MOAIDException("eIDAS.16", + new Object[]{ + Constants.eIDAS_ATTR_PERSONALIDENTIFIER, + "Destination country does not match to SP country"}); + + } + + if (MiscUtil.isEmpty(split.getFirst())) { + Logger.error("eIDAS attribute value for " + Constants.eIDAS_ATTR_PERSONALIDENTIFIER + + " includes NO citizen country. Value:" + ((String)personalIdObj)); + throw new MOAIDException("eIDAS.16", + new Object[]{ + Constants.eIDAS_ATTR_PERSONALIDENTIFIER, + "No or empty citizen country"}); + + } + if (!split.getFirst().equalsIgnoreCase(respCC)) { + Logger.error("eIDAS attribute value for " + Constants.eIDAS_ATTR_PERSONALIDENTIFIER + + " includes a citizen country that does not match to eIDAS Response node. " + + " Value:" + ((String)personalIdObj) + + " Response-Node Country:" + respCC); + throw new MOAIDException("eIDAS.16", + new Object[]{ + Constants.eIDAS_ATTR_PERSONALIDENTIFIER, + "Citizen country does not match to eIDAS-node country that generates the response"}); + + } + } + } + + + + } +} diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/resources/META-INF/services/at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeBuilder b/id/server/modules/moa-id-module-eIDAS/src/main/resources/META-INF/services/at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeBuilder new file mode 100644 index 000000000..62e7c20ab --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/resources/META-INF/services/at.gv.egovernment.moa.id.protocols.builder.attributes.IAttributeBuilder @@ -0,0 +1,6 @@ +at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.eIDASAttrDateOfBirth +at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.eIDASAttrFamilyName +at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.eIDASAttrGivenName +at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.eIDASAttrNaturalPersonalIdentifier +at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.eIDASAttrLegalPersonIdentifier +at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.eIDASAttrLegalName diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/resources/META-INF/services/at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.IeIDASAttribute b/id/server/modules/moa-id-module-eIDAS/src/main/resources/META-INF/services/at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.IeIDASAttribute new file mode 100644 index 000000000..62e7c20ab --- /dev/null +++ b/id/server/modules/moa-id-module-eIDAS/src/main/resources/META-INF/services/at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.IeIDASAttribute @@ -0,0 +1,6 @@ +at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.eIDASAttrDateOfBirth +at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.eIDASAttrFamilyName +at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.eIDASAttrGivenName +at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.eIDASAttrNaturalPersonalIdentifier +at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.eIDASAttrLegalPersonIdentifier +at.gv.egovernment.moa.id.protocols.eidas.attributes.builder.eIDASAttrLegalName diff --git a/id/server/modules/moa-id-module-eIDAS/src/main/resources/resources/templates/eidas_postbinding_template.vm b/id/server/modules/moa-id-module-eIDAS/src/main/resources/resources/templates/eidas_postbinding_template.vm index 3bd225b00..0535d48b6 100644 --- a/id/server/modules/moa-id-module-eIDAS/src/main/resources/resources/templates/eidas_postbinding_template.vm +++ b/id/server/modules/moa-id-module-eIDAS/src/main/resources/resources/templates/eidas_postbinding_template.vm @@ -7,7 +7,7 @@ ## SAMLRequest - String - the Base64 encoded SAML Request ## SAMLResponse - String - the Base64 encoded SAML Response ## Contains target attribute to delegate PEPS authentication out of iFrame - +<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> |