/* * Copyright 2011 Federal Chancellery Austria and * Graz University of Technology * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package at.gv.util.data; import java.io.FileOutputStream; import java.io.Serializable; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.X509EncodedKeySpec; import java.text.ParseException; import java.util.Date; import java.util.List; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.JAXBException; import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.time.DateFormatUtils; import org.apache.commons.lang.time.DateUtils; import org.apache.log4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import at.gv.util.BpkUtil; import at.gv.util.DOMUtils; import at.gv.util.MiscUtil; import at.gv.util.ToStringUtil; import at.gv.util.ex.EgovUtilException; import at.gv.util.ex.InternalErrorException; import at.gv.util.xsd.mandate.Mandate; import at.gv.util.xsd.persondata.IdentificationType; import at.gv.util.xsd.persondata.PersonDataType; import at.gv.util.xsd.persondata.PhysicalPersonType; import at.gv.util.xsd.saml.assertion.AssertionType; import at.gv.util.xsd.saml.assertion.AttributeStatementType; import at.gv.util.xsd.saml.assertion.AttributeType; import at.gv.util.xsd.saml.assertion.NameIdentifierType; import at.gv.util.xsd.saml.assertion.ObjectFactory; import at.gv.util.xsd.saml.assertion.StatementAbstractType; import at.gv.util.xsd.saml.assertion.SubjectConfirmationType; import at.gv.util.xsd.saml.assertion.SubjectType; /** * @author Arne Tauber * @author Thomas Knall */ public final class ElectronicIdentity implements Serializable, Empty, RoleContainer { private static final long serialVersionUID = 1L; private Logger log = Logger.getLogger(this.getClass().getName()); private static final String FIRSTNAME = "firstname"; private static final String LASTNAME = "lastname"; private static final String TITLE = "title"; private static final String DATEOFBIRTH = "dateofbirth"; private static final String EMAIL = "email"; private static final String BASEID = "baseid"; private static final String BPK = "bpk"; private static final String WBPK = "wbpk"; private static final String VZBPK = "vzbpk"; private static final String ZBPK = "zbpk"; private static final String BKU_URL = "bkuurl"; private static final String ROLES = "roles"; private static final String ROLE = "role"; private static final String NAME_QUALIFIER = "namequalifier"; private static final String NAME_IDENTIFIER = "nameidentifier"; private static final String MANDATE_ENABLED = "mandateenabled"; private static final String BPK_NAME_QUALIFIER = "urn:publicid:gv.at:cdid+bpk"; private static final String WBPK_NAME_QUALIFIER_PREFIX = "urn:publicid:gv.at:wbpk"; private static final String BASE_NAME_QUALIFIER = "urn:publicid:gv.at:baseid"; private String firstName; private String title; private String lastName; private Date dateOfBirth; private String bpk; private String wbpk; private String baseId; private String vzbpk; private String zbpk; private String email; private String bkuURL; private String nameQualifier; private String nameIdentifier; private Set roles; private Object userdefinedData; private AssertionType samlAssertion; private Mandate mandate; public String getTitle() { return this.title; } public Object getUserdefinedData() { return this.userdefinedData; } public void setUserdefinedData(Object userdefinedData) { this.userdefinedData = userdefinedData; } public void setTitle(String title) { this.title = title; } public String getBkuURL() { return this.bkuURL; } public void setBkuURL(String bkuURL) { this.bkuURL = bkuURL; } public String getEmail() { return this.email; } public void setEmail(String email) { this.email = email; } public String getWbpk() { return this.wbpk; } public void setWbpk(String wbpk) { this.wbpk = wbpk; } public String getNameQualifier() { return this.nameQualifier; } public void setNameQualifier(String nameQualifier) { this.nameQualifier = nameQualifier; } public String getNameIdentifier() { return this.nameIdentifier; } public void setNameIdentifier(String nameIdentifier) { this.nameIdentifier = nameIdentifier; } public String getZbpk() { return this.zbpk; } public void setZbpk(String zbpk) { this.zbpk = zbpk; } public String getVzbpk() { return this.vzbpk; } public void setVzbpk(String vzbpk) { this.vzbpk = vzbpk; } public String getBaseId() { return this.baseId; } public void setBaseId(String baseId) { this.baseId = baseId; } public String getBpk() { return this.bpk; } public void setBpk(String bpk) { this.bpk = bpk; } public AssertionType getSamlAssertion() { return this.samlAssertion; } public Date getDateOfBirth() { return this.dateOfBirth; } public void setDateOfBirth(Date dateOfBirth) { this.dateOfBirth = dateOfBirth; } public String getFirstName() { return this.firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return this.lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public Set getRoles() { return this.roles; } public void setRoles(Set roles) { MiscUtil.assertNotNull(roles, "Roles"); this.roles = roles; } public ElectronicIdentity addRole(String role) { MiscUtil.assertNotEmpty(role, "Role"); this.roles.add(role); return this; } public ElectronicIdentity() { } public ElectronicIdentity(String firstName, String lastName, Date dateOfBirth) { this(); this.setFirstName(firstName); this.setLastName(lastName); this.setDateOfBirth(dateOfBirth); } public ElectronicIdentity(String firstName, String lastName, String email) { this(); this.setFirstName(firstName); this.setLastName(lastName); this.setEmail(email); } private void updateAll() { if (this.getNameQualifier() != null && this.getNameQualifier().startsWith(WBPK_NAME_QUALIFIER_PREFIX) && MiscUtil.isEmpty(this.getNameIdentifier())) { log.debug("NameQualifier starts with \"" + WBPK_NAME_QUALIFIER_PREFIX + "\" and BaseId is present. Calculating NameIdentifier as wbpk."); this.nameIdentifier = BpkUtil.calcWBPK(this.getBaseId(), this.getNameQualifier()); } if (this.getNameQualifier() != null && this.getNameQualifier().startsWith(WBPK_NAME_QUALIFIER_PREFIX) && MiscUtil.isEmpty(this.getWbpk()) && MiscUtil.isNotEmpty(this.getNameIdentifier())) { log.debug("NameQualifier starts with \"" + WBPK_NAME_QUALIFIER_PREFIX + "\". We have a wbpk."); this.wbpk = this.getNameIdentifier(); } if (BPK_NAME_QUALIFIER.equals(this.getNameQualifier()) && MiscUtil.isEmpty(this.getBpk()) && MiscUtil.isNotEmpty(this.getNameIdentifier())) { log.debug("NameQualifier equals to \"" + BPK_NAME_QUALIFIER + "\". We have a bpk."); this.bpk = this.getNameIdentifier(); } if (MiscUtil.isNotEmpty(this.getBaseId())) { log.debug("BaseId present -> calculating zbpk"); this.zbpk = BpkUtil.calcZBPK(this.getBaseId()); } } void setSamlAssertion(AssertionType samlAssertion) { this.samlAssertion = samlAssertion; } public ElectronicIdentity(Document doc) throws EgovUtilException { this(doc, false); } public ElectronicIdentity(Document doc, boolean isMOAIDAssertion) throws EgovUtilException { this(); try { MiscUtil.assertNotNull(doc, "Document"); JAXBContext ctx = JAXBContext.newInstance(AssertionType.class .getPackage().getName()); JAXBElement assertionElement = (JAXBElement) ctx .createUnmarshaller().unmarshal(doc.getDocumentElement()); if (isMOAIDAssertion) { initializeCitizenCardWithMOAIDAssertion(assertionElement.getValue()); } else { initializeCitizenCard(assertionElement.getValue()); } } catch (JAXBException e) { throw new EgovUtilException(e); } } public ElectronicIdentity(AssertionType assertion) throws EgovUtilException { this(assertion, false); } public ElectronicIdentity(AssertionType assertion, boolean isMOAIDAssertion) throws EgovUtilException { this(); // debug moa-id response log.trace("Debug response: " + System.getProperty("debug.moaid.log.path") != null); if (System.getProperty("debug.moaid.log.path") != null) { try { ObjectFactory of = new ObjectFactory(); JAXBContext ctx = JAXBContext.newInstance(AssertionType.class.getPackage().getName()); String file = System.getProperty("debug.moaid.log.path") + "/" + MiscUtil.formatDate(new Date(), "yyyyMMdd-HHmmss") +".xml"; log.trace("Writing MOA-ID response to: " + file); FileOutputStream fos = new FileOutputStream(file); ctx.createMarshaller().marshal(of.createAssertion(assertion), fos); fos.flush(); fos.close(); } catch(Exception e) { log.debug(e); } } if (isMOAIDAssertion) { initializeCitizenCardWithMOAIDAssertion(assertion); } else { initializeCitizenCard(assertion); } } private void initializeCitizenCard(AssertionType assertion) throws EgovUtilException { MiscUtil.assertNotNull(assertion, "SAMLAssertion"); try { for (StatementAbstractType sat : assertion .getStatementOrSubjectStatementOrAuthenticationStatement()) { if (sat instanceof AttributeStatementType) { AttributeStatementType attrStmt = (AttributeStatementType) sat; SubjectType subject = attrStmt.getSubject(); for (JAXBElement subChild : subject.getContent()) { if (subChild.getValue() instanceof SubjectConfirmationType) { SubjectConfirmationType sct = (SubjectConfirmationType) subChild .getValue(); Element scdNode = (Element) sct.getSubjectConfirmationData(); Element personNode = (Element) DOMUtils .getChildElements(scdNode).get(0); JAXBContext ctx = JAXBContext.newInstance(PhysicalPersonType.class .getPackage().getName()); JAXBElement pptElement = (JAXBElement) ctx .createUnmarshaller().unmarshal(personNode); PhysicalPersonType ppt = pptElement.getValue(); this.baseId = ppt.getIdentification().get(0).getValue().getValue(); this.firstName = ppt.getName().getGivenName().get(0); this.lastName = ppt.getName().getFamilyName().get(0).getValue(); this.dateOfBirth = MiscUtil.parseXMLDate(ppt.getDateOfBirth()); } } } } } catch(JAXBException e) { throw new EgovUtilException(e); } } private void initializeCitizenCardWithMOAIDAssertion(AssertionType assertion) throws EgovUtilException { MiscUtil.assertNotNull(assertion, "SAMLAssertion"); try { AttributeStatementType attrStmt = (AttributeStatementType) assertion .getStatementOrSubjectStatementOrAuthenticationStatement().get(0); // parse subject SubjectType subject = attrStmt.getSubject(); for (JAXBElement subChild : subject.getContent()) { if (subChild.getValue() instanceof SubjectConfirmationType) { SubjectConfirmationType sct = (SubjectConfirmationType) subChild .getValue(); Element scdNode = (Element) sct.getSubjectConfirmationData(); if (scdNode.hasChildNodes()) { Element assertionNode = (Element) DOMUtils.getChildElements(scdNode) .get(0); JAXBContext ctx = JAXBContext.newInstance(AssertionType.class.getPackage().getName()); JAXBElement assertionElement = (JAXBElement) ctx .createUnmarshaller().unmarshal(assertionNode); AssertionType subjectAssertion = assertionElement.getValue(); for (StatementAbstractType sat : subjectAssertion .getStatementOrSubjectStatementOrAuthenticationStatement()) { if (sat instanceof AttributeStatementType) { AttributeStatementType ast = (AttributeStatementType) sat; for (AttributeType attr : ast.getAttribute()) { if ("bPK".equals(attr.getAttributeName())) { Element attrValueNode = (Element) attr.getAttributeValue() .get(0); Element idNode = (Element) DOMUtils.getChildElements( attrValueNode).get(0); ctx = JAXBContext.newInstance(IdentificationType.class .getPackage().getName()); JAXBElement idElement = (JAXBElement) ctx .createUnmarshaller().unmarshal(idNode); IdentificationType idt = (IdentificationType) idElement .getValue(); //this.setBpk(idt.getValue().getValue()); } } } } } } else if (subChild.getValue() instanceof NameIdentifierType) { NameIdentifierType nit = (NameIdentifierType) subChild.getValue(); this.setNameQualifier(nit.getNameQualifier()); this.setNameIdentifier(nit.getValue()); } } for (AttributeType attr : attrStmt.getAttribute()) { if ("PersonData".equals(attr.getAttributeName())) { Element attrValueNode = (Element) attr.getAttributeValue().get(0); Element personNode = (Element) DOMUtils.getChildElements( attrValueNode).get(0); JAXBContext ctx = JAXBContext.newInstance(PhysicalPersonType.class .getPackage().getName()); JAXBElement pptElement = (JAXBElement) ctx .createUnmarshaller().unmarshal(personNode); PhysicalPersonType ppt = pptElement.getValue(); String baseId = ppt.getIdentification().get(0).getValue().getValue(); this.setBaseId(baseId); this.setZbpk(BpkUtil.calcZBPK(baseId)); this.setVzbpk(BpkUtil.calcVZBPK(baseId)); this.setDateOfBirth(MiscUtil.parseXMLDate(ppt.getDateOfBirth())); this.setFirstName(ppt.getName().getGivenName().get(0)); this.setLastName(ppt.getName().getFamilyName().get(0).getValue()); } else if ("bkuURL".equals(attr.getAttributeName())) { Node attrValueNode = (Node) attr.getAttributeValue().get(0); this.setBkuURL(attrValueNode.getFirstChild().getNodeValue()); } else if ("Mandate".equals(attr.getAttributeName())) { Element attrValueNode = (Element) attr.getAttributeValue().get(0); List mandateElementList = DOMUtils.getChildElements(attrValueNode); if (mandateElementList != null && mandateElementList.size() > 0) { // parse mandate JAXBContext ctx = JAXBContext.newInstance(Mandate.class.getPackage().getName()); this.mandate = (Mandate) ctx.createUnmarshaller().unmarshal((Element) mandateElementList.get(0)); } } } } catch (JAXBException e) { throw new EgovUtilException(e); } } /** * Creates a wrapper for buergerkarte person data.
* Important note: properties-files are supposed to contain ISO 8859-1 * character encoding * * @param properties * Properties containing buergerkarte person data as key/value pairs */ public ElectronicIdentity(Properties properties) { this(); this.evaluateProperties(properties); } /** * Fills wrapper with buergerkarte person data from a Properties file.
* Important note: properties-files are supposed to contain ISO 8859-1 * character encoding * * @param properties * Properties containing buergerkarte person data as key/value pairs * @throws CannotResetException */ private void evaluateProperties(Properties properties) { if (properties != null) { this.setFirstName(properties.getProperty(FIRSTNAME)); this.setLastName(properties.getProperty(LASTNAME)); if (properties.getProperty(DATEOFBIRTH) != null) { try { this.setDateOfBirth(DateUtils.parseDate( properties.getProperty(DATEOFBIRTH), new String[] { "yyyy-MM-dd", "dd.MM.yyyy", })); } catch (ParseException e) { log.error(e); } } this.setTitle(properties.getProperty(TITLE)); this.setBpk(properties.getProperty(BPK)); this.setWbpk(properties.getProperty(WBPK)); this.setBaseId(properties.getProperty(BASEID)); this.setNameIdentifier(properties.getProperty(NAME_IDENTIFIER)); this.setNameQualifier(properties.getProperty(NAME_QUALIFIER)); if (MiscUtil.isEmpty(this.getBaseId()) && BASE_NAME_QUALIFIER.equals(this.getNameQualifier())) { this.setBaseId(this.getNameIdentifier()); } this.setEmail(properties.getProperty(EMAIL)); this.setVzbpk(properties.getProperty(VZBPK)); this.setZbpk(properties.getProperty(ZBPK)); this.setBkuURL(properties.getProperty(BKU_URL)); String roles = properties.getProperty(ROLES); if (MiscUtil.isNotEmpty(roles)) { StringTokenizer tokenizer = new StringTokenizer(roles, ","); while (tokenizer.hasMoreTokens()) { String role = StringUtils.trim(tokenizer.nextToken()); if (MiscUtil.isNotEmpty(role)) { this.roles.add(role); } } } String role = StringUtils.trim(properties.getProperty(ROLE)); if (MiscUtil.isNotEmpty(role)) { this.roles.add(role); } this.updateAll(); } } public void calcBpk(String domain) { if (MiscUtil.isEmpty(this.getBaseId())) { throw new InternalErrorException( "Unable to calculate bpk. BaseId has to be set."); } if (MiscUtil.isEmpty(domain)) { throw new IllegalArgumentException( "Unable to calculate bpk. Target/sector/domain must not be empty."); } this.bpk = BpkUtil.calcBPK(this.getBaseId(), domain); } public void calcWbpk() { MiscUtil.assertNotEmpty(this.getBaseId(), "BaseId"); MiscUtil.assertNotEmpty(this.getNameQualifier(), "NameQualifier"); this.wbpk = BpkUtil.calcWBPK(this.getBaseId(), this.getNameQualifier()); this.nameIdentifier = this.wbpk; } protected void calcVzbpk(byte[] rsaPublicKey, String domain) { MiscUtil.assertNotEmpty(domain, "Domain"); if (MiscUtil.isEmpty(this.getBaseId())) { throw new InternalErrorException( "Unable to calculate bpk. BaseId has to be set."); } MiscUtil.assertNotEmpty(rsaPublicKey, "RSAPublicKey"); PublicKey publicKey; try { KeyFactory rsaKeyFac = KeyFactory.getInstance("RSA"); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(rsaPublicKey); publicKey = (RSAPublicKey) rsaKeyFac.generatePublic(keySpec); } catch (InvalidKeySpecException e) { throw new InternalErrorException(e); } catch (NoSuchAlgorithmException e) { throw new InternalErrorException(e); } this.vzbpk = BpkUtil.calcVZBPK(this.getBaseId(), publicKey); } public void calcVzbpk(byte[] rasPublicKey) { this.calcVzbpk(rasPublicKey, BpkUtil.SECTOR_DELIVERY); } public void calcVzbpk() { this.calcVzbpk(BpkUtil.PUBLIC_KEY_ZUSEKOPF_SN01_BASE64.getBytes(), BpkUtil.SECTOR_DELIVERY); } public void calcZbpk() { if (MiscUtil.isEmpty(this.getBaseId())) { throw new InternalErrorException( "Unable to calculate bpk. BaseId has to be set."); } this.setZbpk(BpkUtil.calcZBPK(this.getBaseId())); } @Override public String toString() { return new ToStringBuilder(this) .append("firstName", this.firstName) .append("lastName", this.lastName) .append( "dateOfBirth", this.dateOfBirth != null ? DateFormatUtils.format(this.dateOfBirth, "yyyy-MM-dd") : this.dateOfBirth) .append("title", this.title) .append("email", this.email) //.append("baseId", this.baseId) .append("nameQualifier", this.nameQualifier) .append("nameIdentifier", this.nameIdentifier) .append("bpk", this.bpk) .append("wbpk", this.wbpk) .append("zbpk", this.zbpk) .append("vzbpk", this.vzbpk) .append("bkuURL", this.bkuURL) .append("userdefinedData", this.userdefinedData) .append( "roles", this.roles != null ? ToStringUtil.toString(this.roles, ", ", "\"") : null) .append("samlAssertion", this.samlAssertion != null ? "" : "").toString(); } public boolean isEmpty() { boolean stringsEmpty = MiscUtil.areAllEmpty(this.wbpk, this.nameQualifier, this.nameIdentifier, this.baseId, this.bpk, this.firstName, this.lastName, this.vzbpk, this.zbpk, this.email, this.bkuURL); boolean udEmpty = true; if (this.userdefinedData != null) { if (this.userdefinedData instanceof Empty) { udEmpty = ((Empty) this.userdefinedData).isEmpty(); } else { udEmpty = false; } } return stringsEmpty && udEmpty && this.dateOfBirth == null && MiscUtil.isEmpty(this.roles) && (this.samlAssertion != null); } public boolean hasRole(String role) { return this.roles.contains(role); } public void setMandate(Mandate mandate) { this.mandate = mandate; } public Mandate getMandate() { return mandate; } }