From 71879f208526784a6125430ba84804b47f436180 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Mon, 30 Oct 2023 00:11:11 +0800 Subject: [PATCH] feat: udpates --- .../hatter/tool/signpdf/main/SignPdfMain.java | 24 +++++---- .../tool/signpdf/options/SignOptions.java | 9 ++++ .../{ => sign}/CMSProcessableInputStream.java | 2 +- .../signpdf/{ => sign}/CreateSignature.java | 9 ++-- .../{ => sign}/CreateSignatureBase.java | 53 ++++--------------- .../tool/signpdf/{ => sign}/SigUtils.java | 20 ++++++- .../tool/signpdf/{ => time}/TSAClient.java | 4 +- .../{ => time}/ValidationTimeStamp.java | 2 +- 8 files changed, 63 insertions(+), 60 deletions(-) rename src/main/java/me/hatter/tool/signpdf/{ => sign}/CMSProcessableInputStream.java (97%) rename src/main/java/me/hatter/tool/signpdf/{ => sign}/CreateSignature.java (95%) rename src/main/java/me/hatter/tool/signpdf/{ => sign}/CreateSignatureBase.java (59%) rename src/main/java/me/hatter/tool/signpdf/{ => sign}/SigUtils.java (91%) rename src/main/java/me/hatter/tool/signpdf/{ => time}/TSAClient.java (97%) rename src/main/java/me/hatter/tool/signpdf/{ => time}/ValidationTimeStamp.java (98%) diff --git a/src/main/java/me/hatter/tool/signpdf/main/SignPdfMain.java b/src/main/java/me/hatter/tool/signpdf/main/SignPdfMain.java index 7401a5e..8dbe3b0 100644 --- a/src/main/java/me/hatter/tool/signpdf/main/SignPdfMain.java +++ b/src/main/java/me/hatter/tool/signpdf/main/SignPdfMain.java @@ -1,10 +1,15 @@ package me.hatter.tool.signpdf.main; -import me.hatter.tool.signpdf.CreateSignature; import me.hatter.tool.signpdf.options.SignOptions; +import me.hatter.tool.signpdf.sign.CreateSignature; +import me.hatter.tool.signpdf.sign.SigUtils; +import me.hatter.tool.signpdf.time.TSAClient; import me.hatter.tools.commons.io.RFile; import me.hatter.tools.commons.security.cert.X509CertUtil; import me.hatter.tools.commons.security.key.KeyUtil; +import me.hatter.tools.commons.string.StringUtil; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import java.io.File; import java.security.PrivateKey; @@ -18,7 +23,11 @@ public class SignPdfMain { signOptions.setName("Example User"); signOptions.setLocation("Hangzhou, ZJ"); signOptions.setReason("Testing"); - final String tsaUrl = "https://hatter.ink/ca/sign_timestamp.action"; + + final File inFile = new File("Resume.pdf"); + final String name = inFile.getName(); + final String substring = name.substring(0, name.lastIndexOf('.')); + final File outFile = new File(inFile.getParent(), substring + "_signed.pdf"); final List certs = X509CertUtil.parseX509CertificateList( RFile.from("__certs.pem").string() @@ -28,15 +37,12 @@ public class SignPdfMain { ); final X509Certificate[] chain = certs.toArray(new X509Certificate[0]); - - final CreateSignature signing = new CreateSignature(signOptions, chain, privateKey); + final String signatureAlgorithm = SigUtils.getSignatureAlgorithm(chain[0]); + final ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).build(privateKey); + final CreateSignature signing = new CreateSignature(signOptions, chain, contentSigner); // signing.setExternalSigning(true); - final File inFile = new File("Resume.pdf"); - final String name = inFile.getName(); - final String substring = name.substring(0, name.lastIndexOf('.')); - - final File outFile = new File(inFile.getParent(), substring + "_signed.pdf"); + final String tsaUrl = StringUtil.def(signOptions.getTsaUrl(), TSAClient.DEFAULT_TSA_URL); signing.signDetached(inFile, outFile, tsaUrl); } } diff --git a/src/main/java/me/hatter/tool/signpdf/options/SignOptions.java b/src/main/java/me/hatter/tool/signpdf/options/SignOptions.java index 688371b..83b2271 100644 --- a/src/main/java/me/hatter/tool/signpdf/options/SignOptions.java +++ b/src/main/java/me/hatter/tool/signpdf/options/SignOptions.java @@ -4,6 +4,7 @@ public class SignOptions { private String name; private String location; private String reason; + private String tsaUrl; public String getName() { return name; @@ -28,4 +29,12 @@ public class SignOptions { public void setReason(String reason) { this.reason = reason; } + + public String getTsaUrl() { + return tsaUrl; + } + + public void setTsaUrl(String tsaUrl) { + this.tsaUrl = tsaUrl; + } } diff --git a/src/main/java/me/hatter/tool/signpdf/CMSProcessableInputStream.java b/src/main/java/me/hatter/tool/signpdf/sign/CMSProcessableInputStream.java similarity index 97% rename from src/main/java/me/hatter/tool/signpdf/CMSProcessableInputStream.java rename to src/main/java/me/hatter/tool/signpdf/sign/CMSProcessableInputStream.java index 56b5f3e..22088a9 100644 --- a/src/main/java/me/hatter/tool/signpdf/CMSProcessableInputStream.java +++ b/src/main/java/me/hatter/tool/signpdf/sign/CMSProcessableInputStream.java @@ -1,4 +1,4 @@ -package me.hatter.tool.signpdf; +package me.hatter.tool.signpdf.sign; import org.apache.pdfbox.io.IOUtils; import org.bouncycastle.asn1.ASN1ObjectIdentifier; diff --git a/src/main/java/me/hatter/tool/signpdf/CreateSignature.java b/src/main/java/me/hatter/tool/signpdf/sign/CreateSignature.java similarity index 95% rename from src/main/java/me/hatter/tool/signpdf/CreateSignature.java rename to src/main/java/me/hatter/tool/signpdf/sign/CreateSignature.java index 72be307..64ea9e5 100644 --- a/src/main/java/me/hatter/tool/signpdf/CreateSignature.java +++ b/src/main/java/me/hatter/tool/signpdf/sign/CreateSignature.java @@ -1,17 +1,16 @@ -package me.hatter.tool.signpdf; +package me.hatter.tool.signpdf.sign; import me.hatter.tool.signpdf.options.SignOptions; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.interactive.digitalsignature.ExternalSigningSupport; import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature; import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions; +import org.bouncycastle.operator.ContentSigner; import java.io.*; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; import java.security.UnrecoverableKeyException; -import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Calendar; @@ -33,9 +32,9 @@ import java.util.Calendar; public class CreateSignature extends CreateSignatureBase { private final SignOptions signOptions; - public CreateSignature(SignOptions signOptions, X509Certificate[] certificateChain, PrivateKey privateKey) + public CreateSignature(SignOptions signOptions, X509Certificate[] certificateChain, ContentSigner contentSigner) throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, CertificateException, IOException { - super(certificateChain, privateKey); + super(certificateChain, contentSigner); this.signOptions = signOptions; } diff --git a/src/main/java/me/hatter/tool/signpdf/CreateSignatureBase.java b/src/main/java/me/hatter/tool/signpdf/sign/CreateSignatureBase.java similarity index 59% rename from src/main/java/me/hatter/tool/signpdf/CreateSignatureBase.java rename to src/main/java/me/hatter/tool/signpdf/sign/CreateSignatureBase.java index 8a298fc..f74c425 100644 --- a/src/main/java/me/hatter/tool/signpdf/CreateSignatureBase.java +++ b/src/main/java/me/hatter/tool/signpdf/sign/CreateSignatureBase.java @@ -1,5 +1,6 @@ -package me.hatter.tool.signpdf; +package me.hatter.tool.signpdf.sign; +import me.hatter.tool.signpdf.time.ValidationTimeStamp; import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureInterface; import org.bouncycastle.cert.jcajce.JcaCertStore; import org.bouncycastle.cms.CMSException; @@ -8,49 +9,30 @@ import org.bouncycastle.cms.CMSSignedDataGenerator; import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.OperatorCreationException; -import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import java.io.IOException; import java.io.InputStream; -import java.security.*; +import java.security.GeneralSecurityException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; -import java.security.interfaces.ECPublicKey; -import java.security.interfaces.RSAPublicKey; import java.util.Arrays; // https://svn.apache.org/viewvc/pdfbox/trunk/examples/src/main/java/ // org/apache/pdfbox/examples/signature/CreateSignatureBase.java?view=co public abstract class CreateSignatureBase implements SignatureInterface { - private PrivateKey privateKey; - private X509Certificate[] certificateChain; + private final ContentSigner contentSigner; + private final X509Certificate[] certificateChain; private String tsaUrl; private boolean externalSigning; - public CreateSignatureBase(X509Certificate[] certificateChain, PrivateKey privateKey) - throws KeyStoreException, UnrecoverableKeyException, NoSuchAlgorithmException, IOException, CertificateException { - // grabs the first alias from the keystore and get the private key. An - // alternative method or constructor could be used for setting a specific - // alias that should be used. - this.privateKey = privateKey; + public CreateSignatureBase(X509Certificate[] certificateChain, ContentSigner contentSigner) + throws CertificateException { + this.contentSigner = contentSigner; this.certificateChain = certificateChain; - - final X509Certificate cert = certificateChain[0]; - cert.checkValidity(); - SigUtils.checkCertificateUsage(cert); - } - - public final void setPrivateKey(PrivateKey privateKey) { - this.privateKey = privateKey; - } - - public final void setCertificateChain(final X509Certificate[] certificateChain) { - this.certificateChain = certificateChain; - } - - public X509Certificate[] getCertificateChain() { - return certificateChain; + final X509Certificate firstCertificate = certificateChain[0]; + firstCertificate.checkValidity(); + SigUtils.checkCertificateUsage(firstCertificate); } public void setTsaUrl(String tsaUrl) { @@ -62,20 +44,7 @@ public abstract class CreateSignatureBase implements SignatureInterface { try { final CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); final X509Certificate cert = certificateChain[0]; - // TODO use customer signer - final boolean isEcPublicKey = cert.getPublicKey() instanceof ECPublicKey; - final boolean isRsaPublicKey = cert.getPublicKey() instanceof RSAPublicKey; - final String signatureAlgorithm; - if (isEcPublicKey) { - signatureAlgorithm = "SHA256WithECDSA"; - } else if (isRsaPublicKey) { - signatureAlgorithm = "SHA256WithRSA"; - } else { - throw new RuntimeException("Supported algorithm: " + cert.getPublicKey().getClass()); - } - final ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm) - .build(privateKey); gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder( new JcaDigestCalculatorProviderBuilder().build()).build(contentSigner, cert)); diff --git a/src/main/java/me/hatter/tool/signpdf/SigUtils.java b/src/main/java/me/hatter/tool/signpdf/sign/SigUtils.java similarity index 91% rename from src/main/java/me/hatter/tool/signpdf/SigUtils.java rename to src/main/java/me/hatter/tool/signpdf/sign/SigUtils.java index 92c4e8c..38fda2c 100644 --- a/src/main/java/me/hatter/tool/signpdf/SigUtils.java +++ b/src/main/java/me/hatter/tool/signpdf/sign/SigUtils.java @@ -1,4 +1,4 @@ -package me.hatter.tool.signpdf; +package me.hatter.tool.signpdf.sign; import me.hatter.tools.commons.log.LogTool; import me.hatter.tools.commons.log.LogTools; @@ -13,6 +13,8 @@ import org.bouncycastle.asn1.x509.KeyPurposeId; import java.io.IOException; import java.security.cert.CertificateParsingException; import java.security.cert.X509Certificate; +import java.security.interfaces.ECPublicKey; +import java.security.interfaces.RSAPublicKey; import java.util.List; import java.util.SortedMap; import java.util.TreeMap; @@ -28,9 +30,25 @@ import java.util.TreeMap; public class SigUtils { private static final LogTool LOG = LogTools.getLogTool(SigUtils.class); + public static final String SIGNATURE_ALGORITHM_SHA256_WITH_ECDSA = "SHA256WithECDSA"; + public static final String SIGNATURE_ALGORITHM_SHA256_WITH_RSA = "SHA256WithRSA"; + private SigUtils() { } + public static String getSignatureAlgorithm(X509Certificate cert) { + final boolean isEcPublicKey = cert.getPublicKey() instanceof ECPublicKey; + final boolean isRsaPublicKey = cert.getPublicKey() instanceof RSAPublicKey; + + if (isEcPublicKey) { + return SIGNATURE_ALGORITHM_SHA256_WITH_ECDSA; + } else if (isRsaPublicKey) { + return SIGNATURE_ALGORITHM_SHA256_WITH_RSA; + } else { + throw new RuntimeException("Supported algorithm: " + cert.getPublicKey().getClass()); + } + } + /** * Get the access permissions granted for this document in the DocMDP transform parameters * dictionary. Details are described in the table "Entries in the DocMDP transform parameters diff --git a/src/main/java/me/hatter/tool/signpdf/TSAClient.java b/src/main/java/me/hatter/tool/signpdf/time/TSAClient.java similarity index 97% rename from src/main/java/me/hatter/tool/signpdf/TSAClient.java rename to src/main/java/me/hatter/tool/signpdf/time/TSAClient.java index 840152d..2981ba6 100644 --- a/src/main/java/me/hatter/tool/signpdf/TSAClient.java +++ b/src/main/java/me/hatter/tool/signpdf/time/TSAClient.java @@ -1,4 +1,4 @@ -package me.hatter.tool.signpdf; +package me.hatter.tool.signpdf.time; import me.hatter.tools.commons.io.RStream; import me.hatter.tools.commons.log.LogTool; @@ -29,6 +29,8 @@ import java.security.SecureRandom; public class TSAClient { private static final LogTool LOG = LogTools.getLogTool(TSAClient.class); + public static final String DEFAULT_TSA_URL = "https://hatter.ink/ca/sign_timestamp.action"; + private final URL url; private final String username; private final String password; diff --git a/src/main/java/me/hatter/tool/signpdf/ValidationTimeStamp.java b/src/main/java/me/hatter/tool/signpdf/time/ValidationTimeStamp.java similarity index 98% rename from src/main/java/me/hatter/tool/signpdf/ValidationTimeStamp.java rename to src/main/java/me/hatter/tool/signpdf/time/ValidationTimeStamp.java index 8196aa7..73d5554 100644 --- a/src/main/java/me/hatter/tool/signpdf/ValidationTimeStamp.java +++ b/src/main/java/me/hatter/tool/signpdf/time/ValidationTimeStamp.java @@ -1,4 +1,4 @@ -package me.hatter.tool.signpdf; +package me.hatter.tool.signpdf.time; import org.apache.pdfbox.io.IOUtils; import org.bouncycastle.asn1.*;