java pdfbox - 文档自签名后已被更改或损坏

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

当我尝试使用带有子过滤器的 pdfbox 为 etsi.cades.detached 签署文档时,出现以下错误

我正在从 CSC 获取证书信息和签名哈希。这是我的代码

public InputStream signDocument(InputStream inputStream, String requestId, String providerId, SealFieldLocation location) throws Exception {
        File outFile = null;
        try {
            PDDocument document = PDDocument.load(inputStream);

            outFile = File.createTempFile("signedFIle", ".pdf");

            Certificate[] certificateChain = fetchCertificateChainFromCSC();
            setCertificateChain(certificateChain);

            // sign
            FileOutputStream output = new FileOutputStream(outFile);
            IOUtils.copy(inputStream, output);

            // create signature dictionary
            PDSignature signature = new PDSignature();
            signature.setType(COSName.SIG);


            signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
            signature.setSubFilter(PDSignature.SUBFILTER_ETSI_CADES_DETACHED);
            signature.setName("Test Name");
            signature.setSignDate(Calendar.getInstance());
            Rectangle2D humanRect = new Rectangle2D.Float(location.getLeft(), location.getBottom(), location.getRight(), location.getTop());

            PDRectangle rect = createSignatureRectangle(document, humanRect);

            SignatureOptions signatureOptions = new SignatureOptions();
            signatureOptions.setVisualSignature(createVisualSignatureTemplate(document, 0, rect, signature));
            signatureOptions.setPage(0);
            document.addSignature(signature, signatureOptions);


            ExternalSigningSupport externalSigning =
                    document.saveIncrementalForExternalSigning(output);

            InputStream content = externalSigning.getContent();



            CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
            X509Certificate cert = (X509Certificate) certificateChain[0];
            gen.addCertificates(new JcaCertStore(Arrays.asList(certificateChain)));

            MessageDigest digest = MessageDigest.getInstance("SHA-256");

            // Use a buffer to read the input stream in chunks
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = content.read(buffer)) != -1) {
                digest.update(buffer, 0, bytesRead);
            }

            byte[] hashBytes = digest.digest();

            ESSCertIDv2 certid = new ESSCertIDv2(
                    new AlgorithmIdentifier(new ASN1ObjectIdentifier("1.2.840.113549.1.1.11")),
                    MessageDigest.getInstance("SHA-256").digest(cert.getEncoded())
            );

            SigningCertificateV2 sigcert = new SigningCertificateV2(certid);

            final DERSet attrValues = new DERSet(sigcert);
            Attribute attr = new Attribute(PKCSObjectIdentifiers.id_aa_signingCertificateV2, attrValues);
            ASN1EncodableVector v = new ASN1EncodableVector();
            v.add(attr);

            AttributeTable atttributeTable = new AttributeTable(v);
            //Create a standard attribute table from the passed in parameters - certhash
            CMSAttributeTableGenerator attrGen = new NewSignedAttributeTableGenerator(atttributeTable);

            java.util.Base64.Encoder encoder = java.util.Base64.getEncoder();
            List<String> hash = Arrays.asList(encoder.encodeToString(hashBytes));

            SignHashRequest signHashRequest = new SignHashRequest();
            signHashRequest.setCredentialID(credentialId);
            signHashRequest.setHash(hash);
            signHashRequest.setPIN(pin);
            signHashRequest.setSignAlgo("1.2.840.113549.1.1.11");

            final byte[] signedHash = fetchSignedHashFromCSC(requestId, providerId, accessToken, signHashRequest);

            ContentSigner nonSigner = new ContentSigner() {

                @Override
                public byte[] getSignature() {
                    return signedHash;
                }

                @Override
                public OutputStream getOutputStream() {
                    return new ByteArrayOutputStream();
                }

                @Override
                public AlgorithmIdentifier getAlgorithmIdentifier() {
                    return new DefaultSignatureAlgorithmIdentifierFinder().find( "SHA256WithRSA" );
                }
            };



            org.bouncycastle.asn1.x509.Certificate cert2 = org.bouncycastle.asn1.x509.Certificate.getInstance(ASN1Primitive.fromByteArray(cert.getEncoded()));
            JcaSignerInfoGeneratorBuilder sigb = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build());




            sigb.setSignedAttributeGenerator(attrGen);


            gen.addSignerInfoGenerator(sigb.build(nonSigner, new X509CertificateHolder(cert2)));
            CMSTypedData msg = new CMSProcessableInputStream( inputStream);

            CMSSignedData signedData = gen.generate((CMSTypedData)msg, false);
            byte[] cmsSignature = signedData.getEncoded();

            inputStream.close();

            externalSigning.setSignature(cmsSignature);
            IOUtils.closeQuietly(signatureOptions);

            showSignature(outFile);

            return new FileInputStream(outFile);
        } catch (Exception e) {
            throw e;
        } finally {
            if (outFile != null) {
                outFile.delete();
            }
        }

    }

private PDRectangle createSignatureRectangle(PDDocument doc, Rectangle2D humanRect)
    {
        float x = (float) humanRect.getX();
        float y = (float) humanRect.getY();
        float width = (float) humanRect.getWidth();
        float height = (float) humanRect.getHeight();
        PDPage page = doc.getPage(0);
        PDRectangle pageRect = page.getCropBox();
        PDRectangle rect = new PDRectangle();
        // signing should be at the same position regardless of page rotation.
        switch (page.getRotation())
        {
            case 90:
                rect.setLowerLeftY(x);
                rect.setUpperRightY(x + width);
                rect.setLowerLeftX(y);
                rect.setUpperRightX(y + height);
                break;
            case 180:
                rect.setUpperRightX(pageRect.getWidth() - x);
                rect.setLowerLeftX(pageRect.getWidth() - x - width);
                rect.setLowerLeftY(y);
                rect.setUpperRightY(y + height);
                break;
            case 270:
                rect.setLowerLeftY(pageRect.getHeight() - x - width);
                rect.setUpperRightY(pageRect.getHeight() - x);
                rect.setLowerLeftX(pageRect.getWidth() - y - height);
                rect.setUpperRightX(pageRect.getWidth() - y);
                break;
            case 0:
            default:
                rect.setLowerLeftX(x);
                rect.setUpperRightX(x + width);
                rect.setLowerLeftY(pageRect.getHeight() - y - height);
                rect.setUpperRightY(pageRect.getHeight() - y);
                break;
        }
        return rect;
    }

我认为在计算文档的哈希值时存在一些问题。这个问题也适用于 adbe.pkcs7.detached。如果我们删除添加属性表的代码,那么问题就解决了 adbe.pkcs7.detached。但是对于 etsi.cades.detachhed,需要添加签名证书属性。

certificate pdfbox digital-signature x509certificate pades
© www.soinside.com 2019 - 2024. All rights reserved.