diff options
author | Thomas Lenz <tlenz@iaik.tugraz.at> | 2016-03-10 12:31:38 +0100 |
---|---|---|
committer | Thomas Lenz <tlenz@iaik.tugraz.at> | 2016-03-10 12:31:38 +0100 |
commit | a6cadad81df2b44a99ca452ea1737abf1fa7d3e8 (patch) | |
tree | a9358c03beaed2c8955655304f5b081a40b14360 /id/server | |
parent | e34d8e8a2292a0ea049ab3b3aa6e649aa215e82b (diff) | |
download | moa-id-spss-a6cadad81df2b44a99ca452ea1737abf1fa7d3e8.tar.gz moa-id-spss-a6cadad81df2b44a99ca452ea1737abf1fa7d3e8.tar.bz2 moa-id-spss-a6cadad81df2b44a99ca452ea1737abf1fa7d3e8.zip |
add additional PVP response validation
Diffstat (limited to 'id/server')
9 files changed, 162 insertions, 28 deletions
diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVPTargetConfiguration.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVPTargetConfiguration.java index b8ced1198..e7f2a7d4b 100644 --- a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVPTargetConfiguration.java +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVPTargetConfiguration.java @@ -50,8 +50,11 @@ public class PVPTargetConfiguration extends RequestImpl { public static final String DATAID_INTERFEDERATION_NAMEID = "federatedNameID"; public static final String DATAID_INTERFEDERATION_QAALEVEL = "federatedQAALevel"; + public static final String DATAID_INTERFEDERATION_REQUESTID = "authnReqID"; + private static final long serialVersionUID = 4889919265919638188L; + InboundMessage request; String binding; String consumerURL; diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/builder/assertion/PVP2AssertionBuilder.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/builder/assertion/PVP2AssertionBuilder.java index 7a7044ebf..3aa05bb2d 100644 --- a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/builder/assertion/PVP2AssertionBuilder.java +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/builder/assertion/PVP2AssertionBuilder.java @@ -435,6 +435,7 @@ public class PVP2AssertionBuilder implements PVPConstants { .createSAMLObject(SubjectConfirmationData.class); subjectConfirmationData.setInResponseTo(authnRequest.getID()); subjectConfirmationData.setNotOnOrAfter(new DateTime(authData.getSsoSessionValidTo().getTime())); + subjectConfirmationData.setNotBefore(date); subjectConfirmationData.setRecipient(assertionConsumerService.getLocation()); diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/verification/SAMLVerificationEngineSP.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/verification/SAMLVerificationEngineSP.java index cd80d8c24..1e13da179 100644 --- a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/verification/SAMLVerificationEngineSP.java +++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/verification/SAMLVerificationEngineSP.java @@ -26,10 +26,15 @@ import java.util.ArrayList; import java.util.List; import org.joda.time.DateTime; +import org.opensaml.common.binding.decoding.BasicURLComparator; +import org.opensaml.saml2.core.Audience; +import org.opensaml.saml2.core.AudienceRestriction; import org.opensaml.saml2.core.Conditions; import org.opensaml.saml2.core.EncryptedAssertion; import org.opensaml.saml2.core.Response; import org.opensaml.saml2.core.StatusCode; +import org.opensaml.saml2.core.validator.AudienceRestrictionSchemaValidator; +import org.opensaml.saml2.core.validator.AudienceSchemaValidator; import org.opensaml.saml2.encryption.Decrypter; import org.opensaml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver; import org.opensaml.xml.encryption.ChainingEncryptedKeyResolver; @@ -38,9 +43,11 @@ import org.opensaml.xml.encryption.InlineEncryptedKeyResolver; import org.opensaml.xml.encryption.SimpleRetrievalMethodEncryptedKeyResolver; import org.opensaml.xml.security.credential.Credential; import org.opensaml.xml.security.keyinfo.StaticKeyInfoCredentialResolver; +import org.opensaml.xml.validation.ValidationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import at.gv.egovernment.moa.id.auth.MOAIDAuthConstants; import at.gv.egovernment.moa.id.config.ConfigurationException; import at.gv.egovernment.moa.id.config.auth.AuthConfiguration; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.AssertionValidationExeption; @@ -56,7 +63,17 @@ public class SAMLVerificationEngineSP extends SAMLVerificationEngine { @Autowired AuthConfiguration authConfig; - public void validateAssertion(Response samlResp, boolean validateDestination, Credential assertionDecryption) throws AssertionValidationExeption { + /** + * Validate a PVP response and all included assertions + * + * @param samlResp + * @param validateDestination + * @param assertionDecryption + * @param spEntityID + * @param loggerSPName + * @throws AssertionValidationExeption + */ + public void validateAssertion(Response samlResp, boolean validateDestination, Credential assertionDecryption, String spEntityID, String loggerSPName) throws AssertionValidationExeption { try { if (samlResp.getStatus().getStatusCode().getValue().equals(StatusCode.SUCCESS_URI)) { List<org.opensaml.saml2.core.Assertion> saml2assertions = new ArrayList<org.opensaml.saml2.core.Assertion>(); @@ -74,15 +91,28 @@ public class SAMLVerificationEngineSP extends SAMLVerificationEngine { } if (!isValidDestination && validateDestination) { Logger.warn("PVP 2.1 assertion destination does not match to IDP URL"); - throw new AssertionValidationExeption("PVP 2.1 assertion destination does not match to IDP URL", null); + throw new AssertionValidationExeption("sp.pvp2.07", new Object[]{loggerSPName, "'Destination' attribute is not valid"}); } - //check encrypted Assertion + //validate response issueInstant + DateTime issueInstant = samlResp.getIssueInstant(); + if (issueInstant == null) { + Logger.warn("PVP response does not include a 'IssueInstant' attribute"); + throw new AssertionValidationExeption("sp.pvp2.07", new Object[]{loggerSPName, "'IssueInstant' attribute is not included"}); + + } + if (issueInstant.minusMinutes(MOAIDAuthConstants.TIME_JITTER).isAfterNow()) { + Logger.warn("PVP response: IssueInstant DateTime is not valid anymore."); + throw new AssertionValidationExeption("sp.pvp2.07", new Object[]{loggerSPName, "'IssueInstant' Time is not valid any more"}); + + } + + + //check encrypted Assertions List<EncryptedAssertion> encryAssertionList = samlResp.getEncryptedAssertions(); if (encryAssertionList != null && encryAssertionList.size() > 0) { - //decrypt assertions - + //decrypt assertions Logger.debug("Found encryped assertion. Start decryption ..."); StaticKeyInfoCredentialResolver skicr = @@ -93,13 +123,10 @@ public class SAMLVerificationEngineSP extends SAMLVerificationEngine { encryptedKeyResolver.getResolverChain().add( new EncryptedElementTypeEncryptedKeyResolver() ); encryptedKeyResolver.getResolverChain().add( new SimpleRetrievalMethodEncryptedKeyResolver() ); - Decrypter samlDecrypter = - new Decrypter(null, skicr, encryptedKeyResolver); + Decrypter samlDecrypter = new Decrypter(null, skicr, encryptedKeyResolver); - for (EncryptedAssertion encAssertion : encryAssertionList) { + for (EncryptedAssertion encAssertion : encryAssertionList) saml2assertions.add(samlDecrypter.decrypt(encAssertion)); - - } Logger.debug("Assertion decryption finished. "); @@ -108,35 +135,90 @@ public class SAMLVerificationEngineSP extends SAMLVerificationEngine { } + //validate all assertions List<org.opensaml.saml2.core.Assertion> validatedassertions = new ArrayList<org.opensaml.saml2.core.Assertion>(); for (org.opensaml.saml2.core.Assertion saml2assertion : saml2assertions) { - + + boolean isAssertionValid = true; try { + //schema validation performSchemaValidation(saml2assertion.getDOM()); - + + + //validate DateTime conditions Conditions conditions = saml2assertion.getConditions(); - DateTime notbefore = conditions.getNotBefore().minusMinutes(5); - DateTime notafter = conditions.getNotOnOrAfter(); - if ( notbefore.isAfterNow() || notafter.isBeforeNow() ) { - Logger.warn("PVP2 Assertion is out of Date. " - + "{ Current : " + new DateTime() - + " NotBefore: " + notbefore - + " NotAfter : " + notafter - + " }");; + if (conditions != null) { + DateTime notbefore = conditions.getNotBefore().minusMinutes(5); + DateTime notafter = conditions.getNotOnOrAfter(); + if ( notbefore.isAfterNow() || notafter.isBeforeNow() ) { + isAssertionValid = false; + Logger.info("Assertion:" + saml2assertion.getID() + + " is out of Date. " + + "{ Current : " + new DateTime() + + " NotBefore: " + notbefore + + " NotAfter : " + notafter + + " }");; + + } + + //validate audienceRestrictions are valid for this SP + List<AudienceRestriction> audienceRest = conditions.getAudienceRestrictions(); + if (audienceRest == null || audienceRest.size() == 0) { + Logger.info("Assertion:" + saml2assertion.getID() + + " has not 'AudienceRestriction' element"); + isAssertionValid = false; + + } else { + for (AudienceRestriction el : audienceRest) { + el.registerValidator(new AudienceRestrictionSchemaValidator()); + el.validate(false); + + for (Audience audience : el.getAudiences()) { + audience.registerValidator(new AudienceSchemaValidator()); + audience.validate(false); + + if (!urlCompare(spEntityID, audience.getAudienceURI())) { + Logger.info("Assertion:" + saml2assertion.getID() + + " 'AudienceRestriction' is not valid."); + isAssertionValid = false; + } + } + } + } + } else { + Logger.info("Assertion:" + saml2assertion.getID() + + " contains not 'Conditions' element"); + isAssertionValid = false; + + } + + //add assertion if it is valid + if (isAssertionValid) { + Logger.debug("Add valid Assertion:" + saml2assertion.getID()); validatedassertions.add(saml2assertion); - } + } else + Logger.warn("Remove non-valid Assertion:" + saml2assertion.getID()); } catch (SchemaValidationException e) { + isAssertionValid = false; + Logger.info("Assertion:" + saml2assertion.getID() + + " Schema validation FAILED. Msg:" + e.getMessage()); + + } catch (ValidationException e) { + isAssertionValid = false; + Logger.info("Assertion:" + saml2assertion.getID() + + " AudienceRestriction schema-validation FAILED. Msg:" + e.getMessage()); } } if (validatedassertions.isEmpty()) { Logger.info("No valid PVP 2.1 assertion received."); - throw new AssertionValidationExeption("No valid PVP 2.1 assertion received.", null); + throw new AssertionValidationExeption("sp.pvp2.10", new Object[]{loggerSPName}); + } samlResp.getAssertions().clear(); @@ -146,16 +228,26 @@ public class SAMLVerificationEngineSP extends SAMLVerificationEngine { } else { Logger.info("PVP 2.1 assertion includes an error. Receive errorcode " + samlResp.getStatus().getStatusCode().getValue()); - throw new AssertionValidationExeption("PVP 2.1 assertion includes an error. Receive errorcode " - + samlResp.getStatus().getStatusCode().getValue(), null); + throw new AssertionValidationExeption("sp.pvp2.05", + new Object[]{loggerSPName, + samlResp.getIssuer().getValue(), + samlResp.getStatus().getStatusCode().getValue(), + samlResp.getStatus().getStatusMessage().getMessage()}); + } } catch (DecryptionException e) { Logger.warn("Assertion decrypt FAILED.", e); - throw new AssertionValidationExeption("Assertion decrypt FAILED.", null, e); + throw new AssertionValidationExeption("sp.pvp2.11", null, e); } catch (ConfigurationException e) { throw new AssertionValidationExeption("pvp.12", null, e); } } + + protected static boolean urlCompare(String url1, String url2) { + BasicURLComparator comparator = new BasicURLComparator(); + comparator.setCaseInsensitive(false); + return comparator.compare(url1, url2); + } } diff --git a/id/server/idserverlib/src/main/resources/resources/properties/id_messages_de.properties b/id/server/idserverlib/src/main/resources/resources/properties/id_messages_de.properties index a53d7e920..34ef9c1d0 100644 --- a/id/server/idserverlib/src/main/resources/resources/properties/id_messages_de.properties +++ b/id/server/idserverlib/src/main/resources/resources/properties/id_messages_de.properties @@ -292,6 +292,8 @@ sp.pvp2.06=Receive invalid PVP Response from {0}. Assertion does not contain all sp.pvp2.07=Receive invalid PVP Response from {0}. Attribute {1} is not valid.
sp.pvp2.08=Receive invalid PVP Response from {0}. Response issuer {1} is not valid or allowed.
sp.pvp2.09=Receive invalid PVP Response from {0} {1}. StatusCodes:{2} {3} Msg:{4}
+sp.pvp2.10=Receive invalid PVP Response from {0}. No valid assertion included.
+sp.pvp2.11=Receive invalid PVP Response from {0}. Assertion decryption FAILED.
oauth20.01=Fehlerhafte redirect url
oauth20.02=Fehlender oder ung\u00FCltiger Parameter "{0}"
diff --git a/id/server/idserverlib/src/main/resources/resources/properties/protocol_response_statuscodes_de.properties b/id/server/idserverlib/src/main/resources/resources/properties/protocol_response_statuscodes_de.properties index a81540e2b..27070cc84 100644 --- a/id/server/idserverlib/src/main/resources/resources/properties/protocol_response_statuscodes_de.properties +++ b/id/server/idserverlib/src/main/resources/resources/properties/protocol_response_statuscodes_de.properties @@ -111,6 +111,8 @@ sp.pvp2.06=TODO sp.pvp2.07=TODO sp.pvp2.08=TODO sp.pvp2.09=TODO +sp.pvp2.10=TODO +sp.pvp2.11=TODO validator.00=1102 validator.01=1102 diff --git a/id/server/modules/moa-id-module-elga_mandate_service/src/main/java/at/gv/egovernment/moa/id/auth/modules/elgamandates/ELGAMandatesAuthConstants.java b/id/server/modules/moa-id-module-elga_mandate_service/src/main/java/at/gv/egovernment/moa/id/auth/modules/elgamandates/ELGAMandatesAuthConstants.java index e4eaa5ee7..b50d1cf4e 100644 --- a/id/server/modules/moa-id-module-elga_mandate_service/src/main/java/at/gv/egovernment/moa/id/auth/modules/elgamandates/ELGAMandatesAuthConstants.java +++ b/id/server/modules/moa-id-module-elga_mandate_service/src/main/java/at/gv/egovernment/moa/id/auth/modules/elgamandates/ELGAMandatesAuthConstants.java @@ -75,6 +75,9 @@ public class ELGAMandatesAuthConstants { Collections.unmodifiableList(new ArrayList<Pair<String, String>>() { private static final long serialVersionUID = 1L; { + //add PVP Version attribute + add(Pair.newInstance(PVPConstants.PVP_VERSION_NAME, PVPConstants.PVP_VERSION_FRIENDLY_NAME)); + //request mandate type add(Pair.newInstance(PVPConstants.MANDATE_TYPE_NAME, PVPConstants.MANDATE_TYPE_FRIENDLY_NAME)); diff --git a/id/server/modules/moa-id-module-elga_mandate_service/src/main/java/at/gv/egovernment/moa/id/auth/modules/elgamandates/tasks/ReceiveElgaMandateResponseTask.java b/id/server/modules/moa-id-module-elga_mandate_service/src/main/java/at/gv/egovernment/moa/id/auth/modules/elgamandates/tasks/ReceiveElgaMandateResponseTask.java index 0688e7c64..f976793b8 100644 --- a/id/server/modules/moa-id-module-elga_mandate_service/src/main/java/at/gv/egovernment/moa/id/auth/modules/elgamandates/tasks/ReceiveElgaMandateResponseTask.java +++ b/id/server/modules/moa-id-module-elga_mandate_service/src/main/java/at/gv/egovernment/moa/id/auth/modules/elgamandates/tasks/ReceiveElgaMandateResponseTask.java @@ -46,6 +46,7 @@ import at.gv.egovernment.moa.id.auth.modules.elgamandates.utils.ELGAMandateServi import at.gv.egovernment.moa.id.auth.modules.elgamandates.utils.ELGAMandatesCredentialProvider; import at.gv.egovernment.moa.id.process.api.ExecutionContext; import at.gv.egovernment.moa.id.protocols.pvp2x.PVPConstants; +import at.gv.egovernment.moa.id.protocols.pvp2x.PVPTargetConfiguration; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.IDecoder; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.MOAURICompare; import at.gv.egovernment.moa.id.protocols.pvp2x.binding.PostBinding; @@ -142,6 +143,8 @@ public class ReceiveElgaMandateResponseTask extends AbstractAuthServletTask { } + + //load MOASession object defaultTaskInitialization(request, executionContext); @@ -216,10 +219,28 @@ public class ReceiveElgaMandateResponseTask extends AbstractAuthServletTask { Logger.debug("Start PVP-2.1 assertion processing... "); Response samlResp = (Response) msg.getResponse(); + //validate 'inResponseTo' attribute + String authnReqID = pendingReq.getGenericData( + PVPTargetConfiguration.DATAID_INTERFEDERATION_REQUESTID, String.class); + String inResponseTo = samlResp.getInResponseTo(); + + if (MiscUtil.isEmpty(authnReqID) || MiscUtil.isEmpty(inResponseTo) || + !authnReqID.equals(inResponseTo)) { + Logger.info("Validation of request/response IDs FAILED." + + " ReqID:" + authnReqID + " InRespTo:" + inResponseTo); + throw new AuthnResponseValidationException("sp.pvp2.07", + new Object[]{ELGAMandatesAuthConstants.MODULE_NAME_FOR_LOGGING, + "'InResponseTo'"}); + + } + // check SAML2 response status-code if (samlResp.getStatus().getStatusCode().getValue().equals(StatusCode.SUCCESS_URI)) { //validate PVP 2.1 assertion - samlVerificationEngine.validateAssertion(samlResp, true, credentialProvider.getIDPAssertionEncryptionCredential()); + samlVerificationEngine.validateAssertion(samlResp, true, + credentialProvider.getIDPAssertionEncryptionCredential(), + pendingReq.getAuthURL() + ELGAMandatesAuthConstants.ENDPOINT_METADATA, + ELGAMandatesAuthConstants.MODULE_NAME_FOR_LOGGING); msg.setSAMLMessage(SAML2Utils.asDOMDocument(samlResp).getDocumentElement()); return msg; diff --git a/id/server/modules/moa-id-module-elga_mandate_service/src/main/java/at/gv/egovernment/moa/id/auth/modules/elgamandates/tasks/RequestELGAMandateTask.java b/id/server/modules/moa-id-module-elga_mandate_service/src/main/java/at/gv/egovernment/moa/id/auth/modules/elgamandates/tasks/RequestELGAMandateTask.java index 2a3e72640..d25921167 100644 --- a/id/server/modules/moa-id-module-elga_mandate_service/src/main/java/at/gv/egovernment/moa/id/auth/modules/elgamandates/tasks/RequestELGAMandateTask.java +++ b/id/server/modules/moa-id-module-elga_mandate_service/src/main/java/at/gv/egovernment/moa/id/auth/modules/elgamandates/tasks/RequestELGAMandateTask.java @@ -46,6 +46,7 @@ import at.gv.egovernment.moa.id.auth.modules.elgamandates.utils.ELGAMandateServi import at.gv.egovernment.moa.id.auth.modules.elgamandates.utils.ELGAMandatesCredentialProvider; import at.gv.egovernment.moa.id.config.auth.AuthConfiguration; import at.gv.egovernment.moa.id.process.api.ExecutionContext; +import at.gv.egovernment.moa.id.protocols.pvp2x.PVPTargetConfiguration; import at.gv.egovernment.moa.id.protocols.pvp2x.builder.PVPAuthnRequestBuilder; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.Constants; @@ -137,7 +138,13 @@ public class RequestELGAMandateTask extends AbstractAuthServletTask { //set MandateReferenceValue as RequestID authnReqConfig.setRequestID(moasession.getMandateReferenceValue()); - + pendingReq.setGenericDataToSession( + PVPTargetConfiguration.DATAID_INTERFEDERATION_REQUESTID, + authnReqConfig.getRequestID()); + + //store pending-request + requestStoreage.storePendingRequest(pendingReq); + //build and transmit AuthnRequest authnReqBuilder.buildAuthnRequest(pendingReq, authnReqConfig , response); diff --git a/id/server/modules/moa-id-modules-federated_authentication/src/main/java/at/gv/egovernment/moa/id/auth/modules/federatedauth/tasks/ReceiveAuthnResponseTask.java b/id/server/modules/moa-id-modules-federated_authentication/src/main/java/at/gv/egovernment/moa/id/auth/modules/federatedauth/tasks/ReceiveAuthnResponseTask.java index d5c5354c0..01163efd6 100644 --- a/id/server/modules/moa-id-modules-federated_authentication/src/main/java/at/gv/egovernment/moa/id/auth/modules/federatedauth/tasks/ReceiveAuthnResponseTask.java +++ b/id/server/modules/moa-id-modules-federated_authentication/src/main/java/at/gv/egovernment/moa/id/auth/modules/federatedauth/tasks/ReceiveAuthnResponseTask.java @@ -347,7 +347,10 @@ public class ReceiveAuthnResponseTask extends AbstractAuthServletTask { // check SAML2 response status-code if (samlResp.getStatus().getStatusCode().getValue().equals(StatusCode.SUCCESS_URI)) { //validate PVP 2.1 assertion - samlVerificationEngine.validateAssertion(samlResp, true, credentialProvider.getIDPAssertionEncryptionCredential()); + samlVerificationEngine.validateAssertion(samlResp, true, + credentialProvider.getIDPAssertionEncryptionCredential(), + pendingReq.getAuthURL() + FederatedAuthConstants.ENDPOINT_METADATA, + FederatedAuthConstants.MODULE_NAME_FOR_LOGGING); msg.setSAMLMessage(SAML2Utils.asDOMDocument(samlResp).getDocumentElement()); return msg; |