diff options
Diffstat (limited to 'connector/src/main/java/at/asitplus/eidas/specific/connector/verification')
2 files changed, 358 insertions, 331 deletions
diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/verification/AuthnRequestValidator.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/verification/AuthnRequestValidator.java index c49c0b28..0230521c 100644 --- a/connector/src/main/java/at/asitplus/eidas/specific/connector/verification/AuthnRequestValidator.java +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/verification/AuthnRequestValidator.java @@ -1,6 +1,6 @@ -/******************************************************************************* +/* * Copyright 2018 A-SIT Plus GmbH - * AT-specific eIDAS Connector has been developed in a cooperation between EGIZ, + * AT-specific eIDAS Connector has been developed in a cooperation between EGIZ, * A-SIT Plus GmbH, A-SIT, and Graz University of Technology. * * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by @@ -19,7 +19,8 @@ * 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.asitplus.eidas.specific.connector.verification; import java.util.ArrayList; @@ -31,8 +32,8 @@ import org.apache.commons.lang3.StringUtils; import org.opensaml.saml2.core.AuthnContextClassRef; import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration; import org.opensaml.saml2.core.AuthnRequest; -import org.opensaml.saml2.core.NameID; import org.opensaml.saml2.core.NameIDPolicy; +import org.opensaml.saml2.core.NameIDType; import org.opensaml.saml2.core.RequestedAuthnContext; import org.opensaml.saml2.core.Scoping; import org.opensaml.saml2.metadata.SPSSODescriptor; @@ -41,7 +42,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import at.asitplus.eidas.specific.connector.MSeIDASNodeConstants; +import at.asitplus.eidas.specific.connector.MsEidasNodeConstants; import at.asitplus.eidas.specific.connector.config.ServiceProviderConfiguration; import at.gv.egiz.eaaf.core.api.IRequest; import at.gv.egiz.eaaf.core.api.data.EaafConstants; @@ -58,215 +59,230 @@ import eu.eidas.auth.commons.protocol.eidas.LevelOfAssurance; public class AuthnRequestValidator implements IAuthnRequestValidator { - private static final Logger log = LoggerFactory.getLogger(AuthnRequestValidator.class); - - @Autowired(required=true) private IConfiguration basicConfig; - - @Override - public void validate(HttpServletRequest httpReq, IRequest pendingReq, AuthnRequest authnReq, - SPSSODescriptor spSSODescriptor) throws AuthnRequestValidatorException { - try { - //validate NameIDPolicy - NameIDPolicy nameIDPolicy = authnReq.getNameIDPolicy(); - if (nameIDPolicy != null) { - String nameIDFormat = nameIDPolicy.getFormat(); - if (nameIDFormat != null) { - if ( !(NameID.TRANSIENT.equals(nameIDFormat) || - NameID.PERSISTENT.equals(nameIDFormat)) ) { - - throw new NameIdFormatNotSupportedException(nameIDFormat); - - } - - } else - log.trace("Find NameIDPolicy, but NameIDFormat is 'null'"); - } else - log.trace("AuthnRequest includes no 'NameIDPolicy'"); - - - //post-process RequesterId - String spEntityId = extractScopeRequsterId(authnReq); - if (StringUtils.isEmpty(spEntityId)) { - log.info("NO service-provider entityID in Authn. request. Stop authn. process ... "); - throw new AuthnRequestValidatorException("pvp2.22", - new Object[] {"NO relaying-party entityID in Authn. request"}, pendingReq); - - } else - pendingReq.setRawDataToTransaction(MSeIDASNodeConstants.DATA_REQUESTERID, spEntityId); - - - //post-process ProviderName - String providerName = authnReq.getProviderName(); - if (StringUtils.isEmpty(providerName)) - log.info("Authn. request contains NO SP friendlyName"); - else - pendingReq.setRawDataToTransaction(MSeIDASNodeConstants.DATA_PROVIDERNAME, spEntityId); - - //post-process requested LoA - List<String> reqLoA = extractLoA(authnReq); - - LevelOfAssurance minimumLoAFromConfig = LevelOfAssurance.fromString(basicConfig.getBasicConfiguration( - MSeIDASNodeConstants.PROP_EIDAS_REQUEST_LOA_MINIMUM_LEVEL, - EaafConstants.EIDAS_LOA_HIGH)); - if (minimumLoAFromConfig == null) { - log.warn("Can not load minimum LoA from configuration. Use LoA: {} as default", EaafConstants.EIDAS_LOA_HIGH); - minimumLoAFromConfig = LevelOfAssurance.HIGH; - - } - - log.trace("Validate requested LoA to connector configuration minimum LoA: {} ...", minimumLoAFromConfig); - List<String> allowedLoA = new ArrayList<>(); - for (String loa : reqLoA) { - try { - LevelOfAssurance intLoa = LevelOfAssurance.fromString(loa); - String selectedLoA = EaafConstants.EIDAS_LOA_HIGH; - if (intLoa != null && - intLoa.numericValue() >= minimumLoAFromConfig.numericValue()) { - log.info("Client: {} requested LoA: {} will be upgraded to: {}", - pendingReq.getServiceProviderConfiguration().getUniqueIdentifier(), - loa, - minimumLoAFromConfig); - selectedLoA = intLoa.getValue(); - - } - - if (!allowedLoA.contains(selectedLoA)) { - log.debug("Allow LoA: {} for Client: {}", - selectedLoA, - pendingReq.getServiceProviderConfiguration().getUniqueIdentifier()); - allowedLoA.add(selectedLoA); - - } - - } catch (IllegalArgumentException e) { - log.warn("LoA: {} is currently NOT supported and it will be ignored.", loa); - - } - - } - - pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class).setRequiredLoA(allowedLoA); - - //post-process requested LoA comparison-level - String reqLoAComperison = extractComparisonLevel(authnReq); - pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class).setLoAMachtingMode(reqLoAComperison); - - //validate and process requested attributes - boolean sectorDetected = false; - List<XMLObject> requestedAttributes = authnReq.getExtensions().getUnknownXMLObjects(); - for (XMLObject reqAttrObj : requestedAttributes) { - if (reqAttrObj instanceof EaafRequestedAttributes) { - EaafRequestedAttributes reqAttr = (EaafRequestedAttributes)reqAttrObj; - if (reqAttr.getAttributes() != null && reqAttr.getAttributes().size() != 0 ) { - for (EaafRequestedAttribute el : reqAttr.getAttributes()) { - log.trace("Processing req. attribute '" + el.getName() + "' ... "); - if (el.getName().equals(PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME)) { - if (el.getAttributeValues() != null && el.getAttributeValues().size() == 1) { - String sectorId = el.getAttributeValues().get(0).getDOM().getTextContent(); - ServiceProviderConfiguration spConfig = pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class); - - try { - spConfig.setbPKTargetIdentifier(sectorId); - sectorDetected = true; - - } catch (EaafException e) { - log.info("Requested sector: " + sectorId + " DOES NOT match to allowed sectors for SP: " + spConfig.getUniqueIdentifier()); - } - - } else - log.info("Req. attribute '" + el.getName() + "' contains NO or MORE THEN ONE attribute-values. Ignore full req. attribute"); - - } else - log.debug("Ignore req. attribute: " + el.getName()); - - } - - } else - log.debug("No requested Attributes in Authn. Request"); - - } else - log.info("Ignore unknown requested attribute: " + reqAttrObj.getElementQName().toString()); - - } - - if (!sectorDetected) { - log.info("Authn.Req validation FAILED. Reason: Contains NO or NO VALID target-sector information."); - throw new AuthnRequestValidatorException("pvp2.22", new Object[] {"NO or NO VALID target-sector information"}); - - } - - } catch (EaafStorageException e) { - log.info("Can NOT store Authn. Req. data into pendingRequest." , e); - throw new AuthnRequestValidatorException("internal.02", null, e); - - } - - } - - private String extractComparisonLevel(AuthnRequest authnReq) { - if (authnReq.getRequestedAuthnContext() != null) { - RequestedAuthnContext authContext = authnReq.getRequestedAuthnContext(); - return authContext.getComparison().toString(); - - } - - return null; - } - - private List<String> extractLoA(AuthnRequest authnReq) throws AuthnRequestValidatorException { - List<String> result = new ArrayList<String>(); - if (authnReq.getRequestedAuthnContext() != null) { - RequestedAuthnContext authContext = authnReq.getRequestedAuthnContext(); - if (authContext.getComparison().equals(AuthnContextComparisonTypeEnumeration.MINIMUM)) { - if (authContext.getAuthnContextClassRefs().isEmpty()) { - log.debug("Authn. Req. contains no requested LoA"); - - } else if (authContext.getAuthnContextClassRefs().size() > 1) { - log.info("Authn. Req. contains MORE THAN ONE requested LoA, but " - + AuthnContextComparisonTypeEnumeration.MINIMUM + " allows only one" ); - throw new AuthnRequestValidatorException("pvp2.22", - new Object[] {"Authn. Req. contains MORE THAN ONE requested LoA, but " - + AuthnContextComparisonTypeEnumeration.MINIMUM + " allows only one"}); - - } else - result.add(authContext.getAuthnContextClassRefs().get(0).getAuthnContextClassRef()); - - } else if (authContext.getComparison().equals(AuthnContextComparisonTypeEnumeration.EXACT)) { - for (AuthnContextClassRef el : authContext.getAuthnContextClassRefs()) - result.add(el.getAuthnContextClassRef()); - - } else { - log.info("Currently only '" + AuthnContextComparisonTypeEnumeration.MINIMUM + "' and '" - + AuthnContextComparisonTypeEnumeration.EXACT + "' are supported"); - throw new AuthnRequestValidatorException("pvp2.22", - new Object[] {"Currently only '" + AuthnContextComparisonTypeEnumeration.MINIMUM + "' and '" - + AuthnContextComparisonTypeEnumeration.EXACT + "' are supported"}); - - } - - } - - return result; - } - - private String extractScopeRequsterId(AuthnRequest authnReq) { - if (authnReq.getScoping() != null) { - Scoping scoping = authnReq.getScoping(); - if (scoping.getRequesterIDs() != null && - scoping.getRequesterIDs().size() > 0) { - if (scoping.getRequesterIDs().size() == 1) - return scoping.getRequesterIDs().get(0).getRequesterID(); - - else { - log.info("Authn. request contains more than on RequesterIDs! Only use first one"); - return scoping.getRequesterIDs().get(0).getRequesterID(); - - } - } - } - - return null; - } - + private static final Logger log = LoggerFactory.getLogger(AuthnRequestValidator.class); + + @Autowired(required = true) + private IConfiguration basicConfig; + + @Override + public void validate(HttpServletRequest httpReq, IRequest pendingReq, AuthnRequest authnReq, + SPSSODescriptor spSsoDescriptor) throws AuthnRequestValidatorException { + try { + // validate NameIDPolicy + final NameIDPolicy nameIdPolicy = authnReq.getNameIDPolicy(); + if (nameIdPolicy != null) { + final String nameIdFormat = nameIdPolicy.getFormat(); + if (nameIdFormat != null) { + if (!(NameIDType.TRANSIENT.equals(nameIdFormat) + || NameIDType.PERSISTENT.equals(nameIdFormat))) { + + throw new NameIdFormatNotSupportedException(nameIdFormat); + + } + + } else { + log.trace("Find NameIDPolicy, but NameIDFormat is 'null'"); + } + } else { + log.trace("AuthnRequest includes no 'NameIDPolicy'"); + } + + // post-process RequesterId + final String spEntityId = extractScopeRequsterId(authnReq); + if (StringUtils.isEmpty(spEntityId)) { + log.info("NO service-provider entityID in Authn. request. Stop authn. process ... "); + throw new AuthnRequestValidatorException("pvp2.22", + new Object[] { "NO relaying-party entityID in Authn. request" }, pendingReq); + + } else { + pendingReq.setRawDataToTransaction(MsEidasNodeConstants.DATA_REQUESTERID, spEntityId); + } + + // post-process ProviderName + final String providerName = authnReq.getProviderName(); + if (StringUtils.isEmpty(providerName)) { + log.info("Authn. request contains NO SP friendlyName"); + } else { + pendingReq.setRawDataToTransaction(MsEidasNodeConstants.DATA_PROVIDERNAME, spEntityId); + } + + // post-process requested LoA + final List<String> reqLoA = extractLoA(authnReq); + + LevelOfAssurance minimumLoAFromConfig = LevelOfAssurance.fromString(basicConfig.getBasicConfiguration( + MsEidasNodeConstants.PROP_EIDAS_REQUEST_LOA_MINIMUM_LEVEL, + EaafConstants.EIDAS_LOA_HIGH)); + if (minimumLoAFromConfig == null) { + log.warn("Can not load minimum LoA from configuration. Use LoA: {} as default", + EaafConstants.EIDAS_LOA_HIGH); + minimumLoAFromConfig = LevelOfAssurance.HIGH; + + } + + log.trace("Validate requested LoA to connector configuration minimum LoA: {} ...", + minimumLoAFromConfig); + final List<String> allowedLoA = new ArrayList<>(); + for (final String loa : reqLoA) { + try { + final LevelOfAssurance intLoa = LevelOfAssurance.fromString(loa); + String selectedLoA = EaafConstants.EIDAS_LOA_HIGH; + if (intLoa != null + && intLoa.numericValue() >= minimumLoAFromConfig.numericValue()) { + log.info("Client: {} requested LoA: {} will be upgraded to: {}", + pendingReq.getServiceProviderConfiguration().getUniqueIdentifier(), + loa, + minimumLoAFromConfig); + selectedLoA = intLoa.getValue(); + + } + + if (!allowedLoA.contains(selectedLoA)) { + log.debug("Allow LoA: {} for Client: {}", + selectedLoA, + pendingReq.getServiceProviderConfiguration().getUniqueIdentifier()); + allowedLoA.add(selectedLoA); + + } + + } catch (final IllegalArgumentException e) { + log.warn("LoA: {} is currently NOT supported and it will be ignored.", loa); + + } + + } + + pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class).setRequiredLoA( + allowedLoA); + + // post-process requested LoA comparison-level + final String reqLoAComperison = extractComparisonLevel(authnReq); + pendingReq.getServiceProviderConfiguration(ServiceProviderConfiguration.class).setLoAMachtingMode( + reqLoAComperison); + + // validate and process requested attributes + boolean sectorDetected = false; + final List<XMLObject> requestedAttributes = authnReq.getExtensions().getUnknownXMLObjects(); + for (final XMLObject reqAttrObj : requestedAttributes) { + if (reqAttrObj instanceof EaafRequestedAttributes) { + final EaafRequestedAttributes reqAttr = (EaafRequestedAttributes) reqAttrObj; + if (reqAttr.getAttributes() != null && reqAttr.getAttributes().size() != 0) { + for (final EaafRequestedAttribute el : reqAttr.getAttributes()) { + log.trace("Processing req. attribute '" + el.getName() + "' ... "); + if (el.getName().equals(PvpAttributeDefinitions.EID_SECTOR_FOR_IDENTIFIER_NAME)) { + if (el.getAttributeValues() != null && el.getAttributeValues().size() == 1) { + final String sectorId = el.getAttributeValues().get(0).getDOM().getTextContent(); + final ServiceProviderConfiguration spConfig = pendingReq.getServiceProviderConfiguration( + ServiceProviderConfiguration.class); + + try { + spConfig.setBpkTargetIdentifier(sectorId); + sectorDetected = true; + + } catch (final EaafException e) { + log.info("Requested sector: " + sectorId + " DOES NOT match to allowed sectors for SP: " + + spConfig.getUniqueIdentifier()); + } + + } else { + log.info("Req. attribute '" + el.getName() + + "' contains NO or MORE THEN ONE attribute-values. Ignore full req. attribute"); + } + + } else { + log.debug("Ignore req. attribute: " + el.getName()); + } + + } + + } else { + log.debug("No requested Attributes in Authn. Request"); + } + + } else { + log.info("Ignore unknown requested attribute: " + reqAttrObj.getElementQName().toString()); + } + + } + + if (!sectorDetected) { + log.info("Authn.Req validation FAILED. Reason: Contains NO or NO VALID target-sector information."); + throw new AuthnRequestValidatorException("pvp2.22", new Object[] { + "NO or NO VALID target-sector information" }); + + } + + } catch (final EaafStorageException e) { + log.info("Can NOT store Authn. Req. data into pendingRequest.", e); + throw new AuthnRequestValidatorException("internal.02", null, e); + + } + + } + + private String extractComparisonLevel(AuthnRequest authnReq) { + if (authnReq.getRequestedAuthnContext() != null) { + final RequestedAuthnContext authContext = authnReq.getRequestedAuthnContext(); + return authContext.getComparison().toString(); + + } + + return null; + } + + private List<String> extractLoA(AuthnRequest authnReq) throws AuthnRequestValidatorException { + final List<String> result = new ArrayList<>(); + if (authnReq.getRequestedAuthnContext() != null) { + final RequestedAuthnContext authContext = authnReq.getRequestedAuthnContext(); + if (authContext.getComparison().equals(AuthnContextComparisonTypeEnumeration.MINIMUM)) { + if (authContext.getAuthnContextClassRefs().isEmpty()) { + log.debug("Authn. Req. contains no requested LoA"); + + } else if (authContext.getAuthnContextClassRefs().size() > 1) { + log.info("Authn. Req. contains MORE THAN ONE requested LoA, but " + + AuthnContextComparisonTypeEnumeration.MINIMUM + " allows only one"); + throw new AuthnRequestValidatorException("pvp2.22", + new Object[] { "Authn. Req. contains MORE THAN ONE requested LoA, but " + + AuthnContextComparisonTypeEnumeration.MINIMUM + " allows only one" }); + + } else { + result.add(authContext.getAuthnContextClassRefs().get(0).getAuthnContextClassRef()); + } + + } else if (authContext.getComparison().equals(AuthnContextComparisonTypeEnumeration.EXACT)) { + for (final AuthnContextClassRef el : authContext.getAuthnContextClassRefs()) { + result.add(el.getAuthnContextClassRef()); + } + + } else { + log.info("Currently only '" + AuthnContextComparisonTypeEnumeration.MINIMUM + "' and '" + + AuthnContextComparisonTypeEnumeration.EXACT + "' are supported"); + throw new AuthnRequestValidatorException("pvp2.22", + new Object[] { "Currently only '" + AuthnContextComparisonTypeEnumeration.MINIMUM + "' and '" + + AuthnContextComparisonTypeEnumeration.EXACT + "' are supported" }); + + } + + } + + return result; + } + + private String extractScopeRequsterId(AuthnRequest authnReq) { + if (authnReq.getScoping() != null) { + final Scoping scoping = authnReq.getScoping(); + if (scoping.getRequesterIDs() != null + && scoping.getRequesterIDs().size() > 0) { + if (scoping.getRequesterIDs().size() == 1) { + return scoping.getRequesterIDs().get(0).getRequesterID(); + } else { + log.info("Authn. request contains more than on RequesterIDs! Only use first one"); + return scoping.getRequesterIDs().get(0).getRequesterID(); + + } + } + } + + return null; + } } diff --git a/connector/src/main/java/at/asitplus/eidas/specific/connector/verification/MetadataSignatureVerificationFilter.java b/connector/src/main/java/at/asitplus/eidas/specific/connector/verification/MetadataSignatureVerificationFilter.java index eeaea135..b6dd249a 100644 --- a/connector/src/main/java/at/asitplus/eidas/specific/connector/verification/MetadataSignatureVerificationFilter.java +++ b/connector/src/main/java/at/asitplus/eidas/specific/connector/verification/MetadataSignatureVerificationFilter.java @@ -1,6 +1,6 @@ -/******************************************************************************* +/* * Copyright 2018 A-SIT Plus GmbH - * AT-specific eIDAS Connector has been developed in a cooperation between EGIZ, + * AT-specific eIDAS Connector has been developed in a cooperation between EGIZ, * A-SIT Plus GmbH, A-SIT, and Graz University of Technology. * * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by @@ -19,9 +19,8 @@ * 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.asitplus.eidas.specific.connector.verification; import java.io.IOException; @@ -49,116 +48,128 @@ import at.gv.egiz.eaaf.modules.pvp2.exception.Pvp2MetadataException; import at.gv.egiz.eaaf.modules.pvp2.idp.exception.SamlRequestNotSignedException; import at.gv.egiz.eaaf.modules.pvp2.impl.validation.metadata.AbstractMetadataSignatureFilter; -public class MetadataSignatureVerificationFilter extends AbstractMetadataSignatureFilter{ - private static final Logger log = LoggerFactory.getLogger(MetadataSignatureVerificationFilter.class); - - private String metadataURL; - private List<BasicX509Credential> trustedCredential = new ArrayList<BasicX509Credential>(); - - public MetadataSignatureVerificationFilter(String trustStorePath, String trustStorePassword, String metadataURL) - throws Pvp2MetadataException { - this.metadataURL = metadataURL; - - log.trace("Initialize metadata signature-verification filter with truststore: " + trustStorePath + " ... "); - try { - KeyStore keyStore = KeyStoreUtils.loadKeyStore(trustStorePath, trustStorePassword); - if (keyStore != null) { - //load trusted certificates - Enumeration<String> aliases = keyStore.aliases(); - while(aliases.hasMoreElements()) { - String el = aliases.nextElement(); - log.trace("Process TrustStoreEntry: " + el); - if (keyStore.isCertificateEntry(el)) { - Certificate cert = keyStore.getCertificate(el); - if (cert != null && cert instanceof X509Certificate) { - BasicX509Credential trustedCert = new BasicX509Credential(); - trustedCert.setEntityCertificate((X509Certificate) cert); - this.trustedCredential.add(trustedCert); - log.debug("Add cert: " + ((X509Certificate) cert).getSubjectDN() + " as trusted for metadata: " + metadataURL); - - } else - log.info("Can not process entry: " + el + ". Reason: " + cert.toString()); - - } - } - - - } else - throw new Pvp2MetadataException("pvp2.26", - new Object[] {"Can not open trustStore: " + trustStorePath + " for metadata: " + metadataURL}); - - } catch (KeyStoreException | IOException e) { - log.warn("Can not open trustStore: " + trustStorePath + " for metadata: " + metadataURL + " Reason: " + e.getMessage(), e); - throw new Pvp2MetadataException("pvp2.26", - new Object[] {"Can not open trustStore: " + trustStorePath + " for metadata"}, e); - - } - - - } - - - @Override - protected void verify(EntityDescriptor desc) throws Pvp2MetadataException { - try { - internalVerify(desc); - - } catch (EaafException e) { - log.info("Metadata verification FAILED for: " + metadataURL + " Reason: " +e.getMessage()); - throw new Pvp2MetadataException("pvp2.26", - new Object[] {"Metadata verification FAILED for: " + metadataURL + " Reason: " +e.getMessage()}, e); - - } - } - - @Override - protected void verify(EntitiesDescriptor desc) throws Pvp2MetadataException { - throw new Pvp2MetadataException("pvp2.26", - new Object[] {"EntitiesDescritors are NOT supported"}); - - } - - @Override - protected void verify(EntityDescriptor entity, EntitiesDescriptor desc) throws Pvp2MetadataException { - throw new Pvp2MetadataException("pvp2.26", - new Object[] {"EntitiesDescritors are NOT supported"}); - - } - - private void internalVerify(SignableSAMLObject signedElement) - throws EaafException { - if (signedElement.getSignature() == null) { - throw new SamlRequestNotSignedException(); - } - - try { - SAMLSignatureProfileValidator sigValidator = new SAMLSignatureProfileValidator(); - sigValidator.validate(signedElement.getSignature()); - } catch (ValidationException e) { - log.error("Failed to validate Signature", e); - throw new SamlRequestNotSignedException(e); - } - - boolean isTrusted = false; - for (BasicX509Credential cred : trustedCredential) { - SignatureValidator sigValidator = new SignatureValidator(cred); - try { - sigValidator.validate(signedElement.getSignature()); - isTrusted = true; - - } catch (ValidationException e) { - log.info("Failed to verfiy Signature with cert: " + cred.getEntityCertificate().getSubjectDN() - + " Reason: " + e.getMessage()); - - } - } - - if (!isTrusted) { - log.warn("PVP2 metadata: " + metadataURL + " are NOT trusted!"); - throw new SamlRequestNotSignedException(); - - } - - } +public class MetadataSignatureVerificationFilter extends AbstractMetadataSignatureFilter { + private static final Logger log = LoggerFactory.getLogger(MetadataSignatureVerificationFilter.class); + + private final String metadataUrl; + private final List<BasicX509Credential> trustedCredential = new ArrayList<>(); + + /** + * SAML2 Metadata signature verifier that checks signer certificates based on local TrustStores. + * + * @param trustStorePath Path to truststore + * @param trustStorePassword TrustStore password + * @param metadataUrl URL to PVP2 metadata + * @throws Pvp2MetadataException In case of a verification error + */ + public MetadataSignatureVerificationFilter(String trustStorePath, String trustStorePassword, + String metadataUrl) + throws Pvp2MetadataException { + this.metadataUrl = metadataUrl; + + log.trace("Initialize metadata signature-verification filter with truststore: " + trustStorePath + + " ... "); + try { + final KeyStore keyStore = KeyStoreUtils.loadKeyStore(trustStorePath, trustStorePassword); + if (keyStore != null) { + // load trusted certificates + final Enumeration<String> aliases = keyStore.aliases(); + while (aliases.hasMoreElements()) { + final String el = aliases.nextElement(); + log.trace("Process TrustStoreEntry: " + el); + if (keyStore.isCertificateEntry(el)) { + final Certificate cert = keyStore.getCertificate(el); + if (cert != null && cert instanceof X509Certificate) { + final BasicX509Credential trustedCert = new BasicX509Credential(); + trustedCert.setEntityCertificate((X509Certificate) cert); + this.trustedCredential.add(trustedCert); + log.debug("Add cert: " + ((X509Certificate) cert).getSubjectDN() + " as trusted for metadata: " + + metadataUrl); + + } else { + log.info("Can not process entry: " + el + ". Reason: is null"); + } + + } + } + + } else { + throw new Pvp2MetadataException("pvp2.26", + new Object[] { "Can not open trustStore: " + trustStorePath + " for metadata: " + metadataUrl }); + } + + } catch (KeyStoreException | IOException e) { + log.warn("Can not open trustStore: " + trustStorePath + " for metadata: " + metadataUrl + " Reason: " + + e.getMessage(), e); + throw new Pvp2MetadataException("pvp2.26", + new Object[] { "Can not open trustStore: " + trustStorePath + " for metadata" }, e); + + } + + } + + @Override + protected void verify(EntityDescriptor desc) throws Pvp2MetadataException { + try { + internalVerify(desc); + + } catch (final EaafException e) { + log.info("Metadata verification FAILED for: " + metadataUrl + " Reason: " + e.getMessage()); + throw new Pvp2MetadataException("pvp2.26", + new Object[] { "Metadata verification FAILED for: " + metadataUrl + " Reason: " + e.getMessage() }, + e); + + } + } + + @Override + protected void verify(EntitiesDescriptor desc) throws Pvp2MetadataException { + throw new Pvp2MetadataException("pvp2.26", + new Object[] { "EntitiesDescritors are NOT supported" }); + + } + + @Override + protected void verify(EntityDescriptor entity, EntitiesDescriptor desc) throws Pvp2MetadataException { + throw new Pvp2MetadataException("pvp2.26", + new Object[] { "EntitiesDescritors are NOT supported" }); + + } + + private void internalVerify(SignableSAMLObject signedElement) + throws EaafException { + if (signedElement.getSignature() == null) { + throw new SamlRequestNotSignedException(); + } + + try { + final SAMLSignatureProfileValidator sigValidator = new SAMLSignatureProfileValidator(); + sigValidator.validate(signedElement.getSignature()); + } catch (final ValidationException e) { + log.error("Failed to validate Signature", e); + throw new SamlRequestNotSignedException(e); + } + + boolean isTrusted = false; + for (final BasicX509Credential cred : trustedCredential) { + final SignatureValidator sigValidator = new SignatureValidator(cred); + try { + sigValidator.validate(signedElement.getSignature()); + isTrusted = true; + + } catch (final ValidationException e) { + log.info("Failed to verfiy Signature with cert: " + cred.getEntityCertificate().getSubjectDN() + + " Reason: " + e.getMessage()); + + } + } + + if (!isTrusted) { + log.warn("PVP2 metadata: " + metadataUrl + " are NOT trusted!"); + throw new SamlRequestNotSignedException(); + + } + + } } |