我遇到了签名验证失败的问题时,使用itext签署pdf.我把它作为一个参考'itext-pdf-deferred-signing-results-in-pdf-with-invalid-signature'修改做了,但问题仍然没有解决

 public static void emptySignature(String src, String dest, String fieldname, Certificate[] chain) throws IOException, DocumentException, GeneralSecurityException {
        PdfReader reader = new PdfReader(src);
        FileOutputStream os = new FileOutputStream(dest);
        PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0');
        PdfSignatureAppearance appearance = stamper.getSignatureAppearance();
        appearance.setVisibleSignature(new Rectangle(50, 780, 144, 780), 1, fieldname);
        appearance.setCertificate(chain[0]);
        ExternalSignatureContainer external = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
        MakeSignature.signExternalContainer(appearance, external, 8192);
        InputStream inp = appearance.getRangeStream();
        BouncyCastleDigest digest = new BouncyCastleDigest();
        byte[] hash = Digest算法rithms.digest(inp, digest.getMessageDigest("SHA256"));
//I need to pass this hash value to a third-party system and provide them with the generated signed hash for verification.
        System.out.println("Hash: " + Base64.getEncoder().encodeToString(hash));
    }

    public static void createSignature(String src, String dest, String fieldname, PrivateKey pk, Certificate[] chain) throws IOException, DocumentException, GeneralSecurityException {
        PdfReader reader = new PdfReader(src);
        FileOutputStream os = new FileOutputStream(dest);
        ExternalSignatureContainer external = new 我的外部签名容器(pk, chain);
        MakeSignature.signDeferred(reader, fieldname, os, external);
    }

主要

 public static void 主要(String[] args) throws DocumentException, GeneralSecurityException, IOException {
        //1.get temp empty file
        emptySignature("mypdf.pdf", "temp.pdf", "", null);//before deferred signing I can not get chain? How can I use this argument?
        //2.get cert from third party system
        Certificate[] cert = getCert();
        KeyStore ks = KeyStore.getInstance("PKCS12");
        ks.load(new FileInputStream("aaa.p12"), "ogcio".toCharArray());
        String alias = ks.aliases().nextElement();
        PrivateKey pk = (PrivateKey) ks.getKey(alias, "ogcio".toCharArray());
        createSignature("temp.pdf", "result.pdf", "", pk, cert);
    }

我的外部签名容器

public class 我的外部签名容器 implements ExternalSignatureContainer {
    protected PrivateKey pk;
    protected Certificate[] chain;

    public 我的外部签名容器(PrivateKey pk, Certificate[] chain) {
        this.pk = pk;
        this.chain = chain;
    }

    public byte[] sign(InputStream is) throws GeneralSecurityException {
        try {
            Security.addProvider(new BouncyCastleProvider());
            PrivateKeySignature signature = new PrivateKeySignature(pk, "SHA256", "BC");
            String hash算法rithm = signature.getHash算法rithm();
            BouncyCastleDigest digest = new BouncyCastleDigest();
            PdfPKCS7 sgn = new PdfPKCS7(null, chain, hash算法rithm, null, digest, false);
            byte[] hash = Digest算法rithms.digest(is, digest.getMessageDigest(hash算法rithm));
            byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, MakeSignature.CryptoStandard.CMS);
            byte[] extSignature = signature.sign(sh);
            sgn.setExternalDigest(extSignature, null, signature.getEncryption算法rithm());
            return sgn.getEncodedPKCS7(hash, null, null, null, MakeSignature.CryptoStandard.CMS);
        } catch (IOException ioe) {
            throw new ExceptionConverter(ioe);
        }
    }

    public void modifySigningDictionary(PdfDictionary signDic) {
        //signDic.put(PdfName.FILTER, PdfName.ADOBE_PPKLITE);
        //signDic.put(PdfName.SUBFILTER, PdfName.ADBE_PKCS7_DETACHED);
    }
}

我用上面的代码做的.但签名无效,消息"The document has been altered or decorated since the signs was appliced."" 我的pdf:"https://drive.google.com/file/d/1jcisJPvfMEXoLFP9Ku-p6nFXuNe7ICWr/view?usp=sharing"

推荐答案

现有代码无法成功签署PDF,因为它需要签署者证书才能签署,而iAM Smart签署服务(最终将使用代码)仅提供证书和签名.

这种只返回签名者证书和签名字节的签名服务通常很难用于PDF签名,因为大多数CMS签名容器生成器期望在开始组装待签名属性并请求签名之前访问所有数据(显然签名字节除外).对于PAdES PDF签名的特殊情况,它们甚至根本不能被使用,因为PAdES基线签名要求在待签名属性中提供有关签名者证书的信息.

在 comments 中,幸运的是,iAM Smart服务还提供了返回一个完整的PKCS#7对象(即CMS签名容器)的功能,该对象明确描述用于PDF签名.如果您使用此服务功能,您可以简化现有代码,并使用其使用iText签署PDF,如下所示:

File origFile = new File("deferredContainerSigningOriginal.pdf");
File tempFile = new File("deferredContainerSigningTemp.pdf");
File resultFile = new File("deferredContainerSigningResult.pdf");
String fieldName = "Signature";

byte[] hash = null;

try (
    OutputStream tempOutput = new FileOutputStream(tempFile)
) {
    PdfReader pdfReader = new PdfReader(origFile.getPath());
    hash = prepareAndHashForSigning(pdfReader, tempOutput, fieldName);
}

// execute your signature container request for the calculated hash here 
byte[] signatureContainer = retrievePkcs7ContainerForHash(hash);

try (
    OutputStream resultOutput = new FileOutputStream(resultFile)
) {
    PdfReader pdfReader = new PdfReader(tempFile.getPath());
    injectPkcs7Container(pdfReader, resultOutput, fieldName, signatureContainer);
}

使用以下helper方法和类:

/**
 * This method adds a signature field to the given PDF and sets its value signature
 * dictionary. The placeholder for the signature container therein is filled with 0s.
 * 
 * @return the hash of the signed byte ranges of the added signature.
 * @see #testExternalSignatureContainer()
 */
byte[] prepareAndHashForSigning(PdfReader pdfReader, OutputStream outputStream, String fieldName) throws DocumentException, IOException, GeneralSecurityException {
    PdfStamper pdfStamper = PdfStamper.createSignature(pdfReader, outputStream, (char) 0);
    PdfSignatureAppearance pdfSignatureAppearance = pdfStamper.getSignatureAppearance();
    pdfSignatureAppearance.setVisibleSignature(new Rectangle(50, 680, 144, 780), 1, fieldName);

    ExternalSignatureContainer blankContainer = new ExternalBlankSignatureContainer(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
    MakeSignature.signExternalContainer(pdfSignatureAppearance, blankContainer, 12000);
    InputStream rangeStream = pdfSignatureAppearance.getRangeStream();
    BouncyCastleDigest bouncyCastleDigest = new BouncyCastleDigest();
    byte[] hash = Digest算法rithms.digest(rangeStream, bouncyCastleDigest.getMessageDigest("SHA256"));
    return hash;
}

/**
 * This method sets the signature container placeholder in the signature dictionary
 * value of the signature field with the given name.
 * 
 * @see #testExternalSignatureContainer()
 */
void injectPkcs7Container(PdfReader pdfReader, OutputStream outputStream, String fieldName, byte[] signatureContainer) throws DocumentException, IOException, GeneralSecurityException {
    ExternalSignatureContainer injectingContainer = new InjectingSignatureContainer(signatureContainer);
    MakeSignature.signDeferred(pdfReader, fieldName, outputStream, injectingContainer);
}

/**
 * This is a dummy method that returns the given hash itself instead of a signature
 * container for it. Obviously, it is not for production purposes.
 * 
 * @see #testExternalSignatureContainer()
 */
byte[] retrievePkcs7ContainerForHash(byte[] hash) {
    return hash;
}

/**
 * This {@link ExternalSignatureContainer} implementation returns a pre-generated
 * byte array in its {@link #sign(InputStream)} method.
 */
static class InjectingSignatureContainer implements ExternalSignatureContainer {
    final byte[] signatureContainer;

    public InjectingSignatureContainer(byte[] signatureContainer) {
        this.signatureContainer = signatureContainer;
    }

    @Override
    public byte[] sign(InputStream data) throws GeneralSecurityException {
        return signatureContainer;
    }

    @Override
    public void modifySigningDictionary(PdfDictionary signDic) {
    }
}

(100 test)

这一切与您的代码非常相似,代码已经实现了许多正确的概念,但最终不得不使用不适当的签名服务功能.

请注意,我还修改了一些常量,特别是我将签名容器保留的大小从8192增加到12000(因为你提供的示例签名容器已经是10462大),并且我将矩形的坐标更改为高0.

Java相关问答推荐

ActivityCompat.请求收件箱自动拒绝权限

如果给定层次 struct 级别,如何从其预序穿越构造n元树

Java在模块化jar文件中找不到类,但是javap可以

如何在访问完所有文件后加入所有线程?

使用java访问具体子类特定方法的最佳方法是什么?

XPages-在第二次点击按钮之前延迟

为什么我的ArrayList索引的索引总是返回-1?

Java LocalTime.parse在本地PC上的Spring Boot中工作,但在Docker容器中不工作

我正在try 跟踪数组中最大的两个数字

Java ArrayList的整数和数组的泛型

如何集成语义发布和BitBucket(Java项目)

在Spring Boot应用程序中,server.port=0的默认端口范围是多少?

如何在@CsvSource中传递空格作为值

如何在Spring Boot Auth服务器上正确配置CORS?

Bash数组的单引号元素并使用空格连接

如何设置默认序列生成器分配大小

OpenJDK20:JEP434:Foreign Function&;内存API(第二次预览)

如何使用我的RLE程序解决此问题

将在 Docker 中运行的 Spring Boot 连接到在 Docker 中运行的 PostgreSQL,无需 compose 文件?

Spring Boot应用程序中的自定义constraintvalidator不会被调用