/******************************************************************************* * Copyright 2014 Federal Chancellery Austria * MOA-ID has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, 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. ******************************************************************************/ /* * Copyright 2003 Federal Chancellery Austria * MOA-ID has been developed in a cooperation between BRZ, the Federal * Chancellery Austria - ICT staff unit, 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.validator; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import at.gv.egiz.eaaf.core.api.idp.auth.data.IIdentityLink; import at.gv.egiz.eaaf.core.impl.idp.auth.data.IdentityLink; import at.gv.egiz.eaaf.core.impl.utils.XPathUtils; import at.gv.egovernment.moa.id.auth.exception.ValidateException; import at.gv.egovernment.moa.util.Constants; /** * This class is used to validate an {@link IdentityLink} * returned by the security layer * * @author Stefan Knirsch * @version $Id$ */ public class IdentityLinkValidator implements Constants { // // XPath namespace prefix shortcuts // /** Xpath prefix for reaching PersonData Namespaces */ private static final String PDATA = PD_PREFIX + ":"; /** Xpath prefix for reaching SAML Namespaces */ private static final String SAML = SAML_PREFIX + ":"; /** Xpath prefix for reaching XML-DSIG Namespaces */ private static final String DSIG = DSIG_PREFIX + ":"; /** Xpath prefix for reaching ECDSA Namespaces */ private static final String ECDSA = ECDSA_PREFIX + ":"; /** Xpath expression to the root element */ private static final String ROOT = ""; /** Xpath expression to the SAML:SubjectConfirmationData element */ private static final String SAML_SUBJECT_CONFIRMATION_DATA_XPATH = ROOT + SAML + "AttributeStatement/" + SAML + "Subject/" + SAML + "SubjectConfirmation/" + SAML + "SubjectConfirmationData"; /** Xpath expression to the PersonData:Person element */ private static final String PERSON_XPATH = SAML_SUBJECT_CONFIRMATION_DATA_XPATH + "/" + PDATA + "Person"; /** Xpath expression to the SAML:Attribute element */ private static final String ATTRIBUTE_XPATH = ROOT + SAML + "AttributeStatement/" + SAML + "Attribute"; // /** Xpath expression to the SAML:AttributeName attribute */ // private static final String ATTRIBUTE_NAME_XPATH = // ROOT + SAML + "AttributeStatement/" + SAML + "Attribute/@AttributeName"; // /** Xpath expression to the SAML:AttributeNamespace attribute */ // private static final String ATTRIBUTE_NAMESPACE_XPATH = // ROOT // + SAML // + "AttributeStatement/" // + SAML // + "Attribute/@AttributeNamespace"; // /** Xpath expression to the SAML:AttributeValue element */ // private static final String ATTRIBUTE_VALUE_XPATH = // ROOT // + SAML // + "AttributeStatement/" // + SAML // + "Attribute/" // + SAML // + "AttributeValue"; /** Singleton instance. null, if none has been created. */ private static IdentityLinkValidator instance; /** * Constructor for a singleton IdentityLinkValidator. * @return a new IdentityLinkValidator instance * @throws ValidateException if no instance can be created */ public static synchronized IdentityLinkValidator getInstance() throws ValidateException { if (instance == null) { instance = new IdentityLinkValidator(); } return instance; } /** * Method validate. Validates the {@link IdentityLink} * @param identityLink The identityLink to validate * @throws ValidateException on any validation error */ public void validate(IIdentityLink identityLink) throws ValidateException { Element samlAssertion = identityLink.getSamlAssertion(); //Search the SAML:ASSERTION Object (A2.054) if (samlAssertion == null) { throw new ValidateException("validator.00", null); } // Check how many saml:Assertion/saml:AttributeStatement/ // saml:Subject/ saml:SubjectConfirmation/ // saml:SubjectConfirmationData/pr:Person of type // PhysicalPersonType exist (A2.056) NodeList nl = XPathUtils.selectNodeList(samlAssertion, PERSON_XPATH); // If we have just one Person-Element we don't need to check the attributes int counterPhysicalPersonType = 0; if (nl.getLength() > 1) for (int i = 0; i < nl.getLength(); i++) { String xsiType = ((Element) nl.item(i)) .getAttributeNodeNS( "http://www.w3.org/2001/XMLSchema-instance", "type") .getNodeValue(); // We have to check if xsiType contains "PhysicalPersonType" // An equal-check will fail because of the Namespace-prefix of the attribute value if (xsiType.indexOf("PhysicalPersonType") > -1) counterPhysicalPersonType++; } if (counterPhysicalPersonType > 1) throw new ValidateException("validator.01", null); //Check the SAML:ATTRIBUTES nl = XPathUtils.selectNodeList(samlAssertion, ATTRIBUTE_XPATH); for (int i = 0; i < nl.getLength(); i++) { String attributeName = XPathUtils.getAttributeValue( (Element) nl.item(i), "@AttributeName", null); String attributeNS = XPathUtils.getAttributeValue( (Element) nl.item(i), "@AttributeNamespace", null); if (attributeName.equals("CitizenPublicKey")) { if (attributeNS.equals("http://www.buergerkarte.at/namespaces/personenbindung/20020506#") || attributeNS.equals("urn:publicid:gv.at:namespaces:identitylink:1.2")) { Element attributeValue = (Element) XPathUtils.selectSingleNode((Element) nl.item(i),nSMap, SAML + "AttributeValue/" + DSIG + "RSAKeyValue"); if (attributeValue==null) attributeValue = (Element) XPathUtils.selectSingleNode((Element)nl.item(i), nSMap, SAML + "AttributeValue/" + ECDSA + "ECDSAKeyValue"); if (attributeValue==null) attributeValue = (Element) XPathUtils.selectSingleNode((Element)nl.item(i), nSMap, SAML + "AttributeValue/" + DSIG + "DSAKeyValue"); if (attributeValue == null) throw new ValidateException("validator.02", null); } else throw new ValidateException("validator.03", new Object [] {attributeNS} ); } else throw new ValidateException("validator.04", new Object [] {attributeName} ); } //Check if dsig:Signature exists Element dsigSignature = (Element) XPathUtils.selectSingleNode(samlAssertion,ROOT + DSIG + "Signature"); if (dsigSignature==null) throw new ValidateException("validator.05", new Object[] {"in der Personenbindung"}); } }