aboutsummaryrefslogtreecommitdiff
path: root/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/stork/PEPSConnectorAssertionVerifier.java
diff options
context:
space:
mode:
Diffstat (limited to 'id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/stork/PEPSConnectorAssertionVerifier.java')
-rw-r--r--id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/stork/PEPSConnectorAssertionVerifier.java241
1 files changed, 241 insertions, 0 deletions
diff --git a/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/stork/PEPSConnectorAssertionVerifier.java b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/stork/PEPSConnectorAssertionVerifier.java
new file mode 100644
index 000000000..3048ccbee
--- /dev/null
+++ b/id/server/idserverlib/src/main/java/at/gv/egovernment/moa/id/auth/stork/PEPSConnectorAssertionVerifier.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2011 by Graz University of Technology, Austria
+ * The Austrian STORK Modules have been developed by the E-Government
+ * Innovation Center EGIZ, a joint initiative of the Federal Chancellery
+ * Austria and Graz University of Technology.
+ *
+ * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
+ * the European Commission - subsequent versions of the EUPL (the "Licence");
+ * You may not use this work except in compliance with the Licence.
+ * You may obtain a copy of the Licence at:
+ * http://www.osor.eu/eupl/
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the Licence is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the Licence for the specific language governing permissions and
+ * limitations under the Licence.
+ *
+ * This product combines work with different licenses. See the "NOTICE" text
+ * file for details on the various modules and licenses.
+ * The "NOTICE" text file is part of the distribution. Any derivative works
+ * that you distribute must include a readable copy of the "NOTICE" text file.
+ */
+
+
+/**
+ *
+ */
+package at.gv.egovernment.moa.id.auth.stork;
+
+import java.util.List;
+
+import org.joda.time.DateTime;
+import org.opensaml.saml2.core.Assertion;
+import org.opensaml.saml2.core.Attribute;
+import org.opensaml.saml2.core.Audience;
+import org.opensaml.saml2.core.AudienceRestriction;
+import org.opensaml.saml2.core.Conditions;
+import org.opensaml.saml2.core.SubjectConfirmation;
+import org.opensaml.saml2.core.SubjectConfirmationData;
+import org.opensaml.saml2.metadata.RequestedAttribute;
+
+import at.gv.egovernment.moa.logging.Logger;
+import eu.stork.vidp.messages.saml.STORKAttribute;
+import eu.stork.vidp.messages.util.SAMLUtil;
+
+/**
+ * Verifies the SAML assertion according to the STORK specification
+ * @author bzwattendorfer
+ *
+ */
+public class PEPSConnectorAssertionVerifier implements AssertionVerifier {
+
+ private static final int CLOCK_SKEW_MINUTES = 5;
+
+ private static final boolean IS_USERS_CLIENT_IP_ADDRESS_TO_VERIFY = false;
+
+ /* (non-Javadoc)
+ * @see eu.stork.mw.peps.connector.validation.AssertionVerifier#verifyAssertion(org.opensaml.saml2.core.Assertion, java.lang.String, java.lang.String, java.lang.String)
+ */
+ public void verify(Assertion assertion, String reqIPAddress,
+ String authnRequestID, String recipient, String audience, List<RequestedAttribute> reqAttrList) throws SecurityException {
+
+ //SAML assertion need not to be signed, skipping signature validation
+
+ verifySubjectConfirmation(assertion, reqIPAddress, authnRequestID, recipient);
+
+ Logger.debug("SubjectConfirmationData successfully verified");
+
+ verifyConditions(assertion, audience);
+
+ Logger.debug("Conditions successfully verified");
+ }
+
+
+ private void verifySubjectConfirmation(Assertion assertion, String reqAddress, String requestID, String recipient) throws SecurityException {
+ for (SubjectConfirmation sc : assertion.getSubject().getSubjectConfirmations()) {
+ verifySubjectConfirmationData(sc.getSubjectConfirmationData(), reqAddress, requestID, recipient);
+ }
+
+ }
+
+ private void verifySubjectConfirmationData(SubjectConfirmationData scData, String reqAddress, String requestID, String recipient) throws SecurityException {
+ //NotBefore not allowed in SSO profile
+ verifyNotOnOrAfter(scData.getNotOnOrAfter());
+
+ Logger.trace("NotOnOrAfter successfully verified");
+
+ if(IS_USERS_CLIENT_IP_ADDRESS_TO_VERIFY) {
+ verifyClientAddress(scData, reqAddress);
+ Logger.trace("User's client IP address successfully verified.");
+ } else {
+ Logger.warn("User's client IP address will not be verified.");
+ }
+
+ verifyRecipient(scData, recipient);
+ Logger.trace("Recipient successfully verified");
+
+ verifyInResponseTo(scData, requestID);
+ Logger.trace("InResponseTo successfully verified");
+
+ }
+
+ private void verifyNotBefore(DateTime notBefore) throws SecurityException {
+ if (notBefore.minusMinutes(CLOCK_SKEW_MINUTES).isAfterNow()) {
+ String msg = "Subject/Assertion not yet valid, Timestamp: ";
+ Logger.error(msg + notBefore);
+ throw new SecurityException(msg);
+ }
+
+ Logger.trace("Subject/Assertion already valid, notBefore: " + notBefore);
+
+ }
+
+ private void verifyNotOnOrAfter(DateTime notOnOrAfter) throws SecurityException {
+ if (notOnOrAfter.plusMinutes(CLOCK_SKEW_MINUTES).isBeforeNow()) {
+ String msg = "Subject/Assertion no longer valid.";
+ Logger.error(msg);
+ throw new SecurityException(msg);
+ }
+
+ Logger.trace("Subject/Assertion still valid, notOnOrAfter: " + notOnOrAfter);
+ }
+
+ private void verifyClientAddress(SubjectConfirmationData scData, String reqAddress) throws SecurityException {
+ if (!reqAddress.equals(scData.getAddress())) {
+ String msg = "Response coming from wrong Client-Address";
+ Logger.error("Response coming from wrong Client-Address " + reqAddress + ", expected " + scData.getAddress());
+ throw new SecurityException(msg);
+ }
+
+ }
+
+ private void verifyInResponseTo(SubjectConfirmationData scData, String requestID) throws SecurityException {
+ if (!scData.getInResponseTo().equals(requestID)) {
+ String msg = "Assertion issued for wrong request";
+ Logger.error(msg);
+ throw new SecurityException(msg);
+ }
+ }
+
+ private void verifyRecipient(SubjectConfirmationData scData, String reqRecipient) throws SecurityException {
+ if (!scData.getRecipient().equals(reqRecipient)) {
+ String msg = "Assertion intended for another recipient";
+ Logger.error("Assertion intended for recipient " + scData.getRecipient() + "but expected " + reqRecipient);
+ throw new SecurityException(msg);
+ }
+
+ }
+
+ private void verifyAudience(AudienceRestriction audienceRestriction, String reqAudience) throws SecurityException {
+ for (Audience audience : audienceRestriction.getAudiences()) {
+ if (audience.getAudienceURI().equals(reqAudience))
+ return;
+ }
+ String msg = "Assertion sent to wrong audience";
+ Logger.error("Assertion intended for wrong audience, expected " + reqAudience);
+ throw new SecurityException(msg);
+ }
+
+ private void verifyOneTimeUse(String assertionID) {
+ //not necessarily required to check since notBefore and notOnOrAfter are verified
+ //check response Store for already existing assertion
+
+ }
+
+ private void verifyConditions(Assertion assertion, String reqAudience) throws SecurityException {
+ Conditions conditions = assertion.getConditions();
+
+ verifyNotBefore(conditions.getNotBefore());
+ Logger.trace("NotBefore successfully verified");
+
+ verifyNotOnOrAfter(conditions.getNotOnOrAfter());
+ Logger.trace("NotOnOrAfter successfully verified");
+
+ verifyAudience(conditions.getAudienceRestrictions().get(0), reqAudience);
+
+ Logger.trace("Audience successfully verified");
+
+ }
+
+ public static void validateRequiredAttributes(
+ List<RequestedAttribute> reqAttrList,
+ List<Attribute> attrList)
+ throws STORKException {
+
+ Logger.debug("Starting required attribute validation");
+
+ if (reqAttrList == null || reqAttrList.isEmpty()) {
+ Logger.error("Requested Attributes list is empty.");
+ throw new STORKException("No attributes have been requested");
+ }
+
+ if (attrList == null || attrList.isEmpty()) {
+ Logger.error("STORK AttributeStatement is empty.");
+ throw new STORKException("No attributes have been received");
+ }
+
+ Logger.trace("These attributes have been requested and received: ");
+ int count = 0;
+ for (RequestedAttribute reqAttr : reqAttrList) {
+ Logger.trace("Requested attribute: " + reqAttr.getName() + " isRequired: " + reqAttr.isRequired());
+ for(Attribute attr : attrList) {
+ if (verifyRequestedAttribute(reqAttr, attr))
+ count++;
+ }
+ }
+
+ int numRequiredReqAttr = getNumberOfRequiredAttributes(reqAttrList);
+ Logger.trace("Number of requested required attributes: " + numRequiredReqAttr);
+ Logger.trace("Number of received required attributes: " + count);
+
+ if (count != numRequiredReqAttr) {
+ Logger.error("Not all required attributes have been received");
+ throw new STORKException("Not all required attributes have been received");
+ }
+ Logger.debug("Received all required attributes!");
+
+ }
+
+ private static boolean verifyRequestedAttribute(RequestedAttribute reqAttr, Attribute attr) {
+
+ if ((reqAttr.getName()).equals(attr.getName())) {
+ if (reqAttr.isRequired() && SAMLUtil.getStatusFromAttribute(attr).equals(STORKAttribute.ALLOWED_ATTRIBUTE_STATUS_AVAIL)) {
+ Logger.trace("Received required attribute " + attr.getName() + " status: " + SAMLUtil.getStatusFromAttribute(attr));
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static int getNumberOfRequiredAttributes(List<RequestedAttribute> reqAttrList) {
+ int count = 0;
+ for (RequestedAttribute reqAttr : reqAttrList)
+ if (reqAttr.isRequired()) count++;
+
+ return count;
+ }
+
+
+}