当前位置: 首页 > 知识库问答 >
问题:

根据文件摘要创建pkcs7签名

江志业
2023-03-14

目前,我有一个客户机-服务器应用程序,给定一个PDF文件,它对其进行签名(使用服务器证书),将签名附加到原始文件,并将输出返回给客户机(所有这些都是通过PDFBox实现的)。
我有一个签名处理程序,它是我的外部签名支持(其中的内容是PDF文件)

    public byte[] sign(InputStream content) throws IOException {
    try {
        System.out.println("Generating CMS signed data");
        CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
        ContentSigner sha1Signer = new JcaContentSignerBuilder("Sha1WithRSA").build(privateKey);
        generator.addSignerInfoGenerator(
                new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build())
                        .build(sha1Signer, new X509CertificateHolder(certificate.getEncoded())));
        CMSTypedData cmsData = new CMSProcessableByteArray(IOUtils.toByteArray(content));
        CMSSignedData signedData = generator.generate(cmsData, false);

        return signedData.getEncoded();
    } catch (GeneralSecurityException e) {
        throw new IOException(e);
    } catch (CMSException e) {
        throw new IOException(e);
    } catch (OperatorCreationException e) {
        throw new IOException(e);
    }
}

它工作得很好,但我在想--如果PDF文件太大无法上传怎么办?例如:100MB...这会花上很长时间的!鉴于此,我试图弄清楚,如果不签署PDF文件,是否可能只是签署该文件的散列(如SHA1),然后客户机最终把它放在一起?

更新:

    @Override
public byte[] sign(InputStream content) throws IOException {
    // testSHA1WithRSAAndAttributeTable
    try {
        MessageDigest md = MessageDigest.getInstance("SHA1", "BC");
        List<Certificate> certList = new ArrayList<Certificate>();
        CMSTypedData msg = new CMSProcessableByteArray(IOUtils.toByteArray(content));

        certList.add(certificate);

        Store certs = new JcaCertStore(certList);

        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();

        Attribute attr = new Attribute(CMSAttributes.messageDigest,
                new DERSet(new DEROctetString(md.digest(IOUtils.toByteArray(content)))));

        ASN1EncodableVector v = new ASN1EncodableVector();

        v.add(attr);

        SignerInfoGeneratorBuilder builder = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider())
                .setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v)));

        AlgorithmIdentifier sha1withRSA = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");

        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
        InputStream in = new ByteArrayInputStream(certificate.getEncoded());
        X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in);

        gen.addSignerInfoGenerator(builder.build(
                new BcRSAContentSignerBuilder(sha1withRSA,
                        new DefaultDigestAlgorithmIdentifierFinder().find(sha1withRSA))
                                .build(PrivateKeyFactory.createKey(privateKey.getEncoded())),
                new JcaX509CertificateHolder(cert)));

        gen.addCertificates(certs);

        CMSSignedData s = gen.generate(new CMSAbsentContent(), false);
        return new CMSSignedData(msg, s.getEncoded()).getEncoded();

    } catch (Exception e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        throw new IOException(e);
    }

}
            ExternalSigningSupport externalSigning = document.saveIncrementalForExternalSigning(output);
        byte[] cmsSignature = sign(externalSigning.getContent());
        externalSigning.setSignature(cmsSignature);

问题是Adobe说签名是无效的,因为“文档在签名后已经被更改或损坏”。有人能帮忙吗?

共有1个答案

司马德水
2023-03-14

在他的更新操作几乎是正确的,只有两个错误:

>

  • 他尝试读取InputStream参数内容两次:

    CMSTypedData msg = new CMSProcessableByteArray(IOUtils.toByteArray(content));
    [...]
    Attribute attr = new Attribute(CMSAttributes.messageDigest,
            new DERSet(new DEROctetString(md.digest(IOUtils.toByteArray(content)))));
    

    因此,在第二次尝试之前,所有数据都已从流中读取,因此返回一个空的字节[]。因此消息摘要属性包含错误的哈希值。

    return new CMSSignedData(msg, s.getEncoded()).getEncoded();
    
    // Digest generation step
    MessageDigest md = MessageDigest.getInstance("SHA256", "BC");
    byte[] digest = md.digest(IOUtils.toByteArray(content));
    
    // Separate signature container creation step
    List<Certificate> certList = Arrays.asList(chain);
    JcaCertStore certs = new JcaCertStore(certList);
    
    CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
    
    Attribute attr = new Attribute(CMSAttributes.messageDigest,
            new DERSet(new DEROctetString(digest)));
    
    ASN1EncodableVector v = new ASN1EncodableVector();
    
    v.add(attr);
    
    SignerInfoGeneratorBuilder builder = new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider())
            .setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v)));
    
    AlgorithmIdentifier sha256withRSA = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA256withRSA");
    
    CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
    InputStream in = new ByteArrayInputStream(chain[0].getEncoded());
    X509Certificate cert = (X509Certificate) certFactory.generateCertificate(in);
    
    gen.addSignerInfoGenerator(builder.build(
            new BcRSAContentSignerBuilder(sha256withRSA,
                    new DefaultDigestAlgorithmIdentifierFinder().find(sha256withRSA))
                            .build(PrivateKeyFactory.createKey(pk.getEncoded())),
            new JcaX509CertificateHolder(cert)));
    
    gen.addCertificates(certs);
    
    CMSSignedData s = gen.generate(new CMSAbsentContent(), false);
    return s.getEncoded();
    
    void sign(PDDocument document, OutputStream output, SignatureInterface signatureInterface) throws IOException
    {
        PDSignature signature = new PDSignature();
        signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
        signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
        signature.setName("Example User");
        signature.setLocation("Los Angeles, CA");
        signature.setReason("Testing");
        signature.setSignDate(Calendar.getInstance());
        document.addSignature(signature);
        ExternalSigningSupport externalSigning =
                document.saveIncrementalForExternalSigning(output);
        byte[] cmsSignature = signatureInterface.sign(externalSigning.getContent());
        externalSigning.setSignature(cmsSignature);
    }
    

    (CreateSignature方法符号)

    像这样

    try (   InputStream resource = getClass().getResourceAsStream("test.pdf");
            OutputStream result = new FileOutputStream(new File(RESULT_FOLDER, "testSignedWithSeparatedHashing.pdf"));
            PDDocument pdDocument = PDDocument.load(resource)   )
    {
        sign(pdDocument, result, data -> signWithSeparatedHashing(data));
    }
    

    (CreateSignature测试方法TestSignwithSeparatedHashing)

  •  类似资料:
    • 问题内容: 目前,我有一个客户端-服务器应用程序,给定一个PDF文件,使用服务器证书对其进行签名,将签名与原始文件一起附加,然后将输出返回给客户端(所有这些操作均通过PDFBox实现)。 我有一个签名处理程序,这是我的外部签名支持(其中内容为PDF文件) 它工作正常,但我在想-如果PDF文件太大而无法上传怎么办?例如:100mb …这将永远!鉴于此,我想弄清楚,如果不对PDF文件签名,是否可以仅对

    • 我有一个关于计算PDF文档摘要以用于数字签名的快速问题(与我前面的一个问题有点相关,我试图弄清楚为什么您需要知道客户的证书以创建正确的摘要)。在Adobe关于PDF格式的文档中,指定了以下内容: 字节范围摘要应在文件中的一个字节范围内计算,该范围应由签名字典中的字节范围条目指示。这个范围应该是整个文件,包括签名字典,但不包括签名值本身(内容条目)。 因此,在这一点上,事情似乎相当简单,只需消化除/

    • 我想使用PKCS7容器在PDF文件中创建分离签名。数据(哈希)是预先在另一个设备上用私钥签名的。我想创建一个PKCS7,其中包含签名的数据以及带有公钥的证书。如果不提供私钥并让库签名数据,我似乎无法创建带有bouncy castle的PKCS7。这似乎不起作用:

    • pre { white-space: pre-wrap; } 在本教程中,我们将向您展示如何在数据网格(datagrid)页脚显示摘要信息行。 为了显示页脚行,您应该设置 showFooter 属性为 true,然后准备定义在数据网格(datagrid)数据中的页脚行。以下是示例数据:     {"total":1,"rows":[{"id":1,"name":"Chai","price":18

    • 我有一个google cloud app engine应用程序,这个应用程序是用Java11实现的,并且可以编译它。但由于某种原因,当我将I部署到app engine上时,google无法创建此应用程序的实例。

    • 我已经通过提取jar,更改其MANIFEST. MF文件并使用命令jar cfmnew-6.0.1.jar将其转换为bundle,将jar转换为OSGI bundle。\META-INF\MANIFEST. MF.但是当我试图运行代码时,我得到了错误java.lang.Security异常:无效的签名文件摘要为清单主要属性请提供您的建议如何解决此错误。