/**
*
* The commandline uses the PDF-AS API. *
* * @author wprinz */ public abstract class Main { // 23.11.2010 changed by exthex - added parameters for placeholder handling /** * Command line parameter setting the application mode sign|verify */ protected static final String PARAMETER_MODE = "-mode"; /** * Command line parameter setting the application to connect */ protected static final String PARAMETER_CONNECTOR = "-connector"; /** * Command line parameter setting the signature mode. */ protected static final String PARAMETER_SIGNATURE_MODE = "-sigmode"; /** * Command line parameter setting the signature type. */ protected static final String PARAMETER_SIGNATURE_TYPE = "-sigtype"; /** * Command line parameter setting the username */ protected static final String PARAMETER_USER_NAME = "-username"; /** * Command line parameter setting the users password */ protected static final String PARAMETER_USER_PASSWORD = "-password"; /** * Command line parameter selecting the position of the signature. */ protected static final String PARAMETER_POS = "-pos"; /** * Command line parameter signaling to search the source document for a placeholder for the signature */ protected static final String PARAMETER_PLACEHOLDER_SEARCH = "-searchplaceholder"; /** * Command line parameter selecting the id of the placeholder to use */ protected static final String PARAMETER_PLACEHOLDER_ID = "-placeholder_id"; /** * Command line parameter selecting the match mode for the placeholder */ protected static final String PARAMETER_PLACEHOLDER_MATCH_MODE = "-placeholder_matchmode"; /** * Command line parameter selecting the signature which is going to be * verified. */ protected static final String PARAMETER_VERIFY_WHICH = "-verify_which"; /** * Command line parameter that starts the deployment of the default configuration to the current * user's home folder. * @see Constants#USERHOME_CONFIG_FOLDER */ protected static final String PARAMETER_DEPLOY_DEFAULT_CONFIGURATION = "-ddc"; /** * The application mode sign */ public static final String VALUE_MODE_SIGN = "sign"; /** * The application mode verify */ public static final String VALUE_MODE_VERIFY = "verify"; /** * The application mode sign */ public static final String VALUE_SIGNATURE_MODE_BINARY = "binary"; /** * The application mode verify */ public static final String VALUE_SIGNATURE_MODE_TEXTUAL = "textual"; /** * The application mode verify */ public static final String VALUE_SIGNATURE_MODE_DETACHED = "detached"; /** * The application mode verify */ public static final String VALUE_SIGNATURE_MODE_DETACHED_TEXT = "detachedtextual"; /** * The placeholder match mode STRICT */ public static final String VALUE_PLACEHOLDER_MATCH_MODE_STRICT = "strict"; /** * The placeholder match mode STRICT */ public static final String VALUE_PLACEHOLDER_MATCH_MODE_MODERATE = "moderate"; /** * The placeholder match mode STRICT */ public static final String VALUE_PLACEHOLDER_MATCH_MODE_LENIENT = "lenient"; /** * The log. */ private static final Log logger_ = LogFactory.getLog(Main.class); /** * Main program entry point. * * @param args * The commandline arguments. * @throws IOException */ public static void main(String[] args) throws IOException { // ConfigLogger.setLevel(Level.DEBUG); // search for PARAMETER_DEPLOY_DEFAULT_CONFIGURATION before initializing the SettingsReader for (int i = 0; i < args.length; i++) { if (args[i].trim().equals(PARAMETER_DEPLOY_DEFAULT_CONFIGURATION)) { try { String defaultConfigurationDeployedTo = ConfigUtils.deployDefaultConfiguration(); if (defaultConfigurationDeployedTo != null) { System.out.println("Default configuration successfully deployed to \"" + defaultConfigurationDeployedTo + "\"."); } else { System.out.println("Default configuration has NOT been deployed. Maybe the configuration already exists."); } if (args.length == 1) { // no other parameters supplied; exit application System.exit(0); } } catch (ConfigUtilsException e) { System.err.println("Deployment of default configuration failed: " + e.getMessage()); System.exit(1); } } } SettingsReader.initializeForCommandLine(); ConfigUtils.initializeLogger(); // printUsage(System.out); String mode = null; String signature_mode = null; String connector = null; String signature_type = null; String user_name = null; String user_password = null; String pos_string = null; boolean search_placeholder = true; String placeholderId = null; int placeholderMatchMode = Constants.PLACEHOLDER_MATCH_MODE_MODERATE; int verify_which = -1; String input = null; String output = null; try { // force settings preload SettingsReader.getInstance(); // for (int i = 0; i < args.length; i++) // { // logger_.debug("arg[" + i + "] = " + args[i]); // } for (int i = 0; i < args.length; i++) { String cur_arg = args[i].trim(); if (cur_arg.equals(PARAMETER_DEPLOY_DEFAULT_CONFIGURATION)) { // already applied continue; } if (cur_arg.equals(PARAMETER_MODE)) { i++; if (i >= args.length) { printNoValue(PARAMETER_MODE); return; } mode = args[i]; if (!checkMode(mode)) { printUnrecognizedValue(PARAMETER_MODE, mode); return; } continue; } if (cur_arg.equals(PARAMETER_CONNECTOR)) { i++; if (i >= args.length) { printNoValue(PARAMETER_CONNECTOR); return; } connector = args[i]; if (!checkConnector(connector)) { printUnrecognizedValue(PARAMETER_CONNECTOR, connector); return; } continue; } if (cur_arg.equals(PARAMETER_SIGNATURE_MODE)) { i++; if (i >= args.length) { printNoValue(PARAMETER_SIGNATURE_MODE); return; } signature_mode = args[i]; if (!checkSignatureMode(signature_mode)) { printUnrecognizedValue(PARAMETER_SIGNATURE_MODE, signature_mode); return; } continue; } if (cur_arg.equals(PARAMETER_SIGNATURE_TYPE)) { i++; if (i >= args.length) { printNoValue(PARAMETER_SIGNATURE_TYPE); return; } signature_type = args[i]; if (!checkSignatureType(signature_type)) { printUnrecognizedValue(PARAMETER_SIGNATURE_TYPE, signature_type); return; } continue; } if (cur_arg.equals(PARAMETER_PLACEHOLDER_SEARCH)) { i++; if (i >= args.length) { printNoValue(PARAMETER_PLACEHOLDER_SEARCH); return; } search_placeholder = Boolean.valueOf(args[i]).booleanValue(); continue; } if (cur_arg.equals(PARAMETER_PLACEHOLDER_ID)) { i++; if (i >= args.length) { printNoValue(PARAMETER_PLACEHOLDER_ID); return; } placeholderId = args[i]; continue; } if (cur_arg.equals(PARAMETER_PLACEHOLDER_MATCH_MODE)) { i++; if (i >= args.length) { printNoValue(PARAMETER_PLACEHOLDER_MATCH_MODE); return; } String matchMode = args[i]; if (matchMode.equals(VALUE_PLACEHOLDER_MATCH_MODE_LENIENT)) placeholderMatchMode = Constants.PLACEHOLDER_MATCH_MODE_LENIENT; else if (matchMode.equals(VALUE_PLACEHOLDER_MATCH_MODE_MODERATE)) placeholderMatchMode = Constants.PLACEHOLDER_MATCH_MODE_MODERATE; else if (matchMode.equals(VALUE_PLACEHOLDER_MATCH_MODE_STRICT)) placeholderMatchMode = Constants.PLACEHOLDER_MATCH_MODE_STRICT; else printUnrecognizedValue(PARAMETER_PLACEHOLDER_MATCH_MODE, args[i]); continue; } if (cur_arg.equals(PARAMETER_USER_NAME)) { i++; if (i >= args.length) { printNoValue(PARAMETER_USER_NAME); return; } user_name = args[i]; continue; } if (cur_arg.equals(PARAMETER_USER_PASSWORD)) { i++; if (i >= args.length) { printNoValue(PARAMETER_USER_PASSWORD); return; } user_password = args[i]; continue; } if (cur_arg.equals(PARAMETER_POS)) { i++; if (i >= args.length) { printNoValue(PARAMETER_POS); return; } pos_string = args[i]; continue; } if (cur_arg.equals(PARAMETER_VERIFY_WHICH)) { i++; if (i >= args.length) { printNoValue(PARAMETER_VERIFY_WHICH); return; } String str_verify_which = args[i]; try { verify_which = Integer.parseInt(str_verify_which); } catch (NumberFormatException e) { printUnrecognizedValue(PARAMETER_VERIFY_WHICH, str_verify_which); return; } continue; } if (cur_arg.charAt(0) == '-') { printUnrecognizedOption(cur_arg); return; } if (input == null) { input = cur_arg; continue; } if (output == null) { output = cur_arg; continue; } printUnrecognizedAdditionalCommandlineArgument(cur_arg); return; } if (mode == null) { printMissingParameter("a mode", PARAMETER_MODE); return; } if (connector == null) { printMissingParameter("a connector", PARAMETER_CONNECTOR); return; } if (mode.equals(VALUE_MODE_SIGN)) { if (signature_mode == null) { printMissingParameter("a signature mode", PARAMETER_SIGNATURE_MODE); return; } if (signature_type == null) { SettingsReader settings = SettingsReader.getInstance(); String default_type = settings.getValueFromKey(SignatureTypes.DEFAULT_TYPE); signature_type = default_type; } if (user_name == null) { user_name = ""; // printMissingParameter("a user name", PARAMETER_USER_NAME); // return; } if (user_password == null) { user_password = ""; // printMissingParameter("a user password", PARAMETER_USER_PASSWORD); // return; } } if (input == null) { printMissing("an input document"); return; } File file = new File(input); if (!file.exists()) { System.err.println("The input file '" + input + "' doesn't exist."); return; } if (mode.equals(VALUE_MODE_SIGN) && output == null) { output = generateOutputFileNameFromInput(input, signature_mode); } carryOutCommand(mode, signature_mode, connector, signature_type, user_name, user_password, verify_which, input, output, pos_string, search_placeholder, placeholderId, placeholderMatchMode); } catch (PdfAsException e) { printPresentableException(e); if (output != null) { logger_.debug("Deleting output file on error."); File oFile = new File(output); boolean deleted = oFile.delete(); if (!deleted) { logger_.error("Couldn't delete output file " + output); } } } finally { SettingsReader.clearTemporaryDirectory(); } } protected static void carryOutCommand(final String mode, final String signature_mode, final String connector, final String signature_type, final String user_name, final String user_password, final int verify_which, final String input, String output, final String pos_string, boolean search_placeholder, String placeholderId, int placeholderMatchMode) throws PdfAsException { // File file = new File(input); // // byte[] input_bytes = null; // try // { // FileInputStream fis = new FileInputStream(file); // input_bytes = new byte[(int) file.length()]; // fis.read(input_bytes); // fis.close(); // } // catch (IOException e) // { // throw new PDFDocumentException(201); // } PrintWriter messageOutput = new PrintWriter(System.out); if (mode.equals(VALUE_MODE_SIGN)) { carryOutSign(input, connector, signature_mode, signature_type, pos_string, user_name, user_password, output, messageOutput, search_placeholder, placeholderId, placeholderMatchMode); } else { carryOutVerify(input, connector, verify_which, messageOutput); } messageOutput.flush(); } 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, boolean search_placeholder, String placeholderId, int placeholderMatchMode) throws PdfAsException { messageOutput.println("Signing " + input + "..."); // for performance measurement long startTime = 0; long fileSize = 0; if (logger_.isInfoEnabled()) { startTime = System.currentTimeMillis(); } DataSource dataSource = null; try { File file = new File(input); dataSource = new FileBasedDataSource(file, "application/pdf"); if (logger_.isDebugEnabled()) { fileSize = file.length(); } } catch (IOException e) { throw new PDFDocumentException(201, e); } DataSink dataSink = null; File outputFile = null; try { outputFile = new File(output); dataSink = new FileBasedDataSink(outputFile); } catch (IOException e) { throw new PDFDocumentException(ErrorCode.CANNOT_WRITE_PDF, e); } try { processSign(dataSource, connector, signature_mode, signature_type, pos_string, search_placeholder, placeholderId, placeholderMatchMode, dataSink); } catch (Exception e) { // Exception caught in order to delete file based datasink if (outputFile != null && outputFile.exists()) { logger_.debug("Deleting output file on error."); boolean deleted = outputFile.delete(); if (!deleted) { logger_.error("Couldn't delete output file " + output); } } if (e instanceof PresentableException) { throw (PresentableException)e; } else { logger_.error(e); throw new PresentableException(ErrorCode.UNKNOWN_ERROR, e); } } // for performance measurement if (logger_.isInfoEnabled()) { long endTime = System.currentTimeMillis(); String toReport = "SIGN;" + signature_mode + ";" + input + ";" + fileSize + ";" + (endTime - startTime); logger_.info(toReport); } messageOutput.println("Signing was successful (" + output + ")."); } public static void carryOutVerify(String input, String connector, int verify_which, PrintWriter messageOutput) throws PdfAsException { messageOutput.println("Verifying " + input + "..."); // for performance measurement long startTime = 0; long fileSize = 0; if (logger_.isInfoEnabled()) { startTime = System.currentTimeMillis(); } DataSource dataSource = null; try { File file = new File(input); if (logger_.isDebugEnabled()) { fileSize = file.length(); } String extension = extractExtension(input); if (extension != null && extension.equals("txt")) { try { FileInputStream fis = new FileInputStream(file); byte[] input_bytes = new byte[(int) file.length()]; fis.read(input_bytes); fis.close(); String text = new String(input_bytes, "UTF-8"); dataSource = new StringTextBasedDataSource(text); } catch (UnsupportedEncodingException e) { throw new RuntimeException("Very strange: UTF-8 character encoding not supported.", e); } } else { dataSource = new FileBasedDataSource(file, "application/pdf"); } } catch (IOException e) { throw new PDFDocumentException(ErrorCode.DOCUMENT_CANNOT_BE_READ, e); } VerifyResults results = processVerify(dataSource, connector, verify_which); messageOutput.println("Verification results:"); formatVerifyResults(results, messageOutput); // for performance measurement if (logger_.isInfoEnabled()) { long endTime = System.currentTimeMillis(); String toReport = "VERIFY;" + input + ";" + fileSize + ";" + (endTime - startTime) + ";" + debugVerifyResults(results); logger_.info(toReport); } } /** * Extracts the extension from a file name string. * ** The extension of a file name is whatever text follows the last '.'. *
* * @param file_name * The file name. * @return Returns the extension. If the file name ends with the '.', then an * empty string is returned. If the file name doesn't contain any '.' * or file_name is null, null is returned. */ public static String extractExtension(String file_name) { if (file_name == null) { return null; } int dot_index = file_name.lastIndexOf('.'); if (dot_index < 0) { return null; } return file_name.substring(dot_index + 1); } public static void processSign(DataSource dataSource, String connector, String signature_mode, String signature_type, String pos_string, boolean search_placeholder, String placeholderId, int placeholderMatchMode, DataSink dataSink) throws PdfAsException { TablePos pos = null; if (pos_string != null) { try { pos = PdfAS.parsePositionFromPosString(pos_string); } catch (PDFDocumentException e) { printUnrecognizedValue(PARAMETER_POS, pos_string); return; } } SignaturePositioning posi = null; if (pos != null) { posi = new SignaturePositioning(); if (!pos.isXauto()) { posi.setXAlgorithm(new AbsoluteAxisAlgorithm(pos.getPosX())); } if (!pos.isYauto()) { posi.setYAlgorithm(new AbsoluteAxisAlgorithm(pos.getPosY())); } if (!pos.isWauto()) { posi.setWidthAlgorithm(new AbsoluteAxisAlgorithm(pos.getWidth())); } if (pos.isNewPage()) { posi.setPageAlgorithm(new NewPageAlgorithm()); } if (!pos.isPauto()) { posi.setPageAlgorithm(new AbsolutePageAlgorithm(pos.getPage())); } posi.setFooterLine(pos.getFooterLine()); } PdfAs pdfAs = PdfAsFactory.createPdfAs(new File(SettingsReader.RESOURCES_PATH)); SignParameters sp = new SignParameters(); sp.setDocument(dataSource); sp.setOutput(dataSink); sp.setSignatureType(signature_mode); // TODO detached signaturen! sp.setSignatureDevice(connector); sp.setSignatureProfileId(signature_type); sp.setSignaturePositioning(posi); sp.setCheckForPlaceholder(search_placeholder); sp.setPlaceholderId(placeholderId); sp.setPlaceholderMatchMode(placeholderMatchMode); pdfAs.sign(sp); } public static VerifyResults processVerify(DataSource dataSource, String connector, int verify_which) throws PdfAsException { String verifyMode = Constants.VERIFY_MODE_FULL_CONSERVATIVE; VerificationFilterParameters parameters = SettingsHelper.readVerificationFilterParametersFromSettings(); if (parameters.extractBinarySignaturesOnly()) { verifyMode = Constants.VERIFY_MODE_BINARY_ONLY; } else { if (parameters.assumeOnlySignatureUpdateBlocks()) { verifyMode = Constants.VERIFY_MODE_SEMI_CONSERVATIVE; } else { verifyMode = Constants.VERIFY_MODE_FULL_CONSERVATIVE; } } PdfAs pdfAs = PdfAsFactory.createPdfAs(new File(SettingsReader.RESOURCES_PATH)); VerifyParameters vp = new VerifyParameters(); vp.setDocument(dataSource); vp.setVerifyMode(verifyMode); vp.setSignatureDevice(connector); vp.setReturnHashInputData(false); vp.setVerificationTime(null); vp.setSignatureToVerify(verify_which); VerifyResults vrs = pdfAs.verify(vp); return vrs; } protected static String generateOutputFileNameFromInput(String input, String sig_mode) { String output = input + "_out"; if (sig_mode.startsWith("detached")) { output += ".xml"; } else { output += ".pdf"; } return output; } /** * Prints that the provided option was unrecognized. * * @param option * The unrecognized option. * @throws PresentableException * Forwarded exception. */ protected static void printUnrecognizedOption(final String option) throws PresentableException { System.err.println("Unrecognized option '" + option + "'."); printUsage(System.out); } /** * Prints that the provided value was unrecognized. * * @param parameter * The parameter, which is missing a value. * @throws PresentableException * Forwarded exception. */ protected static void printNoValue(final String parameter) throws PresentableException { System.err.println("The parameter " + parameter + " requires a value as next argument."); printUsage(System.out); } /** * Prints that the provided value was unrecognized. * * @param value * The unrecognized value. * @throws PresentableException * Forwarded exception. */ protected static void printUnrecognizedValue(final String parameter, final String value) throws PresentableException { System.err.println("The parameter " + parameter + " doesn't recognize the provided value '" + value + "'."); printUsage(System.out); } /** * Prints that the provided additional commandline argument was unrecognized. * * @param argument * The unrecognized argument. * @throws PresentableException * Forwarded exception. */ protected static void printUnrecognizedAdditionalCommandlineArgument(final String argument) throws PresentableException { System.err.println("Unrecognized additional commandline argument '" + argument + "'."); printUsage(System.out); } /** * Prints that a certain parameter was missing. * * @param missing_term * A description of the missing parameter ("e.g. a mode"). * @param parameter * The missing parameter itself (e.g. "-mode"). * @throws PresentableException * Forwarded exception. */ protected static void printMissingParameter(final String missing_term, final String parameter) throws PresentableException { printMissing(missing_term + " ('" + parameter + "' parameter)"); } /** * Prints that something is missing. * * @param missing_term * A descriptive message of the missing thing. * @throws PresentableException * Forwarded exception. */ protected static void printMissing(final String missing_term) throws PresentableException { System.err.println("Please specify " + missing_term + "."); printUsage(System.out); } /** * Prints out the ErrorCodeException in a descriptive form. * * @param ece * The ErrorCodeException to be printed. */ protected static void printPresentableException(final PdfAsException e) { String errorMessage = ErrorCodeHelper.formErrorMessage(e); System.err.println(errorMessage); // if (e.getErrorCode() == ErrorCode.PLACEHOLDER_EXCEPTION) // { // PlaceholderException phe = null; // if (e instanceof PlaceholderException) // { // phe = (PlaceholderException) e; // } // else // { // phe = (PlaceholderException) e.getCause(); // } // // System.err.println("Der Platzhalter des Feldes " + phe.getField() + " ist um " + phe.getMissing() + " Bytes zu kurz. "); // } // // System.err.println("Fehler " + e.getErrorCode() + ": " + ErrorCodeHelper.getMessageForErrorCode(e.getErrorCode())); // // if (e instanceof ExternalErrorException) // { // ExternalErrorException eee = (ExternalErrorException) e; // System.err.println("Externer Fehlergrund: " + eee.getExternalErrorCode() + ": " + eee.getExternalErrorMessage()); // } logger_.error(e); } /** * Prints the usage text. * * @param writer * The writer to print the text to. * @throws PresentableException * Forwarded exception. */ public static void printUsage(PrintStream writer) throws PresentableException { writer.println("Usage: pdf-as [[OPTIONS] [output file]|UTILOPTIONS]"); writer.println(" Required OPTIONS:"); writer.println(" " + PARAMETER_MODE + " <" + VALUE_MODE_SIGN + "|" + VALUE_MODE_VERIFY + ">"); writer.println(" " + VALUE_MODE_SIGN + " ... signs a document"); writer.println(" " + VALUE_MODE_VERIFY + " ... verifies a document"); writer.print(" " + PARAMETER_CONNECTOR + " "); ConnectorInformation[] ci = ConnectorFactory.getConnectorInformationArray(); // prepare list of connectors available for commandline ArrayList ciList = new ArrayList(); for (int i = 0; i < ci.length; i++) { String id = ci[i].getIdentifier(); if (ConnectorFactory.isAvailableForCommandline(id)) { ciList.add(ci[i]); } } // list available connectors wrapped in <...|...> Iterator ciIt = ciList.iterator(); writer.print("<"); while (ciIt.hasNext()) { writer.print(((ConnectorInformation) ciIt.next()).getIdentifier()); if (ciIt.hasNext()) { writer.print("|"); } } writer.print(">"); writer.println(); // for (int i = 0; i < ci.length; i++) // { // String id = ci[i].getIdentifier(); // if (!ConnectorFactory.isAvailableForCommandline(id)) // { // continue; // } // writer.print(id); // if (i < ci.length - 1) // { // writer.print("|"); // } // } ciIt = ciList.iterator(); while (ciIt.hasNext()) { ConnectorInformation cinf = (ConnectorInformation) ciIt.next(); writer.println(" " + cinf.getIdentifier() + " ... " + cinf.getDescription()); } // for (int i = 0; i < ci.length; i++) // { // String id = ci[i].getIdentifier(); // if (!ConnectorFactory.isAvailableForCommandline(id)) // { // continue; // } // writer.println(" " + id + " ... " + ci[i].getDescription()); // } writer.println(" UTILity OPTIONS"); writer.println(" " + PARAMETER_DEPLOY_DEFAULT_CONFIGURATION + " ... deploys the default configuration to the current user's home"); writer.println(" OPTIONS for signation:"); writer.println(" " + PARAMETER_SIGNATURE_MODE + " <" + VALUE_SIGNATURE_MODE_BINARY + "|" + VALUE_SIGNATURE_MODE_TEXTUAL + ">"); writer.println(" " + VALUE_SIGNATURE_MODE_BINARY + " ... signs the complete binary document"); writer.println(" " + VALUE_SIGNATURE_MODE_TEXTUAL + " ... signs only the textual portion of the document"); // @iaik: why is this commented out? //writer.println(" " + VALUE_SIGNATURE_MODE_DETACHED + " ... signs the document using the binary mode and returns the xml signature of it."); writer.println(" " + VALUE_SIGNATURE_MODE_DETACHED_TEXT + " ... signs the document using the textual mode and returns the xml signature of it."); writer.print(" " + PARAMETER_SIGNATURE_TYPE + " <"); SignatureTypes sig_types = SignatureTypes.getInstance(); SettingsReader settings = SettingsReader.getInstance(); Set types_array = sig_types.getSignatureTypes(); Iterator it = types_array.iterator(); while (it.hasNext()) { String type = (String) it.next(); writer.print(type); if (it.hasNext()) { writer.print("|"); } } writer.println(">"); writer.println(" ... [optional] the profile to be used. If omitted, the default"); writer.println(" profile is used."); String default_type = settings.getValueFromKey(SignatureTypes.DEFAULT_TYPE); it = types_array.iterator(); while (it.hasNext()) { String type = (String) it.next(); String descr_key = SignatureTypes.SIG_OBJ + type + "." + SignatureTypes.SIG_DESCR; String type_descr = settings.getValueFromKey(descr_key); writer.println(" " + type + " ... " + (type.equals(default_type) ? "(default) " : "") + type_descr); } writer.println(" " + PARAMETER_USER_NAME + "