aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/at/knowcenter/wag/egov/egiz/commandline
diff options
context:
space:
mode:
authornetconomy <netconomy@7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c>2007-08-17 06:10:56 +0000
committernetconomy <netconomy@7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c>2007-08-17 06:10:56 +0000
commit3d982813b34f6f230baf4a467cdc37ec92a77595 (patch)
tree85319d39cee2ded1bb7a2b2dd9e8ea37e3778248 /src/main/java/at/knowcenter/wag/egov/egiz/commandline
parent07f6c8f33b2d700276fe6ec6339ff836c8710131 (diff)
downloadpdf-as-3-3d982813b34f6f230baf4a467cdc37ec92a77595.tar.gz
pdf-as-3-3d982813b34f6f230baf4a467cdc37ec92a77595.tar.bz2
pdf-as-3-3d982813b34f6f230baf4a467cdc37ec92a77595.zip
Performance
git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/trunk@167 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c
Diffstat (limited to 'src/main/java/at/knowcenter/wag/egov/egiz/commandline')
-rw-r--r--src/main/java/at/knowcenter/wag/egov/egiz/commandline/Main.java421
1 files changed, 244 insertions, 177 deletions
diff --git a/src/main/java/at/knowcenter/wag/egov/egiz/commandline/Main.java b/src/main/java/at/knowcenter/wag/egov/egiz/commandline/Main.java
index 65c30b9..ee12fff 100644
--- a/src/main/java/at/knowcenter/wag/egov/egiz/commandline/Main.java
+++ b/src/main/java/at/knowcenter/wag/egov/egiz/commandline/Main.java
@@ -19,34 +19,44 @@ package at.knowcenter.wag.egov.egiz.commandline;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
+import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
-import org.apache.log4j.Logger;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.log4j.PropertyConfigurator;
+import at.gv.egiz.pdfas.commandline.CommandlineConnectorChooser;
+import at.gv.egiz.pdfas.exceptions.ErrorCode;
+import at.gv.egiz.pdfas.exceptions.ErrorCodeHelper;
+import at.gv.egiz.pdfas.exceptions.external.ExternalErrorException;
+import at.gv.egiz.pdfas.framework.config.SettingsHelper;
+import at.gv.egiz.pdfas.framework.input.DataSource;
+import at.gv.egiz.pdfas.framework.input.ExtractionStage;
+import at.gv.egiz.pdfas.framework.input.PdfDataSource;
+import at.gv.egiz.pdfas.framework.output.DataSink;
+import at.gv.egiz.pdfas.framework.vfilter.VerificationFilterParameters;
+import at.gv.egiz.pdfas.impl.input.FileBasedPdfDataSourceImpl;
+import at.gv.egiz.pdfas.impl.input.TextDataSourceImpl;
+import at.gv.egiz.pdfas.impl.output.FileBasedDataSink;
+import at.gv.egiz.pdfas.impl.vfilter.VerificationFilterParametersImpl;
import at.knowcenter.wag.egov.egiz.PdfAS;
import at.knowcenter.wag.egov.egiz.PdfASID;
-import at.knowcenter.wag.egov.egiz.cfg.ConfigLogger;
import at.knowcenter.wag.egov.egiz.cfg.SettingsReader;
import at.knowcenter.wag.egov.egiz.exceptions.ConnectorFactoryException;
-import at.knowcenter.wag.egov.egiz.exceptions.ErrorCodeException;
import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException;
import at.knowcenter.wag.egov.egiz.exceptions.PlaceholderException;
import at.knowcenter.wag.egov.egiz.exceptions.PresentableException;
import at.knowcenter.wag.egov.egiz.exceptions.SettingNotFoundException;
import at.knowcenter.wag.egov.egiz.exceptions.SignatureException;
import at.knowcenter.wag.egov.egiz.exceptions.SignatureTypesException;
-import at.knowcenter.wag.egov.egiz.framework.SignResult;
import at.knowcenter.wag.egov.egiz.framework.SignatorFactory;
-import at.knowcenter.wag.egov.egiz.framework.VerificationFilter;
import at.knowcenter.wag.egov.egiz.framework.signators.BinarySignator_1_0_0;
-import at.knowcenter.wag.egov.egiz.framework.signators.DetachedSignator_1_0_0;
import at.knowcenter.wag.egov.egiz.framework.signators.TextualSignator_1_0_0;
import at.knowcenter.wag.egov.egiz.pdf.SignatureHolder;
import at.knowcenter.wag.egov.egiz.pdf.TablePos;
@@ -132,12 +142,12 @@ public abstract class Main
/**
* The application mode verify
*/
- public static final String VALUE_SIGNATURE_MODE_DETACHED_TEXt = "detachedtextual";
+ public static final String VALUE_SIGNATURE_MODE_DETACHED_TEXT = "detachedtextual";
/**
- * The logger definition.
+ * The log.
*/
- private static final Logger logger_ = ConfigLogger.getLogger(Main.class);
+ private static final Log logger_ = LogFactory.getLog(Main.class);
/**
* Main program entry point.
@@ -152,9 +162,7 @@ public abstract class Main
SettingsReader.initializeForCommandLine();
PropertyConfigurator.configure(SettingsReader.CONFIG_PATH + "log4j.properties");
-
- try
- {
+
// printUsage(System.out);
String mode = null;
@@ -170,7 +178,10 @@ public abstract class Main
String input = null;
String output = null;
-
+
+ try
+ {
+
// for (int i = 0; i < args.length; i++)
// {
// logger_.debug("arg[" + i + "] = " + args[i]);
@@ -378,21 +389,31 @@ public abstract class Main
return;
}
+ if (mode.equals(VALUE_MODE_SIGN) && output == null)
+ {
+ output = generateOutputFileNameFromInput(input, signature_mode);
+ }
+ else
+ {
+ output = null;
+ }
+
carryOutCommand(mode, signature_mode, connector, signature_type, user_name, user_password, verify_which, input, output, pos_string);
}
catch (PresentableException e)
{
- if (e instanceof ErrorCodeException)
- {
- ErrorCodeException ece = (ErrorCodeException) e;
- printErrorCodeException(ece);
- }
- else
+ printPresentableException(e);
+
+ if (output != null)
{
- System.err.println("PresentableException:");
- System.err.println(e.getMessage());
- logger_.debug(e.toString());
+ 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
@@ -401,186 +422,226 @@ public abstract class Main
}
}
- /**
- * Carries out the actual command given via the commandline parameters.
- *
- * <p>
- * This is simply the procedure that is executed after the commandline
- * parameters have been parsed successfully.
- * </p>
- *
- * @param mode
- * The operation mode (e.g. "sign", "verify").
- * @param signature_mode
- * The signature mode (e.g. "binary", "textual").
- * @param connector
- * The connector (e.g. "bku", "a1").
- * @param signature_type
- * The signature type/profile. For signing only.
- * @param user_name
- * The user name. For signing only.
- * @param user_password
- * The user password. For signing only.
- * @param verify_which
- * The number of the signature to be verified. For verifying only.
- * @param input
- * The input file name.
- * @param output
- * The output file name.
- * @throws PresentableException
- */
- 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) throws PresentableException
+ 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) throws PresentableException
+ {
+ // 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);
+ }
+ else
+ {
+ carryOutVerify(input, connector, verify_which, messageOutput);
+ }
+ messageOutput.close();
+ }
+
+ public static void carryOutSign(String input, String connector, String signature_mode, String signature_type, String pos_string, String user_name, String user_password, String output,
+ PrintWriter messageOutput) throws PresentableException
{
- File file = new File(input);
+ messageOutput.println("Signing...");
- byte[] input_bytes = null;
+ PdfDataSource pdfDataSource;
try
{
- FileInputStream fis = new FileInputStream(file);
- input_bytes = new byte[(int) file.length()];
- fis.read(input_bytes);
- fis.close();
+ File file = new File(input);
+ pdfDataSource = new FileBasedPdfDataSourceImpl(file, (int)file.length());
}
catch (IOException e)
{
- throw new PDFDocumentException(201);
+ throw new PDFDocumentException(201, e);
}
- if (mode.equals(VALUE_MODE_SIGN))
+ FileBasedDataSink dataSink = null;
+ try
{
- System.out.println("Signing...");
+ File outputFile = new File(output);
+ dataSink = new FileBasedDataSink(outputFile);
+ }
+ catch (IOException e)
+ {
+ throw new PDFDocumentException(ErrorCode.CANNOT_WRITE_PDF, e);
+ }
- PdfAS.applyStrictMode(input_bytes);
+ processSign(pdfDataSource, connector, signature_mode, signature_type, pos_string, dataSink);
- TablePos pos = null;
- if (pos_string != null)
+ messageOutput.println("Signing was successful.");
+ }
+
+ public static void carryOutVerify(String input, String connector, int verify_which, PrintWriter messageOutput) throws PresentableException
+ {
+ messageOutput.println("Verifying...");
+
+ DataSource dataSource = null;
+ try
+ {
+ File file = new File(input);
+ String extension = VerifyServlet.extractExtension(input);
+ if (extension != null && extension.equals("txt"))
{
try
{
- pos = PdfAS.parsePositionFromPosString(pos_string);
+ 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 TextDataSourceImpl(text);
}
- catch (PDFDocumentException e)
+ catch (UnsupportedEncodingException e)
{
- printUnrecognizedValue(PARAMETER_POS, pos_string);
- return;
-
+ throw new RuntimeException("Very strange: UTF-8 character encoding not supported.", e);
}
}
-
- PdfASID algorithm = translateSignatureModeToPdfASID(signature_mode);
-
- // TODO MOA detached signing is not allowed at the commandline
- logger_.debug("Suggested sign algorithm = " + algorithm);
- if (connector.equals("moa"))
+ else
{
- // TODO: possible new implementation of moa where is commandline call supported
- // it should be checked in config file is it allowed to use MOA detached from command line.
- String msg = "Hinweis: Der MOA Detached Connector ist für die Kommandozeile nicht geeignet. Die Signatur wird daher im alten Base64 enveloping Format ausgeführt.";
- System.out.println(msg);
- logger_.warn(msg);
-
- if (signature_mode.equals(VALUE_SIGNATURE_MODE_BINARY))
- {
- algorithm = BinarySignator_1_0_0.MY_ID;
- }
- if (signature_mode.equals(VALUE_SIGNATURE_MODE_TEXTUAL))
- {
- algorithm = TextualSignator_1_0_0.MY_ID;
- }
+ dataSource = new FileBasedPdfDataSourceImpl(file, (int)file.length());
}
- logger_.debug("Finally used sign algorithm = " + algorithm);
+ }
+ catch (IOException e)
+ {
+ throw new PDFDocumentException(ErrorCode.DOCUMENT_CANNOT_BE_READ, e);
+ }
+
+ List results = processVerify(dataSource, connector, verify_which);
- SignResult sign_result = PdfAS.sign(algorithm, input_bytes, signature_type, connector, user_name, user_password, pos);
+ messageOutput.println("Verification results:");
+ formatVerifyResults(results, messageOutput);
+ }
+
+ public static void processSign(PdfDataSource pdfDataSource, String connector, String signature_mode, String signature_type, String pos_string, DataSink dataSink) throws PresentableException
+ {
+ PdfAS.applyStrictMode(pdfDataSource);
+ TablePos pos = null;
+ if (pos_string != null)
+ {
try
{
- if (output == null)
- {
- output = generateOutputFileNameFromInput(input, sign_result);
- }
-
- FileOutputStream fos = new FileOutputStream(output);
- fos.write(sign_result.getData());
- fos.close();
+ pos = PdfAS.parsePositionFromPosString(pos_string);
}
- catch (IOException e)
+ catch (PDFDocumentException e)
{
- throw new PDFDocumentException(205);
- }
+ printUnrecognizedValue(PARAMETER_POS, pos_string);
+ return;
- System.out.println("Signing was successful.");
+ }
}
- else
- {
- System.out.println("Verifying...");
-
- VerificationFilter vf = new VerificationFilter();
- List signature_holders = null;
+ PdfASID signatorId = translateSignatureModeToPdfASID(signature_mode);
- String extension = VerifyServlet.extractExtension(input);
- if (extension != null && extension.equals("txt"))
+ // TODO MOA detached signing is not allowed at the commandline
+ logger_.debug("Suggested sign algorithm = " + signatorId);
+ if (connector.equals("moa"))
+ {
+ // TODO: possible new implementation of moa where is commandline call
+ // supported
+ // it should be checked in config file is it allowed to use MOA detached
+ // from command line.
+ String msg = "Hinweis: Der MOA Detached Connector ist für die Kommandozeile nicht geeignet. Die Signatur wird daher im alten Base64 enveloping Format ausgeführt.";
+ System.out.println(msg);
+ logger_.warn(msg);
+
+ if (signature_mode.equals(VALUE_SIGNATURE_MODE_BINARY))
{
- try
- {
- String text = new String(input_bytes, "UTF-8");
- signature_holders = vf.extractSignaturesFromPlainText(text);
- }
- catch (UnsupportedEncodingException e)
- {
- throw new PresentableException(e);
- }
+ signatorId = BinarySignator_1_0_0.MY_ID;
}
- else
+ if (signature_mode.equals(VALUE_SIGNATURE_MODE_TEXTUAL))
{
- signature_holders = vf.extractSignaturesFromPdf(input_bytes);
+ signatorId = TextualSignator_1_0_0.MY_ID;
}
+ }
+ logger_.debug("Finally used sign algorithm = " + signatorId);
+
+ String connectorId = CommandlineConnectorChooser.chooseCommandlineConnectorForSign(connector);
- if (signature_holders.isEmpty())
- {
- throw new PDFDocumentException(206);
- }
+ PdfAS.signCommandline(pdfDataSource, dataSink, signatorId, connectorId, signature_type, pos);
- List holders_to_verify = signature_holders;
+// PdfAS.sign(algorithm, pdfDataSource, dataSink, signature_type, connector, pos);
+ }
+
+ public static List processVerify(DataSource dataSource, String connector, int verify_which) throws PresentableException
+ {
+ VerificationFilterParameters parameters = SettingsHelper.readVerificationFilterParametersFromSettings();
+ ExtractionStage es = new ExtractionStage();
- // verify_which - optional argument in command line/web
- if (verify_which >= 0)
- {
- if (verify_which >= signature_holders.size())
- {
- throw new SignatureException(312, "The selected signature to be verified doesn't exist.");
- }
+ List signature_holders = es.extractSignatureHolders(dataSource, parameters);
- SignatureHolder holder = (SignatureHolder) signature_holders.get(verify_which);
- holders_to_verify = new ArrayList();
- holders_to_verify.add(holder);
+ if (signature_holders.isEmpty())
+ {
+ throw new PDFDocumentException(ErrorCode.DOCUMENT_NOT_SIGNED, "Document is unsigned.");
+ }
+
+ List holders_to_verify = signature_holders;
+
+ // verify_which - optional argument in command line/web
+ if (verify_which >= 0)
+ {
+ if (verify_which >= signature_holders.size())
+ {
+ throw new SignatureException(312, "The selected signature to be verified doesn't exist.");
}
- List results = PdfAS.verifySignatureHolders(holders_to_verify, connector);
- System.out.println("Verification results:");
- formatVerifyResults(results, System.out);
+ SignatureHolder holder = (SignatureHolder) signature_holders.get(verify_which);
+ holders_to_verify = new ArrayList();
+ holders_to_verify.add(holder);
}
+
+ List results = PdfAS.verifySignatureHolders(holders_to_verify, connector);
+
+ return results;
}
- /**
- * Generates a suitable output file name for the output regarding the type of
- * the sign_result.
- *
- * @param input
- * The input file name.
- * @param sign_result
- * The sign result.
- * @return Returns the output file name.
- */
- protected static String generateOutputFileNameFromInput(String input,
- SignResult sign_result)
+ // TODO old method - remove
+ // /**
+ // * Generates a suitable output file name for the output regarding the type
+ // of
+ // * the sign_result.
+ // *
+ // * @param input
+ // * The input file name.
+ // * @param sign_result
+ // * The sign result.
+ // * @return Returns the output file name.
+ // */
+ // protected static String generateOutputFileNameFromInput(String input,
+ // SignResult sign_result)
+ // {
+ // String output = input + "_out";
+ // if (sign_result.getMimeType().equals(DetachedSignator_1_0_0.MIME_TYPE))
+ // {
+ // output += ".xml";
+ // }
+ // else
+ // {
+ // output += ".pdf";
+ // }
+ //
+ // return output;
+ // }
+
+ protected static String generateOutputFileNameFromInput(String input, String sig_mode)
{
String output = input + "_out";
- if (sign_result.getMimeType().equals(DetachedSignator_1_0_0.MIME_TYPE))
+ if (sig_mode.equals("detached"))
{
output += ".xml";
}
@@ -628,8 +689,7 @@ public abstract class Main
* @throws PresentableException
* Forwarded exception.
*/
- protected static void printUnrecognizedValue(final String parameter,
- final String value) throws PresentableException
+ 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);
@@ -643,8 +703,7 @@ public abstract class Main
* @throws PresentableException
* Forwarded exception.
*/
- protected static void printUnrecognizedAdditionalCommandlineArgument(
- final String argument) throws PresentableException
+ protected static void printUnrecognizedAdditionalCommandlineArgument(final String argument) throws PresentableException
{
System.err.println("Unrecognized additional commandline argument '" + argument + "'.");
printUsage(System.out);
@@ -660,8 +719,7 @@ public abstract class Main
* @throws PresentableException
* Forwarded exception.
*/
- protected static void printMissingParameter(final String missing_term,
- final String parameter) throws PresentableException
+ protected static void printMissingParameter(final String missing_term, final String parameter) throws PresentableException
{
printMissing(missing_term + " ('" + parameter + "' parameter)");
}
@@ -686,23 +744,32 @@ public abstract class Main
* @param ece
* The ErrorCodeException to be printed.
*/
- protected static void printErrorCodeException(final ErrorCodeException ece)
+ protected static void printPresentableException(final PresentableException e)
{
- if (ece instanceof PlaceholderException)
+ if (e.getErrorCode() == ErrorCode.PLACEHOLDER_EXCEPTION)
{
- PlaceholderException phe = (PlaceholderException) ece;
+ 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 " + ece.getErrorCode() + ": " + ece.getErrorCodeMessage());
+ System.err.println("Fehler " + e.getErrorCode() + ": " + ErrorCodeHelper.getMessageForErrorCode(e.getErrorCode()));
- if (ece.hasExternalErrorMessage())
+ if (e instanceof ExternalErrorException)
{
- System.err.println("Externer Fehlergrund: " + ece.getExternalErrorCode() + ": " + ece.getExternalErrorMessage());
+ ExternalErrorException eee = (ExternalErrorException) e;
+ System.err.println("Externer Fehlergrund: " + eee.getExternalErrorCode() + ": " + eee.getExternalErrorMessage());
}
- logger_.debug(ece.toString());
+ logger_.debug(e);
}
/**
@@ -753,8 +820,8 @@ public abstract class Main
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");
- 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.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();
@@ -831,7 +898,8 @@ public abstract class Main
*/
protected static boolean checkSignatureMode(String signature_mode)
{
- return signature_mode.equals(VALUE_SIGNATURE_MODE_BINARY) || signature_mode.equals(VALUE_SIGNATURE_MODE_TEXTUAL) || signature_mode.equals(VALUE_SIGNATURE_MODE_DETACHED) || signature_mode.equals(VALUE_SIGNATURE_MODE_DETACHED_TEXt);
+ return signature_mode.equals(VALUE_SIGNATURE_MODE_BINARY) || signature_mode.equals(VALUE_SIGNATURE_MODE_TEXTUAL) || signature_mode.equals(VALUE_SIGNATURE_MODE_DETACHED)
+ || signature_mode.equals(VALUE_SIGNATURE_MODE_DETACHED_TEXT);
}
/**
@@ -892,7 +960,7 @@ public abstract class Main
{
return SignatorFactory.MOST_RECENT_DETACHED_SIGNATOR_ID;
}
- if (signature_mode.equals(VALUE_SIGNATURE_MODE_DETACHED_TEXt))
+ if (signature_mode.equals(VALUE_SIGNATURE_MODE_DETACHED_TEXT))
{
return SignatorFactory.MOST_RECENT_DETACHEDTEXT_SIGNATOR_ID;
}
@@ -909,7 +977,7 @@ public abstract class Main
* @throws SettingNotFoundException
* Forwarded exception.
*/
- protected static void formatVerifyResults(List results, PrintStream writer) throws SettingNotFoundException
+ protected static void formatVerifyResults(List results, PrintWriter writer) throws SettingNotFoundException
{
Iterator it = results.iterator();
while (it.hasNext())
@@ -934,8 +1002,7 @@ public abstract class Main
* @throws SettingNotFoundException
* Forwarded exception.
*/
- public static void formatSignatureResponse(SignatureResponse result,
- PrintStream writer) throws SettingNotFoundException
+ public static void formatSignatureResponse(SignatureResponse result, PrintWriter writer) throws SettingNotFoundException
{
writer.println(" Zertifikat:");