aboutsummaryrefslogtreecommitdiff
path: root/id/server/idserverlib/src
diff options
context:
space:
mode:
authorThomas Lenz <tlenz@iaik.tugraz.at>2016-03-10 12:31:38 +0100
committerThomas Lenz <tlenz@iaik.tugraz.at>2016-03-10 12:31:38 +0100
commita6cadad81df2b44a99ca452ea1737abf1fa7d3e8 (patch)
treea9358c03beaed2c8955655304f5b081a40b14360 /id/server/idserverlib/src
parente34d8e8a2292a0ea049ab3b3aa6e649aa215e82b (diff)
downloadmoa-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/idserverlib/src')
-rw-r--r--id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/PVPTargetConfiguration.java3
-rw-r--r--id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/builder/assertion/PVP2AssertionBuilder.java1
-rw-r--r--id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/protocols/pvp2x/verification/SAMLVerificationEngineSP.java142
-rw-r--r--id/server/idserverlib/src/main/resources/resources/properties/id_messages_de.properties2
-rw-r--r--id/server/idserverlib/src/main/resources/resources/properties/protocol_response_statuscodes_de.properties2
5 files changed, 125 insertions, 25 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