From 9bc71794a49d89e1ff6adab76f5f473b94b3d348 Mon Sep 17 00:00:00 2001 From: Andreas Fitzek Date: Thu, 12 Dec 2013 12:17:05 +0100 Subject: Creation of PAdES signatures with local keystore --- .../src/main/java/at/gv/egiz/pdfas/cli/Main.java | 3 +- .../gv/egiz/pdfas/lib/util/CertificateUtils.java | 53 +++++++ .../at/gv/egiz/pdfas/lib/util/package-info.java | 8 ++ .../egiz/pdfas/sigs/pades/PAdESSignerKeystore.java | 154 +++++++++++++++++++++ 4 files changed, 217 insertions(+), 1 deletion(-) create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/CertificateUtils.java create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/package-info.java create mode 100644 signature-standards/sigs-pades/src/main/java/at/gv/egiz/pdfas/sigs/pades/PAdESSignerKeystore.java diff --git a/pdf-as-cli/src/main/java/at/gv/egiz/pdfas/cli/Main.java b/pdf-as-cli/src/main/java/at/gv/egiz/pdfas/cli/Main.java index eeff47c7..578175a8 100644 --- a/pdf-as-cli/src/main/java/at/gv/egiz/pdfas/cli/Main.java +++ b/pdf-as-cli/src/main/java/at/gv/egiz/pdfas/cli/Main.java @@ -27,6 +27,7 @@ import at.gv.egiz.pdfas.lib.api.sign.SignResult; import at.gv.egiz.pdfas.lib.api.verify.VerifyParameter; import at.gv.egiz.pdfas.lib.api.verify.VerifyResult; import at.gv.egiz.pdfas.sigs.pades.PAdESSigner; +import at.gv.egiz.pdfas.sigs.pades.PAdESSignerKeystore; import at.gv.egiz.pdfas.sigs.pkcs7detached.PKCS7DetachedSigner; import at.gv.egiz.sl.util.BKUSLConnector; import at.gv.egiz.sl.util.MOAConnector; @@ -341,7 +342,7 @@ public class Main { keystoreKeypass = ""; } - slConnector = new PKCS7DetachedSigner(keystoreFilename, + slConnector = new PAdESSignerKeystore(keystoreFilename, keystoreAlias, keystoreStorepass, keystoreKeypass, keystoreType); } } diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/CertificateUtils.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/CertificateUtils.java new file mode 100644 index 00000000..2f4fbe10 --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/CertificateUtils.java @@ -0,0 +1,53 @@ +package at.gv.egiz.pdfas.lib.util; + +import iaik.asn1.structures.AlgorithmID; +import iaik.x509.X509Certificate; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECParameterSpec; + +public class CertificateUtils { + public static AlgorithmID[] getAlgorithmIDs(X509Certificate signingCertificate) + throws NoSuchAlgorithmException { + PublicKey publicKey = signingCertificate.getPublicKey(); + String algorithm = publicKey.getAlgorithm(); + AlgorithmID[] algorithms = new AlgorithmID[2]; + AlgorithmID signatureAlgorithm; + AlgorithmID digestAlgorithm; + + if ("DSA".equals(algorithm)) { + signatureAlgorithm = AlgorithmID.dsaWithSHA256; + digestAlgorithm = AlgorithmID.sha256; + } else if ("RSA".equals(algorithm)) { + signatureAlgorithm = AlgorithmID.sha256WithRSAEncryption; + digestAlgorithm = AlgorithmID.sha256; + } else if (("EC".equals(algorithm)) || ("ECDSA".equals(algorithm))) { + + int fieldSize = 0; + if (publicKey instanceof ECPublicKey) { + ECParameterSpec params = ((ECPublicKey) publicKey).getParams(); + fieldSize = params.getCurve().getField().getFieldSize(); + } + + if (fieldSize >= 512) { + signatureAlgorithm = AlgorithmID.ecdsa_With_SHA512; + digestAlgorithm = AlgorithmID.sha512; + } else if (fieldSize >= 256) { + signatureAlgorithm = AlgorithmID.ecdsa_With_SHA256; + digestAlgorithm = AlgorithmID.sha256; + } else { + signatureAlgorithm = AlgorithmID.ecdsa_With_SHA1; + digestAlgorithm = AlgorithmID.sha1; + } + } else { + throw new NoSuchAlgorithmException("Public key algorithm '" + + algorithm + "' not supported."); + } + + algorithms[0] = signatureAlgorithm; + algorithms[1] = digestAlgorithm; + + return algorithms; + } +} diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/package-info.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/package-info.java new file mode 100644 index 00000000..e635fb93 --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/lib/util/package-info.java @@ -0,0 +1,8 @@ +/** + * + */ +/** + * @author afitzek + * + */ +package at.gv.egiz.pdfas.lib.util; \ No newline at end of file diff --git a/signature-standards/sigs-pades/src/main/java/at/gv/egiz/pdfas/sigs/pades/PAdESSignerKeystore.java b/signature-standards/sigs-pades/src/main/java/at/gv/egiz/pdfas/sigs/pades/PAdESSignerKeystore.java new file mode 100644 index 00000000..5ae0ed76 --- /dev/null +++ b/signature-standards/sigs-pades/src/main/java/at/gv/egiz/pdfas/sigs/pades/PAdESSignerKeystore.java @@ -0,0 +1,154 @@ +package at.gv.egiz.pdfas.sigs.pades; + +import iaik.asn1.ASN1Object; +import iaik.asn1.CodingException; +import iaik.asn1.ObjectID; +import iaik.asn1.SEQUENCE; +import iaik.asn1.UTF8String; +import iaik.asn1.structures.AlgorithmID; +import iaik.asn1.structures.Attribute; +import iaik.asn1.structures.ChoiceOfTime; +import iaik.cms.ContentInfo; +import iaik.cms.IssuerAndSerialNumber; +import iaik.cms.SignedData; +import iaik.cms.SignerInfo; +import iaik.smime.ess.ESSCertID; +import iaik.smime.ess.ESSCertIDv2; +import iaik.x509.X509Certificate; + +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 java.security.cert.CertificateException; +import java.util.ArrayList; +import java.util.Date; +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.common.exceptions.PdfAsSignatureException; +import at.gv.egiz.pdfas.lib.api.sign.IPlainSigner; +import at.gv.egiz.pdfas.lib.util.CertificateUtils; + +public class PAdESSignerKeystore implements IPlainSigner { + + private static final Logger logger = LoggerFactory + .getLogger(PAdESSignerKeystore.class); + + PrivateKey privKey; + X509Certificate cert; + + public PAdESSignerKeystore(String file, String alias, String kspassword, + String keypassword, String type) throws PdfAsException { + try { + 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; + } + + private void setMimeTypeAttrib(List attributes, String mimeType) { + String oidStr = "0.4.0.1733.2.1"; + String name = "mime-type"; + ObjectID mimeTypeOID = new ObjectID(oidStr, name); + + Attribute mimeTypeAtt = new Attribute(mimeTypeOID, new ASN1Object[] {new UTF8String(mimeType)}); + attributes.add(mimeTypeAtt); + } + + private void setContentTypeAttrib(List attributes) { + Attribute contentType = new Attribute(ObjectID.contentType, new ASN1Object[] {ObjectID.cms_data}); + attributes.add(contentType); + } + + private void setSigningCertificateAttrib(List attributes, X509Certificate signingCertificate) throws CertificateException, NoSuchAlgorithmException, CodingException { + ObjectID id; + ASN1Object value = new SEQUENCE(); + AlgorithmID[] algorithms = CertificateUtils.getAlgorithmIDs(signingCertificate); + if (algorithms[1].equals(AlgorithmID.sha1)) { + id = ObjectID.signingCertificate; + value.addComponent(new ESSCertID(signingCertificate, true).toASN1Object()); + } + else { + id = ObjectID.signingCertificateV2; + value.addComponent(new ESSCertIDv2(algorithms[1], signingCertificate, true).toASN1Object()); + } + ASN1Object signingCert = new SEQUENCE(); + signingCert.addComponent(value); + Attribute signingCertificateAttrib = new Attribute(id, new ASN1Object[] {signingCert}); + attributes.add(signingCertificateAttrib); + } + + private void setSigningTimeAttrib(List attributes, Date date) { + Attribute signingTime = new Attribute(ObjectID.signingTime, new ASN1Object[] {new ChoiceOfTime(date).toASN1Object()}); + attributes.add(signingTime); + } + + private void setAttributes(String mimeType, X509Certificate signingCertificate, Date signingTime, + SignerInfo signerInfo) throws CertificateException, NoSuchAlgorithmException, CodingException { + List attributes = new ArrayList(); + setMimeTypeAttrib(attributes, mimeType); + setContentTypeAttrib(attributes); + setSigningCertificateAttrib(attributes, signingCertificate); + setSigningTimeAttrib(attributes, signingTime); + Attribute[] attributeArray = attributes.toArray(new Attribute[attributes.size()]); + signerInfo.setSignedAttributes(attributeArray); + } + + public byte[] sign(byte[] input, int[] byteRange) throws PdfAsException { + try { + IssuerAndSerialNumber issuer = new IssuerAndSerialNumber(cert); + + AlgorithmID[] algorithms = CertificateUtils.getAlgorithmIDs(cert); + + SignerInfo signer1 = new SignerInfo(issuer, algorithms[1], + algorithms[0], privKey); + + SignedData si = new SignedData(input, SignedData.EXPLICIT); + si.addCertificates(new Certificate[] { cert }); + setAttributes("application/pdf", cert, new Date(), signer1); + si.addSignerInfo(signer1); + InputStream dataIs = si.getInputStream(); + byte[] buf = new byte[1024]; + int r; + while ((r = dataIs.read(buf)) > 0) + ; // skip data + ContentInfo ci = new ContentInfo(si); + + return ci.getEncoded(); + } catch (NoSuchAlgorithmException e) { + throw new PdfAsSignatureException("Failed to generate Signature", e); + } catch (iaik.cms.CMSException e) { + throw new PdfAsSignatureException("Failed to generate Signature", e); + } catch (IOException e) { + throw new PdfAsSignatureException("Failed to generate Signature", e); + } catch (CertificateException e) { + throw new PdfAsSignatureException("Failed to generate Signature", e); + } catch (CodingException e) { + throw new PdfAsSignatureException("Failed to generate Signature", e); + } + } + + public String getPDFSubFilter() { + return PDSignature.SUBFILTER_ETSI_CADES_DETACHED.getName(); + } + + public String getPDFFilter() { + return PDSignature.FILTER_ADOBE_PPKLITE.getName(); + } + +} \ No newline at end of file -- cgit v1.2.3