From add4460d9619f3586a02ae0d8c028f01903494bc Mon Sep 17 00:00:00 2001 From: Andreas Fitzek Date: Tue, 1 Oct 2013 15:27:32 +0200 Subject: + Extracting information from Certificate (Ognl) + CMS KeyStore Signer (still wrong format) --- .../java/at/gv/egiz/pdfas/cli/DeveloperMain.java | 14 +- .../pdfas/common/settings/IProfileConstants.java | 2 + .../at/gv/egiz/pdfas/common/utils/OgnlUtils.java | 18 ++- .../gv/egiz/pdfas/common/utils/TempFileHelper.java | 121 ++++++++++++++++++ pdf-as-lib/build.gradle | 4 + .../gv/egiz/pdfas/lib/api/sign/IPlainSigner.java | 12 ++ .../gv/egiz/pdfas/lib/api/sign/SignParameter.java | 4 + .../java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java | 17 ++- .../gv/egiz/pdfas/lib/impl/SignParameterImpl.java | 9 ++ .../gv/egiz/pdfas/lib/impl/signing/IPdfSigner.java | 12 ++ .../pdfas/lib/impl/signing/PdfSignerFactory.java | 9 ++ .../egiz/pdfas/lib/impl/signing/package-info.java | 8 ++ .../lib/impl/signing/pdfbox/PADESPDFBOXSigner.java | 141 +++++++++++++++++++++ .../impl/signing/pdfbox/PdfboxSignerWrapper.java | 37 ++++++ .../lib/impl/signing/pdfbox/package-info.java | 8 ++ .../lib/impl/signing/sig_interface/JKSSigner.java | 78 ++++++++++++ .../impl/signing/sig_interface/package-info.java | 8 ++ .../lib/impl/stamping/CertificateResolver.java | 6 +- .../gv/egiz/pdfas/lib/impl/stamping/IResolver.java | 4 +- .../egiz/pdfas/lib/impl/stamping/TableFactory.java | 17 ++- .../pdfas/lib/impl/stamping/ValueResolver.java | 45 +++++-- .../pdfas/lib/impl/status/OperationStatus.java | 18 ++- .../gv/egiz/pdfas/lib/impl/status/PDFObject.java | 15 +++ .../pdfas/lib/impl/status/RequestedSignature.java | 15 +-- 24 files changed, 585 insertions(+), 37 deletions(-) create mode 100644 pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/utils/TempFileHelper.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/sign/IPlainSigner.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/IPdfSigner.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/PdfSignerFactory.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/package-info.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PADESPDFBOXSigner.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PdfboxSignerWrapper.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/package-info.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/sig_interface/JKSSigner.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/sig_interface/package-info.java diff --git a/pdf-as-cli/src/main/java/at/gv/egiz/pdfas/cli/DeveloperMain.java b/pdf-as-cli/src/main/java/at/gv/egiz/pdfas/cli/DeveloperMain.java index cae1171e..4b9a4e33 100644 --- a/pdf-as-cli/src/main/java/at/gv/egiz/pdfas/cli/DeveloperMain.java +++ b/pdf-as-cli/src/main/java/at/gv/egiz/pdfas/cli/DeveloperMain.java @@ -13,22 +13,34 @@ import at.gv.egiz.pdfas.lib.api.ByteArrayDataSource; import at.gv.egiz.pdfas.lib.api.Configuration; import at.gv.egiz.pdfas.lib.api.PdfAs; import at.gv.egiz.pdfas.lib.api.PdfAsFactory; +import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner; import at.gv.egiz.pdfas.lib.api.sign.SignParameter; +import at.gv.egiz.pdfas.lib.impl.signing.IPdfSigner; +import at.gv.egiz.pdfas.lib.impl.signing.sig_interface.JKSSigner; public class DeveloperMain { + public static final String keyStoreFile = "/home/afitzek/devel/pdfas_neu/test.p12"; + public static final String keyStoreType = "PKCS12"; + public static final String keyStorePass = "123456"; + //public static final String keyAlias = "pdf"; + public static final String keyAlias = "ecc_test"; + public static final String keyPass = "123456"; + public static void main(String[] args) { String user_home = System.getProperty("user.home"); String pdfas_dir = user_home + File.separator + "PDF-AS"; PdfAs pdfas = PdfAsFactory.createPdfAs(new File(pdfas_dir)); Configuration config = pdfas.getConfiguration(); - byte[] data; try { + IPlainSigner signer = new JKSSigner(keyStoreFile, keyAlias, keyStorePass, keyPass, keyStoreType); data = StreamUtils.inputStreamToByteArray(new FileInputStream("/home/afitzek/devel/pdfas_neu/simple.pdf")); SignParameter parameter = PdfAsFactory.createSignParameter(config, new ByteArrayDataSource(data)); ByteArrayDataSink bads = new ByteArrayDataSink(); + parameter.setSignatureProfileId("SIGNATURBLOCK_DE_NEU"); parameter.setOutput(bads); + parameter.setPlainSigner(signer); pdfas.sign(parameter); FileOutputStream fos = new FileOutputStream("/home/afitzek/devel/pdfas_neu/simple_out.pdf"); fos.write(bads.getData()); diff --git a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/IProfileConstants.java b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/IProfileConstants.java index 2e2ee024..897ced8e 100644 --- a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/IProfileConstants.java +++ b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/settings/IProfileConstants.java @@ -62,4 +62,6 @@ public interface IProfileConstants { public final static String CFG_DIR = "cfg"; public final static String CFG_FILE = "config.properties"; + public final static String TMP_DIR = "default.pdfastmp_dir"; + public final static String TMP_DIR_DEFAULT_VALUE = "pdfastmp"; } diff --git a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/utils/OgnlUtils.java b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/utils/OgnlUtils.java index e98cb124..4afb8932 100644 --- a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/utils/OgnlUtils.java +++ b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/utils/OgnlUtils.java @@ -1,6 +1,9 @@ package at.gv.egiz.pdfas.common.utils; +import ognl.Ognl; import ognl.OgnlContext; +import ognl.OgnlException; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,7 +19,20 @@ public class OgnlUtils { private static final Logger logger = LoggerFactory.getLogger(OgnlUtils.class); public static String resolvsOgnlExpression(String expression, OgnlContext ctx) { - // TODO! + try { + Object value = Ognl.getValue(expression, ctx); + String valueString = value.toString(); + if(valueString.startsWith("[")) { + valueString = valueString.substring(1); + } + if(valueString.endsWith("]")) { + valueString = valueString.substring(0, valueString.length() - 1); + } + return valueString; + } catch (OgnlException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } return expression; } } diff --git a/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/utils/TempFileHelper.java b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/utils/TempFileHelper.java new file mode 100644 index 00000000..611d7a0b --- /dev/null +++ b/pdf-as-common/src/main/java/at/gv/egiz/pdfas/common/utils/TempFileHelper.java @@ -0,0 +1,121 @@ +package at.gv.egiz.pdfas.common.utils; + +import java.io.File; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.settings.IProfileConstants; +import at.gv.egiz.pdfas.common.settings.ISettings; + + +public class TempFileHelper implements IProfileConstants { + + private static final Logger logger = LoggerFactory.getLogger(TempFileHelper.class); + + private final String tmpFilePrefix = "/tmp/"; + private static final String tmpFileSuffix = ".tmppdf"; + + private String tmpDir = "tmp"; + + private MessageDigest messageDigest = null; + + private List tmpFiles = new ArrayList(); + + public TempFileHelper(ISettings settings) { + initializeMD(); + + String myTmpDir = settings.getValue(TMP_DIR); + if(myTmpDir != null) { + File myTmpDirFile = new File(myTmpDir); + if(!myTmpDirFile.isAbsolute()) { + // relatives tmp dir + myTmpDirFile = new File(settings.getWorkingDirectory() + File.separator + myTmpDir); + } + tmpDir = myTmpDirFile.getAbsolutePath(); + } else { + tmpDir = settings.getWorkingDirectory() + File.separator + TMP_DIR_DEFAULT_VALUE; + } + + logger.info("TempDirHelper for TempDirectory: " + tmpDir); + + createTmpDir(); + } + + @Override + protected void finalize() throws Throwable { + this.deleteTmpDir(); + super.finalize(); + } + + private void deleteTmpDir() { + try { + File tmpdir = new File(tmpDir); + tmpdir.delete(); + } catch (Throwable e) { + logger.error("Failed to delete temporary directory: " + tmpDir, e); + } + } + + private void createTmpDir() { + try { + File tmpdir = new File(tmpDir); + tmpdir.mkdirs(); + } catch (Throwable e) { + logger.error("Failed to create temporary directory: " + tmpDir, e); + } + } + + private void initializeMD() { + try { + messageDigest = MessageDigest.getInstance("SHA1"); + return; + } catch (NoSuchAlgorithmException e) { + logger.warn("SHA1 not available", e); + } + try { + messageDigest = MessageDigest.getInstance("MD5"); + return; + } catch (NoSuchAlgorithmException e) { + logger.warn("MD5 not available", e); + } + throw new RuntimeException("Need at least SHA1 or MD5 Message Digest, none available!"); + } + + + public void setTemporaryDirectory(String directory) { + tmpDir = directory; + createTmpDir(); + } + + public String getHashedHexString(String str) { + byte[] digest = messageDigest.digest(str.getBytes()); + return StringUtils.bytesToHexString(digest); + } + + public String getStaticFilename() { + String uuidString = UUID.randomUUID().toString(); + logger.debug("Generated UUID " + uuidString); + String tmpFilename = tmpFilePrefix + getHashedHexString(uuidString) + tmpFileSuffix; + logger.info("Temporary filename " + tmpFilename); + tmpFiles.add(tmpFilename); + return tmpFilename; + } + + public void deleteFile(String filename) { + try { + File tmpFile = new File(filename); + if(tmpFile.exists()) { + tmpFile.delete(); + tmpFiles.remove(filename); + } + } catch (Throwable e) { + logger.error("Failed to delete temporary file: " + filename, e); + } + } +} diff --git a/pdf-as-lib/build.gradle b/pdf-as-lib/build.gradle index ed000011..fd7c00fc 100644 --- a/pdf-as-lib/build.gradle +++ b/pdf-as-lib/build.gradle @@ -10,6 +10,9 @@ jar { repositories { mavenLocal() mavenCentral() + maven { + url "http://nexus.iaik.tugraz.at/nexus/content/groups/internal" + } } dependencies { @@ -19,6 +22,7 @@ dependencies { compile group: 'org.apache.commons', name: 'commons-io', version: '1.3.2' compile group: 'commons-collections', name: 'commons-collections', version: '3.2' compile group: 'ognl', name: 'ognl', version: '3.0.6' + compile group: 'iaik', name: 'iaik_cms', version: '4.1-moa' compile group: 'eu.europa.ec.joinup.egovlabs.pdf-as.iaik', name: 'iaik_jce_eval_signed', version: '4.0' compile group: 'eu.europa.ec.joinup.egovlabs.pdf-as.iaik', name: 'iaik_ecc_eval_signed', version: '2.19' testCompile group: 'junit', name: 'junit', version: '4.+' diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/sign/IPlainSigner.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/sign/IPlainSigner.java new file mode 100644 index 00000000..0735a0bf --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/sign/IPlainSigner.java @@ -0,0 +1,12 @@ +package at.gv.egiz.pdfas.lib.api.sign; + +import iaik.x509.X509Certificate; + +import java.io.IOException; + +import org.apache.pdfbox.exceptions.SignatureException; + +public interface IPlainSigner { + public X509Certificate getCertificate(); + public byte[] sign(byte[] input) throws SignatureException, IOException; +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/sign/SignParameter.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/sign/SignParameter.java index 0b4a076c..87ec4068 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/sign/SignParameter.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/sign/SignParameter.java @@ -16,4 +16,8 @@ public interface SignParameter extends PdfAsParameter { public void setOutput(DataSink output); public DataSink getOutput(); + + public void setPlainSigner(IPlainSigner signer); + + public IPlainSigner getPlainSigner(); } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java index adef37af..5bda572b 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java @@ -1,5 +1,7 @@ package at.gv.egiz.pdfas.lib.impl; +import iaik.x509.X509Certificate; + import java.io.ByteArrayInputStream; import java.io.File; import java.io.OutputStream; @@ -9,6 +11,7 @@ import org.apache.pdfbox.pdmodel.PDDocument; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sun.misc.Regexp; import at.gv.egiz.pdfas.common.exceptions.PdfAsException; import at.gv.egiz.pdfas.common.exceptions.PdfAsSettingsException; import at.gv.egiz.pdfas.common.settings.ISettings; @@ -25,6 +28,8 @@ import at.gv.egiz.pdfas.lib.impl.configuration.ConfigurationImpl; import at.gv.egiz.pdfas.lib.impl.configuration.PlaceholderConfiguration; import at.gv.egiz.pdfas.lib.impl.configuration.SignatureProfileConfiguration; import at.gv.egiz.pdfas.lib.impl.positioning.Positioning; +import at.gv.egiz.pdfas.lib.impl.signing.IPdfSigner; +import at.gv.egiz.pdfas.lib.impl.signing.PdfSignerFactory; import at.gv.egiz.pdfas.lib.impl.stamping.IPDFStamper; import at.gv.egiz.pdfas.lib.impl.stamping.IPDFVisualObject; import at.gv.egiz.pdfas.lib.impl.stamping.StamperFactory; @@ -83,9 +88,7 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants { if (placeholderConfiguration.isGlobalPlaceholderEnabled()) { // TODO: Do placeholder search } - - // TODO get Certificate - + if (requestedSignature.isVisual()) { logger.info("Creating visual siganture block"); // ================================================================ @@ -95,7 +98,7 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants { .createProfile(signatureProfileID, settings); Table main = TableFactory.createSigTable( - signatureProfileSettings, MAIN, settings); + signatureProfileSettings, MAIN, settings, requestedSignature); IPDFStamper stamper = StamperFactory.createDefaultStamper(settings); IPDFVisualObject visualObject = stamper.createVisualPDFObject( @@ -145,8 +148,10 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants { } // TODO: Create signature - - status.getPdfObject().setSignedDocument(status.getPdfObject().getStampedDocument()); + IPdfSigner signer = PdfSignerFactory.createPdfSigner(); + signer.signPDF(status.getPdfObject(), requestedSignature, status.getSignParamter().getPlainSigner()); + + //status.getPdfObject().setSignedDocument(status.getPdfObject().getStampedDocument()); // ================================================================ // Create SignResult diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/SignParameterImpl.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/SignParameterImpl.java index 7989bca7..acf1f7da 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/SignParameterImpl.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/SignParameterImpl.java @@ -3,12 +3,14 @@ package at.gv.egiz.pdfas.lib.impl; import at.gv.egiz.pdfas.lib.api.Configuration; import at.gv.egiz.pdfas.lib.api.DataSink; import at.gv.egiz.pdfas.lib.api.DataSource; +import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner; import at.gv.egiz.pdfas.lib.api.sign.SignParameter; public class SignParameterImpl extends PdfAsParameterImpl implements SignParameter { protected String signatureProfileId = null; protected String signaturePosition = null; protected DataSink output = null; + protected IPlainSigner signer = null; public SignParameterImpl(Configuration configuration, DataSource dataSource) { @@ -40,6 +42,13 @@ public class SignParameterImpl extends PdfAsParameterImpl implements SignParamet public DataSink getOutput() { return this.output; } + + public void setPlainSigner(IPlainSigner signer) { + this.signer = signer; + } + public IPlainSigner getPlainSigner() { + return this.signer; + } } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/IPdfSigner.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/IPdfSigner.java new file mode 100644 index 00000000..8ff3a276 --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/IPdfSigner.java @@ -0,0 +1,12 @@ +package at.gv.egiz.pdfas.lib.impl.signing; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner; +import at.gv.egiz.pdfas.lib.impl.status.PDFObject; +import at.gv.egiz.pdfas.lib.impl.status.RequestedSignature; + +public interface IPdfSigner { + + void signPDF(PDFObject pdfObject, + RequestedSignature requestedSignature, IPlainSigner signer) throws PdfAsException; +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/PdfSignerFactory.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/PdfSignerFactory.java new file mode 100644 index 00000000..469ea174 --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/PdfSignerFactory.java @@ -0,0 +1,9 @@ +package at.gv.egiz.pdfas.lib.impl.signing; + +import at.gv.egiz.pdfas.lib.impl.signing.pdfbox.PADESPDFBOXSigner; + +public class PdfSignerFactory { + public static IPdfSigner createPdfSigner() { + return new PADESPDFBOXSigner(); + } +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/package-info.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/package-info.java new file mode 100644 index 00000000..6a59486b --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/package-info.java @@ -0,0 +1,8 @@ +/** + * + */ +/** + * @author afitzek + * + */ +package at.gv.egiz.pdfas.lib.impl.signing; \ No newline at end of file diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PADESPDFBOXSigner.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PADESPDFBOXSigner.java new file mode 100644 index 00000000..82ee57fe --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PADESPDFBOXSigner.java @@ -0,0 +1,141 @@ +package at.gv.egiz.pdfas.lib.impl.signing.pdfbox; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Calendar; + +import org.apache.pdfbox.exceptions.COSVisitorException; +import org.apache.pdfbox.exceptions.SignatureException; +import org.apache.pdfbox.pdmodel.PDDocument; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.messages.MessageResolver; +import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; +import at.gv.egiz.pdfas.common.utils.StreamUtils; +import at.gv.egiz.pdfas.common.utils.TempFileHelper; +import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner; +import at.gv.egiz.pdfas.lib.impl.signing.IPdfSigner; +import at.gv.egiz.pdfas.lib.impl.stamping.TableFactory; +import at.gv.egiz.pdfas.lib.impl.stamping.ValueResolver; +import at.gv.egiz.pdfas.lib.impl.status.PDFObject; +import at.gv.egiz.pdfas.lib.impl.status.RequestedSignature; + +public class PADESPDFBOXSigner implements IPdfSigner { + + private static final Logger logger = LoggerFactory.getLogger(PADESPDFBOXSigner.class); + + public void signPDF(PDFObject pdfObject, RequestedSignature requestedSignature, IPlainSigner signer) + throws PdfAsException { + String fisTmpFile = null; + + TempFileHelper helper = pdfObject.getStatus().getTempFileHelper(); + + try { + fisTmpFile = helper.getStaticFilename(); + + // write to temporary file + FileOutputStream fos = new FileOutputStream(new File(fisTmpFile)); + fos.write(pdfObject.getStampedDocument()); + + + FileInputStream fis = new FileInputStream(new File(fisTmpFile)); + + PDDocument doc = PDDocument.load( + new ByteArrayInputStream(pdfObject.getStampedDocument())); + + PDSignature signature = new PDSignature(); + signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); // default filter + signature.setSubFilter(PDSignature.SUBFILTER_ETSI_CADES_DETACHED); + + SignatureProfileSettings signatureProfileSettings = TableFactory + .createProfile(requestedSignature.getSignatureProfileID(), + pdfObject.getStatus().getSettings()); + + ValueResolver resolver = new ValueResolver(); + String signerName = resolver.resolve("SIG_SUBJECT", signatureProfileSettings.getValue("SIG_SUBJECT"), + signatureProfileSettings, requestedSignature); + // TODO: change signature data from certificate + signature.setName(signerName); + //signature.setLocation("signer location"); + signature.setReason("PDF-AS Signatur"); + + + // the signing date, needed for valid signature + signature.setSignDate(Calendar.getInstance()); + + doc.addSignature(signature, new PdfboxSignerWrapper(signer, signature)); + + // pdfbox patched (FIS -> IS) + doc.saveIncremental(fis, fos); + fis.close(); + fos.close(); + + fis = new FileInputStream(new File(fisTmpFile)); + + // write to resulting output stream + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + bos.write(StreamUtils.inputStreamToByteArray(fis)); + fis.close(); + bos.close(); + + pdfObject.setSignedDocument(bos.toByteArray()); + + helper.deleteFile(fisTmpFile); + + } catch (IOException e) { + logger.error(MessageResolver.resolveMessage("error.pdf.sig.01"), e); + throw new PdfAsException("error.pdf.sig.01", e); + } catch(SignatureException e) { + logger.error(MessageResolver.resolveMessage("error.pdf.sig.01"), e); + throw new PdfAsException("error.pdf.sig.01", e); + } catch (COSVisitorException e) { + logger.error(MessageResolver.resolveMessage("error.pdf.sig.01"), e); + throw new PdfAsException("error.pdf.sig.01", e); + } + } + + + public void signPDF(String src, String dst, SignatureInterface signer) throws Exception { + //ByteArrayOutputStream os = new ByteArrayOutputStream(); + FileInputStream fis = new FileInputStream(new File(src)); + FileOutputStream fos = new FileOutputStream(new File(dst)); + byte[] buffer = new byte[8 * 1024]; + byte[] outbuffer; + int c; + while ((c = fis.read(buffer)) != -1) + { + fos.write(buffer, 0, c); + } + fis.close(); + PDDocument doc = PDDocument.load(src); + fis = new FileInputStream(new File(dst)); + + PDSignature signature = new PDSignature(); + signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); // default filter + + signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED); + signature.setName("Andraes Fitzek"); + signature.setLocation("signer location"); + signature.setReason("Test Signature"); + + // the signing date, needed for valid signature + signature.setSignDate(Calendar.getInstance()); + + doc.addSignature(signature, signer); + + // pdfbox patched (FIS -> IS) + doc.saveIncremental(fis, fos); + + fos.close(); + // FileUtils.writeByteArrayToFile(new File(dst), os.toByteArray()); + } + +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PdfboxSignerWrapper.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PdfboxSignerWrapper.java new file mode 100644 index 00000000..fb629dd6 --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/PdfboxSignerWrapper.java @@ -0,0 +1,37 @@ +package at.gv.egiz.pdfas.lib.impl.signing.pdfbox; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.apache.pdfbox.exceptions.SignatureException; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.utils.StreamUtils; +import at.gv.egiz.pdfas.common.utils.StringUtils; +import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner; + +public class PdfboxSignerWrapper implements SignatureInterface { + + private static final Logger logger = LoggerFactory.getLogger(PdfboxSignerWrapper.class); + + private IPlainSigner signer; + private PDSignature signature; + + public PdfboxSignerWrapper(IPlainSigner signer, PDSignature signature) { + this.signer = signer; + this.signature = signature; + } + + public byte[] sign(InputStream inputStream) throws SignatureException, IOException { + byte[] signature = signer.sign(StreamUtils.inputStreamToByteArray(inputStream)); + logger.debug("Signature Data: " + StringUtils.bytesToHexString(signature)); + FileOutputStream fos = new FileOutputStream("/tmp/fos.bin"); + fos.write(signature); + fos.close(); + return signature; + } +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/package-info.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/package-info.java new file mode 100644 index 00000000..85b552f6 --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/pdfbox/package-info.java @@ -0,0 +1,8 @@ +/** + * + */ +/** + * @author afitzek + * + */ +package at.gv.egiz.pdfas.lib.impl.signing.pdfbox; \ No newline at end of file diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/sig_interface/JKSSigner.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/sig_interface/JKSSigner.java new file mode 100644 index 00000000..85697436 --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/sig_interface/JKSSigner.java @@ -0,0 +1,78 @@ +package at.gv.egiz.pdfas.lib.impl.signing.sig_interface; + +import iaik.asn1.structures.AlgorithmID; +import iaik.cms.SignedDataStream; +import iaik.cms.SignerInfo; +import iaik.cms.SubjectKeyID; +import iaik.security.ecc.provider.ECCProvider; +import iaik.security.provider.IAIK; +import iaik.x509.X509Certificate; +import iaik.x509.X509ExtensionException; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.Certificate; + +import org.apache.pdfbox.exceptions.SignatureException; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner; + +public class JKSSigner implements IPlainSigner { + + PrivateKey privKey; + X509Certificate cert; + + public JKSSigner(String file, String alias, String kspassword, + String keypassword, String type) throws PdfAsException { + try { + IAIK.getInstance(); + ECCProvider.addAsProvider(); + KeyStore ks = KeyStore.getInstance(type); + ks.load(new FileInputStream(file), kspassword.toCharArray()); + privKey = (PrivateKey) ks.getKey(alias, keypassword.toCharArray()); + cert = new X509Certificate(ks.getCertificate(alias).getEncoded()); + } catch (Throwable e) { + throw new PdfAsException("Failed to get KeyStore", e); + } + } + + public X509Certificate getCertificate() { + return cert; + } + + public byte[] sign(byte[] input) throws SignatureException, IOException { + try { + SignedDataStream signed_data_stream = new SignedDataStream( + new ByteArrayInputStream(input), SignedDataStream.EXPLICIT); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + signed_data_stream.addCertificates(new Certificate[] { cert }); + + SubjectKeyID subjectKeyId = new SubjectKeyID(cert); + SignerInfo signer1 = new SignerInfo(subjectKeyId, + AlgorithmID.sha256, privKey); + signed_data_stream.addSignerInfo(signer1); + InputStream data_is = signed_data_stream.getInputStream(); + if (signed_data_stream.getMode() == SignedDataStream.EXPLICIT) { + byte[] buf = new byte[1024]; + int r; + while ((r = data_is.read(buf)) > 0) { + // do something useful + } + } + signed_data_stream.writeTo(baos); + return baos.toByteArray(); + } catch (NoSuchAlgorithmException e) { + throw new SignatureException(e); + } catch (X509ExtensionException e) { + throw new SignatureException(e); + } + } + +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/sig_interface/package-info.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/sig_interface/package-info.java new file mode 100644 index 00000000..9f552662 --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/sig_interface/package-info.java @@ -0,0 +1,8 @@ +/** + * + */ +/** + * @author afitzek + * + */ +package at.gv.egiz.pdfas.lib.impl.signing.sig_interface; \ No newline at end of file diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/CertificateResolver.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/CertificateResolver.java index f30c326d..78dcbe23 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/CertificateResolver.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/CertificateResolver.java @@ -3,13 +3,16 @@ package at.gv.egiz.pdfas.lib.impl.stamping; import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; import at.gv.egiz.pdfas.common.utils.DNUtils; import at.gv.egiz.pdfas.common.utils.OgnlUtils; +import at.gv.egiz.pdfas.lib.impl.status.RequestedSignature; import iaik.x509.X509Certificate; import ognl.OgnlContext; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; + import java.util.Map; public class CertificateResolver implements IResolver { @@ -39,7 +42,8 @@ public class CertificateResolver implements IResolver { } - public String resolve(String key, String value, SignatureProfileSettings settings) { + public String resolve(String key, String value, SignatureProfileSettings settings, + RequestedSignature signature) { return OgnlUtils.resolvsOgnlExpression(value, this.ctx); } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/IResolver.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/IResolver.java index 040daf33..921d9eca 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/IResolver.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/IResolver.java @@ -1,6 +1,7 @@ package at.gv.egiz.pdfas.lib.impl.stamping; import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; +import at.gv.egiz.pdfas.lib.impl.status.RequestedSignature; /** * Created with IntelliJ IDEA. @@ -10,5 +11,6 @@ import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; * To change this template use File | Settings | File Templates. */ public interface IResolver { - public String resolve(String key, String value, SignatureProfileSettings settings); + public String resolve(String key, String value, SignatureProfileSettings settings, + RequestedSignature signature); } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/TableFactory.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/TableFactory.java index 45b8b711..9d574af7 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/TableFactory.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/TableFactory.java @@ -3,6 +3,7 @@ package at.gv.egiz.pdfas.lib.impl.stamping; import at.gv.egiz.pdfas.common.settings.IProfileConstants; import at.gv.egiz.pdfas.common.settings.ISettings; import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; +import at.gv.egiz.pdfas.lib.impl.status.RequestedSignature; import at.knowcenter.wag.egov.egiz.pdf.sig.SignatureEntry; import at.knowcenter.wag.egov.egiz.table.Entry; import at.knowcenter.wag.egov.egiz.table.Style; @@ -71,7 +72,8 @@ public class TableFactory implements IProfileConstants { * @see at.knowcenter.wag.egov.egiz.table.Table * @see at.knowcenter.wag.egov.egiz.table.Entry */ - public static Table createSigTable(SignatureProfileSettings profile, String tableID, ISettings configuration ) + public static Table createSigTable(SignatureProfileSettings profile, String tableID, ISettings configuration, + RequestedSignature requestedSignature) { String table_key_prefix = SIG_OBJ + profile.getProfileID() + "." + TABLE; String table_key = table_key_prefix + tableID; @@ -135,7 +137,7 @@ public class TableFactory implements IProfileConstants { if (TYPE_TABLE.equals(key)) { // add a table entry - Table table = createSigTable(profile, type, configuration); + Table table = createSigTable(profile, type, configuration, requestedSignature); if (table != null) { Entry entry = new Entry(Entry.TYPE_TABLE, table, key); @@ -160,8 +162,10 @@ public class TableFactory implements IProfileConstants { if (TYPE_VALUE.equals(type)) { // add a single value entry + ValueResolver resolver = new ValueResolver(); String value = profile.getValue(key); - Entry entry = new Entry(Entry.TYPE_VALUE, value, key); + Entry entry = new Entry(Entry.TYPE_VALUE, + resolver.resolve(key, value, profile, requestedSignature), key); if (entry != null) { entry.setColSpan(2); @@ -181,8 +185,9 @@ public class TableFactory implements IProfileConstants { Entry c_entry = new Entry(Entry.TYPE_CAPTION, caption, key); c_entry.setNoWrap(true); // dferbas fix bug #331 c_entry.setStyle(defaultCaptionStyle_); - - Entry v_entry = new Entry(Entry.TYPE_VALUE, value, key); + ValueResolver resolver = new ValueResolver(); + Entry v_entry = new Entry(Entry.TYPE_VALUE, + resolver.resolve(key, value, profile, requestedSignature), key); v_entry.setStyle(defaultValueStyle_); if (c_entry != null && v_entry != null) { @@ -198,7 +203,7 @@ public class TableFactory implements IProfileConstants { ValueResolver resolver = new ValueResolver(); Entry v_entry = new Entry(Entry.TYPE_VALUE, - resolver.resolve(key, value, profile), key); + resolver.resolve(key, value, profile, requestedSignature), key); v_entry.setStyle(defaultValueStyle_); if (c_entry != null && v_entry != null) { diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/ValueResolver.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/ValueResolver.java index 9052a7eb..19b38f27 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/ValueResolver.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/stamping/ValueResolver.java @@ -3,10 +3,13 @@ package at.gv.egiz.pdfas.lib.impl.stamping; import at.gv.egiz.pdfas.common.settings.IProfileConstants; import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; import at.gv.egiz.pdfas.lib.impl.PdfAsImpl; +import at.gv.egiz.pdfas.lib.impl.status.RequestedSignature; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,11 +18,13 @@ import org.slf4j.LoggerFactory; * Created with IntelliJ IDEA. User: afitzek Date: 9/11/13 Time: 11:11 AM To * change this template use File | Settings | File Templates. */ -public class ValueResolver implements IProfileConstants { +public class ValueResolver implements IProfileConstants, IResolver { private static final Logger logger = LoggerFactory .getLogger(ValueResolver.class); + public static final String PatternRegex = "\\$(\\{[^\\$]*\\})"; + private static final String defaultDateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; public static final String EXP_START = "${"; @@ -31,25 +36,43 @@ public class ValueResolver implements IProfileConstants { // TODO: Use status in real implementation to get currently needed // informations... public String resolve(String key, String value, - SignatureProfileSettings settings) { + SignatureProfileSettings settings, RequestedSignature signature) { logger.debug("Resolving value for key: " + key); logger.debug("Resolving value with value: " + value); if (value != null) { - if (value.startsWith(EXP_START) && value.endsWith(EXP_END)) { - // TODO: handle OGNL expression for key and value - // TODO: Here we need the certificate - CertificateResolver certificateResolver = new CertificateResolver( - null); - String exp = value.substring(EXP_START.length(), value.length() - - EXP_END.length()); - return certificateResolver.resolve(key, exp, settings); + + Pattern pattern = Pattern.compile(PatternRegex); + Matcher matcher = pattern.matcher(value); + CertificateResolver certificateResolver = new CertificateResolver( + signature.getCertificate()); + String result = ""; + int curidx = 0; + if (matcher.find()) { + do { + int idx = matcher.start(0); + int idxe = matcher.end(0); + result += value.substring(curidx, idx); + curidx = idxe; + result += certificateResolver.resolve(key, + matcher.group(1), settings, signature); + } while (matcher.find()); + } else { + result = value; } + /* + * if (value.startsWith(EXP_START) && value.endsWith(EXP_END)) { // + * TODO: handle OGNL expression for key and value // TODO: Here we + * need the certificate + * + * String exp = value.replace('$', ' '); return } + */ + return result; } if (key.equals(SIG_DATE)) { - if(value == null) { + if (value == null) { value = defaultDateFormat; } // Value holds the date format! diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/OperationStatus.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/OperationStatus.java index d7b956a7..a9fd1443 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/OperationStatus.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/OperationStatus.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.Map; import at.gv.egiz.pdfas.common.settings.ISettings; +import at.gv.egiz.pdfas.common.utils.TempFileHelper; import at.gv.egiz.pdfas.lib.api.sign.SignParameter; import at.gv.egiz.pdfas.lib.impl.configuration.GlobalConfiguration; import at.gv.egiz.pdfas.lib.impl.configuration.PlaceholderConfiguration; @@ -12,18 +13,24 @@ import at.gv.egiz.pdfas.lib.impl.configuration.SignatureProfileConfiguration; public class OperationStatus { private SignParameter signParamter; - private PDFObject pdfObject = new PDFObject(); - + private PDFObject pdfObject = new PDFObject(this); private ISettings configuration; private PlaceholderConfiguration placeholderConfiguration = null; private GlobalConfiguration gloablConfiguration = null; private Map signatureProfiles = new HashMap(); + private TempFileHelper helper; public OperationStatus(ISettings configuration, SignParameter signParameter) { this.configuration = configuration; this.signParamter = signParameter; + helper = new TempFileHelper(configuration); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); } // ======================================================================== @@ -73,4 +80,11 @@ public class OperationStatus { this.signParamter = signParamter; } + public TempFileHelper getTempFileHelper() { + return this.helper; + } + + public ISettings getSettings() { + return this.configuration; + } } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/PDFObject.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/PDFObject.java index 1fe64bee..df496f3f 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/PDFObject.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/PDFObject.java @@ -1,10 +1,17 @@ package at.gv.egiz.pdfas.lib.impl.status; public class PDFObject { + + private OperationStatus status; + private byte[] originalDocument; private byte[] stampedDocument; private byte[] signedDocument; + public PDFObject(OperationStatus operationStatus) { + this.status = operationStatus; + } + public byte[] getOriginalDocument() { return originalDocument; } @@ -28,4 +35,12 @@ public class PDFObject { public void setSignedDocument(byte[] signedDocument) { this.signedDocument = signedDocument; } + + public OperationStatus getStatus() { + return status; + } + + public void setStatus(OperationStatus status) { + this.status = status; + } } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/RequestedSignature.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/RequestedSignature.java index a9065644..a2a6acd8 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/RequestedSignature.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/status/RequestedSignature.java @@ -1,5 +1,6 @@ package at.gv.egiz.pdfas.lib.impl.status; +import iaik.x509.X509Certificate; import at.gv.egiz.pdfas.common.exceptions.PdfAsException; import at.gv.egiz.pdfas.common.exceptions.PdfAsSettingsException; import at.knowcenter.wag.egov.egiz.pdf.TablePos; @@ -9,6 +10,7 @@ public class RequestedSignature { private String signatureProfile; private TablePos signaturePosition; private OperationStatus status; + private X509Certificate certificate; //private IPlainSigner signer = null; public RequestedSignature(OperationStatus status) throws PdfAsException { @@ -24,7 +26,8 @@ public class RequestedSignature { throw new PdfAsSettingsException("Failed to determine Signature Profile!"); } } - + certificate = status.getSignParamter().getPlainSigner().getCertificate(); + this.signatureProfile = profileID; if(status.getSignParamter().getSignaturePosition() == null) { @@ -45,13 +48,9 @@ public class RequestedSignature { public String getSignatureProfileID() { return this.signatureProfile; } - -/* - public IPlainSigner getSigner() { - return signer; + + public X509Certificate getCertificate() { + return this.certificate; } - public void setSigner(IPlainSigner signer) { - this.signer = signer; - }*/ } -- cgit v1.2.3