All Downloads are FREE. Search and download functionalities are using the official Maven repository.

se.swedenconnect.opensaml.common.validation.AbstractSignableObjectValidator Maven / Gradle / Ivy

/*
 * Copyright 2016-2021 Sweden Connect
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package se.swedenconnect.opensaml.common.validation;

import org.opensaml.core.criterion.EntityIdCriterion;
import org.opensaml.saml.common.assertion.ValidationContext;
import org.opensaml.saml.common.assertion.ValidationResult;
import org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters;
import org.opensaml.security.SecurityException;
import org.opensaml.security.credential.UsageType;
import org.opensaml.security.criteria.UsageCriterion;
import org.opensaml.xmlsec.signature.SignableXMLObject;
import org.opensaml.xmlsec.signature.Signature;
import org.opensaml.xmlsec.signature.support.SignatureException;
import org.opensaml.xmlsec.signature.support.SignaturePrevalidator;
import org.opensaml.xmlsec.signature.support.SignatureTrustEngine;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Attr;

import net.shibboleth.utilities.java.support.resolver.CriteriaSet;

/**
 * Abstract object validator that supports validating signatures.
 * 
 * 

* Supports the following {@link ValidationContext} static parameters: *

*
    *
  • The static parameters defined in {@link AbstractObjectValidator}.
  • *
  • {@link SAML2AssertionValidationParameters#SIGNATURE_REQUIRED}: Optional. If not supplied, defaults to 'true'. If an object * is signed, the signature is always evaluated and the result factored into the overall validation result, regardless * of the value of this setting.
  • *
  • {@link SAML2AssertionValidationParameters#SIGNATURE_VALIDATION_CRITERIA_SET}: Optional. If not supplied, a minimal criteria * set will be constructed which contains an {@link EntityIdCriterion} containing the Issuer entityID, and a * {@link UsageCriterion} of {@link UsageType#SIGNING}. If it is supplied, but either of those criteria are absent from * the criteria set, they will be added with the above values.
  • *
* * @author Martin Lindström ([email protected]) * * @param * the type of the object that is to be validated */ public abstract class AbstractSignableObjectValidator extends AbstractObjectValidator { /** Class logger. */ private final Logger log = LoggerFactory.getLogger(AbstractSignableObjectValidator.class); /** Trust engine for signature evaluation. */ protected SignatureTrustEngine trustEngine; /** SAML signature profile validator. */ protected SignaturePrevalidator signaturePrevalidator; /** * Constructor. * * @param trustEngine * the trust used to validate the object's signature * @param signaturePrevalidator * the signature pre-validator used to pre-validate the object's signature */ public AbstractSignableObjectValidator(final SignatureTrustEngine trustEngine, final SignaturePrevalidator signaturePrevalidator) { this.trustEngine = trustEngine; this.signaturePrevalidator = signaturePrevalidator; } /** * Validates the signature of the assertion, if it is signed. * * @param token * assertion whose signature will be validated * @param context * current validation context * @return the result of the signature validation */ protected ValidationResult validateSignature(final T token, final ValidationContext context) { Boolean signatureRequired = (Boolean) context.getStaticParameters().get(SAML2AssertionValidationParameters.SIGNATURE_REQUIRED); if (signatureRequired == null) { signatureRequired = Boolean.TRUE; } // Validate params and requirements. if (!token.isSigned()) { if (signatureRequired) { context.setValidationFailureMessage(String.format("%s was required to be signed, but was not", this.getObjectName())); return ValidationResult.INVALID; } else { log.debug(String.format("%s was not required to be signed, and was not signed. Skipping further signature evaluation", this.getObjectName())); return ValidationResult.VALID; } } if (trustEngine == null) { log.warn("Signature validation was necessary, but no signature trust engine was available"); context.setValidationFailureMessage(String.format( "%s signature could not be evaluated due to internal error", this.getObjectName())); return ValidationResult.INDETERMINATE; } return this.performSignatureValidation(token, context); } /** * Handles the actual signature validation. * * @param token * object whose signature will be validated * @param context * current validation context * * @return the validation result */ protected ValidationResult performSignatureValidation(final T token, final ValidationContext context) { // Temporary code until we figure out how to make the OpenSAML unmarshaller to // mark the ID attribute as an ID. // final Attr idAttr = token.getDOM().getAttributeNode("ID"); if (idAttr != null) { idAttr.getOwnerElement().setIdAttributeNode(idAttr, true); } final Signature signature = token.getSignature(); final String tokenIssuer = this.getIssuer(token); log.debug("Attempting signature validation on {} '{}' from Issuer '{}'", this.getObjectName(), this.getID(token), tokenIssuer); try { signaturePrevalidator.validate(signature); } catch (SignatureException e) { final String msg = String.format("%s Signature failed pre-validation: %s", this.getObjectName(), e.getMessage()); log.warn(msg); context.setValidationFailureMessage(msg); return ValidationResult.INVALID; } final CriteriaSet criteriaSet = this.getSignatureValidationCriteriaSet(token, context); try { if (trustEngine.validate(signature, criteriaSet)) { log.debug("Validation of signature of {} '{}' from Issuer '{}' was successful", this.getObjectName(), this.getID(token), tokenIssuer); return ValidationResult.VALID; } else { final String msg = String.format( "Signature of %s '%s' from Issuer '%s' was not valid", this.getObjectName(), this.getID(token), tokenIssuer); log.warn(msg); context.setValidationFailureMessage(msg); return ValidationResult.INVALID; } } catch (SecurityException e) { final String msg = String.format("A problem was encountered evaluating the signature over %s with ID '%s': %s", this.getObjectName(), this.getID(token), e.getMessage()); log.warn(msg); context.setValidationFailureMessage(msg); return ValidationResult.INVALID; } } /** * Get the criteria set that will be used in evaluating the Assertion signature via the supplied trust engine. * * @param token * object whose signature will be validated * @param context * current validation context * @return the criteria set to use */ protected CriteriaSet getSignatureValidationCriteriaSet(final T token, final ValidationContext context) { CriteriaSet criteriaSet = (CriteriaSet) context.getStaticParameters() .get(SAML2AssertionValidationParameters.SIGNATURE_VALIDATION_CRITERIA_SET); if (criteriaSet == null) { criteriaSet = new CriteriaSet(); } if (!criteriaSet.contains(EntityIdCriterion.class)) { final String issuer = this.getIssuer(token); if (issuer != null) { criteriaSet.add(new EntityIdCriterion(issuer)); } } if (!criteriaSet.contains(UsageCriterion.class)) { criteriaSet.add(new UsageCriterion(UsageType.SIGNING)); } return criteriaSet; } /** * Returns the issuer of the signable object. * * @param signableObject * the object being verified * @return the issuer */ protected abstract String getIssuer(final T signableObject); /** * Returns the ID of the signable object. * * @param signableObject * the object being verified * @return the ID */ protected abstract String getID(final T signableObject); /** * Returns the name of the object being validated, e.g. "Assertion". Used for logging. * * @return the object name */ protected abstract String getObjectName(); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy