/*
* Created on 20.11.2003
*
* (c) Stabsstelle IKT-Strategie des Bundes
*/
package at.gv.egovernment.moa.spss.slinterface.transformers;
import java.io.InputStream;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.StringTokenizer;
import javax.servlet.ServletException;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import at.gv.egovernment.moa.spss.slinterface.Constants;
import at.gv.egovernment.moa.spss.slinterface.DOMUtils;
import at.gv.egovernment.moa.spss.slinterface.XPathUtils;
/**
* @author Gregor Karlinger (mailto:gregor.karlinger@siemens.com)
*/
public class SL2MOA
{
private static Logger logger_ = Logger.getLogger(Constants.LH_TRANSFORMERS_);
/**
* Transforms an SL VerifyXMLSignatureRequest
into a MOA VerifyXMLSignatureRequest
*
and makes the following additions to the resulting VerifyXMLSignatureRequest
:
*
DateTime
element to MOA VerifyXMLSignatureRequest
, if no
* one exists and if no etsi:SigningTime
element exists in the xml signature of the MOA
* VerifyXMLSinatureRequestVerifyXMLSignatureRequest
(see @link #extractXMLDocCreationTime(Element)).ReturnHashInputData
element indicating that MOA SP should return
* the hash input data for each dsig:Reference
of the XML signature.TrustProfileID
element indicating the trust profile
* MOA ID should use for evaluating wheter the signer certificate used for creating the XML signature
* contained in the MOA VerifyXMLSignatureRequest
is trusted.slVerifyXMLSignatureRequest
is modified into the moa request.
*
* @pre slVerifyXMLSignatureRequest is a valid instance of the SL Schema (version 1.2 or 1.1).
*
* @throws ServletException if transforming the request fails for any reason.
*/
public static Document toMoaVerifyXMLSignatureRequest(Document slVerifyXMLSignatureRequest,
String trustProfileID) throws ServletException
{
// Namespace to namespace prefix mapping
HashMap prefixMap = new HashMap(4);
prefixMap.put(Constants.NSURI_SL_10_, Constants.NSPRE_SL_10_);
prefixMap.put(Constants.NSURI_SL_11_, Constants.NSPRE_SL_11_);
prefixMap.put(Constants.NSURI_SL_12_, Constants.NSPRE_SL_12_);
prefixMap.put(Constants.NSURI_MOA_12_, Constants.NSPRE_MOA_12_);
// Namespaces to be changed
HashMap nsTransforms = new HashMap();
nsTransforms.put(Constants.NSURI_SL_10_, Constants.NSURI_MOA_12_);
nsTransforms.put(Constants.NSURI_SL_11_, Constants.NSURI_MOA_12_);
nsTransforms.put(Constants.NSURI_SL_12_, Constants.NSURI_MOA_12_);
// Names to be changed
HashMap nameTransforms = new HashMap();
nameTransforms.put(
new QName(Constants.NSURI_SL_11_, "SignatureInfo"),
new QName(Constants.NSURI_MOA_12_,"VerifySignatureInfo"));
nameTransforms.put(
new QName(Constants.NSURI_SL_12_, "SignatureInfo"),
new QName(Constants.NSURI_MOA_12_,"VerifySignatureInfo"));
nameTransforms.put(
new QName(Constants.NSURI_SL_11_, "SignatureEnvironment"),
new QName(Constants.NSURI_MOA_12_,"VerifySignatureEnvironment"));
nameTransforms.put(
new QName(Constants.NSURI_SL_12_, "SignatureEnvironment"),
new QName(Constants.NSURI_MOA_12_,"VerifySignatureEnvironment"));
nameTransforms.put(
new QName(Constants.NSURI_SL_11_, "SignatureLocation"),
new QName(Constants.NSURI_MOA_12_,"VerifySignatureLocation"));
nameTransforms.put(
new QName(Constants.NSURI_SL_12_, "SignatureLocation"),
new QName(Constants.NSURI_MOA_12_,"VerifySignatureLocation"));
nameTransforms.put(
new QName(Constants.NSURI_SL_11_, "Supplement"),
new QName(Constants.NSURI_MOA_12_,"SupplementProfile"));
nameTransforms.put(
new QName(Constants.NSURI_SL_12_, "Supplement"),
new QName(Constants.NSURI_MOA_12_,"SupplementProfile"));
Element verifyRequestElem = slVerifyXMLSignatureRequest.getDocumentElement();
verifyRequestElem.setAttributeNS(Constants.NSURI_NAMESPACES_,
"xmlns:" + Constants.NSPRE_MOA_12_, Constants.NSURI_MOA_12_);
// Convert SL request into MOA request
verifyRequestElem = Utils.transformDeep(verifyRequestElem, prefixMap, nsTransforms, nameTransforms);
// Add DateTime element to MOA VerifyXMLSignature request, if
// - no one exists and
// - no etsi:SigningTime element exists in the xml signature of the MOA VerifyXMLSinatureRequest
if (!dateTimeExists(verifyRequestElem) && !signingTimeExists(verifyRequestElem))
{
// Extract creation date meta information from E-Recht XML document for use in MOA VerifyXMLSignature request
String dateTimeStr = extractXMLDocCreationTime(verifyRequestElem);
if (dateTimeStr != null)
{
// Creation date meta information could be extracted successfully from E-Recht XML document
Element dateTimeElem = slVerifyXMLSignatureRequest.createElementNS(
Constants.NSURI_MOA_12_, Constants.NSPRE_MOA_12_ + ":DateTime");
dateTimeElem.appendChild(slVerifyXMLSignatureRequest.createTextNode(dateTimeStr));
Element verifySignatureInfoElem = DOMUtils.getChildElem(verifyRequestElem, Constants.NSURI_MOA_12_, "VerifySignatureInfo");
verifyRequestElem.insertBefore(dateTimeElem, verifySignatureInfoElem);
}
else
{
logger_.warn("Could not extract creation date meta information from E-Recht XML document.");
}
}
// Add ReturnHashInputData element
Element returnHashInputDataElem = slVerifyXMLSignatureRequest.createElementNS(
Constants.NSURI_MOA_12_, Constants.NSPRE_MOA_12_ + ":ReturnHashInputData");
verifyRequestElem.appendChild(returnHashInputDataElem);
// Add trust profile ID element
Element trustProfileIDElem = slVerifyXMLSignatureRequest.createElementNS(
Constants.NSURI_MOA_12_, Constants.NSPRE_MOA_12_ + ":TrustProfileID");
trustProfileIDElem.appendChild(slVerifyXMLSignatureRequest.createTextNode(trustProfileID));
verifyRequestElem.appendChild(trustProfileIDElem);
return slVerifyXMLSignatureRequest;
}
/**
* Extracts the creation time meta information from the E-Recht XML document that is referenced by
* the XML signature contained in the specified MOA VerifyXMLSignatureRequest
.
*
* @param verifyRequestElem The MOA VerifyXMLSignatureRequest
. It is assumed that the
* request contains an XML signature which signs a E-Recht XML document
* (referring to the E-Recht XML document and transforming it to a corresponding
* XHTML representation respectively). The E-Recht XML document is assumed to
* have a root element with the name erechtdok
in the namespace
* http://www.bka.gv.at
. The creation time meta information is
* assumed to be contained in the attribute h-created
of the root
* element. The value of the attribute h-created
is assumed to have
* the format dd. MMMMM yyyy, hh:mm:ss
where MMMMM denotes the month
* in German prose (see @link #convertMonth(String)).
*
* @return the extracted creation time meta information, or null
, if the extraction fails for
* any reason.
*/
private static String extractXMLDocCreationTime(Element verifyRequestElem)
{
// Get E-Recht XML document using location information in MOA VerifyXMLSignature request
String nSPrefixes = Constants.NSPRE_MOA_12_ + " " + Constants.NSURI_MOA_12_;
String xPathXMLDocumentLocContent =
"//" + Constants.NSPRE_MOA_12_ + ":SupplementProfile" +
"/" + Constants.NSPRE_MOA_12_ + ":Content[@Reference=\"dokument.xml\"]" +
"/" + Constants.NSPRE_MOA_12_ + ":LocRefContent";
Document xmlDocument = null;
try
{
XPathUtils utils = new XPathUtils();
utils.setupContext(xPathXMLDocumentLocContent, verifyRequestElem, nSPrefixes);
NodeList resultNL = utils.selectNodeSet(verifyRequestElem);
if (resultNL == null || resultNL.getLength() < 1)
{
logger_.warn("LocRefContent element for E-Recht XML document not found in MOA VerifyXMLSignatureRequest.");
return null;
}
URL locRefURL = new URL(DOMUtils.getText((Element) resultNL.item(0)));
InputStream locRefURLIS = locRefURL.openStream();
xmlDocument = DOMUtils.parseWellFormed(locRefURLIS);
}
catch (Exception e)
{
String message = "An error occurred while trying to load E-Recht XML document:";
logger_.warn(message, e);
return null;
}
// Extract attribute "h-created" from E-Recht XML document root element
String hCreated = xmlDocument.getDocumentElement().getAttributeNS(null, "h-created");
if (hCreated == null || "".equals(hCreated))
{
logger_.warn("Attribute \"h-created\" not found in E-Recht XML document root element.");
return null;
}
// Convert attribute "h-created" into a java date ("h-created" has formats like "10. März 2006, 11:15:09")
try
{
String dateStr = hCreated.substring(0, hCreated.indexOf(',')).trim();
String timeStr = hCreated.substring(hCreated.indexOf(',') + 1).trim();
StringTokenizer tokenizer = new StringTokenizer(dateStr, " ");
String dateDayStr = tokenizer.nextToken();
int dateDay = Integer.parseInt(dateDayStr.substring(0, dateDayStr.indexOf('.')));
String dateMonthAlphaStr = tokenizer.nextToken();
int dateMonthNum = convertMonth(dateMonthAlphaStr);
int dateYear = Integer.parseInt(tokenizer.nextToken());
tokenizer = new StringTokenizer(timeStr, ":");
int timeHours = Integer.parseInt(tokenizer.nextToken());
int timeMins = Integer.parseInt(tokenizer.nextToken());
int timeSecs = Integer.parseInt(tokenizer.nextToken());
GregorianCalendar calendar = new GregorianCalendar(dateYear, dateMonthNum, dateDay, timeHours, timeMins, timeSecs);
SimpleDateFormat dF = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
return dF.format(calendar.getTime());
}
catch (Throwable t)
{
logger_.warn("Attribute \"h-created\" in E-Recht XML document root element has unexpected format: " + hCreated);
return null;
}
}
/**
* Converts the specified month name into a numeric representation as specified in @link Calendar, e.g.
* @link Calendar#JANUARY.
*
* @param dateMonthAlphaStr The specified month name; must be one of Jänner
, Januar
,
* Februar
, Feber
, März
, April
,
* Mai
, Juni
, Juli
, August
,
* September
, Oktober
, November
, or
* Dezember
.
*
* @return the numeric representation of the specified month.
*
* @throws Exception if dateMonthAlphaStr
contains an invalid month name.
*/
private static int convertMonth(String dateMonthAlphaStr) throws Exception
{
if ("Jänner".equalsIgnoreCase(dateMonthAlphaStr) || "Januar".equalsIgnoreCase(dateMonthAlphaStr)) return Calendar.JANUARY;
if ("Februar".equalsIgnoreCase(dateMonthAlphaStr) || "Feber".equalsIgnoreCase(dateMonthAlphaStr)) return Calendar.FEBRUARY;
if ("März".equalsIgnoreCase(dateMonthAlphaStr)) return Calendar.MARCH;
if ("April".equalsIgnoreCase(dateMonthAlphaStr)) return Calendar.APRIL;
if ("Mai".equalsIgnoreCase(dateMonthAlphaStr)) return Calendar.MAY;
if ("Juni".equalsIgnoreCase(dateMonthAlphaStr)) return Calendar.JUNE;
if ("Juli".equalsIgnoreCase(dateMonthAlphaStr)) return Calendar.JULY;
if ("August".equalsIgnoreCase(dateMonthAlphaStr)) return Calendar.AUGUST;
if ("September".equalsIgnoreCase(dateMonthAlphaStr)) return Calendar.SEPTEMBER;
if ("Oktober".equalsIgnoreCase(dateMonthAlphaStr)) return Calendar.OCTOBER;
if ("November".equalsIgnoreCase(dateMonthAlphaStr)) return Calendar.NOVEMBER;
if ("Dezember".equalsIgnoreCase(dateMonthAlphaStr)) return Calendar.DECEMBER;
String message = "Invalid month identifier found in attribute \"h-created\":" + dateMonthAlphaStr;
logger_.warn(message);
throw new Exception(message);
}
/**
* Checks wheter a DateTime
element exists in the specified MOA
* VerifyXMLSignatureRequest
.
*
* @param moaVerifyXMLSignatureRequest The MOA VerifyXMLSingatureRequest
.
*
* @return true
if the element exists, false
otherwhise.
*
* @throws ServletException if the check fails for any reason.
*/
private static boolean dateTimeExists(Element moaVerifyXMLSignatureRequest) throws ServletException
{
String nSPrefixes = Constants.NSPRE_MOA_12_ + " " + Constants.NSURI_MOA_12_;
String xPathDateTime = "//" + Constants.NSPRE_MOA_12_ + ":DateTime";
NodeList resultNL;
try
{
XPathUtils utils = new XPathUtils();
utils.setupContext(xPathDateTime, moaVerifyXMLSignatureRequest, nSPrefixes);
resultNL = utils.selectNodeSet(moaVerifyXMLSignatureRequest);
}
catch (Exception e)
{
String message = "An error occurred while checking for DateTime element in MOA VerifyXMLSignatureRequest:";
logger_.error(message, e);
throw new ServletException(message, e);
}
if (resultNL == null) return false;
if (resultNL.getLength() < 1) return false;
return true;
}
/**
* Checks whether an etsi:SigningTime
signed attribute exists as part of the XML signature
* contained in the specified MOA VerifyXMLSingatureRequest
.
*
* @param moaVerifyXMLSignatureRequest The MOA VerifyXMLSingatureRequest
.
*
* @return true
, if the attribute exists, false
otherwhise.
*
* @throws ServletException if the check fails for any reason.
*/
private static boolean signingTimeExists(Element moaVerifyXMLSignatureRequest) throws ServletException
{
String nSPrefixes = Constants.NSPRE_ETSI_ + " " + Constants.NSURI_ETSI_;
String xPathDateTime = "//" + Constants.NSPRE_ETSI_ + ":SigningTime";
NodeList resultNL;
try
{
XPathUtils utils = new XPathUtils();
utils.setupContext(xPathDateTime, moaVerifyXMLSignatureRequest, nSPrefixes);
resultNL = utils.selectNodeSet(moaVerifyXMLSignatureRequest);
}
catch (Exception e)
{
String message = "An error occurred while checking for " + Constants.NSPRE_ETSI_ + ":SigningTime element in XML signature in MOA VerifyXMLSignatureRequest:";
logger_.error(message, e);
throw new ServletException(message, e);
}
if (resultNL == null) return false;
if (resultNL.getLength() < 1) return false;
return true;
}
}