From 6ef9bdefc58cb2553f23aaa9711d6341e293c9f7 Mon Sep 17 00:00:00 2001 From: tknall Date: Fri, 10 Oct 2008 11:13:40 +0000 Subject: Deprecated webapp-folder removed from svn repository. New DefaultConfiguration.zip integrated in order to allow mocca signatures. Minor bug concerning choice of cce within the web application fixed. Signature with new online bku MOCCA integrated (new signature device "moc" created). Configuration keys for mocca added. New error codes (371 = signature verification not supported by this connector, 372 = invalid signing time) introduced. Optional check of the signing time for the web application implemented. At signature creation time the signing time is checked for plausibility. This is a workaround for the ITS:mac-linux signing time bug. New configuration key ("signing_time_tolerance") added (applies to web application only) to overcome invalid signing times. A signature is only accepted if its signing time is within a time frame of [current time - signing_time_tolerance, current time + signing_time_tolerance] where signing_time_tolerance is interpreted as seconds. Bugfix: Correct extraction of signatures with wrong signing times implemented. (The order of the signatures is still invalid in case of false signing times.) Optional override of the dynamic creation of the signature retrieval url (locrefcontent) implemented in order to overcome ssl problems (retrieve_signature_data_url_override). Note: Assure that this URL is accessible from the citizen card environment. Download of signed pdf-file for external application interface adjusted. Verification of mocca signed documents implemented. Retrieval of xml response via multipart implemented (mocca strictly follows security layer spec) git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@296 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- .../at/gv/egiz/pdfas/api/commons/Constants.java | 10 + .../java/at/gv/egiz/pdfas/commandline/Main.java | 6 +- .../at/gv/egiz/pdfas/exceptions/ErrorCode.java | 3 + .../pdfas/impl/vfilter/VerificationFilterImpl.java | 44 +- src/main/java/at/gv/egiz/pdfas/utils/WebUtils.java | 100 +++ .../egiz/pdfas/web/helper/SignServletHelper.java | 4 +- .../egiz/pdfas/web/helper/SigningTimeHelper.java | 83 +++ .../java/at/knowcenter/wag/egov/egiz/PdfAS.java | 2 +- .../wag/egov/egiz/pdf/PDFSignatureObjectIText.java | 1 + .../egov/egiz/sig/connectors/ConnectorChooser.java | 24 +- .../moa/MOASoapWithAttachmentConnector.java | 19 +- .../mocca/LocRefDetachedMOCCAConnector.java | 695 +++++++++++++++++++++ .../egiz/sig/sigid/DetachedMOCIdFormatter.java | 48 ++ .../wag/egov/egiz/sig/sigkz/SigKZIDHelper.java | 20 + .../wag/egov/egiz/web/LocalRequestHelper.java | 14 +- .../wag/egov/egiz/web/servlets/DataURLServlet.java | 87 ++- .../wag/egov/egiz/web/servlets/SignServlet.java | 4 +- .../egiz/web/servlets/VerifyPreviewServlet.java | 7 +- .../wag/egov/egiz/web/servlets/VerifyServlet.java | 4 +- 19 files changed, 1124 insertions(+), 51 deletions(-) create mode 100644 src/main/java/at/gv/egiz/pdfas/utils/WebUtils.java create mode 100644 src/main/java/at/gv/egiz/pdfas/web/helper/SigningTimeHelper.java create mode 100644 src/main/java/at/knowcenter/wag/egov/egiz/sig/connectors/mocca/LocRefDetachedMOCCAConnector.java create mode 100644 src/main/java/at/knowcenter/wag/egov/egiz/sig/sigid/DetachedMOCIdFormatter.java (limited to 'src/main/java') diff --git a/src/main/java/at/gv/egiz/pdfas/api/commons/Constants.java b/src/main/java/at/gv/egiz/pdfas/api/commons/Constants.java index f9a3c03..a7bc776 100644 --- a/src/main/java/at/gv/egiz/pdfas/api/commons/Constants.java +++ b/src/main/java/at/gv/egiz/pdfas/api/commons/Constants.java @@ -49,6 +49,16 @@ public final class Constants */ public static String SIGNATURE_DEVICE_BKU = "bku"; + /** + * The signature device a1. + */ + public static String SIGNATURE_DEVICE_A1 = "a1"; + + /** + * The signature device MOCCA (online bku). + */ + public static final String SIGNATURE_DEVICE_MOC = "moc"; + /** * Only binary signatures are verified. */ diff --git a/src/main/java/at/gv/egiz/pdfas/commandline/Main.java b/src/main/java/at/gv/egiz/pdfas/commandline/Main.java index c84b417..44a472b 100644 --- a/src/main/java/at/gv/egiz/pdfas/commandline/Main.java +++ b/src/main/java/at/gv/egiz/pdfas/commandline/Main.java @@ -490,7 +490,7 @@ public abstract class Main public static void carryOutSign(String input, String connector, String signature_mode, String signature_type, String pos_string, String user_name, String user_password, String output, PrintWriter messageOutput) throws PdfAsException { - messageOutput.println("Signing..."); + messageOutput.println("Signing " + input + "..."); // for performance measurement long startTime = 0; @@ -537,12 +537,12 @@ public abstract class Main logger_.info(toReport); } - messageOutput.println("Signing was successful."); + messageOutput.println("Signing was successful (" + output + ")."); } public static void carryOutVerify(String input, String connector, int verify_which, PrintWriter messageOutput) throws PdfAsException { - messageOutput.println("Verifying..."); + messageOutput.println("Verifying " + input + "..."); // for performance measurement long startTime = 0; diff --git a/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java b/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java index 7566c41..062ff6b 100644 --- a/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java +++ b/src/main/java/at/gv/egiz/pdfas/exceptions/ErrorCode.java @@ -40,6 +40,9 @@ public final class ErrorCode public static final int MODIFIED_AFTER_SIGNATION = 316; public static final int NON_BINARY_SIGNATURES_PRESENT = 317; + + public static final int SIGNATURE_VERIFICATION_NOT_SUPPORTED = 371; + public static final int INVALID_SIGNING_TIME = 372; public static final int WEB_EXCEPTION = 330; diff --git a/src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterImpl.java b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterImpl.java index 3ca497b..d9549b0 100644 --- a/src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterImpl.java +++ b/src/main/java/at/gv/egiz/pdfas/impl/vfilter/VerificationFilterImpl.java @@ -413,11 +413,12 @@ public class VerificationFilterImpl implements VerificationFilter { assert partitionResult.size() >= prevPartitionResult.size(); - for (int i = prevPartitionResult.size(); i < partitionResult.size(); i++) - { - SignatureHolder sh = (SignatureHolder) partitionResult.get(i); - extractedSignatures.add(sh); - } +// for (int i = prevPartitionResult.size(); i < partitionResult.size(); i++) +// { +// SignatureHolder sh = (SignatureHolder) partitionResult.get(i); +// extractedSignatures.add(sh); +// } + mergeSignatures(prevPartitionResult, partitionResult, extractedSignatures); } prevPartitionResult = partitionResult; @@ -436,6 +437,39 @@ public class VerificationFilterImpl implements VerificationFilter return signatureHolderChain; } + private void mergeSignatures(List oldList, List newList, List result) { + + for(int i=0; i < newList.size(); i++) { + + SignatureHolder currentNewSh = (SignatureHolder)newList.get(i); + + boolean shAlreadyPresentInOldList = false; + int pos = -1; + + for(int j=0; j add + result.add(currentNewSh); + } + + } + + + return; + } + + protected List flattenOutTextPartitions (List partitions, List blocks) { diff --git a/src/main/java/at/gv/egiz/pdfas/utils/WebUtils.java b/src/main/java/at/gv/egiz/pdfas/utils/WebUtils.java new file mode 100644 index 0000000..4bca486 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/utils/WebUtils.java @@ -0,0 +1,100 @@ +package at.gv.egiz.pdfas.utils; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.knowcenter.wag.egov.egiz.cfg.SettingsReader; +import at.knowcenter.wag.egov.egiz.exceptions.SettingNotFoundException; +import at.knowcenter.wag.egov.egiz.exceptions.SettingsException; +import at.knowcenter.wag.egov.egiz.web.LocalRequestHelper; + +/** + * @author tknall + */ +public final class WebUtils { + + private WebUtils() { + } + + /** + * The log. + */ + private final static Log LOG = LogFactory.getLog(WebUtils.class); + + /** + * The configuration key that replaces a dynamically generated retrieve signature data url. + */ + private final static String RETRIEVE_SIGNATURE_DATA_URL_OVERRIDE_KEY = "retrieve_signature_data_url_override"; + + /** + * Unlike {@link HttpServletResponse#encodeURL(String)} that adds only a + * {@code JSESSIONID} entry to the given url if needed, this method always + * adds the session id (except if already present within the url. + * + * @param url + * The given url. + * @param session + * The {@link HttpSession}. + * @return The given url plus a session id. + */ + public static String addJSessionID(String url, HttpSession session) { + if (url == null) { + return null; + } + if (!StringUtils.containsIgnoreCase(url, ";jsessionid=")) { + url = url + ";jsessionid=" + session.getId(); + LOG.debug("Adding jsessionid " + session.getId()); + } else { + LOG.debug("No need to add a jsessionid."); + } + LOG.debug("Returning url " + url); + return url; + } + + /** + * Unlike {@link HttpServletResponse#encodeURL(String)} that adds only a + * {@code JSESSIONID} entry to the given url if needed, this method always + * adds the session id (except if already present within the url. + * + * @param url + * The given url. + * @param request + * The {@link HttpServletRequest}. + * @return The given url plus a session id. + */ + public static String addJSessionID(String url, HttpServletRequest request) { + return addJSessionID(url, request.getSession()); + } + + /** + * Either dynamically creates locref content url or uses a url provides by the pdf-as + * configuration (key {@code retrieve_signature_data_url_override}). + * @param request The {@link HttpServletRequest}. + * @param response The {@link HttpServletResponse}. + * @return The retrieve signature data url. + */ + public static String buildRetrieveSignatureDataURL(HttpServletRequest request, HttpServletResponse response) { + String override = null; + LOG.debug("Building retrieve signature data url."); + try { + override = SettingsReader.getInstance().getSetting(RETRIEVE_SIGNATURE_DATA_URL_OVERRIDE_KEY, null); + } catch (SettingsException e) { + LOG.error(e); + } + String result; + if (override == null) { + result = WebUtils.addJSessionID(LocalRequestHelper.getLocalContextAddress(request, response) + "/RetrieveSignatureData", request); + } else { + LOG.debug("Override url found: " + override); + result = WebUtils.addJSessionID(override, request); + } + LOG.debug("RetrieveSignatureDataURL = " + result); + return result; + } + +} diff --git a/src/main/java/at/gv/egiz/pdfas/web/helper/SignServletHelper.java b/src/main/java/at/gv/egiz/pdfas/web/helper/SignServletHelper.java index a904ad4..6fc7a1a 100644 --- a/src/main/java/at/gv/egiz/pdfas/web/helper/SignServletHelper.java +++ b/src/main/java/at/gv/egiz/pdfas/web/helper/SignServletHelper.java @@ -20,6 +20,7 @@ import at.gv.egiz.pdfas.framework.SignatorFactory; import at.gv.egiz.pdfas.framework.signator.Signator; import at.gv.egiz.pdfas.impl.output.ByteArrayDataSink; import at.gv.egiz.pdfas.impl.output.FileBasedDataSink; +import at.gv.egiz.pdfas.utils.WebUtils; import at.gv.egiz.pdfas.web.SignSessionInformation; import at.knowcenter.wag.egov.egiz.PdfASID; import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException; @@ -135,7 +136,8 @@ public class SignServletHelper // TODO TR: Web-Applikation verwendet in Loc-Ref-Variante ext. Referenz, um performanter zu sein; // nachfolend auskommentieren, wenn anstatt SwA-Connector LocRef-Connector verwendet wird - URL signature_data_URL = new URL(LocalRequestHelper.getLocalContextAddress(request, response) + "/RetrieveSignatureData"); +// URL signature_data_URL = new URL(WebUtils.addJSessionID(LocalRequestHelper.getLocalContextAddress(request, response) + "/RetrieveSignatureData", request)); + URL signature_data_URL = new URL(WebUtils.buildRetrieveSignatureDataURL(request, response)); String signature_data_url = response.encodeURL(signature_data_URL.toString()); Connector c = ConnectorChooser.chooseWebConnectorForSign(si.connector, si.type, signature_data_url); diff --git a/src/main/java/at/gv/egiz/pdfas/web/helper/SigningTimeHelper.java b/src/main/java/at/gv/egiz/pdfas/web/helper/SigningTimeHelper.java new file mode 100644 index 0000000..673c197 --- /dev/null +++ b/src/main/java/at/gv/egiz/pdfas/web/helper/SigningTimeHelper.java @@ -0,0 +1,83 @@ +package at.gv.egiz.pdfas.web.helper; + +import java.util.Date; + +import org.apache.commons.lang.time.DateFormatUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.gv.egiz.pdfas.framework.signator.SignatorInformation; +import at.knowcenter.wag.egov.egiz.cfg.SettingsReader; +import at.knowcenter.wag.egov.egiz.exceptions.SettingsException; +import at.knowcenter.wag.egov.egiz.exceptions.SignatureException; +import at.knowcenter.wag.egov.egiz.pdf.EGIZDate; +import at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject; + +/** + * This class deals with invalid signing times. + * @author tknall + */ +public final class SigningTimeHelper { + + private SigningTimeHelper() { + } + + private static Integer tolerance = null; + + /** + * The log. + */ + private final static Log LOG = LogFactory.getLog(SigningTimeHelper.class); + + private final static String SIGNING_TIME_TOLERANCE_KEY = "signing_time_tolerance"; + private final static String FORMAT_UTC_DATE_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'"; + + public static void checkSigningTimeAgainstHostTime(SignatorInformation si) throws SignatureException { + checkSigningTimeAgainstHostTime(si.getSignSignatureObject()); + } + + public static synchronized void checkSigningTimeAgainstHostTime(SignSignatureObject sso) throws SignatureException { + if (tolerance == null) { + try { + String toleranceString = SettingsReader.getInstance().getSetting(SIGNING_TIME_TOLERANCE_KEY, "-1"); + tolerance = new Integer(Integer.parseInt(toleranceString)); + } catch (NumberFormatException e) { + LOG.warn("Invalid configuration key = " + SIGNING_TIME_TOLERANCE_KEY + ". Disabling signing time check."); + tolerance = new Integer(-1); + } catch (SettingsException e) { + LOG.error("Error reading settings. Disabling signing time check.", e); + tolerance = new Integer(-1); + } + } + if (tolerance.intValue() == -1) { + return; + } + + // signing time + Date signingTime = EGIZDate.parseDateFromString(sso.getDate()); + + // current time + Date currentTime = new Date(); + + // lower limit + Date lowerLimit = new Date(currentTime.getTime() - tolerance.intValue()*1000); + + // upper limit + Date upperLimit = new Date(currentTime.getTime() + tolerance.intValue()*1000); + + String signingTimeString = DateFormatUtils.formatUTC(signingTime, FORMAT_UTC_DATE_PATTERN); + + if (LOG.isDebugEnabled()) { + String lower = DateFormatUtils.formatUTC(lowerLimit, FORMAT_UTC_DATE_PATTERN); + String upper = DateFormatUtils.formatUTC(upperLimit, FORMAT_UTC_DATE_PATTERN); + LOG.debug("Checking if signing time " + signingTimeString + " is valid according to the given time frame [ " + lower + ", " + upper + " ]."); + } + + if (signingTime.before(lowerLimit) || signingTime.after(upperLimit)) { + throw new SignatureException(ErrorCode.INVALID_SIGNING_TIME, "The signing time " + signingTimeString + " is out of the given tolerance of " + tolerance.intValue() + " seconds."); + } + + } + +} diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/PdfAS.java b/src/main/java/at/knowcenter/wag/egov/egiz/PdfAS.java index ab93b94..113f13e 100644 --- a/src/main/java/at/knowcenter/wag/egov/egiz/PdfAS.java +++ b/src/main/java/at/knowcenter/wag/egov/egiz/PdfAS.java @@ -96,7 +96,7 @@ public abstract class PdfAS * The current version of the pdf-as library. This version string is logged on every invocation * of the api or the web application. */ - public static final String PDFAS_VERSION = "3.0.6-20080715"; + public static final String PDFAS_VERSION = "3.0.7-20080923"; /** * The key of the strict mode setting. diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureObjectIText.java b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureObjectIText.java index 2053264..75e90c5 100644 --- a/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureObjectIText.java +++ b/src/main/java/at/knowcenter/wag/egov/egiz/pdf/PDFSignatureObjectIText.java @@ -421,6 +421,7 @@ public class PDFSignatureObjectIText implements PDFSignatureObject { pdf_cell.setColspan(cell.getColSpan()); } + // TODO[tknall]: Check if cell nowrap may be used to prevent wrapping of cells containing keys. if (cell.isNoWrap()) { pdf_cell.setNoWrap(true); diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/sig/connectors/ConnectorChooser.java b/src/main/java/at/knowcenter/wag/egov/egiz/sig/connectors/ConnectorChooser.java index e991e04..7188273 100644 --- a/src/main/java/at/knowcenter/wag/egov/egiz/sig/connectors/ConnectorChooser.java +++ b/src/main/java/at/knowcenter/wag/egov/egiz/sig/connectors/ConnectorChooser.java @@ -6,6 +6,7 @@ package at.knowcenter.wag.egov.egiz.sig.connectors; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import at.gv.egiz.pdfas.api.commons.Constants; import at.gv.egiz.pdfas.framework.ConnectorParameters; import at.knowcenter.wag.egov.egiz.PdfASID; import at.knowcenter.wag.egov.egiz.exceptions.ConnectorException; @@ -16,6 +17,7 @@ import at.knowcenter.wag.egov.egiz.sig.connectors.bku.MultipartDetachedBKUConnec import at.knowcenter.wag.egov.egiz.sig.connectors.bku.OldEnvelopingBase64BKUConnector; import at.knowcenter.wag.egov.egiz.sig.connectors.moa.EnvelopingBase64MOAConnector; import at.knowcenter.wag.egov.egiz.sig.connectors.moa.MOASoapWithAttachmentConnector; +import at.knowcenter.wag.egov.egiz.sig.connectors.mocca.LocRefDetachedMOCCAConnector; import at.knowcenter.wag.egov.egiz.sig.sigid.HotfixIdFormatter; /** @@ -39,15 +41,21 @@ public final class ConnectorChooser log.debug("Choosing LocalConnector for signation..."); log.debug("connector type = " + connector); - - if (!connector.equals("bku")) - { - log.error("Currently only the BKU connector is fully implemented."); - } - - log.debug("choosing locref detached BKU connector."); + ConnectorParameters cp = new ConnectorParameters(); cp.setProfileId(profile); + + if (Constants.SIGNATURE_DEVICE_MOC.equals(connector)) { + + return new LocRefDetachedMOCCAConnector(cp, loc_ref_url); + + } else if (Constants.SIGNATURE_DEVICE_BKU.equals(connector)){ + + return new LocRefDetachedBKUConnector(cp, loc_ref_url); + + } + + log.error("Currently only the BKU connector is fully implemented."); return new LocRefDetachedBKUConnector(cp, loc_ref_url); } @@ -58,7 +66,7 @@ public final class ConnectorChooser log.debug("connector type = " + connector); - if (!connector.equals("moa")) + if (!connector.equals(Constants.SIGNATURE_DEVICE_MOA)) { log.error("Currently only the MOA connector is available for non local WEB signation."); } diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/sig/connectors/moa/MOASoapWithAttachmentConnector.java b/src/main/java/at/knowcenter/wag/egov/egiz/sig/connectors/moa/MOASoapWithAttachmentConnector.java index 4cc09e1..44a7c38 100644 --- a/src/main/java/at/knowcenter/wag/egov/egiz/sig/connectors/moa/MOASoapWithAttachmentConnector.java +++ b/src/main/java/at/knowcenter/wag/egov/egiz/sig/connectors/moa/MOASoapWithAttachmentConnector.java @@ -11,6 +11,8 @@ import java.util.Date; import java.util.Properties; import java.util.SimpleTimeZone; import java.util.TimeZone; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -29,6 +31,7 @@ import at.knowcenter.wag.egov.egiz.sig.connectors.bku.BKUHelper; import at.knowcenter.wag.egov.egiz.sig.connectors.bku.BKUPostConnection; import at.knowcenter.wag.egov.egiz.sig.connectors.bku.DetachedBKUConnector; import at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject; +import at.knowcenter.wag.egov.egiz.sig.connectors.mocca.LocRefDetachedMOCCAConnector; import at.knowcenter.wag.egov.egiz.sig.sigid.DetachedLocRefMOAIdFormatter; import at.knowcenter.wag.egov.egiz.sig.sigkz.SigKZIDHelper; import at.knowcenter.wag.egov.egiz.tools.CodingHelper; @@ -186,15 +189,19 @@ public class MOASoapWithAttachmentConnector implements Connector String verify_request_template = this.environment.getVerifyRequestTemplate(); String xml_content = null; - if (!SigKZIDHelper.isMOASigned(so)) - { + + if (SigKZIDHelper.isMOASigned(so)) { + log.debug("MOA signature detected."); + xml_content = prepareXMLContent(data, so); + } else if (SigKZIDHelper.isMOCCASigned(so)) { + log.debug("MOCCA signature detected."); + LocRefDetachedMOCCAConnector mocca_connector = new LocRefDetachedMOCCAConnector(this.params, "not needed here"); + xml_content = mocca_connector.prepareXMLContent(data, so); + } else { + log.debug("Generic signature (not MOA/MOCCA) signature detected."); DetachedBKUConnector bku_connector = new DetachedBKUConnector(this.params, "not needed here"); xml_content = bku_connector.prepareXMLContent(data, so); } - else - { - xml_content = prepareXMLContent(data, so); - } String verify_request_xml = verify_request_template.replaceFirst(TemplateReplaces.XML_CONTENT_REPLACE, xml_content); verify_request_xml = verify_request_xml.replaceFirst(TemplateReplaces.TRUST_PROFILE_ID_REPLACE, this.environment.getVerifyTrustProfileId()); diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/sig/connectors/mocca/LocRefDetachedMOCCAConnector.java b/src/main/java/at/knowcenter/wag/egov/egiz/sig/connectors/mocca/LocRefDetachedMOCCAConnector.java new file mode 100644 index 0000000..8ae6d5f --- /dev/null +++ b/src/main/java/at/knowcenter/wag/egov/egiz/sig/connectors/mocca/LocRefDetachedMOCCAConnector.java @@ -0,0 +1,695 @@ +package at.knowcenter.wag.egov.egiz.sig.connectors.mocca; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.gv.egiz.pdfas.framework.ConnectorParameters; +import at.gv.egiz.pdfas.web.helper.SigningTimeHelper; +import at.knowcenter.wag.egov.egiz.cfg.SettingsReader; +import at.knowcenter.wag.egov.egiz.exceptions.ConnectorException; +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; +import at.knowcenter.wag.egov.egiz.exceptions.SettingsException; +import at.knowcenter.wag.egov.egiz.exceptions.SignatureException; +import at.knowcenter.wag.egov.egiz.sig.SignatureData; +import at.knowcenter.wag.egov.egiz.sig.SignatureObject; +import at.knowcenter.wag.egov.egiz.sig.SignatureResponse; +import at.knowcenter.wag.egov.egiz.sig.X509Cert; +import at.knowcenter.wag.egov.egiz.sig.connectors.Connector; +import at.knowcenter.wag.egov.egiz.sig.connectors.LocalConnector; +import at.knowcenter.wag.egov.egiz.sig.connectors.TemplateReplaces; +import at.knowcenter.wag.egov.egiz.sig.connectors.bku.BKUHelper; +import at.knowcenter.wag.egov.egiz.sig.connectors.bku.BKUPostConnection; +import at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject; +import at.knowcenter.wag.egov.egiz.sig.sigid.DetachedMOCIdFormatter; +import at.knowcenter.wag.egov.egiz.sig.sigid.IdFormatter; +import at.knowcenter.wag.egov.egiz.tools.CodingHelper; +import at.knowcenter.wag.egov.egiz.tools.FileHelper; + +/** + * Connector for MOCCA. + * @author tknall + */ +public class LocRefDetachedMOCCAConnector implements Connector, LocalConnector { + + private static Log log = LogFactory.getLog(LocRefDetachedMOCCAConnector.class); + + /** + * The connector parameters. + */ + protected ConnectorParameters params = null; + + /** + * The environment of this connector containing templates. + */ + protected Environment environment = null; + + /** + * Constructor that builds the configuration environment for this connector according to the + * given profile. + * @param connectorParameters The connectot parameters. + * @throws ConnectorException Thrown in case of error. + */ + public LocRefDetachedMOCCAConnector(ConnectorParameters connectorParameters, String loc_ref_content) throws ConnectorException { + this.params = connectorParameters; + this.environment = new Environment(this.params.getProfileId(), loc_ref_content); + } + + /** + * Sends the request to the given URL. This method handles communication exceptions. + * The actual send work is done by doPostRequestMultipart. + * @see BKUPostConnection#doPostRequestMultipart(String, String, SignatureData) + * @param url The URL to send the request to. + * @param request_string The request XML. + * @param data The data. + * @return Returns the response properties containing among others the response XML. + * @throws ConnectorException Thrown in case of an error. + */ + protected Properties sendRequest(String url, String request_string, SignatureData data) throws ConnectorException { + try { + Properties response_properties = BKUPostConnection.doPostRequestMultipart(url, request_string, data); + return response_properties; + } catch (Exception e) { + ConnectorException se = new ConnectorException(320, e); + throw se; + } + } + + /** + * Starts a signature process. + * @param data The data to be signed. + * @return Returns the signature object containing the signed data. + * @throws ConnectorException Thrown in case of an error. + */ + public SignSignatureObject doSign(SignatureData data) throws ConnectorException { + log.debug("doSign:"); + + String sign_request_xml = prepareSignRequest(data); + log.debug("sign_request_xml = " + sign_request_xml); + + String url = this.environment.getSignURL(); + Properties response_properties = sendRequest(url, sign_request_xml, data); + + SignSignatureObject sso = analyzeSignResponse(response_properties); + + sso.response_properties = response_properties; + + log.debug("doSign finished."); + return sso; + } + + /** + * Verification is not supported by MOCCA. Therefore this method always throws a + * {@link ConnectorException} with error code {@link ErrorCode#SIGNATURE_VERIFICATION_NOT_SUPPORTED}. + */ + public SignatureResponse doVerify(SignatureData data, SignSignatureObject so) throws ConnectorException { + throw new ConnectorException(ErrorCode.SIGNATURE_VERIFICATION_NOT_SUPPORTED, "Signature Verification is not supported by MOCCA."); + } + + /** + * This method analyzes a signature response of the signature device. + * @param response_properties The response elements of the signature device. + * @return The parsed signed signature object. + * @throws ConnectorException Thrown in case of an error. + */ + public SignSignatureObject analyzeSignResponse(Properties response_properties) throws ConnectorException { + log.debug("analyzeSignResponse:"); + String response_string = response_properties.getProperty(BKUPostConnection.RESPONSE_STRING_KEY); + BKUHelper.checkResponseForError(response_string); + SignSignatureObject so = this.parseCreateXMLResponse(response_string, new DetachedMOCIdFormatter()); + so.response_properties = response_properties; + log.debug("analyzeSignResponse finished."); + return so; + } + + /** + * This method parses the signature creation response of the signature device. + * @param xmlResponse The response string. + * @return Returns the parsed signature object holding the data. + * @see SignatureObject + * @see CodingHelper + * @see X509Cert + */ + public SignSignatureObject parseCreateXMLResponse(String xmlResponse, IdFormatter id_formatter) throws ConnectorException { + + Pattern iss_nam_p_s = Pattern.compile("<[\\w]*:?X509IssuerName>"); + Pattern iss_nam_p_e = Pattern.compile(""); + Pattern sig_tim_p_s = Pattern.compile("<[\\w]*:?SigningTime>"); + Pattern sig_tim_p_e = Pattern.compile(""); + Pattern ser_num_p_s = Pattern.compile("<[\\w]*:?X509SerialNumber>"); + Pattern ser_num_p_e = Pattern.compile(""); + Pattern sig_cer_p_s = Pattern.compile("<[\\w]*:?X509Certificate>"); + Pattern sig_cer_p_e = Pattern.compile(""); + + Matcher iss_nam_m_s = iss_nam_p_s.matcher(xmlResponse); + Matcher iss_nam_m_e = iss_nam_p_e.matcher(xmlResponse); + Matcher sig_tim_m_s = sig_tim_p_s.matcher(xmlResponse); + Matcher sig_tim_m_e = sig_tim_p_e.matcher(xmlResponse); + Matcher ser_num_m_s = ser_num_p_s.matcher(xmlResponse); + Matcher ser_num_m_e = ser_num_p_e.matcher(xmlResponse); + Matcher sig_cer_m_s = sig_cer_p_s.matcher(xmlResponse); + Matcher sig_cer_m_e = sig_cer_p_e.matcher(xmlResponse); + + // SignatureValue + String sig_val = null; + Matcher signatureValueMatcher = Pattern.compile("<(\\w+:)?SignatureValue( Id=\"[\\w-]+\")?>\\s*(.*)\\s*").matcher(xmlResponse); + if (signatureValueMatcher.find()) { + sig_val = signatureValueMatcher.group(3); + } + log.debug("sig_val = " + sig_val); + + // X509IssuerName + String iss_nam = null; + if (iss_nam_m_s.find() && iss_nam_m_e.find()) { + iss_nam = xmlResponse.substring(iss_nam_m_s.end(), iss_nam_m_e.start()); + } + log.debug("iss_nam = " + iss_nam); + + // X509SerialNumber + String ser_num = null; + if (ser_num_m_s.find() && ser_num_m_e.find()) { + ser_num = BKUHelper.removeAllWhitespace(xmlResponse.substring(ser_num_m_s.end(), ser_num_m_e.start())); + } + log.debug("ser_num = " + ser_num); + + // SigningTime + String sig_tim = null; + if (sig_tim_m_s.find() && sig_tim_m_e.find()) { + sig_tim = xmlResponse.substring(sig_tim_m_s.end(), sig_tim_m_e.start()); + } + log.debug("sig_tim = " + sig_tim); + + // X509Certificate + X509Certificate cert = null; + if (sig_cer_m_s.find() && sig_cer_m_e.find()) { + String sig_cer = BKUHelper.removeAllWhitespace(xmlResponse.substring(sig_cer_m_s.end(), sig_cer_m_e.start())); + + try { + byte[] der = CodingHelper.decodeBase64(sig_cer); + ByteArrayInputStream bais = new ByteArrayInputStream(der); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + cert = (X509Certificate) cf.generateCertificate(bais); + bais.close(); + } catch (UnsupportedEncodingException e) { + throw new ConnectorException(300, e); + } catch (CertificateException e) { + throw new ConnectorException(300, e); + } catch (IOException e) { + throw new ConnectorException(300, e); + } + } + log.debug("X509Certificate = " + cert); + + if (log.isDebugEnabled()) { + + String cert_iss = cert.getIssuerDN().getName(); + log.debug("certificate's issuer = " + cert_iss); + log.debug("response's issuer = " + iss_nam); + log.debug("issuer matches = " + cert_iss.equals(iss_nam)); + log.debug("ser number matches = " + cert.getSerialNumber().toString().equals(ser_num)); + } + + // extract Signature Id's + String[] ids = extractIds(xmlResponse); + String final_ids = id_formatter.formatIds(ids); + + SignSignatureObject so = new SignSignatureObject(); + so.date = sig_tim; + so.issuer = iss_nam; + so.signatureValue = sig_val; + so.x509Certificate = cert; + + so.id = final_ids; + + return so; + } + + /** + * Extraction of the id attributes from the xml response. + * @param xmlResponse The xml response. + * @return The parsed id attributes. + */ + public final static String[] extractIds(String xmlResponse) { + return new String[] { extractId(xmlResponse) }; + } + + /** + * There is only one special common part of all id attributes of this connector that has to be + * stored. This method returns that single part. + * @param xmlResponse The xml response. + * @return The parsed common part of all id attributes. + */ + private final static String extractId(String xmlResponse) { + final Pattern ID_PATTERN = Pattern.compile("Id\\s*=\\s*\"\\s*Signature-([\\p{XDigit}]+)-\\d+\\s*\""); + Matcher matcher = ID_PATTERN.matcher(xmlResponse); + if (matcher.find() && matcher.groupCount() > 0) { + return matcher.group(1); + } + return null; + } + + /** + * Verification is not supported by MOCCA. Therefore this method always throws a + * {@link ConnectorException} with error code {@link ErrorCode#SIGNATURE_VERIFICATION_NOT_SUPPORTED}. + */ + public SignatureResponse analyzeVerifyResponse(Properties response_properties) throws ConnectorException { + throw new ConnectorException(ErrorCode.SIGNATURE_VERIFICATION_NOT_SUPPORTED, "Signature Verification is not supported by MOCCA."); + } + + /** + * Prepares the signature request xml to be sent using the sign request template. + * @param data The signature data. + * @return Returns the sign request xml to be sent. + * @throws ConnectorException Thrown in case of an error. + */ + public String prepareSignRequest(SignatureData data) throws ConnectorException { + log.debug("prepareSignRequestDetached:"); + + String sign_request_template = this.environment.getSignRequestTemplate(); + + String sign_keybox_identifier = this.environment.getSignKeyboxIdentifier(); + String mime_type = data.getMimeType(); + String loc_ref_content = this.environment.getLocRefContent(); + + if (log.isDebugEnabled()) { + log.debug("sign keybox identifier = " + sign_keybox_identifier); + log.debug("mime type = " + mime_type); + log.debug("loc_ref_content = " + loc_ref_content); + } + + String sign_request_xml = sign_request_template.replaceFirst(TemplateReplaces.KEYBOX_IDENTIFIER_REPLACE, sign_keybox_identifier); + sign_request_xml = sign_request_xml.replaceFirst(TemplateReplaces.MIME_TYPE_REPLACE, mime_type); + sign_request_xml = sign_request_xml.replaceFirst(TemplateReplaces.LOC_REF_CONTENT_REPLACE, loc_ref_content); + + log.debug("sign_request_xml = " + sign_request_xml); + log.debug("prepareSignRequestDetached finished."); + return sign_request_xml; + } + + /** + * Verification is not supported by MOCCA. Therefore this method always throws a + * {@link ConnectorException} with error code {@link ErrorCode#SIGNATURE_VERIFICATION_NOT_SUPPORTED}. + */ + public String prepareVerifyRequest(SignatureData data, SignSignatureObject so) throws ConnectorException { + throw new ConnectorException(ErrorCode.SIGNATURE_VERIFICATION_NOT_SUPPORTED, "Signature Verification is not supported by MOCCA."); + } + + /** + * Prepares the xml content of a signature creation request including the link to the signature data. + * @param data The signature data. + * @param so The signature object containing the signature information. + * @return Returns the xml content. + * @throws ConnectorException Thrown in case of an error. + */ + public String prepareXMLContent(SignatureData data, SignSignatureObject so) throws ConnectorException { + log.debug("prepareXMLContent:"); + try { + String verify_template = this.environment.getVerifyTemplate(); + + String ids_string = so.getSigID(); + String sigId = this.parseSigId(ids_string); + + X509Certificate cert = so.getX509Certificate(); + String cert_alg = this.environment.getCertAlgEcdsa(); + if (cert.getPublicKey().getAlgorithm().indexOf("RSA") >= 0) + { + cert_alg = this.environment.getCertAlgRsa(); + } + + // cert alg replace + String verify_xml = verify_template.replaceFirst(TemplateReplaces.CERT_ALG_REPLACE, cert_alg); + + // data digest replace + byte[] data_value_hash = CodingHelper.buildDigest(data.getDataSource()); + String object_data_hash = CodingHelper.encodeBase64(data_value_hash); + + // template replacements + + verify_xml = verify_xml.replaceFirst(TemplateReplaces.DIGEST_VALUE_SIGNED_DATA_REPLACE, object_data_hash); + verify_xml = verify_xml.replaceFirst(TemplateReplaces.SIGNATURE_VALUE_REPLACE, so.getSignatureValue()); + + // X.509 Certificate replace + byte[] der = cert.getEncoded(); + byte[] cert_hash = CodingHelper.buildDigest(der); + String certDigest = CodingHelper.encodeBase64(cert_hash); + String x509_cert_string = CodingHelper.encodeBase64(der); + verify_xml = verify_xml.replaceFirst(TemplateReplaces.X509_CERTIFICATE_REPLACE, x509_cert_string); + + // Qualified Properties replaces + verify_xml = verify_xml.replaceAll(TemplateReplaces.SIG_ID_REPLACE, sigId); + verify_xml = verify_xml.replaceFirst(TemplateReplaces.SIGNING_TIME_REPLACE, so.getDate()); + verify_xml = verify_xml.replaceFirst(TemplateReplaces.DIGEST_VALUE_CERTIFICATE_REPLACE, certDigest); + verify_xml = verify_xml.replaceFirst(TemplateReplaces.X509_ISSUER_NAME_REPLACE, so.getIssuer()); + verify_xml = verify_xml.replaceFirst(TemplateReplaces.X509_SERIAL_NUMBER_REPLACE, so.getSerialNumber()); + // SigDataRefReplace already done above + verify_xml = verify_xml.replaceFirst(TemplateReplaces.MIME_TYPE_REPLACE, data.getMimeType()); + + // Signed Properties hash + Pattern spPattern = Pattern.compile("(<(\\w+:)?SignedProperties.*>.*)"); + Matcher matcher = spPattern.matcher(verify_xml); + if (matcher.find()) { + log.debug("SignedProperties found."); + String string_to_be_hashed = matcher.group(1); + log.debug("SignedProperties string to be hashed: " + string_to_be_hashed); + final byte[] bytes_to_be_hashed = string_to_be_hashed.getBytes("UTF-8"); + byte[] sig_prop_code = CodingHelper.buildDigest(bytes_to_be_hashed); + String sig_prop_hash = CodingHelper.encodeBase64(sig_prop_code); + + verify_xml = verify_xml.replaceFirst(TemplateReplaces.DIGEST_VALUE_SIGNED_PROPERTIES_REPLACE, sig_prop_hash); + } + + log.debug("prepareXMLContent finished."); + return verify_xml; + } catch (Exception e) { + log.debug(e); + throw new ConnectorException(310, e); + } + } + + /** + * Holds environment configuration information like templates. + * @author wprinz + */ + public static class Environment { + + /** + * The configuration key of the sign keybox identifier. + */ + protected static final String SIGN_KEYBOX_IDENTIFIER_KEY = "moc.sign.KeyboxIdentifier"; + + /** + * The configuration key of the sign request template. + */ + protected static final String SIGN_REQUEST_TEMPLATE_KEY = "moc.sign.request.detached"; + + /** + * The configuration key of the sign URL. + */ + protected static final String SIGN_URL_KEY = "moc.sign.url"; + + /** + * BKU template file prefix + */ + protected static final String TEMPLATE_FILE_PREFIX = "./templates/moc."; + + /** + * signing file template sufix + */ + protected static final String SIGN_TEMPLATE_FILE_SUFIX = ".sign.request.xml"; + + /** + * verifing template file sufix + */ + /* signature verification is not supported by mocca + protected static final String VERIFY_REQUEST_TEMPLATE_FILE_SUFIX = ".verify.request.xml"; + */ + + /** + * verifing file template key sufix + */ + protected static final String VERIFY_TEMPLATE_SUFIX = ".verify.template.xml"; + + /** + * The configuration key of the verify request template. + */ + /* signature verification is not supported by mocca + protected static final String VERIFY_REQUEST_TEMPLATE_KEY = "moc.verify.request.detached"; + */ + + /** + * The configuration key of the verify template. + */ + protected static final String VERIFY_TEMPLATE_KEY = "moc.verify.template.detached"; + + /** + * The configuration key of the verify URL. + */ + /* signature verification is not supported by mocca + protected static final String xxxVERIFY_URL_KEY = "moc.verify.url"; + */ + + /** + * The configuration key for the ECDSA cert alg property. + */ + protected static final String ECDSA_CERT_ALG_KEY = "cert.alg.ecdsa"; + + /** + * The configuration key for the RSA cert alg property. + */ + protected static final String RSA_CERT_ALG_KEY = "cert.alg.rsa"; + + protected String profile = null; + + protected String loc_ref_content = null; + + protected String sign_keybox_identifier = null; + + protected String sign_request_template = null; + + protected String sign_url = null; + + /* signature verification is not supported by mocca + protected String verify_request_template = null; + */ + + protected String verify_template = null; + + /* signature verification is not supported by mocca + protected String verify_url = null; + */ + + protected String cert_alg_ecdsa = null; + + protected String cert_alg_rsa = null; + + /** + * Initializes the environment with a given profile. + * @param profile The configuration profile. + * @throws ConnectorException Thrown in case of an error. + */ + public Environment(String profile, String loc_ref_content) throws ConnectorException { + this.profile = profile; + + this.loc_ref_content = loc_ref_content; + + SettingsReader settings = null; + try { + settings = SettingsReader.getInstance(); + } catch (SettingsException e) { + throw new ConnectorException(300, e); + } + + this.sign_keybox_identifier = getConnectorValueFromProfile(settings, profile, SIGN_KEYBOX_IDENTIFIER_KEY); + + + // SIGN REQUEST + + // try specific file + String sign_request_filename = TEMPLATE_FILE_PREFIX + settings.getValueFromKey("default.moc.algorithm.id") + SIGN_TEMPLATE_FILE_SUFIX; + log.debug("Trying to load specific sign request file " + sign_request_filename); + this.sign_request_template = FileHelper.readFromFile(SettingsReader.relocateFile(sign_request_filename)); + + // try default request file + if (this.sign_request_template == null) { + sign_request_filename = getConnectorValueFromProfile(settings, profile, SIGN_REQUEST_TEMPLATE_KEY); + log.debug("Specific file not found. Trying default sign request file " + sign_request_filename); + this.sign_request_template = FileHelper.readFromFile(SettingsReader.relocateFile(sign_request_filename)); + } + + // request file is needed !!! + if (this.sign_request_template == null) { + throw new ConnectorException(300, "Can not read the create xml request template"); + } + + this.sign_url = getConnectorValueFromProfile(settings, profile, SIGN_URL_KEY); + + + // VERIFY REQUEST + /* signature verification is not supported by mocca + + // try specific file + String verify_request_filename = TEMPLATE_FILE_PREFIX + settings.getValueFromKey("default.moc.algorithm.id") + VERIFY_REQUEST_TEMPLATE_FILE_SUFIX; + log.debug("Trying to load specific verify request file " + verify_request_filename); + this.verify_request_template = FileHelper.readFromFile(SettingsReader.relocateFile(verify_request_filename)); + + // try default request file + if (this.verify_request_template == null) { + verify_request_filename = getConnectorValueFromProfile(settings, profile, VERIFY_REQUEST_TEMPLATE_KEY); + log.debug("Specific file not found. Trying default verify request file " + verify_request_filename); + this.verify_request_template = FileHelper.readFromFile(SettingsReader.relocateFile(verify_request_filename)); + } + + // request file is needed !!! + if (this.verify_request_template == null) { + throw new ConnectorException(ErrorCode.SETTING_NOT_FOUND, "Can not read the verify xml request template"); + } + + */ + + // load template file + // try specific file + String verify_filename = TEMPLATE_FILE_PREFIX + settings.getValueFromKey("default.moc.algorithm.id") + VERIFY_TEMPLATE_SUFIX; + log.debug("Trying to load specific signature template file " + verify_filename); + this.verify_template = FileHelper.readFromFile(SettingsReader.relocateFile(verify_filename)); + + // try default signature template file + if (this.verify_template == null) { + verify_filename = getConnectorValueFromProfile(settings, profile, VERIFY_TEMPLATE_KEY); + log.debug("Specific signature template file not found. Trying default signature template file " + verify_filename); + this.verify_template = FileHelper.readFromFile(SettingsReader.relocateFile(verify_filename)); + } + + // signature template is needed !!! + if (this.verify_template == null) { + throw new ConnectorException(ErrorCode.SETTING_NOT_FOUND, "Can not read the verify template"); + } + + /* signature verification is not supported by mocca + this.verify_url = getConnectorValueFromProfile(settings, profile, VERIFY_URL_KEY); + */ + + this.cert_alg_ecdsa = settings.getValueFromKey(ECDSA_CERT_ALG_KEY); + + this.cert_alg_rsa = settings.getValueFromKey(RSA_CERT_ALG_KEY); + + } + + /** + * Returns the profile name. + * @return The profile name. + */ + public String getProfile() { + return this.profile; + } + + /** + * Returns the LocRef content. + * + * @return Returns the LocRef content. + */ + public String getLocRefContent() { + return this.loc_ref_content; + } + + /** + * Returns the sign keybox identifier. + * + * @return Returns the sign keybox identifier. + */ + public String getSignKeyboxIdentifier() { + return this.sign_keybox_identifier; + } + + /** + * Returns the sign request template. + * + * @return Returns the sign request template. + */ + public String getSignRequestTemplate() { + return this.sign_request_template; + } + + /** + * Returns the sign URL. + * + * @return Returns the sign URL. + */ + public String getSignURL() { + return this.sign_url; + } + + /** + * Returns the verify request template. + * + * @return Returns the verify request template. + */ + /* signature verification is not supported by mocca + public String getVerifyRequestTemplate() { + return this.verify_request_template; + } + */ + + /** + * Returns the verify template. + * + * @return Returns the verify template. + */ + public String getVerifyTemplate() { + return this.verify_template; + } + + /** + * Returns the verify URL. + * + * @return Returns the verify URL. + */ + /* signature verification is not supported by mocca + public String getVerifyURL() { + return this.verify_url; + } + */ + + /** + * Returns the ecdsa cert alg property. + * + * @return Returns the ecdsa cert alg property. + */ + public String getCertAlgEcdsa() { + return this.cert_alg_ecdsa; + } + + /** + * Returns the rsa cert alg property. + * + * @return Returns the rsa cert alg property. + */ + public String getCertAlgRsa() { + return this.cert_alg_rsa; + } + + /** + * Reads the configuration entry given by the key, first from the given + * profile, if not found from the defaults. + * + * @param settings + * The settings. + * @param profile + * The profile. + * @param key + * The configuration key. + * @return Returns the configuration entry. + */ + public static String getConnectorValueFromProfile(SettingsReader settings, String profile, String key) { + String value = settings.getValueFromKey("sig_obj." + profile + "." + key); //$NON-NLS-2$ + if (value == null) { + value = settings.getValueFromKey(key); + } + return value; + } + } + + /** + * Parses the common part for all id attributes from a given signature parameter string. + * @param sigIdString The given signature parameter string. + * @return The common part of all id attributes. + */ + protected String parseSigId(String sigIdString) { + int pos = sigIdString.indexOf("@"); + String result = null; + if (pos != -1) { + result = sigIdString.substring(pos+1).trim(); + } + return result; + } + +} diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/sig/sigid/DetachedMOCIdFormatter.java b/src/main/java/at/knowcenter/wag/egov/egiz/sig/sigid/DetachedMOCIdFormatter.java new file mode 100644 index 0000000..c942b73 --- /dev/null +++ b/src/main/java/at/knowcenter/wag/egov/egiz/sig/sigid/DetachedMOCIdFormatter.java @@ -0,0 +1,48 @@ +/** + * + */ +package at.knowcenter.wag.egov.egiz.sig.sigid; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.knowcenter.wag.egov.egiz.cfg.SettingsReader; +import at.knowcenter.wag.egov.egiz.exceptions.SettingsException; + +/** + * @author tknall + * + */ +public class DetachedMOCIdFormatter implements IdFormatter { + + public static String SIG_ID_PREFIX = "etsi-moc-1.0"; + + /** + * Key value in property file + */ + public static final String SIG_ID_PROPERTY_KEY = "default.moc.algorithm.id"; + + /** + * The log. + */ + private static Log log = LogFactory.getLog(DetachedIdFormatter.class); + + /** + * @see at.knowcenter.wag.egov.egiz.sig.sigid.IdFormatter#formatIds(java.lang.String[]) + */ + public String formatIds(String[] ids) { + // read id from property file and use it + String prefix = null; + try { + prefix = SettingsReader.getInstance().getValueFromKey(SIG_ID_PROPERTY_KEY); + } catch (SettingsException e) { + log.error(e.getMessage(), e); + } + prefix = StringUtils.defaultIfEmpty(prefix, SIG_ID_PREFIX); + + StringBuffer formattedIds = new StringBuffer(prefix).append("@").append(ids[0]); + return formattedIds.toString(); + } + +} diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/sig/sigkz/SigKZIDHelper.java b/src/main/java/at/knowcenter/wag/egov/egiz/sig/sigkz/SigKZIDHelper.java index 5206ed1..67c5e15 100644 --- a/src/main/java/at/knowcenter/wag/egov/egiz/sig/sigkz/SigKZIDHelper.java +++ b/src/main/java/at/knowcenter/wag/egov/egiz/sig/sigkz/SigKZIDHelper.java @@ -3,6 +3,8 @@ */ package at.knowcenter.wag.egov.egiz.sig.sigkz; +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -12,6 +14,7 @@ import at.knowcenter.wag.egov.egiz.exceptions.InvalidIDException; import at.knowcenter.wag.egov.egiz.framework.SignatorFactory; import at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject; import at.knowcenter.wag.egov.egiz.sig.sigid.DetachedLocRefMOAIdFormatter; +import at.knowcenter.wag.egov.egiz.sig.sigid.DetachedMOCIdFormatter; import at.knowcenter.wag.egov.egiz.sig.sigid.HotfixIdFormatter; /** @@ -87,6 +90,23 @@ public final class SigKZIDHelper return isMOASigned(kz, sig_id); } + + /** + * @author tknall + */ + public static boolean isMOCCASigned(SignSignatureObject so) { + String sig_kz = so.kz; + String sig_id = so.id; + if (StringUtils.isEmpty(sig_kz) || StringUtils.isEmpty(sig_id)) { + return false; + } + String[] ids = sig_id.split("@"); + if (ArrayUtils.isEmpty(ids)) { + return false; + } + String prefix = ids[0]; + return DetachedMOCIdFormatter.SIG_ID_PREFIX.equals(prefix); + } public static boolean isOldBKU(PdfASID sig_kz, String sig_id) throws ConnectorException { diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/web/LocalRequestHelper.java b/src/main/java/at/knowcenter/wag/egov/egiz/web/LocalRequestHelper.java index 15792b9..0490c48 100644 --- a/src/main/java/at/knowcenter/wag/egov/egiz/web/LocalRequestHelper.java +++ b/src/main/java/at/knowcenter/wag/egov/egiz/web/LocalRequestHelper.java @@ -29,6 +29,7 @@ import javax.servlet.http.HttpServletResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import at.gv.egiz.pdfas.utils.WebUtils; import at.gv.egiz.pdfas.web.CurrentLocalOperation; import at.gv.egiz.pdfas.web.SignSessionInformation; import at.gv.egiz.pdfas.web.VerifySessionInformation; @@ -88,8 +89,9 @@ public abstract class LocalRequestHelper */ public static String processLocalSign(SignSessionInformation si, HttpServletRequest request, HttpServletResponse response) throws IOException, PresentableException { - String host = request.getServerName(); // "129.27.153.77" - URL loc_ref_URL = new URL(getLocalContextAddress(request, response) + "/RetrieveSignatureData"); + String host = request.getServerName(); +// URL loc_ref_URL = new URL(WebUtils.addJSessionID(getLocalContextAddress(request, response) + "/RetrieveSignatureData", request)); + URL loc_ref_URL = new URL(WebUtils.buildRetrieveSignatureDataURL(request, response)); String loc_ref_url = response.encodeURL(loc_ref_URL.toString()); LocalConnector c = ConnectorChooser.chooseLocalConnectorForSign(si.connector, si.type, loc_ref_url); @@ -100,8 +102,9 @@ public abstract class LocalRequestHelper si.outputAvailable = false; si.response_properties = null; - URL data_URL = new URL(request.getScheme(), host, request.getServerPort(), request.getContextPath() + "/DataURL"); + URL data_URL = new URL(request.getScheme(), host, request.getServerPort(), WebUtils.addJSessionID(request.getContextPath() + "/DataURL", request)); String data_url = response.encodeURL(data_URL.toString()); + logger.debug("data_url = " + data_url); request.setAttribute("local_request_url", local_request_url); request.setAttribute("data_url", data_url); @@ -180,7 +183,8 @@ public abstract class LocalRequestHelper // si.finished = false; String host = request.getServerName(); - URL loc_ref_URL = new URL(getLocalContextAddress(request, response) + "/RetrieveSignatureData"); +// URL loc_ref_URL = new URL(WebUtils.addJSessionID(getLocalContextAddress(request, response) + "/RetrieveSignatureData", request)); + URL loc_ref_URL = new URL(WebUtils.buildRetrieveSignatureDataURL(request, response)); String loc_ref_url = response.encodeURL(loc_ref_URL.toString()); for (int i = 0; i < si.currentLocalOperation.requests.length; i++) @@ -216,7 +220,7 @@ public abstract class LocalRequestHelper String local_request_url = getLocalServiceAddress(si.type, si.connector); - URL data_URL = new URL(request.getScheme(), host, request.getServerPort(), request.getContextPath() + "/DataURL"); + URL data_URL = new URL(request.getScheme(), host, request.getServerPort(), WebUtils.addJSessionID(request.getContextPath() + "/DataURL", request)); String data_url = response.encodeURL(data_URL.toString()); request.setAttribute("local_request_url", local_request_url); diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/DataURLServlet.java b/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/DataURLServlet.java index 19a82c3..2adc4b1 100644 --- a/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/DataURLServlet.java +++ b/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/DataURLServlet.java @@ -8,17 +8,24 @@ import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.net.URL; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; import java.util.Properties; import javax.servlet.RequestDispatcher; import javax.servlet.ServletContext; import javax.servlet.ServletException; +import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; +import org.apache.commons.fileupload.FileItem; +import org.apache.commons.fileupload.FileItemFactory; +import org.apache.commons.fileupload.FileUploadException; +import org.apache.commons.fileupload.disk.DiskFileItemFactory; +import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -29,12 +36,14 @@ import at.gv.egiz.pdfas.web.SignSessionInformation; import at.gv.egiz.pdfas.web.VerifySessionInformation; import at.gv.egiz.pdfas.web.helper.SessionHelper; import at.gv.egiz.pdfas.web.helper.SignServletHelper; +import at.gv.egiz.pdfas.web.helper.SigningTimeHelper; import at.gv.egiz.pdfas.web.helper.TempDirHelper; import at.knowcenter.wag.egov.egiz.PdfASID; import at.knowcenter.wag.egov.egiz.exceptions.ConnectorException; import at.knowcenter.wag.egov.egiz.exceptions.InvalidIDException; import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; import at.knowcenter.wag.egov.egiz.exceptions.SignatorFactoryException; +import at.knowcenter.wag.egov.egiz.exceptions.SignatureException; import at.knowcenter.wag.egov.egiz.pdf.SignatureHolder; import at.knowcenter.wag.egov.egiz.sig.SignatureResponse; import at.knowcenter.wag.egov.egiz.sig.connectors.ConnectorChooser; @@ -156,15 +165,42 @@ public class DataURLServlet extends HttpServlet protected boolean isNullResponse(String xml_response) { - return xml_response.indexOf("NullOperationResponse") >= 0; + return xml_response != null && xml_response.indexOf("NullOperationResponse") != -1; } - protected void processSign(HttpServletRequest request, HttpServletResponse response, SignSessionInformation si) throws ServletException, IOException, ConnectorException, SignatorException, SignatorFactoryException + private static String retrieveXMLResponse(HttpServletRequest request) throws ServletException { + log.debug("Trying to fetch XMLResponse..."); + String xml_response = null; + if (ServletFileUpload.isMultipartContent(request)) { + log.debug("Response is multipart."); + FileItemFactory factory = new DiskFileItemFactory(); + ServletFileUpload upload = new ServletFileUpload(factory); + try { + List items = upload.parseRequest(request); + Iterator iter = items.iterator(); + while (iter.hasNext()) { + FileItem item = (FileItem) iter.next(); + if (item.isFormField() && "XMLResponse".equals(item.getFieldName())) { + log.debug("XMLResponse part found."); + xml_response = item.getString(); + break; + } + } + } catch (FileUploadException e) { + throw new ServletException(e); + } + } else { + xml_response = request.getParameter("XMLResponse"); + } + log.debug("XMLResponse = " + xml_response); + return xml_response; + } + + protected void processSign(HttpServletRequest request, HttpServletResponse response, SignSessionInformation si) throws ServletException, IOException, ConnectorException, SignatorException, SignatorFactoryException, SignatureException { log.trace("processSign"); - String xml_response = request.getParameter("XMLResponse"); //$NON-NLS-1$ - log.debug("xml_response = " + xml_response); //$NON-NLS-1$ + String xml_response = retrieveXMLResponse(request); if (isNullResponse(xml_response)) { @@ -202,6 +238,9 @@ public class DataURLServlet extends HttpServlet si.si.setSignSignatureObject(c.analyzeSignResponse(si.response_properties)); + // workaround for invalid signing time + SigningTimeHelper.checkSigningTimeAgainstHostTime(si.si); + PdfASID algorithm = FormFields.translateSignatureModeToPdfASID(si.mode); Signator signator = SignatorFactory.createSignator(algorithm); @@ -227,21 +266,33 @@ public class DataURLServlet extends HttpServlet } else { - HttpSession session = request.getSession(true); - log.debug("Putting signed document into session (" + session.getId() + ")."); - session.setAttribute(SessionAttributes.SIGNED_PDF_DOCUMENT, si); -// String serverURL = LocalRequestHelper.getLocalServerAddress(request, response); - String downloadURL = response.encodeRedirectURL(LocalRequestHelper.getLocalContextAddress(request, response) + "/ProvidePDF"); - log.debug("Creating download URL \"" + downloadURL + "\"."); - session.setAttribute(SessionAttributes.DOWNLOAD_URL_FOR_SIGNED_PDF_DOCUMENT, downloadURL); - -// String redirectURL = response.encodeRedirectURL("/pdf-as/jsp/download.jsp"); -// log.debug("Redirecting to " + redirectURL + "."); -// response.sendRedirect(redirectURL); - temporaryRedirect(LocalRequestHelper.getLocalContextAddress(request, response) + "/jsp/download.jsp", response); + // tzefferer: If PDF-AS has been called by an external web-application, we do not + // redirect to download.jsp but return the sign-response immediately + if (si.exappinf != null) { + log.debug("Entering external application interface mode. Skipping redirection to download page."); + SignServletHelper.returnSignResponse(si, response); + + // Not needed due to redirection of returnSignResponse. + // Just to clarify that there must not be any code after returnSignResponse. + return; + } else { + log.debug("Preparing download page."); + HttpSession session = request.getSession(true); + log.debug("Putting signed document into session (" + session.getId() + ")."); + session.setAttribute(SessionAttributes.SIGNED_PDF_DOCUMENT, si); + String downloadURL = response.encodeRedirectURL(LocalRequestHelper.getLocalContextAddress(request, response) + "/ProvidePDF"); + log.debug("Creating download URL \"" + downloadURL + "\"."); + session.setAttribute(SessionAttributes.DOWNLOAD_URL_FOR_SIGNED_PDF_DOCUMENT, downloadURL); + Cookie cookie = new Cookie("JSESSIONID", session.getId()); + response.addCookie(cookie); + temporaryRedirect(response.encodeRedirectURL(LocalRequestHelper.getLocalContextAddress(request, response) + "/jsp/download.jsp") , response); + + // Not needed due to temporaryRedirect. + // Just to clarify that there must not be any code after temporaryRedirect. + return; + } - return; -// SignServletHelper.returnSignResponse(si, response); + // do not insert any code within this else block ! } } } diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/SignServlet.java b/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/SignServlet.java index 6330f0c..124b2a3 100644 --- a/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/SignServlet.java +++ b/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/SignServlet.java @@ -45,6 +45,7 @@ import at.gv.egiz.pdfas.exceptions.ErrorCode; import at.gv.egiz.pdfas.exceptions.ErrorCodeHelper; import at.gv.egiz.pdfas.exceptions.external.ExternalErrorException; import at.gv.egiz.pdfas.framework.input.PdfDataSource; +import at.gv.egiz.pdfas.utils.WebUtils; import at.gv.egiz.pdfas.web.SignSessionInformation; import at.gv.egiz.pdfas.web.helper.SignServletHelper; import at.gv.egiz.pdfas.web.helper.TempDirHelper; @@ -282,7 +283,8 @@ public class SignServlet extends HttpServlet if (ud.preview) { String submit_url = response.encodeURL(request.getContextPath() + "/SignPreview"); - String signature_data_url = response.encodeURL(request.getContextPath() + "/RetrieveSignatureData"); +// String signature_data_url = response.encodeURL(WebUtils.addJSessionID(request.getContextPath() + "/RetrieveSignatureData", request)); + String signature_data_url = response.encodeURL(WebUtils.buildRetrieveSignatureDataURL(request, response)); request.setAttribute("submit_url", submit_url); request.setAttribute("signature_data_url", signature_data_url); diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/VerifyPreviewServlet.java b/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/VerifyPreviewServlet.java index 9b8583d..5e1819e 100644 --- a/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/VerifyPreviewServlet.java +++ b/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/VerifyPreviewServlet.java @@ -41,6 +41,7 @@ import org.apache.commons.logging.LogFactory; import at.gv.egiz.pdfas.framework.input.TextDataSource; import at.gv.egiz.pdfas.utils.StreamUtils; +import at.gv.egiz.pdfas.utils.WebUtils; import at.gv.egiz.pdfas.web.VerifySessionInformation; import at.gv.egiz.pdfas.web.helper.SessionHelper; import at.gv.egiz.pdfas.web.helper.TempDirHelper; @@ -566,7 +567,8 @@ public class VerifyPreviewServlet extends HttpServlet } String host = request.getServerName(); - URL loc_ref_URL = new URL(LocalRequestHelper.getLocalContextAddress(request, response) + "/RetrieveSignatureData"); +// URL loc_ref_URL = new URL(WebUtils.addJSessionID(LocalRequestHelper.getLocalContextAddress(request, response) + "/RetrieveSignatureData", request)); + URL loc_ref_URL = new URL(WebUtils.buildRetrieveSignatureDataURL(request, response)); String loc_ref_url = response.encodeURL(loc_ref_URL.toString()); List results = PdfAS.verifySignatureHoldersWeb(holders_to_verify, si, loc_ref_url); @@ -685,7 +687,8 @@ public class VerifyPreviewServlet extends HttpServlet } String host = request.getServerName(); - URL loc_ref_URL = new URL(LocalRequestHelper.getLocalContextAddress(request, response) + "/RetrieveSignatureData"); +// URL loc_ref_URL = new URL(WebUtils.addJSessionID(LocalRequestHelper.getLocalContextAddress(request, response) + "/RetrieveSignatureData", request)); + URL loc_ref_URL = new URL(WebUtils.buildRetrieveSignatureDataURL(request, response)); String loc_ref_url = response.encodeURL(loc_ref_URL.toString()); List results = PdfAS.verifySignatureHoldersWeb(holders_to_verify, si, loc_ref_url); diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/VerifyServlet.java b/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/VerifyServlet.java index 387ae08..1029a5f 100644 --- a/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/VerifyServlet.java +++ b/src/main/java/at/knowcenter/wag/egov/egiz/web/servlets/VerifyServlet.java @@ -45,6 +45,7 @@ import at.gv.egiz.pdfas.framework.input.ExtractionStage; import at.gv.egiz.pdfas.framework.input.PdfDataSource; import at.gv.egiz.pdfas.framework.input.TextDataSource; import at.gv.egiz.pdfas.framework.vfilter.VerificationFilterParameters; +import at.gv.egiz.pdfas.utils.WebUtils; import at.gv.egiz.pdfas.web.VerifySessionInformation; import at.gv.egiz.pdfas.web.helper.TempDirHelper; import at.knowcenter.wag.egov.egiz.PdfAS; @@ -151,7 +152,8 @@ public class VerifyServlet extends HttpServlet String host = request.getServerName(); // TODO still required for old communication with MOA-SS/SP - URL loc_ref_URL = new URL(LocalRequestHelper.getLocalContextAddress(request, response) + "/RetrieveSignatureData"); +// URL loc_ref_URL = new URL(WebUtils.addJSessionID(LocalRequestHelper.getLocalContextAddress(request, response) + "/RetrieveSignatureData", request)); + URL loc_ref_URL = new URL(WebUtils.buildRetrieveSignatureDataURL(request, response)); String loc_ref_url = response.encodeURL(loc_ref_URL.toString()); List results = PdfAS.verifySignatureHoldersWeb(signature_holders, si, loc_ref_url); -- cgit v1.2.3