/** * Copyright 2006 by Know-Center, Graz, Austria * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a * joint initiative of the Federal Chancellery Austria and Graz University of * Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. * * $Id: PdfAS.java,v 1.5 2006/10/31 08:04:50 wprinz Exp $ */ package at.knowcenter.wag.egov.egiz; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Properties; import java.util.Vector; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import at.gv.egiz.pdfas.api.analyze.NonTextObjectInfo; import at.gv.egiz.pdfas.api.commons.DynamicSignatureProfileImpl; import at.gv.egiz.pdfas.api.commons.SignatureInformation; import at.gv.egiz.pdfas.api.timestamp.TimeStamper; import at.gv.egiz.pdfas.api.verify.VerifyParameters; import at.gv.egiz.pdfas.api.xmldsig.ExtendedSignatureInformation; import at.gv.egiz.pdfas.api.xmldsig.XMLDsigData; import at.gv.egiz.pdfas.commandline.CommandlineConnectorChooser; import at.gv.egiz.pdfas.exceptions.ErrorCode; import at.gv.egiz.pdfas.exceptions.framework.CorrectorException; import at.gv.egiz.pdfas.framework.ConnectorParameters; import at.gv.egiz.pdfas.framework.input.PdfDataSource; import at.gv.egiz.pdfas.framework.input.correction.Corrector; import at.gv.egiz.pdfas.framework.input.correction.CorrectorFactory; import at.gv.egiz.pdfas.framework.output.DataSink; import at.gv.egiz.pdfas.framework.signator.SignatorInformation; import at.gv.egiz.pdfas.impl.api.commons.PdfDataSourceAdapter; import at.gv.egiz.pdfas.impl.input.ByteArrayPdfDataSourceImpl; import at.gv.egiz.pdfas.impl.input.helper.DataSourceHelper; import at.gv.egiz.pdfas.utils.PDFASUtils; import at.knowcenter.wag.egov.egiz.cfg.OverridePropertyHolder; import at.knowcenter.wag.egov.egiz.cfg.SettingsReader; import at.knowcenter.wag.egov.egiz.exceptions.ConnectorException; import at.knowcenter.wag.egov.egiz.exceptions.ConnectorFactoryException; import at.knowcenter.wag.egov.egiz.exceptions.NormalizeException; import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException; 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.exceptions.SignatureTypesException; import at.knowcenter.wag.egov.egiz.framework.FoundBlock; import at.knowcenter.wag.egov.egiz.framework.FoundKey; import at.knowcenter.wag.egov.egiz.pdf.AbsoluteTextSignature; import at.knowcenter.wag.egov.egiz.pdf.BinarySignatureHolder; import at.knowcenter.wag.egov.egiz.pdf.ObjectExtractor; import at.knowcenter.wag.egov.egiz.pdf.PDFSignatureCreation; import at.knowcenter.wag.egov.egiz.pdf.PDFSignatureObject; import at.knowcenter.wag.egov.egiz.pdf.PDFUtilities; import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction; import at.knowcenter.wag.egov.egiz.pdf.SignatureHolder; import at.knowcenter.wag.egov.egiz.pdf.TablePos; import at.knowcenter.wag.egov.egiz.pdf.TextualSignature; import at.knowcenter.wag.egov.egiz.pdf.TextualSignatureHolder; import at.knowcenter.wag.egov.egiz.sig.SignatureData; import at.knowcenter.wag.egov.egiz.sig.SignatureDataImpl; import at.knowcenter.wag.egov.egiz.sig.SignatureObject; import at.knowcenter.wag.egov.egiz.sig.SignatureResponse; import at.knowcenter.wag.egov.egiz.sig.SignatureTypeDefinition; import at.knowcenter.wag.egov.egiz.sig.SignatureTypes; import at.knowcenter.wag.egov.egiz.sig.connectors.Connector; import at.knowcenter.wag.egov.egiz.sig.connectors.ConnectorChooser; import at.knowcenter.wag.egov.egiz.sig.connectors.bku.SignSignatureObject; import at.knowcenter.wag.egov.egiz.sig.signatureobject.SignatureObjectHelper; import at.knowcenter.wag.egov.egiz.tools.Normalizer; import at.knowcenter.wag.exactparser.ParseDocument; import at.knowcenter.wag.exactparser.parsing.PDFUtils; import at.knowcenter.wag.exactparser.parsing.results.HeaderParseResult; import com.lowagie.text.Rectangle; import com.lowagie.text.pdf.PdfPTable; import com.lowagie.text.pdf.PdfReader; /** * This class contains the major methods used by both, the commandline and the * webapp, to perform signation and verification. * * @author wprinz * @author mruhmer */ public abstract class PdfAS { //23.11.2010 changed by exthex - added method: verifyExtendedSignatureHolders(List extended_signature_info, String connectorType, boolean returnHashInputData, Date verificationTime) /** * 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.2"; /** * The key of the strict mode setting. */ public static final String STRICT_MODE_KEY = "strict_mode"; /** * The left/right border. */ public static final float SIGNATURE_BORDER = 100f; /** * The top/bottom border. */ public static final float SIGNATURE_MARGIN = 20f; /** * The Mime Type of a PDF document. */ public static final String PDF_MIME_TYPE = "application/pdf"; public static final String CORRECT_DOCUMENT_IF_NECESSARY_KEY = "correct_document_if_necessary"; /** * The logger definition. */ private static final Log log = LogFactory.getLog(PdfAS.class); /** * Tells, if strict PDF checking the PDF version is enabled. * * @return Returns true, if incoming PDFs should be checked strictly. */ public static boolean isStrictPdfChecking() { try { SettingsReader settings = SettingsReader.getInstance(); String strict_mode = settings.getSetting(STRICT_MODE_KEY, "false"); if (strict_mode.equals("true")) { return true; } } catch (SettingsException e) { log.error(e.getMessage(), e); } return false; } /** * Checks the version of the given PDF to be 1.4 or lower. * *

* This scans the first 1000 bytes of the PDF for the pdf Header. *

* * @param pdfDataSource * The PDF. * @return Returns true, if the given PDF is strict 1.4, false otherwise. * @throws PDFDocumentException * Forwarded exception. */ public static boolean isPdf14(PdfDataSource pdfDataSource) throws PDFDocumentException { try { byte[] firstBytes = new byte[1000]; InputStream is = pdfDataSource.createInputStream(); is.read(firstBytes); is.close(); HeaderParseResult hpr = PDFUtils.parseHeader(firstBytes, 0); if (hpr.major <= 1 && hpr.minor <= 4) { return true; } return false; } catch (Exception e) { throw new PDFDocumentException(201, e); } } /** * Applies strict version mode on the PDF and throws an exception, if the pdf * is not 1.4. * *

* Furthermore (independently of strict mode) the PDF is checked for * parsability. *

* * @param pdfDataSource * The pdf to be checked against strict mode. * @throws PDFDocumentException * @throws SettingsException * @throws CorrectorException */ public static PdfDataSource applyStrictMode(PdfDataSource pdfDataSource) throws PDFDocumentException, SettingsException, CorrectorException { if (isStrictPdfChecking()) { if (!isPdf14(pdfDataSource)) { throw new PDFDocumentException(201, "StrictMode: The pdf version is not 1.4 or lower."); } } try { byte[] pdf = DataSourceHelper.convertDataSourceToByteArray(pdfDataSource); ParseDocument.parseDocument(pdf); } catch (Exception e) { log.debug("Error while parsing Document.", e); boolean tryToCorrect = SettingsReader.getInstance().getSetting(CORRECT_DOCUMENT_IF_NECESSARY_KEY, "false").equals("true"); if (!tryToCorrect) { throw new PDFDocumentException(201, e); } log.info("Correcting document..."); Corrector cor = CorrectorFactory.createCorrector(); PdfDataSource correctedDS = cor.correctDocument(pdfDataSource); log.info("Correction finished."); return correctedDS; } return pdfDataSource; } public static at.gv.egiz.pdfas.api.io.DataSource applyStrictMode (at.gv.egiz.pdfas.api.io.DataSource dataSource) throws PDFDocumentException, SettingsException, CorrectorException { if (dataSource.getMimeType().equals("application/pdf")) { PdfDataSource pdfDS = new PdfDataSourceAdapter(dataSource); PdfDataSource correctedDS = applyStrictMode(pdfDS); if (correctedDS != pdfDS) { return new at.gv.egiz.pdfas.impl.api.commons.DataSourceApiAdapter(correctedDS); } } return dataSource; } // TODO: unused method - remove // /** // * Verifies the given PDF document. // * // * @param pdf // * The PDF document. // * @param connector // * The connector. // * @return Returns the List of results. // * @throws PresentableException // * Forwarded exception. // */ // public static List verifyPdf(final byte[] pdf, final String connector) // throws PresentableException // { // VerificationFilter vf = new VerificationFilter(); // List signature_holders = vf.extractSignaturesFromPdf(pdf); // if (signature_holders.isEmpty()) // { // throw new PDFDocumentException(206); // } // // List results = verifySignatureHolders(signature_holders, connector); // // return results; // } // TODO: unused method - remove // /** // * Verifies the given text that is supposed to be extracted from a PDF // * document using text extraction mechanisms. // * // * @param text // * The text to be verified. // * @param connector // * The connecor. // * @return Returns the List of results. // * @throws PresentableException // * Forwarded exception. // */ // public static List verifyText(final String text, final String connector) // throws PresentableException // { // VerificationFilter vf = new VerificationFilter(); // List signature_holders = vf.extractSignaturesFromPlainText(text); // if (signature_holders.isEmpty()) // { // throw new PDFDocumentException(206); // } // // List results = verifySignatureHolders(signature_holders, connector); // // return results; // } /** * Extracts all signature blocks from the given raw text using textual mode. * * @param raw_text * The raw text. * @return Returns a List of all SignatureHolders extracted from the text. * @throws PDFDocumentException * F.e. * @throws SignatureException * F.e. * @throws SignatureTypesException * @throws NormalizeException */ public static List extractSignatureHoldersTextual(String raw_text, boolean old_style) throws PDFDocumentException, SignatureException, SignatureTypesException, NormalizeException { List signature_holders = new ArrayList(); String text = raw_text; for (;;) { TextualSignatureHolder holder = extractSignatureHolderTextual(text, old_style); if (holder == null) { break; } { log.debug("Found holder: " + holder.getSignatureObject().getSignationType()); } signature_holders.add(0, holder); text = holder.getSignedText(); } return signature_holders; } /** * Extracts the last signature holder from the given text. * * @param raw_text * @param old_style * @return Returns the found singature holder, or null, if none could be * found. * @throws SignatureException * @throws SignatureTypesException * @throws NormalizeException */ public static TextualSignatureHolder extractSignatureHolderTextual(String raw_text, boolean old_style) throws SignatureException, SignatureTypesException, NormalizeException { List signatureTypes_ = AbsoluteTextSignature.getSignatureTypesForTextAnalysis(); List found_blocks = new ArrayList(); for (int cur_type = 0; cur_type < signatureTypes_.size(); cur_type++) { SignatureTypeDefinition cur_std = (SignatureTypeDefinition) signatureTypes_.get(cur_type); List found_keys = findBlockInText(raw_text, cur_std, old_style); if (found_keys != null) { FoundBlock found_block = new FoundBlock(); found_block.found_keys = found_keys; found_block.end_index = raw_text.length(); found_block.std = cur_std; found_blocks.add(found_block); } } List last_most_blocks = sortOutEarlyBlocks(found_blocks); List minimum_blocks = sortOutLargeBlocks(last_most_blocks); if (minimum_blocks.size() > 1) { log.debug("There are still " + minimum_blocks.size() + " candidates:"); for (int i = 0; i < minimum_blocks.size(); i++) { FoundBlock fb = (FoundBlock) minimum_blocks.get(i); log.debug(" fb: " + fb.std.getType()); } log.debug("... checking for Semantic Equality."); } boolean semantic_equality = checkForSemanticEquality(minimum_blocks); if (minimum_blocks.size() > 1) { log.debug("... Semantic Equality = " + semantic_equality); } if (!semantic_equality) { throw new SignatureException(ErrorCode.NOT_SEMANTICALLY_EQUAL, "The found blocks are not semantically equal."); } if (!minimum_blocks.isEmpty()) { FoundBlock actual_block = (FoundBlock) minimum_blocks.get(0); String signed_text = raw_text.substring(0, actual_block.getFirstKey().start_index); SignatureObject signatureObject_ = new SignatureObject(); signatureObject_.setSigType(actual_block.std.getType()); signatureObject_.initByType(); int end_index = actual_block.end_index; for (int i = 0; i < actual_block.found_keys.size(); i++) { FoundKey cur_key = (FoundKey) actual_block.found_keys.get(i); int start_index = cur_key.getStartIndex() + cur_key.caption.length(); String value = raw_text.substring(start_index, end_index); signatureObject_.setSigValueCaption(cur_key.getKey(), value, cur_key.caption); end_index = cur_key.getStartIndex(); } // this normalization is required to get rid of possible trailing // newlines. String normalized_text = normalizeText(signed_text); TextualSignatureHolder holder = new TextualSignatureHolder(normalized_text, signatureObject_); return holder; } return null; } /** * Tries to find a block of the given type in the text. * * @param text * The text. * @param sig_type_def * The type of the block. * @param old_style * Tells, if the block is old style (SIG_KZ will be ignored), or if * it is a new block. * @return Returns a List of the found keys of the block, or null, if the * block could not be found. */ public static List findBlockInText(String text, SignatureTypeDefinition sig_type_def, boolean old_style) { Vector keys = sig_type_def.getRevertSortedKeys(); Vector captions = sig_type_def.getRevertSortedCaptions(); int last_index = text.length(); List found_keys = new ArrayList(); for (int key_idx = 0; key_idx < keys.size(); key_idx++) { String key = (String) keys.get(key_idx); // logger_.debug("Key="+key); if (old_style && key.equals(SignatureTypes.SIG_KZ)) { // If separating the old style way - skip The "Kennzeichnung" // key, because it wasn't present in old profiles. continue; } String caption = (String) captions.get(key_idx); // int found_index = text.lastIndexOf(caption); // we're searching for captions that start at the beginning of the line. int found_index = text.lastIndexOf("\n" + caption) + 1;// text.lastIndexOf("\n" // + caption) + 1; // // the +1 // text.lastIndexOf(caption) // + 1; // // compensates the // \n if (found_index == 0) { // try without /n found_index = text.lastIndexOf(caption); // fix #331 here ?? } log.debug("found key:" + caption + " at index:" + found_index); if (key.equals(SignatureTypes.SIG_ID)) { if (found_index < 0 || found_index >= last_index) { // not found, SIG_ID is not required continue; } FoundKey fk = new FoundKey(key, caption, found_index); found_keys.add(fk); } else { if (found_index < 0 || found_index >= last_index) { // one key is not found - the profile doesn't match. return null; } FoundKey fk = new FoundKey(key, caption, found_index); found_keys.add(fk); last_index = found_index; } } sortFoundKeysDescendingly(found_keys); boolean matched = checkThatOrderIsCorrectAndCorrectFoundKeys(found_keys, keys, old_style); // boolean found_required = checkFoundRequiredKeys(found_keys, old_style); // logger_.debug("KKKKKKKKKKmatched="+matched); if (matched) { return found_keys; } return null; } /** * Sorts the FoundKeys List descendingly according to the start indices of the * found keys (the first found key in the list will have the highest start * index, the second one the second highest and so forth). * * @param found_keys * The List of FoundKey objects to be sorted. */ public static void sortFoundKeysDescendingly(List found_keys) { // sort the found_keys according to their start pos reversely. Collections.sort(found_keys, new Comparator() { public int compare(Object arg0, Object arg1) { FoundKey fk0 = (FoundKey) arg0; FoundKey fk1 = (FoundKey) arg1; // sort reversely! return fk1.start_index - fk0.start_index; } }); } /** * Sorts the FoundKeys List ascendingly according to the start indices of the * found keys (the first found key in the list will have the lowest start * index, the second one the second lowest and so forth). * * @param found_keys * The List of FoundKey objects to be sorted. */ public static void sortFoundKeysAscendingly(List found_keys) { // sort the found_keys according to their start pos. Collections.sort(found_keys, new Comparator() { public int compare(Object arg0, Object arg1) { FoundKey fk0 = (FoundKey) arg0; FoundKey fk1 = (FoundKey) arg1; return fk0.start_index - fk1.start_index; } }); } /** * Checks that the found keys are in correct order regarding SIG_ID as * optional key. * *

* If the SIG_ID key is misplaced, it will be removed from the found keys * list. *

* * @param found_keys * The found keys ordered descendingly to their start position * @param profile_keys * The profile keys. * @param old_style * Tells, if SIG_KZ should be ignored, or not. * @return Returns true, if the keys are correct. */ public static boolean checkThatOrderIsCorrectAndCorrectFoundKeys(List found_keys, List profile_keys, boolean old_style) { int found_index = 0; for (int profile_index = 0; profile_index < profile_keys.size(); profile_index++) { String key = (String) profile_keys.get(profile_index); if (old_style && key.equals(SignatureTypes.SIG_KZ)) { continue; } FoundKey found_key = (FoundKey) found_keys.get(found_index); boolean match = key.equals(found_key.getKey()); if (match) { found_index++; continue; } if (key.equals(SignatureTypes.SIG_ID)) { continue; } // doesn't match return false; } // remove all fields above the found_index - they are not correctly matched // indices (should be only the ID int size = found_keys.size(); for (int i = found_index; i < size; i++) { // this removes all (size - found_index) objects above found_index found_keys.remove(found_index); } return true; } /** * Sorts out early blocks and leaves only those at the bottom of the text. * * @param found_blocks * The found blocks. * @return Returns a list of the last blocks. */ public static List sortOutEarlyBlocks(List found_blocks) { int last_most_index = Integer.MIN_VALUE; List last_most_blocks = new ArrayList(); for (int block_index = 0; block_index < found_blocks.size(); block_index++) { FoundBlock block = (FoundBlock) found_blocks.get(block_index); int this_last_index = block.getLastKey().start_index; if (this_last_index < last_most_index) { // this block cannot be the last most block. continue; } if (this_last_index == last_most_index) { last_most_blocks.add(block); continue; } if (this_last_index > last_most_index) { last_most_blocks = new ArrayList(); last_most_blocks.add(block); last_most_index = this_last_index; } } return last_most_blocks; } /** * Sorts out large blocks. * * @param found_blocks * The found blocks. * @return Returns a list of the smallest blocks. */ public static List sortOutLargeBlocks(List found_blocks) { int last_min_size = Integer.MAX_VALUE; List min_size_blocks = new ArrayList(); for (int block_index = 0; block_index < found_blocks.size(); block_index++) { FoundBlock block = (FoundBlock) found_blocks.get(block_index); int size = block.getSize(); if (size > last_min_size) { // this block is larger continue; } if (size == last_min_size) { min_size_blocks.add(block); continue; } if (size < last_min_size) { min_size_blocks = new ArrayList(); min_size_blocks.add(block); last_min_size = size; } } return min_size_blocks; } /** * Checks the list of blocks for semantic equality. * * @param found_blocks * The list of found blocks. * @return Returns true if all blocks are semantically equal. */ public static boolean checkForSemanticEquality(List found_blocks) { if (found_blocks.size() <= 1) { return true; } for (int block_index = 0; block_index < found_blocks.size() - 1; block_index++) { FoundBlock first_block = (FoundBlock) found_blocks.get(block_index); FoundBlock second_block = (FoundBlock) found_blocks.get(block_index + 1); if (!first_block.isSemanticallyEqual(second_block)) { return false; } } return true; } public static int getIndexOfFoundKey(List found_keys, String key) { for (int i = 0; i < found_keys.size(); i++) { FoundKey fk = (FoundKey) found_keys.get(i); if (fk.getKey().equals(key)) { return i; } } return -1; } public static boolean containsFoundKey(List found_keys, String key) { return getIndexOfFoundKey(found_keys, key) >= 0; } /** * Checks the found keys for the required keys regarding the old style. * * @param found_keys * The found keys. * @param old_style * Flag that tells, if KZ is not required. * @return Returns true, if all required keys were found. */ public static boolean checkFoundRequiredKeys(List found_keys, boolean old_style) { if (!containsFoundKey(found_keys, SignatureTypes.SIG_DATE)) { return false; } if (!containsFoundKey(found_keys, SignatureTypes.SIG_ISSUER)) { return false; } if (!containsFoundKey(found_keys, SignatureTypes.SIG_NUMBER)) { return false; } if (!containsFoundKey(found_keys, SignatureTypes.SIG_VALUE)) { return false; } if (!old_style && !containsFoundKey(found_keys, SignatureTypes.SIG_KZ)) { return false; } return true; } public static List verifySignatureHolders(List signature_holders, String connectorType, boolean returnHashInputData, Date verificationTime) throws PDFDocumentException, NormalizeException, SignatureException, ConnectorException, ConnectorFactoryException { List results = new ArrayList(); for (int i = 0; i < signature_holders.size(); i++) { SignatureHolder holder = (SignatureHolder) signature_holders.get(i); // logger_.debug(); // logger_.debug(); // logger_.debug("Verifying Holder " + i + "..."); // logger_.debug("holder[" + i + "].signed_text = " + // holder.signed_text); // logger_.debug("holder[" + i + "].sig_obj = "); // logger_.debug("holder[" + i + "].type = " + // (holder.signature_object.isTextual() ? "textual" : "binary")); // logger_.debug(holder.signature_object.toString()); SignatureResponse result = verify(holder, connectorType, returnHashInputData, verificationTime, null); results.add(result); // logger_.debug(); // logger_.debug("check[" + i + "].cert = " + // result.getCertificateCheckInfo()); // logger_.debug("check[" + i + "].sig = " + // result.getSignatureCheckInfo().trim()); // logger_.debug("check[" + i + "].manifest = " + // result.getSignatureManifestCheckInfo()); // logger_.debug(); } return results; } /** * Verify a list of signatures * * @param extended_signature_info a list of {@link ExtendedSignatureInformation} to be verified * @param connectorType the connector to use for verification * @param returnHashInputData * @param verificationTime * @param verifySignatureAtIndex only verify the signature at the given index in the extended_signature_info list. A value < 0 means to verify all signatures in the list. * @return a list of {@link SignatureResponse}s * @throws PDFDocumentException * @throws NormalizeException * @throws SignatureException * @throws ConnectorException * @throws ConnectorFactoryException */ public static List verifyExtendedSignatureHolders(List extended_signature_info, String connectorType, boolean returnHashInputData, Date verificationTime, int verifySignatureAtIndex) throws PDFDocumentException, NormalizeException, SignatureException, ConnectorException, ConnectorFactoryException { List results = new ArrayList(); for (int i = 0; i < extended_signature_info.size(); i++) { if (verifySignatureAtIndex < 0 || verifySignatureAtIndex == i) { ExtendedSignatureInformation sigInfo = (ExtendedSignatureInformation) extended_signature_info.get(i); SignatureInformation si = sigInfo.getSignatureInformation(); SignatureHolder holder = (SignatureHolder) si.getInternalSignatureInformation(); SignatureResponse result = verify(holder, connectorType, returnHashInputData, verificationTime, sigInfo.getXmlDsigData()); results.add(result); } } return results; } /** * Verifies a SignatureHolder using the given connector. * * @param signature_holder * The SignatureHolder to be verified. * @param connector * The connector. * @return Returns the SignatureResponse object. * @throws NormalizeException * F.e. * @throws PDFDocumentException * F.e. * @throws SignatureException * F.e. * @throws ConnectorException * @throws ConnectorFactoryException */ public static SignatureResponse verify(SignatureHolder signature_holder, String connectorType, boolean returnHashInputData, Date verificationTime, XMLDsigData dsig) throws NormalizeException, PDFDocumentException, SignatureException, ConnectorException, ConnectorFactoryException { // String text_to_be_verified = signature_holder.getSignedText(); // logger_.debug("verify text_to_be_verified"+text_to_be_verified); SignatureObject so_to_be_verified = signature_holder.getSignatureObject(); // if (text_to_be_verified == null) // { // throw new SignatureException(311, "Document can not be verified because // the text to be verified is either null."); // } // if (text_to_be_verified.length() <= 0) // { // throw new SignatureException(311, "Document can not be verified because // the length of the text to be verified is 0. (length = " + // text_to_be_verified.length() + ")"); // } if (so_to_be_verified == null) { throw new SignatureException(312, "Document can not be verified because no signature object are set."); } // fixed by tknall if (so_to_be_verified.getX509Cert() == null) { throw new SignatureException(ErrorCode.CERTIFICATE_NOT_FOUND, "Document certificate is not defined."); } SignSignatureObject so = SignatureObjectHelper.convertSignatureObjectToSignSignatureObject(so_to_be_verified); SignatureData sd = convertSignatureHolderToSignatureData(signature_holder); String profile = so_to_be_verified.getSignatureTypeDefinition().getType(); // Connector c = ConnectorChooser.chooseCommandlineConnectorForVerify(connector, so_to_be_verified.getKZ(), so.id, profile); String connectorId = CommandlineConnectorChooser.chooseCommandlineConnectorForVerify(connectorType, so_to_be_verified.getKZ(), so.id, profile); ConnectorParameters cp = new ConnectorParameters(); cp.setProfileId(profile); cp.setReturnHashInputData(returnHashInputData); cp.setVerificationTime(verificationTime); Connector c = at.gv.egiz.pdfas.framework.ConnectorFactory.createConnector(connectorId, cp); return executeVerify(dsig, so, sd, c); } private static SignatureResponse executeVerify(XMLDsigData dsig, SignSignatureObject so, SignatureData sd, Connector c) throws ConnectorException { SignatureResponse res = null; try { return c.doVerify(sd, so, dsig); } catch (ConnectorException e) { if (VerifyParameters.isSuppressVerifyExceptions()) { res = new SignatureResponse(); res.setVerificationImpossibleEx(e); } else { throw e; } } return res; } public static SignatureResponse verifyWeb(SignatureHolder signature_holder, String connector, String loc_ref) throws NormalizeException, PDFDocumentException, SignatureException, ConnectorException { // String text_to_be_verified = signature_holder.getSignedText(); // logger_.debug("verify text_to_be_verified"+text_to_be_verified); SignatureObject so_to_be_verified = signature_holder.getSignatureObject(); // if (text_to_be_verified == null) // { // throw new SignatureException(311, "Document can not be verified because // the text to be verified is either null."); // } // if (text_to_be_verified.length() <= 0) // { // throw new SignatureException(311, "Document can not be verified because // the length of the text to be verified is 0. (length = " + // text_to_be_verified.length() + ")"); // } if (so_to_be_verified == null) { throw new SignatureException(312, "Document can not be verified because no signature object are set."); } // added by tknall if (so_to_be_verified.getX509Cert() == null) { throw new SignatureException(ErrorCode.CERTIFICATE_NOT_FOUND, "Document certificate is not defined."); } SignSignatureObject so = SignatureObjectHelper.convertSignatureObjectToSignSignatureObject(so_to_be_verified); SignatureData sd = convertSignatureHolderToSignatureData(signature_holder); String profile = so_to_be_verified.getSignatureTypeDefinition().getType(); Connector c = ConnectorChooser.chooseWebConnectorForVerify(connector, so_to_be_verified.getKZ(), so.id, profile, loc_ref); return executeVerify(null, so, sd, c); } public static SignatureData convertSignatureHolderToSignatureData(SignatureHolder signature_holder) { SignatureData sd = null; if (signature_holder instanceof BinarySignatureHolder) { BinarySignatureHolder bsh = (BinarySignatureHolder) signature_holder; sd = new SignatureDataImpl(bsh.getDataSource(), "application/pdf"); } else { TextualSignatureHolder tsh = (TextualSignatureHolder)signature_holder; sd = new SignatureDataImpl(tsh.getDataSource(), "text/plain", "UTF-8"); } return sd; } /** * Signs the given text with the provided connector using the given signature * type. * * @param data_to_sign * The data to be signed. * @param signature_type * The type of the signature. * @param connector * The connector. * @return Returns the corresponding SignatureObject. * @throws SignatureException * F.e. * @throws PDFDocumentException * F.e. * @throws ConnectorException */ public static SignSignatureObject sign(final SignatureData data_to_sign, final Connector connector, final TimeStamper timeStamper) throws SignatureException, PDFDocumentException, ConnectorException { // if (data_to_sign == null || data_to_sign.getData() == null) // { // throw new SignatureException(301, "Signature can not be produced. Data is null."); //$NON-NLS-1$ // } // if (data_to_sign.getData().length <= 0) // { // throw new SignatureException(301, "Signature can not be produced. Data is // empty. (length = " + data_to_sign.getData().length + ")"); //$NON-NLS-1$ // //$NON-NLS-2$ // } SignSignatureObject signed_signature_object = connector.doSign(data_to_sign); // call timestamper if available if (timeStamper != null) { log.debug("execute timestamping with stamper: " + timeStamper.getClass()); signed_signature_object.sigTimeStamp = timeStamper.applyTimeStamp(signed_signature_object.getSignatureValue()); } return signed_signature_object; } /** * Helper method that creates a SignatureObject and initializes it with the * given type. * * @param signature_type * The type. * @return Returns the created SignatureObject. * @throws SignatureException * f.e. * @throws SignatureTypesException * f.e. */ public static SignatureObject createSignatureObjectFromType(final String signature_type) throws SignatureException, SignatureTypesException { SignatureObject sig_obj = new SignatureObject(); sig_obj.setSigType(signature_type); sig_obj.initByType(); return sig_obj; } // /** // * @deprecated moved to Main.processSign use signCommandline instead // * @param pdfDataSource // * @param dataSink // * @param signatorId // * @param connectorType // * @param profile // * @param pos // * @throws PresentableException // */ // public static void sign(PdfDataSource pdfDataSource, DataSink dataSink, PdfASID signatorId, final String connectorType, final String profile, TablePos pos) throws PresentableException // { //// //Signator signator = SignatorFactory.createSignator(algorithm); //// at.gv.egiz.pdfas.framework.signator.Signator signator = at.gv.egiz.pdfas.framework.SignatorFactory.createSignator(algorithm); //// //// //IncrementalUpdateInformation iui = signator.prepareSign(pdfDataSource, signature_type, pos, ConnectorFactory.needsSIG_ID(connector)); //// SignatorInformation si = signator.prepareSign(pdfDataSource, signature_type, pos, ConnectorFactory.needsSIG_ID(connector)); // // // Connector c = ConnectorChooser.chooseCommandlineConnectorForSign(connector, signature_type); // //// SignSignatureObject sso = sign(si.getSignatureData(), c); //// //// si.setSignSignatureObject(sso); //// //// signator.finishSign(si, dataSink); // // String connectorId = CommandlineConnectorChooser.chooseCommandlineConnectorForSign(connectorType); // // signCommandline(pdfDataSource, dataSink, signatorId, connectorId, profile, pos); // } public static SignatorInformation signCommandline(PdfDataSource pdfDataSource, DataSink dataSink, PdfASID signatorId, String connectorId, final String profile, final String signatureKeyIdentifier, TablePos pos, TimeStamper timeStamper, Properties overrideProps) throws PresentableException { try { at.gv.egiz.pdfas.framework.signator.Signator signator = at.gv.egiz.pdfas.framework.SignatorFactory.createSignator(signatorId); if (overrideProps != null) { OverridePropertyHolder.setOverrideProps(overrideProps); } ConnectorParameters cp = new ConnectorParameters(); cp.setProfileId(profile); cp.setSignatureKeyIdentifier(signatureKeyIdentifier); Connector c = at.gv.egiz.pdfas.framework.ConnectorFactory.createConnector(connectorId, cp); // SignatorInformation si = signator.prepareSign(pdfDataSource, profile, pos, ConnectorFactory.needsSIG_ID(connector)); SignatorInformation si = signator.prepareSign(pdfDataSource, profile, pos, timeStamper); SignSignatureObject sso = sign(si.getSignatureData(), c, timeStamper); si.setSignSignatureObject(sso); signator.finishSign(si, dataSink); return si; } finally { OverridePropertyHolder.removeProperties(); DynamicSignatureProfileImpl.disposeLocalProfile(); } } public static SignatorInformation signCommandline(PdfDataSource pdfDataSource, DataSink dataSink, PdfASID signatorId, String connectorId, final String profile, TablePos pos) throws PresentableException { return signCommandline(pdfDataSource, dataSink, signatorId, connectorId, profile, null, pos, null, null); } /** * Extracts and normalizes the text from the pdf. * * @param pdf * The PDF document. * @return Returns the text String. * @throws PresentableException * F.e. */ // public static String extractNormalizedTextTextual(final byte[] pdf) throws PresentableException // { // // ByteArrayInputStream bais = new ByteArrayInputStream(pdf); // // String raw_document_text = TextualSignature.extractTextTextual(bais); // // // // String document_text = normalizeText(raw_document_text); // // return extractNormalizedTextTextual(pdf, pdf.length); // } /** * * @param pdfDs * @return List of {@link NonTextObjectInfo} */ public static List extractNonTextualObjects(PdfDataSource pdfDs) { return ObjectExtractor.extractNonTextInfo(pdfDs); } /** * @deprecated * Use version with explicit encoding {@link PdfAS#extractNormalizedTextTextual(PdfDataSource, String)}. * This one uses cp1252. * * @param pdfDataSource * @return * @throws PresentableException */ public static String extractNormalizedTextTextual(PdfDataSource pdfDataSource) throws PresentableException { return extractNormalizedTextTextual(pdfDataSource, "cp1252"); } public static String extractNormalizedTextTextual(PdfDataSource pdfDataSource, String encoding) throws PresentableException { String raw_document_text = TextualSignature.extractTextTextual(pdfDataSource, encoding); String document_text = normalizeText(raw_document_text); return document_text; } /** @deprecated */ public static String extractNormalizedTextTextual(byte [] pdf, int length) throws PresentableException { ByteArrayPdfDataSourceImpl pdfDataSource = new ByteArrayPdfDataSourceImpl(pdf, length); String raw_document_text = TextualSignature.extractTextTextual(pdfDataSource, "cp1252"); String document_text = normalizeText(raw_document_text); return document_text; } // public static String extractNormalizedTextTextual(InputStream pdfInputStream, int length) throws PresentableException // { // DelimitedInputStream dis = new DelimitedInputStream(pdfInputStream, length); // String raw_document_text = TextualSignature.extractTextTextual(dis); // String document_text = normalizeText(raw_document_text); // return document_text; // } // /** // * Extracts and normalizes the text from the pdf. // * // * @param pdf // * The PDF document. // * @param length // * The length of the PDF document. The decument is considered to be // * that long even if the byte array is longer. // * @return Returns the text String. // * @throws PresentableException // * F.e. // */ // public static String extractNormalizedTextTextual(final byte[] pdf, final int length) throws PresentableException // { // ByteArrayInputStream bais = new ByteArrayInputStream(pdf, 0, length); // String raw_document_text = TextualSignature.extractTextTextual(bais); // // logger_.info("extractNormalizedTextTextual // // raw_document_text="+raw_document_text); // String document_text = normalizeText(raw_document_text); // // logger_.info("extractNormalizedTextTextual document_text // // ="+document_text); // return document_text; // } /** * Normalizes the given text. * * @param text * The text to be normalized. * @return Returns the normalized text. * @throws NormalizeException * F.e. */ public static String normalizeText(final String text) throws NormalizeException { Normalizer normalizer = new Normalizer(); String normalized_text = normalizer.normalize(text, false); // fix #331 ?? //normalized_text = normalized_text.replaceAll("\n", ""); return normalized_text; } /** * Creates the iText PDFPTable from a given SignatureObject. * * @param signature_object * The SignatureObject. * @return Returns the created PDFPTable. * @throws PDFDocumentException * F.e. * @throws SignatureException * F.e. */ public static PdfPTable createPdfPTableFromSignatureObject(final SignatureObject signature_object) throws PDFDocumentException, SignatureException { PDFSignatureCreation creation = new PDFSignatureCreation(signature_object); PDFSignatureObject pdf_sig_obj = creation.getPDFSignatureObject(); PdfPTable pdf_table = (PdfPTable) pdf_sig_obj.getSignatureObject(); return pdf_table; } /** * Evalutates absolute positioning and prepares the PositioningInstruction for * placing the table. * * @param pos * The absolute positioning parameter. If null it is sought in the * profile definition. * @param signature_type * The profile definition of the table to be written. * @param pdf * The pdf. * @param pdf_table * The pdf table to be written. * @return Returns the PositioningInformation. * @throws PDFDocumentException * F.e. * @throws SettingsException * F.e. */ public static PositioningInstruction determineTablePositioning(TablePos pos, String signature_type, PdfDataSource pdfDataSource, PdfPTable pdf_table) throws PDFDocumentException, SettingsException { if (pos == null) { String pos_string = SettingsReader.getInstance().getSetting(SignatureTypes.SIG_OBJ + signature_type + ".pos", null); if (pos_string != null) { pos = PdfAS.parsePositionFromPosString(pos_string); } } if (pos == null) { // The default algorithm. x,y,w =auto ,p=lastpage, f:ignored because // y:auto pos = new TablePos(); } // System.out.println("Tablepos="+pos); return PdfAS.adjustSignatureTableandCalculatePosition(pdfDataSource, pdf_table, pos); } /** * Sets the width of the table according to the layout of the document and * calculates the y position where the PDFPTable should be placed. * * @param pdf * The PDF document. * @param pdf_table * The PDFPTable to be placed. * @return Returns the position where the PDFPTable should be placed. * @throws PDFDocumentException * F.e. */ public static PositioningInstruction adjustSignatureTableandCalculatePosition(final PdfDataSource pdfDataSource, PdfPTable pdf_table, TablePos pos) throws PDFDocumentException { // first check pageinstruction in TablePos-object // new,auto,absolut PdfReader reader = readInPdfDocument(pdfDataSource); PDFASUtils.checkReaderPermissions(reader); // get pages of currentdocument int doc_pages = reader.getNumberOfPages(); int page = doc_pages; boolean make_new_page = pos.isNewPage(); if (!(pos.isNewPage() || pos.isPauto())) { // we should posit signaturtable on this page page = pos.getPage(); // System.out.println("XXXXPAGE="+page+" doc_pages="+doc_pages); if (page > doc_pages) { make_new_page = true; page = doc_pages; // throw new PDFDocumentException(227, "Page number is to big(=" + page+ // ") cannot be parsed."); } } // getPagedimensions Rectangle psize = reader.getPageSizeWithRotation(page); int page_rotation = reader.getPageRotation(page); // [tknall] for iText 1.x.x: // float page_width = psize.width(); // float page_height = psize.height(); // [tknall] for iText 2.x.x: // float page_width = psize.getWidth(); // float page_height = psize.getHeight(); float page_width = psize.getWidth(); float page_height = psize.getHeight(); // now we can calculate x-position float pre_pos_x = SIGNATURE_BORDER / 2; if (!pos.isXauto()) { // we do have absolute x pre_pos_x = pos.getPosX(); } // calculate width // center float pre_width = page_width - pre_pos_x - pre_pos_x; if (!pos.isWauto()) { // we do have absolute width pre_width = pos.getWidth(); if (pos.isXauto()) { // center x pre_pos_x = (page_width - pre_width) / 2; } } final float pos_x = pre_pos_x; final float width = pre_width; // Signatur table dimensions are complete pdf_table.setTotalWidth(width); pdf_table.setLockedWidth(true); final float table_height = pdf_table.getTotalHeight(); // now check pos_y float pos_y = pos.getPosY(); if (!pos.isYauto()) { // we do have y-position too --> all parameters but page ok if (make_new_page) { page++; } return new PositioningInstruction(make_new_page, page, pos_x, pos_y); } // pos_y is auto if (make_new_page) { // ignore footer in new page page++; pos_y = page_height - SIGNATURE_BORDER / 2; return new PositioningInstruction(make_new_page, page, pos_x, pos_y); } // up to here no checks have to be made if Tablesize and Pagesize are fit // Now we have to getfreespace in page and reguard footerline float footer_line = pos.getFooterLine(); float pre_page_length = PDFUtilities.calculatePageLength(pdfDataSource, page - 1, page_height - footer_line, page_rotation); if (pre_page_length == Float.NEGATIVE_INFINITY) { // we do have an empty page or nothing in area above footerline pre_page_length = page_height; // no text --> SIGNATURE_BORDER pos_y = page_height - SIGNATURE_BORDER / 2; if (pos_y - footer_line <= table_height) { make_new_page = true; if (!pos.isPauto()) { // we have to correct pagenumber page = reader.getNumberOfPages(); } page++; // no text --> SIGNATURE_BORDER pos_y = page_height - SIGNATURE_BORDER / 2; } return new PositioningInstruction(make_new_page, page, pos_x, pos_y); } final float page_length = pre_page_length; // we do have text take SIGNATURE_MARGIN pos_y = page_height - page_length - SIGNATURE_MARGIN; if (pos_y - footer_line <= table_height) { make_new_page = true; if (!pos.isPauto()) { // we have to correct pagenumber in case of absolute page and not enough // space page = reader.getNumberOfPages(); } page++; // no text --> SIGNATURE_BORDER pos_y = page_height - SIGNATURE_BORDER / 2; } return new PositioningInstruction(make_new_page, page, pos_x, pos_y); } // /** // * Sets the width of the table according to the layout of the document and // * calculates the y position where the PDFPTable should be placed. // * // * @deprecated // * @param pdf // * The PDF document. // * @param pdf_table // * The PDFPTable to be placed. // * @return Returns the position where the PDFPTable should be placed. // * @throws PDFDocumentException // * F.e. // */ // public static PositioningInstruction adjustTableAndCalculatePosition(final byte[] pdf, PdfPTable pdf_table) throws PDFDocumentException // { // boolean make_new_page = false; // // PdfReader reader = readInPdfDocument(pdf); // // int page = reader.getNumberOfPages(); // Rectangle psize = reader.getPageSizeWithRotation(page); // float page_width = psize.width(); // float page_height = psize.height(); // // final float width = page_width - SIGNATURE_BORDER; // pdf_table.setTotalWidth(width); // pdf_table.setLockedWidth(true); // // final float pos_x = SIGNATURE_BORDER / 2; // // final float table_height = pdf_table.getTotalHeight(); // final float page_length = PDFUtilities.calculateLastPageLength(pdf, page_height); // float pos_y = page_height - page_length - SIGNATURE_MARGIN; // // if (pos_y <= table_height) // { // make_new_page = true; // page++; // // pos_y = page_height - SIGNATURE_BORDER / 2; // } // // return new PositioningInstruction(make_new_page, page, pos_x, pos_y); // } // /** // * Sets the width of the table according to the layout of the document and // * calculates the y position where the PDFPTable should be placed. // * // *

// * This algorithm tries to position the table between the end of the text and // * the footer line. // *

// * // * @deprecated // * @param pdf // * The PDF document. // * @param pdf_table // * The PDFPTable to be placed. // * @return Returns the position where the PDFPTable should be placed. // * @throws PDFDocumentException // * F.e. // */ // public static PositioningInstruction adjustTableAndCalculatePositionRegardingFooter(final byte[] pdf, PdfPTable pdf_table, float footer_line) throws PDFDocumentException // { // boolean make_new_page = false; // // PdfReader reader = readInPdfDocument(pdf); // // int page = reader.getNumberOfPages(); // Rectangle psize = reader.getPageSizeWithRotation(page); // float page_width = psize.width(); // float page_height = psize.height(); // // final float width = page_width - SIGNATURE_BORDER; // pdf_table.setTotalWidth(width); // pdf_table.setLockedWidth(true); // // final float pos_x = SIGNATURE_BORDER / 2; // // final float table_height = pdf_table.getTotalHeight(); // // final float page_length = PDFUtilities.calculateLastPageLength(pdf, page_height - footer_line); // float pos_y = page_height - page_length - SIGNATURE_MARGIN; // // if (pos_y - footer_line <= table_height) // { // make_new_page = true; // page++; // // pos_y = page_height - SIGNATURE_BORDER / 2; // } // // return new PositioningInstruction(make_new_page, page, pos_x, pos_y); // } /** * Creates an iText Reader that parses the document. *

* This is a convenience function for wrapping the Reader's exceptions into * PDFDocumentException. *

* * @param pdf * The PDF document. * @return Returns the created PdfReader. * @throws PDFDocumentException * F.e. */ public static PdfReader readInPdfDocument(final PdfDataSource pdfDataSource) throws PDFDocumentException { try { //InputStream is = pdfDataSource.createInputStream(); // PERF: byte array instead of stream byte [] pdf_data = pdfDataSource.getAsByteArray(); PdfReader reader = new PdfReader(pdf_data); //is.close(); return reader; } catch (IOException e) { throw new PDFDocumentException(201, e); } } /** * Parses the TablePos object from a given String with the appropriate format. * * @param pos_string * The pos string. e.g. x:40.0;y:auto;w:auto;p:1;f:300.0 * @return Returns the parsed TablePos object. * @throws PDFDocumentException * Thrown, if the String doesn't have the proper format. */ public static TablePos parsePositionFromPosString(String pos_string) throws PDFDocumentException { TablePos pos = new TablePos(pos_string); return pos; } }