From 1219abaf9f0029e39f5fbdf342fd4ebf07144b5b Mon Sep 17 00:00:00 2001 From: Andreas Fitzek Date: Fri, 11 Jul 2014 13:38:49 +0200 Subject: added Signature Verification Level --- .../at/gv/egiz/pdfas/api/ws/PDFASSignRequest.java | 10 + .../at/gv/egiz/pdfas/api/ws/PDFASSignResponse.java | 18 ++ .../pdfas/api/ws/PDFASVerificationResponse.java | 34 +++ .../at/gv/egiz/pdfas/api/ws/VerificationLevel.java | 23 ++ .../java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java | 16 +- pdf-as-web/build.gradle | 4 + .../gv/egiz/pdfas/web/config/WebConfiguration.java | 159 +++++++++----- .../at/gv/egiz/pdfas/web/helper/PdfAsHelper.java | 231 ++++++++++++++++----- .../at/gv/egiz/pdfas/web/servlets/PDFData.java | 11 + .../pdfas/web/servlets/UIEntryPointServlet.java | 15 ++ .../at/gv/egiz/pdfas/web/store/DBRequestStore.java | 121 +++++++++++ .../at/gv/egiz/pdfas/web/store/db/Request.java | 52 +++++ .../at/gv/egiz/pdfas/web/ws/PDFASSigningImpl.java | 45 +++- 13 files changed, 625 insertions(+), 114 deletions(-) create mode 100644 pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASVerificationResponse.java create mode 100644 pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/VerificationLevel.java create mode 100644 pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/DBRequestStore.java create mode 100644 pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/db/Request.java diff --git a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASSignRequest.java b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASSignRequest.java index 71328d36..3e714ea9 100644 --- a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASSignRequest.java +++ b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASSignRequest.java @@ -39,6 +39,7 @@ public class PDFASSignRequest implements Serializable { String requestID; byte[] inputData; PDFASSignParameters parameters; + VerificationLevel verificationLevel; @XmlElement(required = true, nillable = false, name="requestID") public String getRequestID() { @@ -49,6 +50,15 @@ public class PDFASSignRequest implements Serializable { this.requestID = requestID; } + @XmlElement(required = false, nillable = true, name="verificationLevel") + public VerificationLevel getVerificationLevel() { + return verificationLevel; + } + + public void setVerificationLevel(VerificationLevel verificationLevel) { + this.verificationLevel = verificationLevel; + } + @XmlElement(required = true, nillable = false, name="inputData") public byte[] getInputData() { return inputData; diff --git a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASSignResponse.java b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASSignResponse.java index 20b0cebb..19517a13 100644 --- a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASSignResponse.java +++ b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASSignResponse.java @@ -39,6 +39,8 @@ public class PDFASSignResponse implements Serializable { String requestID; String error; byte[] signedPDF; + byte[] signerCertificate; + PDFASVerificationResponse verificationResponse; String redirectUrl; @XmlElement(required = true, nillable = false, name="requestID") @@ -57,6 +59,22 @@ public class PDFASSignResponse implements Serializable { this.signedPDF = signedPDF; } + @XmlElement(required = false, nillable = false, name="signerCertificate") + public byte[] getSignerCertificate() { + return signerCertificate; + } + public void setSignerCertificate(byte[] signerCertificate) { + this.signerCertificate = signerCertificate; + } + + @XmlElement(required = false, nillable = false, name="verificationResponse") + public PDFASVerificationResponse getVerificationResponse() { + return verificationResponse; + } + public void setVerificationResponse(PDFASVerificationResponse verificationResponse) { + this.verificationResponse = verificationResponse; + } + @XmlElement(required = false, name="error") public String getError() { return error; diff --git a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASVerificationResponse.java b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASVerificationResponse.java new file mode 100644 index 00000000..c4fb438d --- /dev/null +++ b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/PDFASVerificationResponse.java @@ -0,0 +1,34 @@ +package at.gv.egiz.pdfas.api.ws; + +import java.io.Serializable; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlType; + +@XmlType(name="VerificationResponse") +public class PDFASVerificationResponse implements Serializable { + + /** + * + */ + private static final long serialVersionUID = -2581929633991566751L; + + int valueCode; + int certificateCode; + + @XmlElement(required = true, nillable = false, name="valueCode") + public int getValueCode() { + return valueCode; + } + public void setValueCode(int valueCode) { + this.valueCode = valueCode; + } + + @XmlElement(required = true, nillable = false, name="certificateCode") + public int getCertificateCode() { + return certificateCode; + } + public void setCertificateCode(int certificateCode) { + this.certificateCode = certificateCode; + } +} diff --git a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/VerificationLevel.java b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/VerificationLevel.java new file mode 100644 index 00000000..ca4d893e --- /dev/null +++ b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/api/ws/VerificationLevel.java @@ -0,0 +1,23 @@ +package at.gv.egiz.pdfas.api.ws; + +import javax.xml.bind.annotation.XmlType; + +@XmlType(name = "VerificationLevel") +public enum VerificationLevel { + + INTEGRITY_ONLY("intOnly"), FULL_CERT_PATH("full"); + + private final String name; + + private VerificationLevel(String s) { + name = s; + } + + public boolean equalsName(String otherName) { + return (otherName == null) ? false : name.equals(otherName); + } + + public String toString() { + return name; + } +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java index 40e7faf5..7815717e 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java @@ -232,6 +232,16 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants { .getDictionaryObject(COSName.ACRO_FORM); COSArray fields = (COSArray) acroForm .getDictionaryObject(COSName.FIELDS); + + int lastSig = -1; + for (int i = 0; i < fields.size(); i++) { + COSDictionary field = (COSDictionary) fields.getObject(i); + String type = field.getNameAsString("FT"); + if ("Sig".equals(type)) { + lastSig = i; + } + } + for (int i = 0; i < fields.size(); i++) { COSDictionary field = (COSDictionary) fields.getObject(i); String type = field.getNameAsString("FT"); @@ -242,7 +252,11 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants { // verify only specific siganture! verifyThis = signatureToVerify == currentSignature; } - + + if(signatureToVerify == -2) { + verifyThis = i == lastSig; + } + if (verifyThis) { logger.trace("Found Signature: "); COSBase base = field.getDictionaryObject("V"); diff --git a/pdf-as-web/build.gradle b/pdf-as-web/build.gradle index 07aa0ea2..9cb59c57 100644 --- a/pdf-as-web/build.gradle +++ b/pdf-as-web/build.gradle @@ -27,8 +27,12 @@ dependencies { compile group: 'org.slf4j', name: 'slf4j-log4j12', version: '1.7.5' compile group: 'opensymphony', name: 'sitemesh', version: '2.4.2' compile 'javax.xml.ws:jaxws-api:2.2.11' + compile "commons-codec:commons-codec:1.9" compile 'com.sun.xml.ws:jaxws-rt:2.2.8' compile 'com.sun.xml.bind:jaxb-impl:2.2.7' + compile "org.hibernate:hibernate-core:4.3.5.Final" + compile "org.hibernate:hibernate-entitymanager:4.3.5.Final" + compile "mysql:mysql-connector-java:5.1.28" compile group: 'org.apache.pdfbox', name: 'pdfbox', version: '1.8.5' providedCompile "javax.servlet:servlet-api:2.5" testCompile group: 'junit', name: 'junit', version: '4.+' diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java index 299c166f..288b62c4 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/config/WebConfiguration.java @@ -35,58 +35,61 @@ import org.slf4j.LoggerFactory; public class WebConfiguration { - public static final String PUBLIC_URL = "public.url"; public static final String LOCAL_BKU_URL = "bku.local.url"; public static final String ONLINE_BKU_URL = "bku.online.url"; public static final String MOBILE_BKU_URL = "bku.mobile.url"; public static final String ERROR_DETAILS = "error.showdetails"; public static final String PDF_AS_WORK_DIR = "pdfas.dir"; - + public static final String MOA_SS_ENABLED = "moa.enabled"; public static final String SOAP_SIGN_ENABLED = "soap.sign.enabled"; - + public static final String KEYSTORE_ENABLED = "ks.enabled"; public static final String KEYSTORE_FILE = "ks.file"; public static final String KEYSTORE_TYPE = "ks.type"; public static final String KEYSTORE_PASS = "ks.pass"; public static final String KEYSTORE_ALIAS = "ks.key.alias"; public static final String KEYSTORE_KEY_PASS = "ks.key.pass"; - + public static final String WHITELIST_ENABLED = "whitelist.enabled"; public static final String WHITELIST_VALUE_PRE = "whitelist.url."; - + public static final String REQUEST_STORE = "request.store"; public static final String REQUEST_STORE_INMEM = "at.gv.egiz.pdfas.web.store.InMemoryRequestStore"; - + public static final String REQUEST_STORE_DB = "at.gv.egiz.pdfas.web.store.DBRequestStore"; + public static final String DB_REQUEST_TIMEOUT = "request.db.timeout"; + public static final String HIBERNATE_PREFIX = "hibernate.props."; + private static Properties properties = new Properties(); - + private static Properties hibernateProps = new Properties(); + private static final Logger logger = LoggerFactory .getLogger(WebConfiguration.class); - + private static List whiteListregEx = new ArrayList(); public static void configure(String config) { - + properties.clear(); whiteListregEx.clear(); - + try { properties.load(new FileInputStream(config)); - } catch(Exception e) { + } catch (Exception e) { logger.error("Failed to load configuration: " + e.getMessage()); throw new RuntimeException(e); } - - if(isWhiteListEnabled()) { + + if (isWhiteListEnabled()) { Iterator keyIt = properties.keySet().iterator(); - while(keyIt.hasNext()) { + while (keyIt.hasNext()) { Object keyObj = keyIt.next(); - if(keyObj != null) { + if (keyObj != null) { String key = keyObj.toString(); - if(key.startsWith(WHITELIST_VALUE_PRE)) { + if (key.startsWith(WHITELIST_VALUE_PRE)) { String whitelist_expr = properties.getProperty(key); - if(whitelist_expr != null) { + if (whitelist_expr != null) { whiteListregEx.add(whitelist_expr); logger.debug("URL Whitelist: " + whitelist_expr); } @@ -94,21 +97,52 @@ public class WebConfiguration { } } } - + + Iterator keyIt = properties.keySet().iterator(); + while (keyIt.hasNext()) { + Object keyObj = keyIt.next(); + if (keyObj != null) { + String key = keyObj.toString(); + if (key.startsWith(HIBERNATE_PREFIX)) { + String value = properties.getProperty(key); + if (value != null) { + String hibKey = key.replace(HIBERNATE_PREFIX, ""); + hibernateProps.put(hibKey, value); + } + } + } + } + + if (hibernateProps.size() != 0) { + logger.debug("DB Properties: "); + Iterator hibkeyIt = hibernateProps.keySet().iterator(); + while (hibkeyIt.hasNext()) { + Object keyObj = hibkeyIt.next(); + if (keyObj != null) { + String key = keyObj.toString(); + String value = hibernateProps.getProperty(key); + logger.debug(" {}: {}", key, value); + } + } + } + String pdfASDir = getPdfASDir(); - if(pdfASDir == null) { + if (pdfASDir == null) { logger.error("Please configure pdf as working directory in the web configuration"); - throw new RuntimeException("Please configure pdf as working directory in the web configuration"); + throw new RuntimeException( + "Please configure pdf as working directory in the web configuration"); } - + File f = new File(pdfASDir); - - if(!f.exists() || !f.isDirectory()) { - logger.error("Pdf As working directory does not exists or is not a directory!: " + pdfASDir); - throw new RuntimeException("Pdf As working directory does not exists or is not a directory!"); + + if (!f.exists() || !f.isDirectory()) { + logger.error("Pdf As working directory does not exists or is not a directory!: " + + pdfASDir); + throw new RuntimeException( + "Pdf As working directory does not exists or is not a directory!"); } } - + public static String getPublicURL() { return properties.getProperty(PUBLIC_URL); } @@ -124,71 +158,75 @@ public class WebConfiguration { public static String getHandyBKUURL() { return properties.getProperty(MOBILE_BKU_URL); } - + public static String getPdfASDir() { return properties.getProperty(PDF_AS_WORK_DIR); } - + public static String getKeystoreFile() { return properties.getProperty(KEYSTORE_FILE); } + public static String getKeystoreType() { return properties.getProperty(KEYSTORE_TYPE); } + public static String getKeystorePass() { return properties.getProperty(KEYSTORE_PASS); } + public static String getKeystoreAlias() { return properties.getProperty(KEYSTORE_ALIAS); } + public static String getKeystoreKeyPass() { return properties.getProperty(KEYSTORE_KEY_PASS); } - + public static boolean getMOASSEnabled() { String value = properties.getProperty(MOA_SS_ENABLED); - if(value != null) { - if(value.equals("true")) { + if (value != null) { + if (value.equals("true")) { return true; } } return false; } - + public static boolean getKeystoreEnabled() { String value = properties.getProperty(KEYSTORE_ENABLED); - if(value != null) { - if(value.equals("true")) { + if (value != null) { + if (value.equals("true")) { return true; } } return false; } - + public static boolean getSoapSignEnabled() { String value = properties.getProperty(SOAP_SIGN_ENABLED); - if(value != null) { - if(value.equals("true")) { + if (value != null) { + if (value.equals("true")) { return true; } } return false; } - + public static boolean isShowErrorDetails() { String value = properties.getProperty(ERROR_DETAILS); - if(value != null) { - if(value.equals("true")) { + if (value != null) { + if (value.equals("true")) { return true; } } return false; } - + public static boolean isWhiteListEnabled() { String value = properties.getProperty(WHITELIST_ENABLED); - if(value != null) { - if(value.equals("true")) { + if (value != null) { + if (value.equals("true")) { return true; } } @@ -196,32 +234,49 @@ public class WebConfiguration { } public static synchronized boolean isProvidePdfURLinWhitelist(String url) { - if(isWhiteListEnabled()) { - + if (isWhiteListEnabled()) { + Iterator patterns = whiteListregEx.iterator(); - while(patterns.hasNext()) { + while (patterns.hasNext()) { String pattern = patterns.next(); try { - if(url.matches(pattern)) { + if (url.matches(pattern)) { return true; } - } catch(Throwable e) { + } catch (Throwable e) { logger.error("Error in matching regex: " + pattern, e); } } - + return false; } return true; } + + public static Properties getHibernateProps() { + return (Properties) hibernateProps.clone(); + } + + public static int getDBTimeout() { + String value = properties.getProperty(DB_REQUEST_TIMEOUT); + int ivalue = 600; + if (value != null) { + try { + ivalue = Integer.parseInt(value); + } catch(NumberFormatException e) { + logger.error("DB request Timeout not a number", e); + } + } + return ivalue; + } public static String getStoreClass() { String cls = properties.getProperty(REQUEST_STORE); - - if(cls != null) { + + if (cls != null) { return cls; } - + return REQUEST_STORE_INMEM; } } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java index 041c7df3..b79075a1 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/helper/PdfAsHelper.java @@ -45,14 +45,19 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import javax.xml.bind.JAXBElement; +import javax.xml.ws.WebServiceException; +import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import at.gv.egiz.pdfas.api.ws.PDFASSignParameters; +import at.gv.egiz.pdfas.api.ws.PDFASVerificationResponse; +import at.gv.egiz.pdfas.api.ws.VerificationLevel; import at.gv.egiz.pdfas.api.ws.PDFASSignParameters.Connector; +import at.gv.egiz.pdfas.api.ws.PDFASSignResponse; import at.gv.egiz.pdfas.common.exceptions.PdfAsException; import at.gv.egiz.pdfas.lib.api.ByteArrayDataSink; import at.gv.egiz.pdfas.lib.api.ByteArrayDataSource; @@ -65,6 +70,7 @@ import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner; import at.gv.egiz.pdfas.lib.api.sign.SignParameter; import at.gv.egiz.pdfas.lib.api.sign.SignResult; import at.gv.egiz.pdfas.lib.api.verify.VerifyParameter; +import at.gv.egiz.pdfas.lib.api.verify.VerifyParameter.SignatureVerificationLevel; import at.gv.egiz.pdfas.lib.api.verify.VerifyResult; import at.gv.egiz.pdfas.sigs.pades.PAdESSigner; import at.gv.egiz.pdfas.sigs.pades.PAdESSignerKeystore; @@ -99,6 +105,9 @@ public class PdfAsHelper { private static final String PDF_USERENTRY_PAGE = "/userentry"; private static final String PDF_ERR_URL = "PDF_ERR_URL"; private static final String PDF_FILE_NAME = "PDF_FILE_NAME"; + private static final String PDF_SIGNER_CERT = "PDF_SIGNER_CERT"; + private static final String PDF_VER_LEVEL = "PDF_VER_LEVEL"; + private static final String PDF_VER_RESP = "PDF_VER_RESP"; private static final String PDF_INVOKE_URL = "PDF_INVOKE_URL"; private static final String REQUEST_FROM_DU = "REQ_DATA_URL"; private static final String SIGNATURE_DATA_HASH = "SIGNATURE_DATA_HASH"; @@ -158,7 +167,8 @@ public class PdfAsHelper { String posR = PdfAsParameterExtractor.getSigPosR(request); String posF = PdfAsParameterExtractor.getSigPosF(request); - if (posP == null && posW == null && posX == null && posY == null && posR == null && posF == null) { + if (posP == null && posW == null && posX == null && posY == null + && posR == null && posF == null) { return null; } @@ -217,7 +227,7 @@ public class PdfAsHelper { } else { sb.append("p:auto;"); } - + if (posR != null) { try { Float.parseFloat(posR); @@ -230,7 +240,7 @@ public class PdfAsHelper { } else { sb.append("r:0;"); } - + if (posF != null) { try { Float.parseFloat(posF); @@ -279,6 +289,27 @@ public class PdfAsHelper { return results; } + public static List synchornousVerify(byte[] pdfData, + int signIdx, SignatureVerificationLevel lvl) throws Exception { + logger.debug("Verifing Signature index: " + signIdx); + + Configuration config = pdfAs.getConfiguration(); + + ByteArrayDataSource dataSource = new ByteArrayDataSource(pdfData); + + VerifyParameter verifyParameter = PdfAsFactory.createVerifyParameter( + config, dataSource); + + verifyParameter.setSignatureVerificationLevel(lvl); + verifyParameter.setDataSource(dataSource); + verifyParameter.setConfiguration(config); + verifyParameter.setWhichSignature(signIdx); + + List results = pdfAs.verify(verifyParameter); + + return results; + } + /** * Create synchronous PDF Signature * @@ -311,7 +342,7 @@ public class PdfAsHelper { IPlainSigner signer; if (connector.equals("moa")) { signer = new PAdESSigner(new MOAConnector(config)); - } else if(connector.equals("jks")) { + } else if (connector.equals("jks")) { signer = new PAdESSignerKeystore( WebConfiguration.getKeystoreFile(), WebConfiguration.getKeystoreAlias(), @@ -351,7 +382,8 @@ public class PdfAsHelper { * @return The signed pdf data * @throws Exception */ - public static byte[] synchornousServerSignature(byte[] pdfData, PDFASSignParameters params) throws Exception { + public static PDFASSignResponse synchornousServerSignature(byte[] pdfData, + PDFASSignParameters params) throws Exception { Configuration config = pdfAs.getConfiguration(); // Generate Sign Parameter @@ -359,15 +391,15 @@ public class PdfAsHelper { new ByteArrayDataSource(pdfData)); // Get Connector - + IPlainSigner signer; if (params.getConnector().equals(Connector.MOA)) { - if(!WebConfiguration.getMOASSEnabled()) { + if (!WebConfiguration.getMOASSEnabled()) { throw new PdfAsWebException("MOA connector disabled."); } signer = new PAdESSigner(new MOAConnector(config)); - } else if(params.getConnector().equals(Connector.JKS)) { - if(!WebConfiguration.getKeystoreEnabled()) { + } else if (params.getConnector().equals(Connector.JKS)) { + if (!WebConfiguration.getKeystoreEnabled()) { throw new PdfAsWebException("JKS connector disabled."); } signer = new PAdESSignerKeystore( @@ -391,15 +423,20 @@ public class PdfAsHelper { // set Signature Position signParameter.setSignaturePosition(params.getPosition()); - pdfAs.sign(signParameter); + SignResult signResult = pdfAs.sign(signParameter); - return output.getData(); + PDFASSignResponse signResponse = new PDFASSignResponse(); + signResponse.setSignedPDF(output.getData()); + signResponse.setSignerCertificate(signResult.getSignerCertificate() + .getEncoded()); + + return signResponse; } - + public static void startSignature(HttpServletRequest request, - HttpServletResponse response, ServletContext context, byte[] pdfData, - String connector, String position, String transactionId, String profile) - throws Exception { + HttpServletResponse response, ServletContext context, + byte[] pdfData, String connector, String position, + String transactionId, String profile) throws Exception { // TODO: Protect session so that only one PDF can be signed during one // session @@ -424,14 +461,14 @@ public class PdfAsHelper { new ByteArrayDataSource(pdfData)); logger.info("Setting TransactionID: " + transactionId); - + signParameter.setTransactionId(transactionId); - + IPlainSigner signer; if (connector.equals("bku") || connector.equals("onlinebku") || connector.equals("mobilebku")) { BKUSLConnector conn = new BKUSLConnector(config); - //conn.setBase64(true); + // conn.setBase64(true); signer = new PAdESSigner(conn); session.setAttribute(PDF_SL_CONNECTOR, conn); } else { @@ -477,25 +514,27 @@ public class PdfAsHelper { return data; } - public static byte[] generateVisualBlock(String profile, int resolution) throws IOException, CertificateException, PdfAsException { - X509Certificate cert = new X509Certificate(PdfAsHelper.class.getResourceAsStream("/qualified.cer")); + public static byte[] generateVisualBlock(String profile, int resolution) + throws IOException, CertificateException, PdfAsException { + X509Certificate cert = new X509Certificate( + PdfAsHelper.class.getResourceAsStream("/qualified.cer")); Configuration config = pdfAs.getConfiguration(); - SignParameter parameter = PdfAsFactory.createSignParameter( - config, null); + SignParameter parameter = PdfAsFactory + .createSignParameter(config, null); parameter.setSignatureProfileId(profile); - Image img = pdfAs.generateVisibleSignaturePreview(parameter, - cert, resolution); - - if(img == null) { + Image img = pdfAs.generateVisibleSignaturePreview(parameter, cert, + resolution); + + if (img == null) { return null; } - + ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write((RenderedImage) img, "png", baos); baos.close(); return baos.toByteArray(); } - + public static void injectCertificate(HttpServletRequest request, HttpServletResponse response, InfoboxReadResponseType infoboxReadResponseType, @@ -554,8 +593,8 @@ public class PdfAsHelper { HttpSession session = request.getSession(); StatusRequest statusRequest = (StatusRequest) session .getAttribute(PDF_STATUS); -// IPlainSigner plainSigner = (IPlainSigner) session -// .getAttribute(PDF_SIGNER); + // IPlainSigner plainSigner = (IPlainSigner) session + // .getAttribute(PDF_SIGNER); String connector = (String) session.getAttribute(PDF_SL_INTERACTIVE); @@ -568,7 +607,8 @@ public class PdfAsHelper { logger.debug("Needing Certificate from BKU"); // build SL Request to read certificate InfoboxReadRequestType readCertificateRequest = bkuSLConnector - .createInfoboxReadRequest(statusRequest.getSignParameter()); + .createInfoboxReadRequest(statusRequest + .getSignParameter()); JAXBElement readRequest = of .createInfoboxReadRequest(readCertificateRequest); @@ -583,32 +623,39 @@ public class PdfAsHelper { StringEscapeUtils.escapeHtml4(slRequest)); template = template.replace("##DataURL##", url); template = template.replace("##LOCALE##", locale); - - if(statusRequest.getSignParameter().getTransactionId() != null) { - template = template.replace("##ADDITIONAL##", ""); + + if (statusRequest.getSignParameter().getTransactionId() != null) { + template = template.replace( + "##ADDITIONAL##", + ""); } else { template = template.replace("##ADDITIONAL##", ""); } - + response.getWriter().write(template); - //TODO: set content type of response!! + // TODO: set content type of response!! response.setContentType("text/html"); response.getWriter().close(); } else if (statusRequest.needSignature()) { logger.debug("Needing Signature from BKU"); // build SL Request for cms signature - RequestPackage pack = bkuSLConnector - .createCMSRequest(statusRequest.getSignatureData(), - statusRequest.getSignatureDataByteRange(), - statusRequest.getSignParameter()); + RequestPackage pack = bkuSLConnector.createCMSRequest( + statusRequest.getSignatureData(), + statusRequest.getSignatureDataByteRange(), + statusRequest.getSignParameter()); String slRequest = SLMarschaller .marshalToString(of - .createCreateCMSSignatureRequest(pack.getRequestType())); + .createCreateCMSSignatureRequest(pack + .getRequestType())); logger.trace("SL Request: " + slRequest); - + response.setContentType("text/xml"); response.getWriter().write(slRequest); response.getWriter().close(); @@ -621,9 +668,32 @@ public class PdfAsHelper { DataSink output = result.getOutputDocument(); if (output instanceof ByteArrayDataSink) { ByteArrayDataSink byteDataSink = (ByteArrayDataSink) output; - PdfAsHelper.setSignedPdf(request, response, - byteDataSink.getData()); + byte[] signedPdf = byteDataSink.getData(); + + PDFASVerificationResponse verResponse = new PDFASVerificationResponse(); + List verResults = PdfAsHelper + .synchornousVerify(signedPdf, -2, + PdfAsHelper.getVerificationLevel(request)); + + if (verResults.size() != 1) { + throw new WebServiceException( + "Document verification failed!"); + } + VerifyResult verifyResult = verResults.get(0); + + verResponse.setCertificateCode(verifyResult + .getCertificateCheck().getCode()); + verResponse.setValueCode(verifyResult.getValueCheckCode() + .getCode()); + + PdfAsHelper.setPDFASVerificationResponse(request, verResponse); + PdfAsHelper.setSignedPdf(request, response, signedPdf); PdfAsHelper.gotoProvidePdf(context, request, response); + + String signerCert = Base64.encodeBase64String(result + .getSignerCertificate().getEncoded()); + + PdfAsHelper.setSignerCertificate(request, signerCert); } else { throw new PdfAsWebException("No Signature data available"); } @@ -689,7 +759,7 @@ public class PdfAsHelper { HttpSession session = request.getSession(); session.setAttribute(PDF_SIGNED_DATA, signedData); } - + public static void setLocale(HttpServletRequest request, HttpServletResponse response, String locale) { HttpSession session = request.getSession(); @@ -832,21 +902,25 @@ public class PdfAsHelper { HttpServletResponse response) { return generateURL(request, response, PDF_PDFDATA_PAGE); } - + public static String generateUserEntryURL(String storeId) { String publicURL = WebConfiguration.getPublicURL(); - if(publicURL == null) { - logger.error("To use this functionality " + WebConfiguration.PUBLIC_URL + " has to be configured in the web configuration"); + if (publicURL == null) { + logger.error("To use this functionality " + + WebConfiguration.PUBLIC_URL + + " has to be configured in the web configuration"); return null; } - + String baseURL = publicURL + PDF_USERENTRY_PAGE; try { - return baseURL + "?" + UIEntryPointServlet.REQUEST_ID_PARAM + "=" + URLEncoder.encode(storeId, "UTF-8"); - } catch(UnsupportedEncodingException e) { + return baseURL + "?" + UIEntryPointServlet.REQUEST_ID_PARAM + "=" + + URLEncoder.encode(storeId, "UTF-8"); + } catch (UnsupportedEncodingException e) { logger.warn("Encoding not supported for URL encoding", e); } - return baseURL + "?" + UIEntryPointServlet.REQUEST_ID_PARAM + "=" + storeId; + return baseURL + "?" + UIEntryPointServlet.REQUEST_ID_PARAM + "=" + + storeId; } public static String generateBKUURL(String connector) { @@ -903,6 +977,53 @@ public class PdfAsHelper { return "document.pdf"; } + public static void setSignerCertificate(HttpServletRequest request, + String value) { + HttpSession session = request.getSession(); + session.setAttribute(PDF_SIGNER_CERT, value); + } + + public static String getSignerCertificate(HttpServletRequest request) { + HttpSession session = request.getSession(); + Object obj = session.getAttribute(PDF_SIGNER_CERT); + if (obj != null) { + return obj.toString(); + } + return null; + } + + public static void setVerificationLevel(HttpServletRequest request, + SignatureVerificationLevel lvl) { + HttpSession session = request.getSession(); + session.setAttribute(PDF_VER_LEVEL, lvl); + } + + public static SignatureVerificationLevel getVerificationLevel( + HttpServletRequest request) { + HttpSession session = request.getSession(); + Object obj = session.getAttribute(PDF_VER_LEVEL); + if (obj != null && obj instanceof SignatureVerificationLevel) { + return (SignatureVerificationLevel) obj; + } + return SignatureVerificationLevel.INTEGRITY_ONLY_VERIFICATION; + } + + public static void setPDFASVerificationResponse(HttpServletRequest request, + PDFASVerificationResponse resp) { + HttpSession session = request.getSession(); + session.setAttribute(PDF_VER_RESP, resp); + } + + public static PDFASVerificationResponse getPDFASVerificationResponse( + HttpServletRequest request) { + HttpSession session = request.getSession(); + Object obj = session.getAttribute(PDF_VER_RESP); + if (obj != null && obj instanceof PDFASVerificationResponse) { + return (PDFASVerificationResponse) obj; + } + return null; + } + public static void setVerificationResult(HttpServletRequest request, List value) { HttpSession session = request.getSession(); @@ -944,11 +1065,11 @@ public class PdfAsHelper { } return false; } - + public static String getVersion() { return PdfAsFactory.getVersion(); } - + public static String getSCMRevision() { return PdfAsFactory.getSCMRevision(); } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java index 745e5ca8..cef19a76 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/PDFData.java @@ -34,6 +34,7 @@ import javax.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import at.gv.egiz.pdfas.api.ws.PDFASVerificationResponse; import at.gv.egiz.pdfas.web.helper.PdfAsHelper; /** @@ -94,6 +95,16 @@ public class PDFData extends HttpServlet { } } response.setHeader("Content-Disposition", "inline;filename=" + PdfAsHelper.getPDFFileName(request)); + String pdfCert = PdfAsHelper.getSignerCertificate(request); + if(pdfCert != null) { + response.setHeader("Signer-Certificate", pdfCert); + } + + PDFASVerificationResponse resp = PdfAsHelper.getPDFASVerificationResponse(request); + if(resp != null) { + response.setHeader("CertificateCheckCode", String.valueOf(resp.getCertificateCode())); + response.setHeader("ValueCheckCode", String.valueOf(resp.getValueCode())); + } response.setContentType("application/pdf"); OutputStream os = response.getOutputStream(); os.write(signedData); diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java index 8eacd3cd..3ff6eb09 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/servlets/UIEntryPointServlet.java @@ -37,6 +37,7 @@ import org.slf4j.LoggerFactory; import at.gv.egiz.pdfas.api.ws.PDFASSignRequest; import at.gv.egiz.pdfas.api.ws.PDFASSignParameters.Connector; import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.lib.api.verify.VerifyParameter.SignatureVerificationLevel; import at.gv.egiz.pdfas.web.config.WebConfiguration; import at.gv.egiz.pdfas.web.exception.PdfAsStoreException; import at.gv.egiz.pdfas.web.exception.PdfAsWebException; @@ -92,6 +93,19 @@ public class UIEntryPointServlet extends HttpServlet { String errorUrl = pdfAsRequest.getParameters().getInvokeErrorURL(); PdfAsHelper.setErrorURL(req, resp, errorUrl); + SignatureVerificationLevel lvl = SignatureVerificationLevel.INTEGRITY_ONLY_VERIFICATION; + if(pdfAsRequest.getVerificationLevel() != null) { + switch (pdfAsRequest.getVerificationLevel()) { + case INTEGRITY_ONLY: + lvl = SignatureVerificationLevel.INTEGRITY_ONLY_VERIFICATION; + break; + default: + lvl = SignatureVerificationLevel.FULL_VERIFICATION; + break; + } + } + PdfAsHelper.setVerificationLevel(req, lvl); + if(pdfAsRequest.getInputData() == null) { throw new PdfAsException("No Signature data available"); } @@ -135,6 +149,7 @@ public class UIEntryPointServlet extends HttpServlet { } catch (Throwable e) { + logger.error("Failed to process Request: ", e); PdfAsHelper.setSessionException(req, resp, e.getMessage(), e); PdfAsHelper.gotoError(getServletContext(), req, resp); } diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/DBRequestStore.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/DBRequestStore.java new file mode 100644 index 00000000..6ca6d9a4 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/DBRequestStore.java @@ -0,0 +1,121 @@ +package at.gv.egiz.pdfas.web.store; + +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; + +import org.hibernate.Query; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.service.ServiceRegistry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.api.ws.PDFASSignRequest; +import at.gv.egiz.pdfas.web.config.WebConfiguration; +import at.gv.egiz.pdfas.web.store.db.Request; + +public class DBRequestStore implements IRequestStore { + + private static final Logger logger = LoggerFactory + .getLogger(DBRequestStore.class); + + private SessionFactory sessions; + private ServiceRegistry serviceRegistry; + + public DBRequestStore() { + Configuration cfg = new Configuration(); + cfg.addAnnotatedClass(Request.class); + cfg.setProperties(WebConfiguration.getHibernateProps()); + + serviceRegistry = new StandardServiceRegistryBuilder().applySettings( + cfg.getProperties()).build(); + + sessions = cfg.buildSessionFactory(serviceRegistry); + } + + private void cleanOldRequests() { + int seconds = WebConfiguration.getDBTimeout(); + Calendar calendar = Calendar.getInstance(); + calendar.add(Calendar.SECOND, (-1)* seconds); + Date date = calendar.getTime(); + SimpleDateFormat dt = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss"); + logger.info("Clearing Entries before: " + dt.format(date)); + Session session = null; + Transaction tx = null; + try { + session = sessions.openSession(); + tx = session.beginTransaction(); + Query query = session.createQuery("delete from Request as req" + + " where req.created < :date"); + query.setCalendar("date", calendar); + query.executeUpdate(); + } catch(Throwable e) { + logger.error("Failed to save Request", e); + tx.rollback(); + } finally { + if(session != null) { + session.close(); + } + } + } + + public String createNewStoreEntry(PDFASSignRequest request) { + // Clean Old Requests + this.cleanOldRequests(); + Session session = null; + Transaction tx = null; + try { + session = sessions.openSession(); + tx = session.beginTransaction(); + Request dbRequest = new Request(); + dbRequest.setSignRequest(request); + dbRequest.setCreated(Calendar.getInstance().getTime()); + session.save(dbRequest); + + tx.commit(); + return dbRequest.getId(); + } catch(Throwable e) { + logger.error("Failed to save Request", e); + tx.rollback(); + return null; + } finally { + if(session != null) { + session.close(); + } + } + } + + public PDFASSignRequest fetchStoreEntry(String id) { + // Clean Old Requests + this.cleanOldRequests(); + + Session session = null; + Transaction tx = null; + try { + session = sessions.openSession(); + tx = session.beginTransaction(); + Request dbRequest = (Request) session.get(Request.class, id); + + PDFASSignRequest request = dbRequest.getSignRequest(); + + session.delete(dbRequest); + + tx.commit(); + return request; + } catch(Throwable e) { + logger.error("Failed to fetch Request", e); + tx.rollback(); + return null; + } finally { + if(session != null) { + session.close(); + } + } + + } + +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/db/Request.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/db/Request.java new file mode 100644 index 00000000..d7377166 --- /dev/null +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/store/db/Request.java @@ -0,0 +1,52 @@ +package at.gv.egiz.pdfas.web.store.db; + +import java.util.Date; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.hibernate.annotations.GenericGenerator; + +import at.gv.egiz.pdfas.api.ws.PDFASSignRequest; + +@Entity +@Table(name = "requests") +public class Request { + + private String uuid; + private Date created; + private PDFASSignRequest signRequest; + + @Id + @GeneratedValue(generator = "uuid") + @GenericGenerator(name = "uuid", strategy = "uuid2") + @Column(name = "id", unique = true) + public String getId() { + return this.uuid; + } + + public void setId(String uuid) { + this.uuid = uuid; + } + + @Column(name = "created", nullable = false) + public Date getCreated() { + return this.created; + } + + public void setCreated(Date created) { + this.created = created; + } + + @Column(name = "signRequest", nullable = false, length = 52428800) + public PDFASSignRequest getSignRequest() { + return this.signRequest; + } + + public void setSignRequest(PDFASSignRequest signRequest) { + this.signRequest = signRequest; + } +} diff --git a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/ws/PDFASSigningImpl.java b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/ws/PDFASSigningImpl.java index 8ade0063..cca66f82 100644 --- a/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/ws/PDFASSigningImpl.java +++ b/pdf-as-web/src/main/java/at/gv/egiz/pdfas/web/ws/PDFASSigningImpl.java @@ -35,11 +35,14 @@ import org.slf4j.LoggerFactory; import at.gv.egiz.pdfas.api.ws.PDFASBulkSignRequest; import at.gv.egiz.pdfas.api.ws.PDFASBulkSignResponse; -import at.gv.egiz.pdfas.api.ws.PDFASSignParameters; +import at.gv.egiz.pdfas.api.ws.PDFASSignParameters.Connector; import at.gv.egiz.pdfas.api.ws.PDFASSignRequest; import at.gv.egiz.pdfas.api.ws.PDFASSignResponse; import at.gv.egiz.pdfas.api.ws.PDFASSigning; -import at.gv.egiz.pdfas.api.ws.PDFASSignParameters.Connector; +import at.gv.egiz.pdfas.api.ws.PDFASVerificationResponse; +import at.gv.egiz.pdfas.api.ws.VerificationLevel; +import at.gv.egiz.pdfas.lib.api.verify.VerifyParameter.SignatureVerificationLevel; +import at.gv.egiz.pdfas.lib.api.verify.VerifyResult; import at.gv.egiz.pdfas.web.config.WebConfiguration; import at.gv.egiz.pdfas.web.helper.PdfAsHelper; import at.gv.egiz.pdfas.web.store.RequestStore; @@ -51,7 +54,7 @@ public class PDFASSigningImpl implements PDFASSigning { private static final Logger logger = LoggerFactory .getLogger(PDFASSigningImpl.class); - public byte[] signPDFDokument(byte[] inputDocument, + /*public byte[] signPDFDokument(byte[] inputDocument, PDFASSignParameters parameters) { checkSoapSignEnabled(); try { @@ -65,7 +68,7 @@ public class PDFASSigningImpl implements PDFASSigning { throw new WebServiceException("Server Signature failed."); } } - } + }*/ public PDFASSignResponse signPDFDokument(PDFASSignRequest request) { checkSoapSignEnabled(); @@ -78,12 +81,42 @@ public class PDFASSigningImpl implements PDFASSigning { if(request.getParameters().getConnector().equals(Connector.MOA) || request.getParameters().getConnector().equals(Connector.JKS)) { // Plain server based signatures!! - response.setSignedPDF(signPDFDokument(request.getInputData(), - request.getParameters())); + response = PdfAsHelper.synchornousServerSignature(request.getInputData(), + request.getParameters()); + + PDFASVerificationResponse verResponse = new PDFASVerificationResponse(); + VerifyResult verifyResult = null; + if(request.getVerificationLevel().equals(VerificationLevel.FULL_CERT_PATH)) { + List verResults = PdfAsHelper.synchornousVerify(response.getSignedPDF(), -1, + SignatureVerificationLevel.FULL_VERIFICATION); + + if(verResults.size() != 1) { + throw new WebServiceException("Document verification failed!"); + } + verifyResult = verResults.get(0); + } else { + List verResults = PdfAsHelper.synchornousVerify(response.getSignedPDF(), -1, + SignatureVerificationLevel.INTEGRITY_ONLY_VERIFICATION); + + if(verResults.size() != 1) { + throw new WebServiceException("Document verification failed!"); + } + verifyResult = verResults.get(0); + } + + verResponse.setCertificateCode(verifyResult.getCertificateCheck().getCode()); + verResponse.setValueCode(verifyResult.getValueCheckCode().getCode()); + response.setVerificationResponse(verResponse); } else { // Signatures with user interaction!! String id = RequestStore.getInstance().createNewStoreEntry(request); + + if(id == null) { + throw new WebServiceException("Failed to store request"); + } + String userEntryURL = PdfAsHelper.generateUserEntryURL(id); + logger.debug("Generated request store: " + id); logger.debug("Generated UI URL: " + userEntryURL); -- cgit v1.2.3