diff options
author | Andreas Fitzek <andreas.fitzek@iaik.tugraz.at> | 2013-10-02 10:28:30 +0200 |
---|---|---|
committer | Andreas Fitzek <andreas.fitzek@iaik.tugraz.at> | 2013-10-02 10:28:30 +0200 |
commit | 0876981fd70fdab07f7c3e1666cf77071b5fe03d (patch) | |
tree | 0661571d1d757383fee936d1c4648d2c31b43ddb /pdf-as-lib | |
parent | add4460d9619f3586a02ae0d8c028f01903494bc (diff) | |
download | pdf-as-4-0876981fd70fdab07f7c3e1666cf77071b5fe03d.tar.gz pdf-as-4-0876981fd70fdab07f7c3e1666cf77071b5fe03d.tar.bz2 pdf-as-4-0876981fd70fdab07f7c3e1666cf77071b5fe03d.zip |
+ added PKCS7 detached siganture standard via keystore
+ added simple verification implementation
Diffstat (limited to 'pdf-as-lib')
10 files changed, 314 insertions, 101 deletions
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<VerifyResult> 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<Map.Entry<COSName, COSBase>> iterator = + * dict.entrySet().iterator(); + * + * while(iterator.hasNext()) { Map.Entry<COSName, COSBase> + * 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<VerifyResult> verify(byte[] contentData, byte[] signatureContent) throws PdfAsException; + public List<FilterEntry> 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<String, HashMap<String, IVerifyFilter>> filterMap = new HashMap<String, HashMap<String, IVerifyFilter>>(); + + public VerifierDispatcher(ISettings settings) { + // TODO: read config build verify filter + try { + Class<? extends IVerifyFilter> cls = (Class<? extends IVerifyFilter>) Class.forName(currentClass); + IVerifyFilter fitler = cls.newInstance(); + List<FilterEntry> entries = fitler.getFiters(); + Iterator<FilterEntry> it = entries.iterator(); + while(it.hasNext()) { + FilterEntry entry = it.next(); + HashMap<String, IVerifyFilter> filters = filterMap.get(entry.getFilter().getName()); + if(filters == null) { + filters = new HashMap<String, IVerifyFilter>(); + 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<String, IVerifyFilter> 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 |