diff options
24 files changed, 585 insertions, 37 deletions
| 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<String> tmpFiles = new ArrayList<String>(); +     +    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<String, SignatureProfileConfiguration> signatureProfiles =   				new HashMap<String, SignatureProfileConfiguration>(); +	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; -    }*/  } | 
