/*******************************************************************************
* 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.parser;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.PublicKey;
import java.security.interfaces.RSAPublicKey;
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.auth.data.IdentityLink;
import at.gv.egovernment.moa.id.auth.exception.ECDSAConverterException;
import at.gv.egovernment.moa.id.auth.exception.ParseException;
import at.gv.egovernment.moa.id.util.ECDSAKeyValueConverter;
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;
/**
* 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 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 = "";
/** 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 */
public static final String PERSON_IDENT_VALUE_XPATH =
PERSON_XPATH
+ "/"
+ PDATA
+ "Identification/"
+ PDATA
+ "Value";
/** Xpath expression to the Identification Value element */
public 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";
/** The IssueInstant attribute of the SAML assertion */
private static final String ISSUE_INSTANT_ATTR = "IssueInstant";
/**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);
}
}
/**
* Sets the <@link assertionElem>.
* @param xmlAssertion the assertion element
* @throws ParseException on any parsing error
*/
public IdentityLinkAssertionParser(Element xmlAssertion) throws ParseException {
assertionElem = xmlAssertion;
}
/**
* 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 ParseException {
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.setIssueInstant(assertionElem.getAttribute(ISSUE_INSTANT_ATTR));
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, ""));
String givenname = XPathUtils.getElementValue(assertionElem, PERSON_GIVEN_NAME_XPATH, "");
String familyname = XPathUtils.getElementValue(assertionElem, PERSON_FAMILY_NAME_XPATH, "");
// replace ' in name with '
givenname = givenname.replaceAll("'", "'");
familyname = familyname.replaceAll("'", "'");
identityLink.setGivenName(givenname);
identityLink.setFamilyName(familyname);
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;
}
}