From 0876981fd70fdab07f7c3e1666cf77071b5fe03d Mon Sep 17 00:00:00 2001 From: Andreas Fitzek Date: Wed, 2 Oct 2013 10:28:30 +0200 Subject: + added PKCS7 detached siganture standard via keystore + added simple verification implementation --- pdf-as-cli/build.gradle | 1 + .../java/at/gv/egiz/pdfas/cli/DeveloperMain.java | 11 +- .../gv/egiz/pdfas/common/utils/TempFileHelper.java | 14 ++- .../gv/egiz/pdfas/lib/api/sign/IPlainSigner.java | 3 + .../gv/egiz/pdfas/lib/api/verify/VerifyResult.java | 4 + .../java/at/gv/egiz/pdfas/lib/impl/PdfAsImpl.java | 139 ++++++++++++++++++--- .../lib/impl/signing/pdfbox/PADESPDFBOXSigner.java | 8 +- .../lib/impl/signing/sig_interface/JKSSigner.java | 78 ------------ .../gv/egiz/pdfas/lib/impl/verify/FilterEntry.java | 28 +++++ .../egiz/pdfas/lib/impl/verify/IVerifyFilter.java | 11 ++ .../pdfas/lib/impl/verify/VerifierDispatcher.java | 61 +++++++++ .../pdfas/lib/impl/verify/VerifyResultImpl.java | 75 +++++++++++ .../egiz/pdfas/lib/impl/verify/package-info.java | 8 ++ settings.gradle | 2 +- signature-standards/sigs-pcks7detached/.gitignore | 1 + .../sigs-pcks7detached/build.gradle | 34 +++++ .../sigs/pkcs7detached/PKCS7DetachedSigner.java | 88 +++++++++++++ .../sigs/pkcs7detached/PKCS7DetachedVerifier.java | 74 +++++++++++ .../pdfas/sigs/pkcs7detached/package-info.java | 8 ++ 19 files changed, 540 insertions(+), 108 deletions(-) delete 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/verify/FilterEntry.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/IVerifyFilter.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/VerifierDispatcher.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/VerifyResultImpl.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/package-info.java create mode 100644 signature-standards/sigs-pcks7detached/.gitignore create mode 100644 signature-standards/sigs-pcks7detached/build.gradle create mode 100644 signature-standards/sigs-pcks7detached/src/main/java/at/gv/egiz/pdfas/sigs/pkcs7detached/PKCS7DetachedSigner.java create mode 100644 signature-standards/sigs-pcks7detached/src/main/java/at/gv/egiz/pdfas/sigs/pkcs7detached/PKCS7DetachedVerifier.java create mode 100644 signature-standards/sigs-pcks7detached/src/main/java/at/gv/egiz/pdfas/sigs/pkcs7detached/package-info.java diff --git a/pdf-as-cli/build.gradle b/pdf-as-cli/build.gradle index 48a9b659..f66529ec 100644 --- a/pdf-as-cli/build.gradle +++ b/pdf-as-cli/build.gradle @@ -14,6 +14,7 @@ repositories { dependencies { compile project (':pdf-as-lib') compile project (':stamper:stmp-itext') + compile project (':signature-standards:sigs-pcks7detached') compile group: 'commons-collections', name: 'commons-collections', version: '3.2' testCompile group: 'junit', name: 'junit', version: '4.+' } 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 4b9a4e33..0e114eae 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 @@ -15,8 +15,9 @@ 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; +import at.gv.egiz.pdfas.lib.api.verify.VerifyParameter; +import at.gv.egiz.pdfas.lib.impl.VerifyParameterImpl; +import at.gv.egiz.pdfas.sigs.pkcs7detached.PKCS7DetachedSigner; public class DeveloperMain { @@ -34,7 +35,7 @@ public class DeveloperMain { Configuration config = pdfas.getConfiguration(); byte[] data; try { - IPlainSigner signer = new JKSSigner(keyStoreFile, keyAlias, keyStorePass, keyPass, keyStoreType); + IPlainSigner signer = new PKCS7DetachedSigner(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(); @@ -45,6 +46,10 @@ public class DeveloperMain { FileOutputStream fos = new FileOutputStream("/home/afitzek/devel/pdfas_neu/simple_out.pdf"); fos.write(bads.getData()); fos.close(); + + VerifyParameter verify = new VerifyParameterImpl(config, new ByteArrayDataSource(bads.getData())); + pdfas.verify(verify); + } catch (FileNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); 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 index 611d7a0b..03cd28f3 100644 --- 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 @@ -27,6 +27,8 @@ public class TempFileHelper implements IProfileConstants { private List tmpFiles = new ArrayList(); + private boolean needsDeletion = false; + public TempFileHelper(ISettings settings) { initializeMD(); @@ -37,9 +39,12 @@ public class TempFileHelper implements IProfileConstants { // relatives tmp dir myTmpDirFile = new File(settings.getWorkingDirectory() + File.separator + myTmpDir); } - tmpDir = myTmpDirFile.getAbsolutePath(); + tmpDir = myTmpDirFile.getAbsolutePath() + File.separator; } else { - tmpDir = settings.getWorkingDirectory() + File.separator + TMP_DIR_DEFAULT_VALUE; + String uuidString = UUID.randomUUID().toString(); + logger.debug("Generated UUID " + uuidString); + tmpDir = settings.getWorkingDirectory() + File.separator + TMP_DIR_DEFAULT_VALUE + File.separator + getHashedHexString(uuidString) + File.separator; + needsDeletion = true; } logger.info("TempDirHelper for TempDirectory: " + tmpDir); @@ -66,6 +71,9 @@ public class TempFileHelper implements IProfileConstants { try { File tmpdir = new File(tmpDir); tmpdir.mkdirs(); + if(needsDeletion) { + tmpdir.deleteOnExit(); + } } catch (Throwable e) { logger.error("Failed to create temporary directory: " + tmpDir, e); } @@ -101,7 +109,7 @@ public class TempFileHelper implements IProfileConstants { public String getStaticFilename() { String uuidString = UUID.randomUUID().toString(); logger.debug("Generated UUID " + uuidString); - String tmpFilename = tmpFilePrefix + getHashedHexString(uuidString) + tmpFileSuffix; + String tmpFilename = tmpDir + getHashedHexString(uuidString) + tmpFileSuffix; logger.info("Temporary filename " + tmpFilename); tmpFiles.add(tmpFilename); return tmpFilename; 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 index 0735a0bf..6155a245 100644 --- 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 @@ -4,9 +4,12 @@ import iaik.x509.X509Certificate; import java.io.IOException; +import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.exceptions.SignatureException; public interface IPlainSigner { public X509Certificate getCertificate(); public byte[] sign(byte[] input) throws SignatureException, IOException; + public String getPDFSubFilter(); + public String getPDFFilter(); } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/verify/VerifyResult.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/verify/VerifyResult.java index 339f7b15..b9d286c3 100644 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/verify/VerifyResult.java +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/api/verify/VerifyResult.java @@ -1,5 +1,6 @@ package at.gv.egiz.pdfas.lib.api.verify; +import iaik.x509.X509Certificate; import at.gv.egiz.pdfas.common.exceptions.PdfAsException; public interface VerifyResult { @@ -47,4 +48,7 @@ public interface VerifyResult { * certificate. */ public boolean isQualifiedCertificate(); + + + public X509Certificate getSignerCertificate(); } 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 5bda572b..a8cee107 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,12 +1,24 @@ package at.gv.egiz.pdfas.lib.impl; +import iaik.cms.CMSException; +import iaik.cms.CMSParsingException; +import iaik.cms.SignedData; +import iaik.cms.SignerInfo; import iaik.x509.X509Certificate; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.IOException; import java.io.OutputStream; +import java.security.SignatureException; import java.util.List; +import org.apache.pdfbox.cos.COSArray; +import org.apache.pdfbox.cos.COSBase; +import org.apache.pdfbox.cos.COSDictionary; +import org.apache.pdfbox.cos.COSName; +import org.apache.pdfbox.cos.COSString; import org.apache.pdfbox.pdmodel.PDDocument; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -17,6 +29,7 @@ import at.gv.egiz.pdfas.common.exceptions.PdfAsSettingsException; import at.gv.egiz.pdfas.common.settings.ISettings; import at.gv.egiz.pdfas.common.settings.Settings; import at.gv.egiz.pdfas.common.settings.SignatureProfileSettings; +import at.gv.egiz.pdfas.common.utils.StringUtils; import at.gv.egiz.pdfas.lib.api.Configuration; import at.gv.egiz.pdfas.lib.api.IConfigurationConstants; import at.gv.egiz.pdfas.lib.api.PdfAs; @@ -36,25 +49,28 @@ import at.gv.egiz.pdfas.lib.impl.stamping.StamperFactory; import at.gv.egiz.pdfas.lib.impl.stamping.TableFactory; import at.gv.egiz.pdfas.lib.impl.status.OperationStatus; import at.gv.egiz.pdfas.lib.impl.status.RequestedSignature; +import at.gv.egiz.pdfas.lib.impl.verify.IVerifyFilter; +import at.gv.egiz.pdfas.lib.impl.verify.VerifierDispatcher; import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction; import at.knowcenter.wag.egov.egiz.pdf.TablePos; import at.knowcenter.wag.egov.egiz.table.Table; public class PdfAsImpl implements PdfAs, IConfigurationConstants { - private static final Logger logger = LoggerFactory.getLogger(PdfAsImpl.class); - + private static final Logger logger = LoggerFactory + .getLogger(PdfAsImpl.class); + private Settings settings; - + public PdfAsImpl(File cfgFile) { logger.info("Initializing PDF-AS with config: " + cfgFile.getPath()); this.settings = new Settings(cfgFile); } - + public SignResult sign(SignParameter parameter) throws PdfAsException { logger.trace("sign started"); - + // TODO: verify signParameter try { @@ -62,7 +78,7 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants { if (!(parameter.getConfiguration() instanceof ISettings)) { throw new PdfAsSettingsException("Invalid settings object!"); } - + ISettings settings = (ISettings) parameter.getConfiguration(); OperationStatus status = new OperationStatus(settings, parameter); PlaceholderConfiguration placeholderConfiguration = status @@ -76,7 +92,7 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants { .getSignatureProfileID(); logger.info("Selected signature Profile: " + signatureProfileID); - + SignatureProfileConfiguration signatureProfileConfiguration = status .getSignatureProfileConfiguration(signatureProfileID); @@ -88,7 +104,7 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants { if (placeholderConfiguration.isGlobalPlaceholderEnabled()) { // TODO: Do placeholder search } - + if (requestedSignature.isVisual()) { logger.info("Creating visual siganture block"); // ================================================================ @@ -98,9 +114,11 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants { .createProfile(signatureProfileID, settings); Table main = TableFactory.createSigTable( - signatureProfileSettings, MAIN, settings, requestedSignature); + signatureProfileSettings, MAIN, settings, + requestedSignature); - IPDFStamper stamper = StamperFactory.createDefaultStamper(settings); + IPDFStamper stamper = StamperFactory + .createDefaultStamper(settings); IPDFVisualObject visualObject = stamper.createVisualPDFObject( status.getPdfObject(), main); @@ -149,19 +167,22 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants { // TODO: Create signature IPdfSigner signer = PdfSignerFactory.createPdfSigner(); - signer.signPDF(status.getPdfObject(), requestedSignature, status.getSignParamter().getPlainSigner()); - - //status.getPdfObject().setSignedDocument(status.getPdfObject().getStampedDocument()); - + signer.signPDF(status.getPdfObject(), requestedSignature, status + .getSignParamter().getPlainSigner()); + + // status.getPdfObject().setSignedDocument(status.getPdfObject().getStampedDocument()); + // ================================================================ // Create SignResult - SignResultImpl result = new SignResultImpl(status.getSignParamter().getOutput()); - OutputStream outputStream = result.getOutputDocument().createOutputStream(); - + SignResultImpl result = new SignResultImpl(status.getSignParamter() + .getOutput()); + OutputStream outputStream = result.getOutputDocument() + .createOutputStream(); + outputStream.write(status.getPdfObject().getSignedDocument()); - + outputStream.close(); - + return result; } catch (Throwable e) { logger.error("sign failed " + e.getMessage(), e); @@ -172,7 +193,85 @@ public class PdfAsImpl implements PdfAs, IConfigurationConstants { } public List verify(VerifyParameter parameter) { - // TODO Auto-generated method stub + try { + ISettings settings = (ISettings) parameter.getConfiguration(); + VerifierDispatcher verifier = new VerifierDispatcher(settings); + PDDocument doc = PDDocument.load(new ByteArrayInputStream(parameter + .getDataSource().getByteData())); + + COSDictionary trailer = doc.getDocument().getTrailer(); + COSDictionary root = (COSDictionary) trailer + .getDictionaryObject(COSName.ROOT); + COSDictionary acroForm = (COSDictionary) root + .getDictionaryObject(COSName.ACRO_FORM); + COSArray fields = (COSArray) acroForm + .getDictionaryObject(COSName.FIELDS); + for (int i = 0; i < fields.size(); i++) { + COSDictionary field = (COSDictionary) fields.getObject(i); + String type = field.getNameAsString("FT"); + if ("Sig".equals(type)) { + logger.trace("Found Signature: "); + COSBase base = field.getDictionaryObject("V"); + COSDictionary dict = (COSDictionary) base; + + logger.debug("Signer: " + + dict.getNameAsString("Name")); + logger.debug("SubFilter: " + + dict.getNameAsString("SubFilter")); + logger.debug("Filter: " + + dict.getNameAsString("Filter")); + logger.debug("Modified: " + dict.getNameAsString("M")); + COSArray byteRange = (COSArray) dict + .getDictionaryObject("ByteRange"); + + + StringBuilder sb = new StringBuilder(); + int[] bytes = new int[byteRange.size()]; + for (int j = 0; j < byteRange.size(); j++) { + bytes[j] = byteRange.getInt(j); + sb.append(" " + bytes[j]); + } + + logger.debug("ByteRange" + sb.toString()); + + COSString content = (COSString) dict + .getDictionaryObject("Contents"); + /*logger.trace("Content: " + + StringUtils.bytesToHexString(content.getBytes()));*/ + + ByteArrayOutputStream contentData = new ByteArrayOutputStream(); + for (int j = 0; j < bytes.length; j = j + 2) { + int offset = bytes[j]; + int length = bytes[j + 1]; + contentData.write(parameter.getDataSource() + .getByteData(), offset, length); + } + contentData.close(); + + IVerifyFilter verifyFilter = + verifier.getVerifier(dict.getNameAsString("Filter"), dict.getNameAsString("SubFilter")); + + verifyFilter.verify(contentData.toByteArray(), content.getBytes()); + + /* + * Iterator> iterator = + * dict.entrySet().iterator(); + * + * while(iterator.hasNext()) { Map.Entry + * entry = iterator.next(); System.out.println("Key: " + * +entry.getKey().toString()); + * + * } + */ + + } + } + } catch (IOException e) { + e.printStackTrace(); + } catch (PdfAsException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } return null; } 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 index 82ee57fe..7f16a87a 100644 --- 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 @@ -8,6 +8,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.util.Calendar; +import org.apache.pdfbox.cos.COSName; import org.apache.pdfbox.exceptions.COSVisitorException; import org.apache.pdfbox.exceptions.SignatureException; import org.apache.pdfbox.pdmodel.PDDocument; @@ -52,8 +53,8 @@ public class PADESPDFBOXSigner implements IPdfSigner { new ByteArrayInputStream(pdfObject.getStampedDocument())); PDSignature signature = new PDSignature(); - signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); // default filter - signature.setSubFilter(PDSignature.SUBFILTER_ETSI_CADES_DETACHED); + signature.setFilter(COSName.getPDFName(signer.getPDFFilter())); // default filter + signature.setSubFilter(COSName.getPDFName(signer.getPDFSubFilter())); SignatureProfileSettings signatureProfileSettings = TableFactory .createProfile(requestedSignature.getSignatureProfileID(), @@ -62,7 +63,8 @@ public class PADESPDFBOXSigner implements IPdfSigner { 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"); 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 deleted file mode 100644 index 85697436..00000000 --- a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/signing/sig_interface/JKSSigner.java +++ /dev/null @@ -1,78 +0,0 @@ -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/verify/FilterEntry.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/FilterEntry.java new file mode 100644 index 00000000..59b20c97 --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/FilterEntry.java @@ -0,0 +1,28 @@ +package at.gv.egiz.pdfas.lib.impl.verify; + +import org.apache.pdfbox.cos.COSName; + +public class FilterEntry { + private COSName filter; + private COSName subFilter; + + public FilterEntry(COSName filter, COSName subfilter) { + this.filter = filter; + this.subFilter = subfilter; + } + + public COSName getFilter() { + return filter; + } + public void setFilter(COSName filter) { + this.filter = filter; + } + public COSName getSubFilter() { + return subFilter; + } + public void setSubFilter(COSName subFilter) { + this.subFilter = subFilter; + } + + +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/IVerifyFilter.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/IVerifyFilter.java new file mode 100644 index 00000000..7aca582b --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/IVerifyFilter.java @@ -0,0 +1,11 @@ +package at.gv.egiz.pdfas.lib.impl.verify; + +import java.util.List; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.lib.api.verify.VerifyResult; + +public interface IVerifyFilter { + public List verify(byte[] contentData, byte[] signatureContent) throws PdfAsException; + public List getFiters(); +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/VerifierDispatcher.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/VerifierDispatcher.java new file mode 100644 index 00000000..0de3a71e --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/VerifierDispatcher.java @@ -0,0 +1,61 @@ +package at.gv.egiz.pdfas.lib.impl.verify; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.common.settings.ISettings; + +public class VerifierDispatcher { + + private static final Logger logger = LoggerFactory.getLogger(VerifierDispatcher.class); + + public static final String currentClass = "at.gv.egiz.pdfas.sigs.pkcs7detached.PKCS7DetachedVerifier"; + + public Map> filterMap = new HashMap>(); + + public VerifierDispatcher(ISettings settings) { + // TODO: read config build verify filter + try { + Class cls = (Class) Class.forName(currentClass); + IVerifyFilter fitler = cls.newInstance(); + List entries = fitler.getFiters(); + Iterator it = entries.iterator(); + while(it.hasNext()) { + FilterEntry entry = it.next(); + HashMap filters = filterMap.get(entry.getFilter().getName()); + if(filters == null) { + filters = new HashMap(); + filterMap.put(entry.getFilter().getName(), filters); + } + + IVerifyFilter oldFilter = filters.get(entry.getSubFilter().getName()); + + if(oldFilter != null) { + throw new PdfAsException("Filter allready registered"); + } + + filters.put(entry.getSubFilter().getName(), fitler); + logger.debug("Registered Filter: " + cls.getName() + " for " + entry.getFilter().getName() + "/" + entry.getSubFilter().getName()); + } + } catch(Throwable e) { + e.printStackTrace(); + } + + } + + public IVerifyFilter getVerifier(String filter, String subfilter) { + HashMap filters = filterMap.get(filter); + if(filters == null) { + return null; + } + + return filters.get(subfilter); + } +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/VerifyResultImpl.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/VerifyResultImpl.java new file mode 100644 index 00000000..451c1706 --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/VerifyResultImpl.java @@ -0,0 +1,75 @@ +package at.gv.egiz.pdfas.lib.impl.verify; + +import iaik.x509.X509Certificate; +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.lib.api.verify.SignatureCheck; +import at.gv.egiz.pdfas.lib.api.verify.VerifyResult; + +public class VerifyResultImpl implements VerifyResult { + + private boolean verificationDone; + private boolean qualifiedCertificate; + private PdfAsException verificationException; + private SignatureCheck certificateCheck; + private SignatureCheck valueCheck; + private SignatureCheck manifestCheck; + + private X509Certificate signerCertificate; + + public boolean isVerificationDone() { + return verificationDone; + } + + public void setVerificationDone(boolean value) { + this.verificationDone = value; + } + + public PdfAsException getVerificationException() { + return verificationException; + } + + public void setVerificationException(PdfAsException e) { + verificationException = e; + } + + public SignatureCheck getCertificateCheck() { + return certificateCheck; + } + + public void setCertificateCheck(SignatureCheck certificateCheck) { + this.certificateCheck=certificateCheck; + } + + public SignatureCheck getValueCheckCode() { + return valueCheck; + } + + public void setValueCheckCode(SignatureCheck valueCheck) { + this.valueCheck=valueCheck; + } + + public SignatureCheck getManifestCheckCode() { + return manifestCheck; + } + + public void setManifestCheckCode(SignatureCheck manifestCheck) { + this.manifestCheck=manifestCheck; + } + + public boolean isQualifiedCertificate() { + return qualifiedCertificate; + } + + public void setQualifiedCertificate(boolean value) { + this.qualifiedCertificate = value; + } + + public X509Certificate getSignerCertificate() { + return signerCertificate; + } + + public void setSignerCertificate(X509Certificate signerCertificate) { + this.signerCertificate = signerCertificate; + } + +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/package-info.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/package-info.java new file mode 100644 index 00000000..393a65b4 --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/impl/verify/package-info.java @@ -0,0 +1,8 @@ +/** + * + */ +/** + * @author afitzek + * + */ +package at.gv.egiz.pdfas.lib.impl.verify; \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index f439cf3c..4e98475c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ -include "pdf-as-common", "stamper:stmp-itext", "signature-standards:sigs-pades", "pdf-as-lib", "pdf-as-cli" +include "pdf-as-common", "stamper:stmp-itext", "signature-standards:sigs-pcks7detached", "signature-standards:sigs-pades", "pdf-as-lib", "pdf-as-cli" diff --git a/signature-standards/sigs-pcks7detached/.gitignore b/signature-standards/sigs-pcks7detached/.gitignore new file mode 100644 index 00000000..5e56e040 --- /dev/null +++ b/signature-standards/sigs-pcks7detached/.gitignore @@ -0,0 +1 @@ +/bin diff --git a/signature-standards/sigs-pcks7detached/build.gradle b/signature-standards/sigs-pcks7detached/build.gradle new file mode 100644 index 00000000..cc0a4e33 --- /dev/null +++ b/signature-standards/sigs-pcks7detached/build.gradle @@ -0,0 +1,34 @@ +apply plugin: 'java' +apply plugin: 'eclipse' + +jar { + manifest { + attributes 'Implementation-Title': 'Signature Standard PKCS7 Detached', 'Implementation-Version': version + } +} + +repositories { + mavenCentral() +} + +dependencies { + compile project (':pdf-as-lib') + compile project (':pdf-as-common') + 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' + compile group: 'commons-collections', name: 'commons-collections', version: '3.2' + testCompile group: 'junit', name: 'junit', version: '4.+' +} + +test { + systemProperties 'property': 'value' +} + +uploadArchives { + repositories { + flatDir { + dirs 'repos' + } + } +} diff --git a/signature-standards/sigs-pcks7detached/src/main/java/at/gv/egiz/pdfas/sigs/pkcs7detached/PKCS7DetachedSigner.java b/signature-standards/sigs-pcks7detached/src/main/java/at/gv/egiz/pdfas/sigs/pkcs7detached/PKCS7DetachedSigner.java new file mode 100644 index 00000000..864a31d1 --- /dev/null +++ b/signature-standards/sigs-pcks7detached/src/main/java/at/gv/egiz/pdfas/sigs/pkcs7detached/PKCS7DetachedSigner.java @@ -0,0 +1,88 @@ +package at.gv.egiz.pdfas.sigs.pkcs7detached; + +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.cos.COSName; +import org.apache.pdfbox.exceptions.SignatureException; +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner; + +public class PKCS7DetachedSigner implements IPlainSigner { + + PrivateKey privKey; + X509Certificate cert; + + public PKCS7DetachedSigner(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); + } + } + + public String getPDFSubFilter() { + return PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED.getName(); + } + + public String getPDFFilter() { + return PDSignature.FILTER_ADOBE_PPKLITE.getName(); + } + +} diff --git a/signature-standards/sigs-pcks7detached/src/main/java/at/gv/egiz/pdfas/sigs/pkcs7detached/PKCS7DetachedVerifier.java b/signature-standards/sigs-pcks7detached/src/main/java/at/gv/egiz/pdfas/sigs/pkcs7detached/PKCS7DetachedVerifier.java new file mode 100644 index 00000000..7807850b --- /dev/null +++ b/signature-standards/sigs-pcks7detached/src/main/java/at/gv/egiz/pdfas/sigs/pkcs7detached/PKCS7DetachedVerifier.java @@ -0,0 +1,74 @@ +package at.gv.egiz.pdfas.sigs.pkcs7detached; + +import iaik.cms.SignedData; +import iaik.cms.SignerInfo; +import iaik.x509.X509Certificate; + +import java.io.ByteArrayInputStream; +import java.security.SignatureException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import at.gv.egiz.pdfas.common.exceptions.PdfAsException; +import at.gv.egiz.pdfas.lib.api.verify.VerifyResult; +import at.gv.egiz.pdfas.lib.impl.verify.FilterEntry; +import at.gv.egiz.pdfas.lib.impl.verify.IVerifyFilter; +import at.gv.egiz.pdfas.lib.impl.verify.VerifyResultImpl; + +public class PKCS7DetachedVerifier implements IVerifyFilter { + + private static final Logger logger = LoggerFactory.getLogger(PKCS7DetachedVerifier.class); + + public List verify(byte[] contentData, byte[] signatureContent) + throws PdfAsException { + try { + List result = new ArrayList(); + SignedData signedData = new SignedData(new ByteArrayInputStream( + signatureContent)); + signedData.setContent(contentData); + + // get the signer infos + SignerInfo[] signerInfos = signedData.getSignerInfos(); + // verify the signatures + for (int i = 0; i < signerInfos.length; i++) { + VerifyResultImpl verifyResult = new VerifyResultImpl(); + try { + + // verify the signature for SignerInfo at index i + X509Certificate signer_cert = signedData.verify(i); + // if the signature is OK the certificate of the + // signer is returned + logger.info("Signature OK from signer: " + + signer_cert.getSubjectDN()); + verifyResult.setSignerCertificate(signer_cert); + } catch (SignatureException ex) { + // if the signature is not OK a SignatureException + // is thrown + logger.info("Signature ERROR from signer: " + + signedData.getCertificate( + signerInfos[i].getSignerIdentifier()) + .getSubjectDN()); + + verifyResult.setSignerCertificate( + signedData.getCertificate(signerInfos[i].getSignerIdentifier())); + } + result.add(verifyResult); + } + + return result; + } catch (Throwable e) { + throw new PdfAsException("Verify failed", e); + } + } + + public List getFiters() { + List result = new ArrayList(); + result.add(new FilterEntry(PDSignature.FILTER_ADOBE_PPKLITE, PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED)); + return result; + } + +} diff --git a/signature-standards/sigs-pcks7detached/src/main/java/at/gv/egiz/pdfas/sigs/pkcs7detached/package-info.java b/signature-standards/sigs-pcks7detached/src/main/java/at/gv/egiz/pdfas/sigs/pkcs7detached/package-info.java new file mode 100644 index 00000000..69a99830 --- /dev/null +++ b/signature-standards/sigs-pcks7detached/src/main/java/at/gv/egiz/pdfas/sigs/pkcs7detached/package-info.java @@ -0,0 +1,8 @@ +/** + * + */ +/** + * @author afitzek + * + */ +package at.gv.egiz.pdfas.sigs.pkcs7detached; \ No newline at end of file -- cgit v1.2.3