From bee5dd259a4438d45ecd1bcc26dfba12875236d6 Mon Sep 17 00:00:00 2001 From: Thomas Lenz Date: Tue, 26 Jun 2018 11:03:48 +0200 Subject: initial commit --- .../impl/verification/SAMLVerificationEngine.java | 183 +++++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/verification/SAMLVerificationEngine.java (limited to 'eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/verification/SAMLVerificationEngine.java') diff --git a/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/verification/SAMLVerificationEngine.java b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/verification/SAMLVerificationEngine.java new file mode 100644 index 00000000..fe147ea7 --- /dev/null +++ b/eaaf_modules/eaaf_module_pvp2_core/src/main/java/at/gv/egiz/eaaf/modules/pvp2/impl/verification/SAMLVerificationEngine.java @@ -0,0 +1,183 @@ +/******************************************************************************* + *******************************************************************************/ +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.xml.SAMLConstants; +import org.opensaml.common.xml.SAMLSchemaBuilder; +import org.opensaml.saml2.core.RequestAbstractType; +import org.opensaml.saml2.core.StatusResponseType; +import org.opensaml.saml2.metadata.IDPSSODescriptor; +import org.opensaml.saml2.metadata.SPSSODescriptor; +import org.opensaml.security.MetadataCriteria; +import org.opensaml.security.SAMLSignatureProfileValidator; +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.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.w3c.dom.Element; +import org.xml.sax.SAXException; + +import at.gv.egiz.eaaf.core.exceptions.InvalidProtocolRequestException; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IPVPMetadataProvider; +import at.gv.egiz.eaaf.modules.pvp2.api.metadata.IRefreshableMetadataProvider; +import at.gv.egiz.eaaf.modules.pvp2.exception.SchemaValidationException; +import at.gv.egiz.eaaf.modules.pvp2.impl.message.InboundMessage; +import at.gv.egiz.eaaf.modules.pvp2.impl.message.PVPSProfileRequest; +import at.gv.egiz.eaaf.modules.pvp2.impl.message.PVPSProfileResponse; + +@Service("SAMLVerificationEngine") +public class SAMLVerificationEngine { + private static final Logger log = LoggerFactory.getLogger(SAMLVerificationEngine.class); + + + @Autowired(required=true) IPVPMetadataProvider metadataProvider; + + public void verify(InboundMessage msg, SignatureTrustEngine sigTrustEngine ) throws org.opensaml.xml.security.SecurityException, Exception { + try { + if (msg instanceof PVPSProfileRequest && + ((PVPSProfileRequest)msg).getSamlRequest() instanceof RequestAbstractType) + verifyRequest(((RequestAbstractType)((PVPSProfileRequest)msg).getSamlRequest()), sigTrustEngine); + + else + verifyIDPResponse(((PVPSProfileResponse)msg).getResponse(), sigTrustEngine); + + } catch (InvalidProtocolRequestException e) { + if (StringUtils.isEmpty(msg.getEntityID())) { + throw e; + + } + log.debug("PVP2X message validation FAILED. Relead metadata for entityID: " + msg.getEntityID()); + + if (metadataProvider == null || + !(metadataProvider instanceof IRefreshableMetadataProvider) || + !((IRefreshableMetadataProvider)metadataProvider).refreshMetadataProvider(msg.getEntityID())) + throw e; + + else { + log.trace("PVP2X metadata reload finished. Check validate message again."); + + if (msg instanceof PVPSProfileRequest && + ((PVPSProfileRequest)msg).getSamlRequest() instanceof RequestAbstractType) + verifyRequest(((RequestAbstractType)((PVPSProfileRequest)msg).getSamlRequest()), sigTrustEngine); + + else + verifyIDPResponse(((PVPSProfileResponse)msg).getResponse(), sigTrustEngine); + + } + log.trace("Second PVP2X message validation finished"); + } + } + + public void verifySLOResponse(StatusResponseType samlObj, SignatureTrustEngine sigTrustEngine ) throws InvalidProtocolRequestException { + verifyResponse(samlObj, sigTrustEngine, SPSSODescriptor.DEFAULT_ELEMENT_NAME); + + } + + public void verifyIDPResponse(StatusResponseType samlObj, SignatureTrustEngine sigTrustEngine) throws InvalidProtocolRequestException{ + verifyResponse(samlObj, sigTrustEngine, IDPSSODescriptor.DEFAULT_ELEMENT_NAME); + + } + + private void verifyResponse(StatusResponseType samlObj, SignatureTrustEngine sigTrustEngine, QName defaultElementName) throws InvalidProtocolRequestException{ + 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 InvalidProtocolRequestException("pvp2.21", new Object[] {}, "Signature is not conform to SAML signature profile"); + + } catch (SchemaValidationException e) { + throw new InvalidProtocolRequestException("pvp2.22", new Object[] {e.getMessage()}, "SAML response does not fit XML scheme"); + + } + + CriteriaSet criteriaSet = new CriteriaSet(); + criteriaSet.add( new EntityIDCriteria(samlObj.getIssuer().getValue()) ); + criteriaSet.add( new MetadataCriteria(defaultElementName, SAMLConstants.SAML20P_NS) ); + criteriaSet.add( new UsageCriteria(UsageType.SIGNING) ); + + try { + if (!sigTrustEngine.validate(samlObj.getSignature(), criteriaSet)) { + throw new InvalidProtocolRequestException("pvp2.21", new Object[] {}, "Signature verification FAILED on SAML response"); + } + } catch (org.opensaml.xml.security.SecurityException e) { + log.warn("PVP2x message signature validation FAILED.", e); + throw new InvalidProtocolRequestException("pvp2.21", new Object[] {}, "Signature verification FAILED on SAML response"); + } + } + + private void verifyRequest(RequestAbstractType samlObj, SignatureTrustEngine sigTrustEngine ) throws InvalidProtocolRequestException { + 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 InvalidProtocolRequestException("pvp2.21", new Object[] {}, "Scheme validation FAILED on SAML request"); + + } catch (SchemaValidationException e) { + throw new InvalidProtocolRequestException("pvp2.22", new Object[] {e.getMessage()}, "Scheme verification FAILED on SAML request"); + + } + + CriteriaSet criteriaSet = new CriteriaSet(); + criteriaSet.add( new EntityIDCriteria(samlObj.getIssuer().getValue()) ); + criteriaSet.add( new MetadataCriteria(SPSSODescriptor.DEFAULT_ELEMENT_NAME, SAMLConstants.SAML20P_NS) ); + criteriaSet.add( new UsageCriteria(UsageType.SIGNING) ); + + try { + if (!sigTrustEngine.validate(samlObj.getSignature(), criteriaSet)) { + throw new InvalidProtocolRequestException("pvp2.21", new Object[] {}, "Signature verification FAILED on SAML request"); + } + } catch (org.opensaml.xml.security.SecurityException e) { + log.warn("PVP2x message signature validation FAILED.", e); + throw new InvalidProtocolRequestException("pvp2.21", new Object[] {}, "Signature verification FAILED on SAML request"); + } + } + + protected 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}); + + } + +} -- cgit v1.2.3