/******************************************************************************* *******************************************************************************/ package at.gv.egiz.eaaf.modules.pvp2.impl.verification; import javax.xml.namespace.QName; import javax.xml.transform.dom.DOMSource; import javax.xml.validation.Schema; import javax.xml.validation.Validator; import org.apache.commons.lang3.StringUtils; import org.opensaml.common.SignableSAMLObject; import org.opensaml.common.xml.SAMLConstants; import org.opensaml.common.xml.SAMLSchemaBuilder; import org.opensaml.security.MetadataCriteria; import org.opensaml.security.SAMLSignatureProfileValidator; import org.opensaml.ws.message.MessageContext; import org.opensaml.ws.security.SecurityPolicyException; import org.opensaml.ws.security.SecurityPolicyRule; import org.opensaml.xml.XMLObject; import org.opensaml.xml.security.CriteriaSet; import org.opensaml.xml.security.credential.UsageType; import org.opensaml.xml.security.criteria.EntityIDCriteria; import org.opensaml.xml.security.criteria.UsageCriteria; import org.opensaml.xml.signature.SignatureTrustEngine; import org.opensaml.xml.validation.ValidationException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Element; import org.xml.sax.SAXException; import at.gv.egiz.eaaf.modules.pvp2.exception.SchemaValidationException; /** * @author tlenz * */ public abstract class AbstractRequestSignedSecurityPolicyRule implements SecurityPolicyRule { private static final Logger log = LoggerFactory.getLogger(AbstractRequestSignedSecurityPolicyRule.class); private SignatureTrustEngine trustEngine = null; private QName peerEntityRole = null; /** * @param peerEntityRole * */ public AbstractRequestSignedSecurityPolicyRule(SignatureTrustEngine trustEngine, QName peerEntityRole) { this.trustEngine = trustEngine; this.peerEntityRole = peerEntityRole; } /** * Reload the PVP metadata for a given entity * * @param entityID for which the metadata should be refreshed. * @return true if the refresh was successful, otherwise false */ protected abstract boolean refreshMetadataProvider(String entityID); protected abstract SignableSAMLObject getSignedSAMLObject(XMLObject inboundData); /* (non-Javadoc) * @see org.opensaml.ws.security.SecurityPolicyRule#evaluate(org.opensaml.ws.message.MessageContext) */ @Override public void evaluate(MessageContext context) throws SecurityPolicyException { try { verifySignature(context); } catch (SecurityPolicyException e) { if (StringUtils.isEmpty(context.getInboundMessageIssuer())) { throw e; } log.debug("PVP2X message validation FAILED. Reload metadata for entityID: " + context.getInboundMessageIssuer()); if (!refreshMetadataProvider(context.getInboundMessageIssuer())) throw e; else { log.trace("PVP2X metadata reload finished. Check validate message again."); verifySignature(context); } log.trace("Second PVP2X message validation finished"); } } private void verifySignature(MessageContext context) throws SecurityPolicyException { SignableSAMLObject samlObj = getSignedSAMLObject(context.getInboundMessage()); if (samlObj != null && samlObj.getSignature() != null) { SAMLSignatureProfileValidator profileValidator = new SAMLSignatureProfileValidator(); try { profileValidator.validate(samlObj.getSignature()); performSchemaValidation(samlObj.getDOM()); } catch (ValidationException e) { log.warn("Signature is not conform to SAML signature profile", e); throw new SecurityPolicyException("Signature is not conform to SAML signature profile"); } catch (SchemaValidationException e) { log.warn("Signature is not conform to SAML signature profile", e); throw new SecurityPolicyException("Signature is not conform to SAML signature profile"); } CriteriaSet criteriaSet = new CriteriaSet(); criteriaSet.add( new EntityIDCriteria(context.getInboundMessageIssuer()) ); criteriaSet.add( new MetadataCriteria(peerEntityRole, SAMLConstants.SAML20P_NS) ); criteriaSet.add( new UsageCriteria(UsageType.SIGNING) ); try { if (!trustEngine.validate(samlObj.getSignature(), criteriaSet)) { throw new SecurityPolicyException("Signature validation FAILED."); } log.debug("PVP message signature valid."); } catch (org.opensaml.xml.security.SecurityException e) { log.info("PVP2x message signature validation FAILED. Message:" + e.getMessage()); throw new SecurityPolicyException("Signature validation FAILED."); } } else { throw new SecurityPolicyException("PVP Message is not signed."); } } private void performSchemaValidation(Element source) throws SchemaValidationException { String err = null; try { Schema test = SAMLSchemaBuilder.getSAML11Schema(); Validator val = test.newValidator(); val.validate(new DOMSource(source)); log.debug("Schema validation check done OK"); return; } catch (SAXException e) { err = e.getMessage(); if (log.isDebugEnabled() || log.isTraceEnabled()) log.warn("Schema validation FAILED with exception:", e); else log.warn("Schema validation FAILED with message: "+ e.getMessage()); } catch (Exception e) { err = e.getMessage(); if (log.isDebugEnabled() || log.isTraceEnabled()) log.warn("Schema validation FAILED with exception:", e); else log.warn("Schema validation FAILED with message: "+ e.getMessage()); } throw new SchemaValidationException("pvp2.22", new Object[]{err}); } }