Itext pdf 延迟签名且签名无效

问题描述 投票:0回答:1

我在使用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 = DigestAlgorithms.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 MyExternalSignatureContainer(pk, chain);
        MakeSignature.signDeferred(reader, fieldname, os, external);
    }

主要

 public static void main(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 MyExternalSignatureContainer implements ExternalSignatureContainer {
    protected PrivateKey pk;
    protected Certificate[] chain;

    public MyExternalSignatureContainer(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 hashAlgorithm = signature.getHashAlgorithm();
            BouncyCastleDigest digest = new BouncyCastleDigest();
            PdfPKCS7 sgn = new PdfPKCS7(null, chain, hashAlgorithm, null, digest, false);
            byte[] hash = DigestAlgorithms.digest(is, digest.getMessageDigest(hashAlgorithm));
            byte[] sh = sgn.getAuthenticatedAttributeBytes(hash, null, null, MakeSignature.CryptoStandard.CMS);
            byte[] extSignature = signature.sign(sh);
            sgn.setExternalDigest(extSignature, null, signature.getEncryptionAlgorithm());
            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);
    }
}

我使用上面的一些代码来做。但签名无效,并显示消息“自应用签名以来,文档已被更改或损坏”。 我的 pdf:“https://drive.google.com/file/d/1jcisJPvfMEXoLFP9Ku-p6nFXuNe7ICWr/view?usp=sharing

java pdf itext digital-signature
1个回答
0
投票

现有代码无法成功签署 PDF,因为在签名之前需要签名者证书,而 iAM 智能签名服务(最终将使用该代码)仅提供证书和签名。

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

在评论中幸运的是,iAM 智能服务还提供了返回明确配置用于 PDF 签名的完整 PKCS#7 对象(即 CMS 签名容器)的功能。如果使用此服务功能,则可以简化现有代码并使用它来使用 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);
}

使用以下辅助方法和类:

/**
 * 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 = DigestAlgorithms.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) {
    }
}

CreateDeferredSignature测试)

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

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

© www.soinside.com 2019 - 2024. All rights reserved.