/******************************************************************************* * 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. *******************************************************************************/ package at.gv.egovernment.moa.id.protocols.pvp2x.builder.assertion; import java.security.MessageDigest; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.joda.time.DateTime; import org.opensaml.common.xml.SAMLConstants; import org.opensaml.saml2.core.Assertion; import org.opensaml.saml2.core.Attribute; import org.opensaml.saml2.core.AttributeQuery; import org.opensaml.saml2.core.AttributeStatement; import org.opensaml.saml2.core.Audience; import org.opensaml.saml2.core.AudienceRestriction; import org.opensaml.saml2.core.AuthnContext; import org.opensaml.saml2.core.AuthnContextClassRef; import org.opensaml.saml2.core.AuthnRequest; import org.opensaml.saml2.core.AuthnStatement; import org.opensaml.saml2.core.Conditions; import org.opensaml.saml2.core.Issuer; import org.opensaml.saml2.core.NameID; import org.opensaml.saml2.core.RequestedAuthnContext; import org.opensaml.saml2.core.Subject; import org.opensaml.saml2.core.SubjectConfirmation; import org.opensaml.saml2.core.SubjectConfirmationData; import org.opensaml.saml2.core.impl.AuthnRequestImpl; import org.opensaml.saml2.metadata.AssertionConsumerService; import org.opensaml.saml2.metadata.AttributeConsumingService; import org.opensaml.saml2.metadata.EntityDescriptor; import org.opensaml.saml2.metadata.NameIDFormat; import org.opensaml.saml2.metadata.RequestedAttribute; import org.opensaml.saml2.metadata.SPSSODescriptor; import org.w3c.dom.Element; import at.gv.e_government.reference.namespace.mandates._20040701_.Mandate; import at.gv.e_government.reference.namespace.persondata._20020228_.CorporateBodyType; import at.gv.e_government.reference.namespace.persondata._20020228_.IdentificationType; import at.gv.e_government.reference.namespace.persondata._20020228_.PhysicalPersonType; import at.gv.egovernment.moa.id.auth.builder.BPKBuilder; import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; import at.gv.egovernment.moa.id.auth.exception.MOAIDException; import at.gv.egovernment.moa.id.config.ConfigurationException; import at.gv.egovernment.moa.id.config.auth.AuthConfigurationProvider; import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; import at.gv.egovernment.moa.id.data.IAuthData; import at.gv.egovernment.moa.id.data.SLOInformationImpl; import at.gv.egovernment.moa.id.protocols.pvp2x.PVPConstants; import at.gv.egovernment.moa.id.protocols.pvp2x.builder.PVPAttributeBuilder; import at.gv.egovernment.moa.id.protocols.pvp2x.config.PVPConfiguration; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.MandateAttributesNotHandleAbleException; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.NoMandateDataAvailableException; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.PVP2Exception; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.QAANotSupportedException; import at.gv.egovernment.moa.id.protocols.pvp2x.exceptions.UnprovideableAttributeException; import at.gv.egovernment.moa.id.protocols.pvp2x.utils.CheckMandateAttributes; import at.gv.egovernment.moa.id.protocols.pvp2x.utils.SAML2Utils; import at.gv.egovernment.moa.id.util.MandateBuilder; import at.gv.egovernment.moa.id.util.QAALevelVerifier; import at.gv.egovernment.moa.id.util.Random; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.util.Base64Utils; import at.gv.egovernment.moa.util.Constants; import at.gv.egovernment.moa.util.MiscUtil; public class PVP2AssertionBuilder implements PVPConstants { public static Assertion buildAssertion(AttributeQuery attrQuery, List reqAttributes, IAuthData authData, DateTime date, String sessionIndex) throws ConfigurationException { AuthnContextClassRef authnContextClassRef = SAML2Utils.createSAMLObject(AuthnContextClassRef.class); authnContextClassRef.setAuthnContextClassRef(authData.getQAALevel()); List attrList = new ArrayList(); if (reqAttributes != null) { Iterator it = reqAttributes.iterator(); while (it.hasNext()) { String reqAttributName = it.next(); try { Attribute attr = PVPAttributeBuilder.buildAttribute( reqAttributName, null, authData); if (attr == null) { Logger.error( "Attribute generation failed! for " + reqAttributName); } else { attrList.add(attr); } } catch (PVP2Exception e) { Logger.error( "Attribute generation failed! for " + reqAttributName); } catch (Exception e) { Logger.error( "General Attribute generation failed! for " + reqAttributName); } } } NameID subjectNameID = SAML2Utils.createSAMLObject(NameID.class); subjectNameID.setFormat(attrQuery.getSubject().getNameID().getFormat()); subjectNameID.setValue(attrQuery.getSubject().getNameID().getValue()); SubjectConfirmationData subjectConfirmationData = null; return buildGenericAssertion(attrQuery.getIssuer().getValue(), date, authnContextClassRef, attrList, subjectNameID, subjectConfirmationData, sessionIndex, new DateTime(authData.getSsoSessionValidTo().getTime())); } public static Assertion buildAssertion(AuthnRequest authnRequest, IAuthData authData, EntityDescriptor peerEntity, DateTime date, AssertionConsumerService assertionConsumerService, SLOInformationImpl sloInformation) throws MOAIDException { RequestedAuthnContext reqAuthnContext = authnRequest .getRequestedAuthnContext(); AuthnContextClassRef authnContextClassRef = SAML2Utils .createSAMLObject(AuthnContextClassRef.class); OAAuthParameter oaParam = AuthConfigurationProvider.getInstance() .getOnlineApplicationParameter( peerEntity.getEntityID()); if (reqAuthnContext == null) { authnContextClassRef.setAuthnContextClassRef(authData.getQAALevel()); } else { boolean stork_qaa_1_4_found = false; List reqAuthnContextClassRefIt = reqAuthnContext .getAuthnContextClassRefs(); if (reqAuthnContextClassRefIt.size() == 0) { QAALevelVerifier.verifyQAALevel(authData.getQAALevel(), STORK_QAA_1_4); stork_qaa_1_4_found = true; authnContextClassRef.setAuthnContextClassRef(STORK_QAA_1_4); } else { for (AuthnContextClassRef authnClassRef : reqAuthnContextClassRefIt) { String qaa_uri = authnClassRef.getAuthnContextClassRef(); if (qaa_uri.trim().equals(STORK_QAA_1_4) || qaa_uri.trim().equals(STORK_QAA_1_3) || qaa_uri.trim().equals(STORK_QAA_1_2) || qaa_uri.trim().equals(STORK_QAA_1_1)) { if (authData.isForeigner()) { QAALevelVerifier.verifyQAALevel(authData.getQAALevel(), STORK_QAA_PREFIX + oaParam.getQaaLevel()); stork_qaa_1_4_found = true; authnContextClassRef.setAuthnContextClassRef(authData.getQAALevel()); } else { QAALevelVerifier.verifyQAALevel(authData.getQAALevel(), qaa_uri.trim()); stork_qaa_1_4_found = true; authnContextClassRef.setAuthnContextClassRef(authData.getQAALevel()); } break; } } } if (!stork_qaa_1_4_found) { throw new QAANotSupportedException(STORK_QAA_1_4); } } SPSSODescriptor spSSODescriptor = peerEntity .getSPSSODescriptor(SAMLConstants.SAML20P_NS); //add Attributes to Assertion List attrList = new ArrayList(); if (spSSODescriptor.getAttributeConsumingServices() != null && spSSODescriptor.getAttributeConsumingServices().size() > 0) { Integer aIdx = authnRequest.getAttributeConsumingServiceIndex(); int idx = 0; AttributeConsumingService attributeConsumingService = null; if (aIdx != null) { idx = aIdx.intValue(); attributeConsumingService = spSSODescriptor .getAttributeConsumingServices().get(idx); } else { List attrConsumingServiceList = spSSODescriptor.getAttributeConsumingServices(); for (AttributeConsumingService el : attrConsumingServiceList) { if (el.isDefault()) attributeConsumingService = el; } } if (attributeConsumingService != null) { Iterator it = attributeConsumingService .getRequestAttributes().iterator(); while (it.hasNext()) { RequestedAttribute reqAttribut = it.next(); try { Attribute attr = PVPAttributeBuilder.buildAttribute( reqAttribut.getName(), oaParam, authData); if (attr == null) { if (reqAttribut.isRequired()) { throw new UnprovideableAttributeException( reqAttribut.getName()); } } else { attrList.add(attr); } } catch (PVP2Exception e) { Logger.error( "Attribute generation failed! for " + reqAttribut.getFriendlyName(), e); if (reqAttribut.isRequired()) { throw new UnprovideableAttributeException( reqAttribut.getName()); } } catch (Exception e) { Logger.error( "General Attribute generation failed! for " + reqAttribut.getFriendlyName(), e); if (reqAttribut.isRequired()) { throw new UnprovideableAttributeException( reqAttribut.getName()); } } } } } NameID subjectNameID = SAML2Utils.createSAMLObject(NameID.class); //build nameID and nameID Format from moasession if (authData.isUseMandate()) { Element mandate = authData.getMandate(); if(mandate == null) { throw new NoMandateDataAvailableException(); } Mandate mandateObject = MandateBuilder.buildMandate(mandate); if(mandateObject == null) { throw new NoMandateDataAvailableException(); } CorporateBodyType corporation = mandateObject.getMandator().getCorporateBody(); PhysicalPersonType pysicalperson = mandateObject.getMandator().getPhysicalPerson(); IdentificationType id; if(corporation != null && corporation.getIdentification().size() > 0) id = corporation.getIdentification().get(0); else if (pysicalperson != null && pysicalperson.getIdentification().size() > 0) id = pysicalperson.getIdentification().get(0); else { Logger.error("Failed to generate IdentificationType"); throw new NoMandateDataAvailableException(); } String bpktype = id.getType(); String bpk = id.getValue().getValue(); if (bpktype.equals(Constants.URN_PREFIX_BASEID)) { if (oaParam.getBusinessService()) { subjectNameID.setValue(new BPKBuilder().buildWBPK(bpk, oaParam.getIdentityLinkDomainIdentifier())); if (oaParam.getIdentityLinkDomainIdentifier().startsWith(AuthenticationSession.REGISTERANDORDNR_PREFIX_)) subjectNameID.setNameQualifier(oaParam.getIdentityLinkDomainIdentifier()); else subjectNameID.setNameQualifier(Constants.URN_PREFIX_WBPK + "+" + oaParam.getIdentityLinkDomainIdentifier()); } else { subjectNameID.setValue(new BPKBuilder().buildBPK(bpk, oaParam.getTarget())); if (oaParam.getTarget().startsWith(Constants.URN_PREFIX_CDID + "+")) subjectNameID.setNameQualifier(oaParam.getTarget()); else subjectNameID.setNameQualifier(Constants.URN_PREFIX_CDID + "+" + oaParam.getTarget()); } } else { subjectNameID.setNameQualifier(bpktype); subjectNameID.setValue(bpk); } } else { subjectNameID.setNameQualifier(authData.getBPKType()); subjectNameID.setValue(authData.getBPK()); } String nameIDFormat = NameID.TRANSIENT; //get NameIDFormat from request AuthnRequest authnReq = (AuthnRequestImpl) authnRequest; if (authnReq.getNameIDPolicy() != null) { nameIDFormat = authnReq.getNameIDPolicy().getFormat(); } else { //get NameIDFormat from metadata List metadataNameIDFormats = spSSODescriptor.getNameIDFormats(); if (metadataNameIDFormats != null) { for (NameIDFormat el : metadataNameIDFormats) { if (NameID.PERSISTENT.equals(el.getFormat())) { nameIDFormat = NameID.PERSISTENT; break; } else if (NameID.TRANSIENT.equals(el.getFormat()) || NameID.UNSPECIFIED.equals(el.getFormat())) break; } } } if (NameID.TRANSIENT.equals(nameIDFormat) || NameID.UNSPECIFIED.equals(nameIDFormat)) { String random = Random.nextRandom(); String nameID = subjectNameID.getValue(); try { MessageDigest md = MessageDigest.getInstance("SHA-1"); byte[] hash = md.digest((nameID + random).getBytes("ISO-8859-1")); subjectNameID.setValue(Base64Utils.encode(hash)); subjectNameID.setNameQualifier(null); subjectNameID.setFormat(NameID.TRANSIENT); } catch (Exception e) { Logger.warn("PVP2 subjectNameID error", e); throw new MOAIDException("pvp2.13", null, e); } } else subjectNameID.setFormat(nameIDFormat); String sessionIndex = null; //if request is a reauthentication and NameIDFormat match reuse old session information if (MiscUtil.isNotEmpty(authData.getNameID()) && MiscUtil.isNotEmpty(authData.getNameIDFormat()) && nameIDFormat.equals(authData.getNameIDFormat())) { subjectNameID.setValue(authData.getNameID()); sessionIndex = authData.getSessionIndex(); } else sessionIndex = SAML2Utils.getSecureIdentifier(); SubjectConfirmationData subjectConfirmationData = SAML2Utils .createSAMLObject(SubjectConfirmationData.class); subjectConfirmationData.setInResponseTo(authnRequest.getID()); subjectConfirmationData.setNotOnOrAfter(new DateTime(authData.getSsoSessionValidTo().getTime())); subjectConfirmationData.setRecipient(assertionConsumerService.getLocation()); //set SLO information sloInformation.setUserNameIdentifier(subjectNameID.getValue()); sloInformation.setNameIDFormat(subjectNameID.getFormat()); sloInformation.setSessionIndex(sessionIndex); return buildGenericAssertion(peerEntity.getEntityID(), date, authnContextClassRef, attrList, subjectNameID, subjectConfirmationData, sessionIndex, subjectConfirmationData.getNotOnOrAfter()); } private static Assertion buildGenericAssertion(String entityID, DateTime date, AuthnContextClassRef authnContextClassRef, List attrList, NameID subjectNameID, SubjectConfirmationData subjectConfirmationData, String sessionIndex, DateTime isValidTo) throws ConfigurationException { Assertion assertion = SAML2Utils.createSAMLObject(Assertion.class); AuthnContext authnContext = SAML2Utils .createSAMLObject(AuthnContext.class); authnContext.setAuthnContextClassRef(authnContextClassRef); AuthnStatement authnStatement = SAML2Utils .createSAMLObject(AuthnStatement.class); authnStatement.setAuthnInstant(date); authnStatement.setSessionIndex(sessionIndex); authnStatement.setAuthnContext(authnContext); assertion.getAuthnStatements().add(authnStatement); AttributeStatement attributeStatement = SAML2Utils .createSAMLObject(AttributeStatement.class); attributeStatement.getAttributes().addAll(attrList); if (attributeStatement.getAttributes().size() > 0) { assertion.getAttributeStatements().add(attributeStatement); } Subject subject = SAML2Utils.createSAMLObject(Subject.class); subject.setNameID(subjectNameID); SubjectConfirmation subjectConfirmation = SAML2Utils .createSAMLObject(SubjectConfirmation.class); subjectConfirmation.setMethod(SubjectConfirmation.METHOD_BEARER); subjectConfirmation.setSubjectConfirmationData(subjectConfirmationData); subject.getSubjectConfirmations().add(subjectConfirmation); Conditions conditions = SAML2Utils.createSAMLObject(Conditions.class); AudienceRestriction audienceRestriction = SAML2Utils .createSAMLObject(AudienceRestriction.class); Audience audience = SAML2Utils.createSAMLObject(Audience.class); audience.setAudienceURI(entityID); audienceRestriction.getAudiences().add(audience); conditions.setNotBefore(date); conditions.setNotOnOrAfter(isValidTo); conditions.getAudienceRestrictions().add(audienceRestriction); assertion.setConditions(conditions); Issuer issuer = SAML2Utils.createSAMLObject(Issuer.class); issuer.setValue(PVPConfiguration.getInstance().getIDPPublicPath()); issuer.setFormat(NameID.ENTITY); assertion.setIssuer(issuer); assertion.setSubject(subject); assertion.setID(SAML2Utils.getSecureIdentifier()); assertion.setIssueInstant(date); return assertion; } }