package at.gv.egovernment.moa.id.auth.parser; import java.security.interfaces.RSAPublicKey; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; import java.security.PublicKey; import java.util.ArrayList; import java.util.List; import org.w3c.dom.Element; import org.w3c.dom.traversal.NodeIterator; import at.gv.egovernment.moa.id.*; import at.gv.egovernment.moa.id.auth.data.IdentityLink; 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.XPathUtils; import at.gv.egovernment.moa.id.util.ECDSAKeyValueConverter; /** * Parses an identity link <saml:Assertion> * @author Paul Ivancsics * @version $Id$ */ public class IdentityLinkAssertionParser { // // XPath namespace prefix shortcuts // /** Xpath prefix for reaching PersonData Namespaces */ private static final String PDATA = Constants.PD_PREFIX + ":"; /** Xpath prefix for reaching SecurityLayer 1.0 Namespaces */ private static final String SL10 = Constants.SL10_PREFIX + ":"; /** Xpath prefix for reaching SAML Namespaces */ private static final String SAML = Constants.SAML_PREFIX + ":"; /** Xpath prefix for reaching XML-DSIG Namespaces */ private static final String DSIG = Constants.DSIG_PREFIX + ":"; /** Xpath prefix for reaching ECDS Namespaces */ private static final String ECDSA = Constants.ECDSA_PREFIX + ":"; /** Xpath expression to the root element */ private static final String ROOT = "/" + SAML + "Assertion/"; /** Xpath expression to the SAMLSubjectConfirmationData element */ private static final String SAML_SUBJECT_CONFIRMATION_DATA_XPATH = ROOT + SAML + "AttributeStatement/" + SAML + "Subject/" + SAML + "SubjectConfirmation/" + SAML + "SubjectConfirmationData"; /** Xpath expression to the PersonData element */ private static final String PERSON_XPATH = SAML_SUBJECT_CONFIRMATION_DATA_XPATH + "/" + PDATA + "Person"; /** Xpath expression to the PersonData GivenName element */ private static final String PERSON_GIVEN_NAME_XPATH = PERSON_XPATH + "/" + PDATA + "Name/" + PDATA + "GivenName"; /** Xpath expression to the PersonData FamilyName element */ private static final String PERSON_FAMILY_NAME_XPATH = PERSON_XPATH + "/" + PDATA + "Name/" + PDATA + "FamilyName"; /** Xpath expression to the PersonData DateOfBirth element */ private static final String PERSON_DATE_OF_BIRTH_XPATH = PERSON_XPATH + "/" + PDATA + "DateOfBirth"; /** Xpath expression to the Identification element */ private static final String PERSON_IDENT_XPATH = PERSON_XPATH + "/" + PDATA + "Identification"; /** Xpath expression to the Identification Value element */ private static final String PERSON_IDENT_VALUE_XPATH = PERSON_XPATH + "/" + PDATA + "Identification/" + PDATA + "Value"; /** Xpath expression to the Identification Value element */ private static final String PERSON_IDENT_TYPE_XPATH = PERSON_XPATH + "/" + PDATA + "Identification/" + PDATA + "Type"; /** Xpath expression to the RSAKeyValue element */ private static final String RSA_KEY_VALUE_XPATH = ROOT + SAML + "AttributeStatement/" + SAML + "Attribute/" + SAML + "AttributeValue/" + DSIG + "RSAKeyValue"; /** Xpath expression to the ECKeyValue element */ private static final String ECDSA_KEY_VALUE_XPATH = ROOT + SAML + "AttributeStatement/" + SAML + "Attribute/" + SAML + "AttributeValue/" + ECDSA + "ECDSAKeyValue"; /** Xpath expression to the RSA Modulus element */ private static final String RSA_KEY_MODULUS_XPATH = DSIG + "Modulus"; /** Xpath expression to the RSA Exponent element */ private static final String RSA_KEY_EXPONENT_XPATH = DSIG + "Exponent"; /** Xpath expression to the DSIG X509Certificate element */ private static final String DSIG_CERTIFICATES_XPATH = ROOT + DSIG + "Signature/" + DSIG + "KeyInfo/" + DSIG + "X509Data/" + DSIG + "X509Certificate"; /** Xpath expression to the DSIG Transforms element */ private static final String DSIG_REFERENCE_TRANSFORMATION_XPATH = ROOT + DSIG + "Signature/" + DSIG + "SignedInfo/" + DSIG + "Reference/" + DSIG + "Transforms"; /**This is the root element of the XML-Document provided by the Security Layer Card*/ private Element assertionElem; /** * Constructor for IdentityLinkAssertionParser. * A DOM-representation of the incoming String will be created * @param xmlAssertion <saml:Assertion> as String * @throws ParseException on any parsing error */ public IdentityLinkAssertionParser(String xmlAssertion) throws ParseException { try { InputStream s = new ByteArrayInputStream(xmlAssertion.getBytes("UTF-8")); assertionElem = DOMUtils.parseXmlValidating(s); } catch (Throwable t) { throw new ParseException("parser.01", new Object[] { t.toString()}, t); } } /** * Constructor for IdentityLinkAssertionParser. * A DOM-representation of the incoming Inputstream will be created * @param xmlAssertion <saml:Assertion> as InputStream * @throws ParseException on any parsing error */ public IdentityLinkAssertionParser(InputStream xmlAssertion) throws Exception { try { assertionElem = DOMUtils.parseXmlValidating(xmlAssertion); } catch (Throwable t) { throw new ParseException("parser.01", new Object[] { t.toString() }, t); } } /** * Parses the identity link from the <saml:Assertion> * @return Identity link * @throws ParseException on any parsing error */ public IdentityLink parseIdentityLink() throws ParseException { IdentityLink identityLink; try { identityLink = new IdentityLink(); identityLink.setSamlAssertion(assertionElem); identityLink.setPrPerson((Element) XPathUtils.selectSingleNode(assertionElem, PERSON_XPATH)); identityLink.setIdentificationValue( XPathUtils.getElementValue(assertionElem, PERSON_IDENT_VALUE_XPATH, "")); identityLink.setIdentificationType( XPathUtils.getElementValue(assertionElem, PERSON_IDENT_TYPE_XPATH, "")); identityLink.setGivenName( XPathUtils.getElementValue(assertionElem, PERSON_GIVEN_NAME_XPATH, "")); identityLink.setFamilyName( XPathUtils.getElementValue(assertionElem, PERSON_FAMILY_NAME_XPATH, "")); identityLink.setDateOfBirth( XPathUtils.getElementValue(assertionElem, PERSON_DATE_OF_BIRTH_XPATH, "")); NodeIterator dsigRefTransforms = XPathUtils.selectNodeIterator(assertionElem, DSIG_REFERENCE_TRANSFORMATION_XPATH); List transElems = new ArrayList(); Element transformsElem; while ((transformsElem = (Element) dsigRefTransforms.nextNode()) != null) { transElems.add(transformsElem); } Element[] result = new Element[transElems.size()]; transElems.toArray(result); identityLink.setDsigReferenceTransforms(result); identityLink.setPublicKey(getPublicKeys()); } catch (Throwable t) { throw new ParseException("parser.01", new Object[] { t.toString() }, t); } return identityLink; } /** * Parses an array of Public Keys from the <InfoboxReadResponse> * @return RSAPublicKey[] * @throws IOException can occur when decoding the base64 values of the modulus and exponent */ public PublicKey[] getPublicKeys() throws IOException, ECDSAConverterException{ List pubKeys = new ArrayList(); //Try to get RSA-Keys NodeIterator rsaIter = XPathUtils.selectNodeIterator(assertionElem, Constants.nSMap, RSA_KEY_VALUE_XPATH); Element rsaElem; while ((rsaElem = (Element) rsaIter.nextNode()) != null) { String modulus = XPathUtils.getElementValue(rsaElem, RSA_KEY_MODULUS_XPATH, ""); String exponent = XPathUtils.getElementValue(rsaElem, RSA_KEY_EXPONENT_XPATH, ""); RSAPublicKey resPub = new iaik.security.rsa.RSAPublicKey( new BigInteger(1, Base64Utils.decode(modulus, true)), new BigInteger(1, Base64Utils.decode(exponent, true))); pubKeys.add(resPub); } //Try to get ECDSA-Keys NodeIterator ecdsaIter = XPathUtils.selectNodeIterator(assertionElem, Constants.nSMap, ECDSA_KEY_VALUE_XPATH); Element ecdsaElem; PublicKey ecPubKey = null; while ((ecdsaElem = (Element) ecdsaIter.nextNode()) != null) { try { ecPubKey = ECDSAKeyValueConverter.element2ECDSAPublicKey(ecdsaElem); pubKeys.add(ecPubKey); } catch(Exception e) { throw new ECDSAConverterException("parser.03", new Object[] { e.toString() }, e); } } PublicKey[] result = new PublicKey[pubKeys.size()]; pubKeys.toArray(result); return result; } /** * Parses a string array of decoded base64 certificates from * the <InfoboxReadResponse> found in the dsig-signature * @return String[] with raw-certificates from the dsig-signature keyinfo * @throws Exception */ public String[] getCertificates() throws Exception { List certs = new ArrayList(); NodeIterator rsaIter = XPathUtils.selectNodeIterator(assertionElem, DSIG_CERTIFICATES_XPATH); Element certElem; while ((certElem = (Element) rsaIter.nextNode()) != null) { String content = DOMUtils.getText(certElem); certs.add(new String(Base64Utils.decode(content, true))); } String[] result = new String[certs.size()]; certs.toArray(result); return result; } }