diff options
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.java | 241 |
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; + } +
+
+}
|