/******************************************************************************* * 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.saml1; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.List; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.Marshaller; import javax.xml.namespace.QName; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import org.w3c.dom.Element; import org.xml.sax.SAXException; import at.gv.egovernment.moa.id.auth.AuthenticationServer; import at.gv.egovernment.moa.id.auth.builder.AuthenticationDataAssertionBuilder; import at.gv.egovernment.moa.id.auth.builder.BPKBuilder; import at.gv.egovernment.moa.id.auth.builder.PersonDataBuilder; import at.gv.egovernment.moa.id.auth.builder.SAMLArtifactBuilder; import at.gv.egovernment.moa.id.auth.data.AuthenticationSession; import at.gv.egovernment.moa.id.auth.data.ExtendedSAMLAttribute; import at.gv.egovernment.moa.id.auth.data.IdentityLink; import at.gv.egovernment.moa.id.auth.exception.AuthenticationException; import at.gv.egovernment.moa.id.auth.exception.BuildException; import at.gv.egovernment.moa.id.auth.exception.ParseException; import at.gv.egovernment.moa.id.auth.exception.ServiceException; import at.gv.egovernment.moa.id.auth.exception.ValidateException; import at.gv.egovernment.moa.id.auth.parser.SAMLArtifactParser; import at.gv.egovernment.moa.id.auth.validator.parep.ParepUtils; import at.gv.egovernment.moa.id.commons.db.ex.MOADatabaseException; import at.gv.egovernment.moa.id.config.ConfigurationException; import at.gv.egovernment.moa.id.config.auth.OAAuthParameter; import at.gv.egovernment.moa.id.config.auth.data.SAML1ConfigurationParameters; import at.gv.egovernment.moa.id.data.AuthenticationData; import at.gv.egovernment.moa.id.moduls.IRequest; import at.gv.egovernment.moa.id.storage.AssertionStorage; //import at.gv.egovernment.moa.id.util.IdentityLinkReSigner; 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.DOMUtils; import at.gv.egovernment.moa.util.MiscUtil; import at.gv.egovernment.moa.util.StringUtils; import at.gv.util.xsd.persondata.IdentificationType; import at.gv.util.xsd.persondata.IdentificationType.Value; import at.gv.util.xsd.persondata.PersonNameType; import at.gv.util.xsd.persondata.PersonNameType.FamilyName; import at.gv.util.xsd.persondata.PhysicalPersonType; public class SAML1AuthenticationServer extends AuthenticationServer { private static SAML1AuthenticationServer instance; public static SAML1AuthenticationServer getInstace() { if (instance == null) instance = new SAML1AuthenticationServer(); return instance; } private static AssertionStorage authenticationDataStore = AssertionStorage.getInstance(); /** * time out in milliseconds used by {@link cleanup} for authentication data * store */ private static final long authDataTimeOut = 2 * 60 * 1000; // default 2 minutes public Throwable getErrorResponse(String samlArtifact) throws AuthenticationException { try { new SAMLArtifactParser(samlArtifact).parseAssertionHandle(); } catch (ParseException ex) { throw new AuthenticationException("1205", new Object[] { samlArtifact, ex.toString() }); } Throwable error = null; synchronized (authenticationDataStore) { try { error = authenticationDataStore .get(samlArtifact, Throwable.class); authenticationDataStore.remove(samlArtifact); } catch (MOADatabaseException e) { Logger.error("Assertion not found for SAML Artifact: " + samlArtifact); throw new AuthenticationException("1206", new Object[] { samlArtifact }); } } return error; } /** * Retrieves AuthenticationData indexed by the SAML artifact. * The AuthenticationData is deleted from the store upon end of * this call. * * @return AuthenticationData */ public String getSaml1AuthenticationData(String samlArtifact) throws AuthenticationException { try { new SAMLArtifactParser(samlArtifact).parseAssertionHandle(); } catch (ParseException ex) { throw new AuthenticationException("1205", new Object[] { samlArtifact, ex.toString() }); } String authData = null; synchronized (authenticationDataStore) { // System.out.println("assertionHandle: " + assertionHandle); try { authData = authenticationDataStore .get(samlArtifact, String.class, authDataTimeOut); } catch (MOADatabaseException e) { Logger.error("Assertion not found for SAML Artifact: " + samlArtifact); throw new AuthenticationException("1206", new Object[] { samlArtifact }); } } authenticationDataStore.remove(samlArtifact); Logger.debug("Assertion delivered for SAML Artifact: " + samlArtifact); return authData; } public String BuildErrorAssertion(Throwable error, IRequest protocolRequest) throws BuildException, MOADatabaseException { String samlArtifact = new SAMLArtifactBuilder().build( protocolRequest.getOAURL(), protocolRequest.getRequestID(), null); authenticationDataStore.put(samlArtifact, error); return samlArtifact; } public String BuildSAMLArtifact(OAAuthParameter oaParam, SAML1AuthenticationData authData, String sourceID) throws ConfigurationException, BuildException, AuthenticationException { //Load SAML1 Parameter from OA config SAML1ConfigurationParameters saml1parameter = oaParam.getSAML1Parameter(); boolean useCondition = saml1parameter.isUseCondition(); int conditionLength = saml1parameter.getConditionLength(); try { //set BASE64 encoded signer certificate String signerCertificateBase64 = ""; if (saml1parameter.isProvideCertificate()) { byte[] signerCertificate = authData.getSignerCertificate(); if (signerCertificate != null) { signerCertificateBase64 = Base64Utils .encode(signerCertificate); } else { Logger.info("\"provideCertificate\" is \"true\", but no signer certificate available"); } } //set prPersion boolean provideStammzahl = saml1parameter.isProvideStammzahl() || oaParam.getBusinessService(); String prPerson = ""; String ilAssertion = ""; if (authData.getIdentityLink() != null) { prPerson = new PersonDataBuilder().build(authData.getIdentityLink(), provideStammzahl); //set IdentityLink for assortion if (saml1parameter.isProvideIdentityLink()) { ilAssertion = authData.getIdentityLink().getSerializedSamlAssertion(); if (!provideStammzahl) ilAssertion = StringUtils.replaceAll(ilAssertion, authData.getIdentityLink() .getIdentificationValue(), ""); } } else { Logger.info("No IdentityLink available! Build attribute 'PersonDate' from givenname, familyname and dateofbirth. "); PhysicalPersonType person = new PhysicalPersonType(); PersonNameType name = new PersonNameType(); person.setName(name); FamilyName familyName = new FamilyName(); name.getFamilyName().add(familyName ); IdentificationType id = new IdentificationType(); person.getIdentification().add(id ); Value value = new Value(); id.setValue(value ); id.setType(authData.getIdentificationType()); //add baseID if it is requested and available if ( MiscUtil.isNotEmpty(authData.getIdentificationValue()) && saml1parameter.isProvideIdentityLink() ) value.setValue(authData.getIdentificationValue()); else value.setValue(""); familyName.setValue(authData.getFamilyName()); familyName.setPrimary("undefined"); name.getGivenName().add(authData.getGivenName()); person.setDateOfBirth(authData.getFormatedDateOfBirth()); JAXBContext jc = JAXBContext.newInstance("at.gv.util.xsd.persondata"); Marshaller m = jc.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); // m.setProperty("com.sun.xml.bind.namespacePrefixMapper", new NamespacePrefixMapper() { // public String getPreferredPrefix(String arg0, String arg1, boolean arg2) { // if (Constants.PD_NS_URI.equals(arg0)) // return Constants.PD_PREFIX; // else // return arg1; // } // }); ByteArrayOutputStream stream = new ByteArrayOutputStream(); m.marshal( new JAXBElement(new QName(Constants.PD_NS_URI,"Person"), PhysicalPersonType.class, person), stream); prPerson = StringUtils.removeXMLDeclaration(new String(stream.toByteArray(), "UTF-8")); stream.close(); } //set Authblock String authBlock = ""; if (authData.getAuthBlock() != null) { authBlock = saml1parameter.isProvideAUTHBlock() ? authData.getAuthBlock() : ""; } else { Logger.info("\"provideAuthBlock\" is \"true\", but no authblock available"); } String samlAssertion; if (authData.isUseMandate()) { List oaAttributes = authData.getExtendedSAMLAttributesOA(); //only provide full mandate if it is included. //In case of federation only a short mandate could be include if (saml1parameter.isProvideFullMandatorData() && authData.getMISMandate().isFullMandateIncluded()) { try { ExtendedSAMLAttribute[] extendedSAMLAttributes = addExtendedSamlAttributes( authData.getMISMandate(), oaParam.getBusinessService(), saml1parameter.isProvideStammzahl()); if (extendedSAMLAttributes != null) { String identifier = "MISService"; String friendlyName ="MISService"; int length = extendedSAMLAttributes.length; for (int i = 0; i < length; i++) { ExtendedSAMLAttribute samlAttribute = extendedSAMLAttributes[i]; Object value = verifySAMLAttribute(samlAttribute, i, identifier, friendlyName); if ((value instanceof String) || (value instanceof Element)) { switch (samlAttribute.getAddToAUTHBlock()) { case ExtendedSAMLAttribute.ADD_TO_AUTHBLOCK: replaceExtendedSAMLAttribute(oaAttributes, samlAttribute); break; case ExtendedSAMLAttribute.NOT_ADD_TO_AUTHBLOCK: replaceExtendedSAMLAttribute(oaAttributes, samlAttribute); break; case ExtendedSAMLAttribute.ADD_TO_AUTHBLOCK_ONLY: break; default: Logger .info("Invalid return value from method \"getAddToAUTHBlock()\" (" + samlAttribute.getAddToAUTHBlock() + ") in SAML attribute number " + (i + 1) + " for infobox " + identifier); throw new ValidateException("validator.47", new Object[] { friendlyName, String.valueOf((i + 1)) }); } } else { Logger .info("The type of SAML-Attribute number " + (i + 1) + " returned from " + identifier + "-infobox validator is not valid. Must be either \"java.Lang.String\"" + " or \"org.w3c.dom.Element\""); throw new ValidateException("validator.46", new Object[] { identifier, String.valueOf((i + 1)) }); } } } } catch (SAXException e) { throw new AuthenticationException("auth.16", new Object[] { GET_MIS_SESSIONID }, e); } catch (IOException e) { throw new AuthenticationException("auth.16", new Object[] { GET_MIS_SESSIONID }, e); } catch (ParserConfigurationException e) { throw new AuthenticationException("auth.16", new Object[] { GET_MIS_SESSIONID }, e); } catch (TransformerException e) { throw new AuthenticationException("auth.16", new Object[] { GET_MIS_SESSIONID }, e); } } String mandateDate = generateMandateDate(oaParam, authData); samlAssertion = new AuthenticationDataAssertionBuilder().buildMandate( authData, prPerson, mandateDate, authBlock, ilAssertion, authData.getBkuURL(), signerCertificateBase64, oaParam.getBusinessService(), oaAttributes, useCondition, conditionLength); } else { samlAssertion = new AuthenticationDataAssertionBuilder().build( authData, prPerson, authBlock, ilAssertion, authData.getBkuURL(), signerCertificateBase64, oaParam.getBusinessService(), authData.getExtendedSAMLAttributesOA(), useCondition, conditionLength); } //authData.setSamlAssertion(samlAssertion); String samlArtifact = new SAMLArtifactBuilder().build( authData.getIssuer(), Random.nextRandom(), sourceID); storeAuthenticationData(samlArtifact, samlAssertion); Logger.info("Anmeldedaten angelegt, SAML Artifakt " + samlArtifact); return samlArtifact; } catch (Throwable ex) { throw new BuildException("builder.00", new Object[] { "AuthenticationData", ex.toString() }, ex); } } private String generateMandateDate(OAAuthParameter oaParam, AuthenticationData authData ) throws AuthenticationException, BuildException, ParseException, ConfigurationException, ServiceException, ValidateException { if (authData == null) throw new AuthenticationException("auth.10", new Object[] { REQ_VERIFY_AUTH_BLOCK, PARAM_SESSIONID }); IdentityLink tempIdentityLink = null; Element mandate = authData.getMandate(); if (authData.isUseMandate()) { tempIdentityLink = new IdentityLink(); Element mandator = ParepUtils.extractMandator(mandate); String dateOfBirth = ""; Element prPerson = null; String familyName = ""; String givenName = ""; String identificationType = ""; String identificationValue = ""; if (mandator != null) { boolean physical = ParepUtils.isPhysicalPerson(mandator); if (physical) { familyName = ParepUtils.extractText(mandator, "descendant-or-self::pr:Name/pr:FamilyName/text()"); givenName = ParepUtils.extractText(mandator, "descendant-or-self::pr:Name/pr:GivenName/text()"); dateOfBirth = ParepUtils .extractMandatorDateOfBirth(mandator); } else { familyName = ParepUtils.extractMandatorFullName(mandator); } identificationType = ParepUtils.getIdentification(mandator, "Type"); identificationValue = ParepUtils.extractMandatorWbpk(mandator); prPerson = ParepUtils.extractPrPersonOfMandate(mandate); if (physical && oaParam.getBusinessService() && identificationType != null && Constants.URN_PREFIX_BASEID .equals(identificationType)) { // now we calculate the wbPK and do so if we got it from the // BKU //load IdentityLinkDomainType from OAParam String type = oaParam.getIdentityLinkDomainIdentifier(); if (type.startsWith(Constants.URN_PREFIX_WBPK + "+")) identificationType = type; else identificationType = Constants.URN_PREFIX_WBPK + "+" + type; identificationValue = new BPKBuilder().buildWBPK( identificationValue, identificationType); ParepUtils .HideStammZahlen(prPerson, true, null, null, true); } tempIdentityLink.setDateOfBirth(dateOfBirth); tempIdentityLink.setFamilyName(familyName); tempIdentityLink.setGivenName(givenName); tempIdentityLink.setIdentificationType(identificationType); tempIdentityLink.setIdentificationValue(identificationValue); tempIdentityLink.setPrPerson(prPerson); try { tempIdentityLink.setSamlAssertion(authData.getIdentityLink() .getSamlAssertion()); } catch (Exception e) { throw new ValidateException("validator.64", null); } } } Element mandatePerson = tempIdentityLink.getPrPerson(); String mandateData = null; try { boolean provideStammzahl = oaParam.getSAML1Parameter().isProvideStammzahl(); String oatargetType; if(oaParam.getBusinessService()) { if (oaParam.getIdentityLinkDomainIdentifier().startsWith(AuthenticationSession.REGISTERANDORDNR_PREFIX_)) oatargetType = oaParam.getIdentityLinkDomainIdentifier(); else oatargetType = AuthenticationSession.REGISTERANDORDNR_PREFIX_+oaParam.getIdentityLinkDomainIdentifier(); } else { oatargetType = AuthenticationSession.TARGET_PREFIX_ + oaParam.getTarget(); } Element prIdentification = (Element) mandatePerson .getElementsByTagNameNS(Constants.PD_NS_URI, "Identification").item(0); if (!oatargetType.equals(tempIdentityLink.getIdentificationType())) { String isPrPerson = mandatePerson.getAttribute("xsi:type"); if (!StringUtils.isEmpty(isPrPerson)) { if (isPrPerson.equalsIgnoreCase("pr:PhysicalPerson")) { String baseid = getBaseId(mandatePerson); Element identificationBpK = createIdentificationBPK(mandatePerson, baseid, oaParam.getTarget()); if (!provideStammzahl) { prIdentification.getFirstChild().setTextContent(""); } mandatePerson.insertBefore(identificationBpK, prIdentification); } } } else { // Element identificationBpK = mandatePerson.getOwnerDocument() // .createElementNS(Constants.PD_NS_URI, "Identification"); // Element valueBpK = mandatePerson.getOwnerDocument().createElementNS( // Constants.PD_NS_URI, "Value"); // // valueBpK.appendChild(mandatePerson.getOwnerDocument().createTextNode( // tempIdentityLink.getIdentificationValue())); // Element typeBpK = mandatePerson.getOwnerDocument().createElementNS( // Constants.PD_NS_URI, "Type"); // typeBpK.appendChild(mandatePerson.getOwnerDocument().createTextNode( // "urn:publicid:gv.at:cdid+bpk")); // identificationBpK.appendChild(valueBpK); // identificationBpK.appendChild(typeBpK); // // mandatePerson.insertBefore(identificationBpK, prIdentification); } mandateData = DOMUtils.serializeNode(mandatePerson); } catch (TransformerException e1) { throw new AuthenticationException("auth.16", new Object[] { GET_MIS_SESSIONID }); } catch (IOException e1) { throw new AuthenticationException("auth.16", new Object[] { GET_MIS_SESSIONID }); } return mandateData; } /** * Stores authentication data indexed by the assertion handle contained in * the given saml artifact. * * @param samlArtifact * SAML artifact * @param authData * authentication data * @throws AuthenticationException * when SAML artifact is invalid */ private void storeAuthenticationData(String samlArtifact, String samlAssertion) throws AuthenticationException { try { SAMLArtifactParser parser = new SAMLArtifactParser(samlArtifact); // check type code 0x0001 byte[] typeCode = parser.parseTypeCode(); if (typeCode[0] != 0 || typeCode[1] != 1) throw new AuthenticationException("auth.06", new Object[] { samlArtifact }); parser.parseAssertionHandle(); synchronized (authenticationDataStore) { Logger.debug("Assertion stored for SAML Artifact: " + samlArtifact); authenticationDataStore.put(samlArtifact, samlAssertion); } } catch (AuthenticationException ex) { throw ex; } catch (Throwable ex) { throw new AuthenticationException("auth.06", new Object[] { samlArtifact }); } } }