iText7和Adobe使用附件不同地验证相同PDF / A-3a的签名

时间:2018-04-13 08:05:58

标签: java signing itext7

我创建了带附件的PDF / A文档。我已经签了两次(第一次使用PdfSigner.CERTIFIED_FORM_FILLING,第二次使用PdfSigner.NOT_CERTIFIED)。然后我验证了签名。我使用的所有代码都来自iText7提供的示例。验证按预期进行,但是当我在Adobe中打开文档时,它在第一次签名时给了我一个错误,说文档已经更改(签名验证期间出现错误。意外的字节范围值定义了签名数据的范围& #39)。

我用来创建PDF的代码:

public class PdfA3WitAttachment {
    public static final String DATA = "resources/data/united_states.csv";
    public static final String FONT = "resources/fonts/FreeSans.ttf";
    public static final String BOLD_FONT = "resources/fonts/FreeSansBold.ttf";
    public static final String INTENT = "resources/color/sRGB_CS_profile.icm";

    /** An image resource. */
    public static final String FOX = "resources/images/fox.bmp";
    /** An image resource. */
    public static final String DOG = "resources/images/dog.bmp";


    public static final String DEST = "results/withAttachment.pdf";

    public static void main(String args[]) throws IOException {
        File file = new File(DEST);
        file.getParentFile().mkdirs();
        new PdfA3WitAttachment().createPdf(DEST);
    }

    public void createPdf(String dest) throws IOException {
        PdfADocument pdf = new PdfADocument(new PdfWriter(dest),
            PdfAConformanceLevel.PDF_A_3A,
            new PdfOutputIntent("Custom", "", "http://www.color.org",
                    "sRGB IEC61966-2.1", new FileInputStream(INTENT)));
        Document document = new Document(pdf, PageSize.A4.rotate());
        document.setMargins(20, 20, 20, 20);

        //Setting some required parameters
        pdf.setTagged();
        pdf.getCatalog().setLang(new PdfString("en-US"));
        pdf.getCatalog().setViewerPreferences(
                new PdfViewerPreferences().setDisplayDocTitle(true));
        PdfDocumentInfo info = pdf.getDocumentInfo();
        info.setTitle("iText7 PDF/A-3 example");

        //Add attachment
        PdfDictionary parameters = new PdfDictionary();
        parameters.put(PdfName.ModDate, new PdfDate().getPdfObject());
        PdfFileSpec fileSpec = PdfFileSpec.createEmbeddedFileSpec(
            pdf, Files.readAllBytes(Paths.get(DATA)), "united_states.csv",
            "united_states.csv", new PdfName("text/csv"), parameters,
            PdfName.Data, false);
        fileSpec.put(new PdfName("AFRelationship"), new PdfName("Data"));
        pdf.addFileAttachment("united_states.csv", fileSpec);
        PdfArray array = new PdfArray();
        array.add(fileSpec.getPdfObject().getIndirectReference());
        pdf.getCatalog().put(new PdfName("AF"), array);

        //Embed fonts
        PdfFont font = PdfFontFactory.createFont(FONT, true);

        // Create content
        Image fox = new Image(ImageDataFactory.create(FOX));
        fox.getAccessibilityProperties().setAlternateDescription("fox");
        Image dog = new Image(ImageDataFactory.create(DOG));
        dog.getAccessibilityProperties().setAlternateDescription("dog");
        document.add(
            new Paragraph()
                .setFont(font)
                .setFontSize(20)
                .add(new Text("The quick brown "))
                .add(fox)
                .add(new Text(" jumps over the lazy "))
                .add(dog));
        // step 4
        document.close();

        //Close document
        document.close();
    }

    public void process(Table table, String line, PdfFont font, boolean isHeader) {
        StringTokenizer tokenizer = new StringTokenizer(line, ";");
        while (tokenizer.hasMoreTokens()) {
            if (isHeader) {
                table.addHeaderCell(new Cell().setHorizontalAlignment(HorizontalAlignment.CENTER).add(new Paragraph(tokenizer.nextToken()).setHorizontalAlignment(HorizontalAlignment.CENTER).setFont(font)));
            } else {
                table.addCell(new Cell().setHorizontalAlignment(HorizontalAlignment.CENTER).add(new Paragraph(tokenizer.nextToken()).setHorizontalAlignment(HorizontalAlignment.CENTER).setFont(font)));
            }
        }
    }
}

用于签名和验证的准则:

public class SignWithCertificate {

    /*public static final String SRC = "./results/zugferd/pdfa/quickbrownfox4.pdf";
    public static final String DEST = "./results/zugferd/pdfa/quickbrownfox4_cacert.pdf";
    public static final String DEST1 = "./results/zugferd/pdfa/quickbrownfox4_cacert_sign.pdf";*/

    public static final String SRC = "./results/withAttachment.pdf";
    public static final String DEST = "./results/withAttachment_cacert.pdf";
    public static final String DEST1 = "./results/withAttachment_cacert_sign.pdf";


    public void sign(String src, String dest, Certificate[] chain, PrivateKey pk, String digestAlgorithm, String provider, PdfSigner.CryptoStandard subfilter,
            String reason, String location, IOcspClient ocspClient, ITSAClient tsaClient, int estimatedSize)
            throws GeneralSecurityException, IOException {

        boolean appendMode = false;

        System.out.println("_____________CERTIFICATE SIGN________________");
        System.out.println(appendMode);
        System.out.println("_____________________________________________");

        // Creating the reader and the signer
        PdfReader reader = new PdfReader(src);
        PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), appendMode);
        signer.setCertificationLevel(PdfSigner.CERTIFIED_FORM_FILLING);

        // Creating the appearance
        PdfSignatureAppearance appearance = signer.getSignatureAppearance().setReason(reason).setLocation(location).setReuseAppearance(false);
        Rectangle rect = new Rectangle(36, 648, 200, 100);
        appearance.setPageRect(rect).setPageNumber(1);
        signer.setFieldName("cert");

        // Creating the signature
        IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
        IExternalDigest digest = new BouncyCastleDigest();
        Collection<ICrlClient> crlList = new ArrayList<>();
        crlList.add(new CrlClientOnline(chain));
        signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
    }

    public void signAgain(String src, String dest, Certificate[] chain, PrivateKey pk, String digestAlgorithm, String provider,
            PdfSigner.CryptoStandard subfilter, String reason, String location, IOcspClient ocspClient, ITSAClient tsaClient,
            int estimatedSize) throws GeneralSecurityException, IOException {

        boolean appendMode = true;

        System.out.println("_______________________ SIGN________________");
        System.out.println(appendMode);
        System.out.println("_____________________________________________");

        // Creating the reader and the signer
        PdfReader reader = new PdfReader(src);
        PdfSigner signer = new PdfSigner(reader, new FileOutputStream(dest), appendMode);
        signer.setCertificationLevel(PdfSigner.NOT_CERTIFIED);

        // Creating the appearance
        PdfSignatureAppearance appearance = signer.getSignatureAppearance().setReason(reason).setLocation(location).setReuseAppearance(false);
        Rectangle rect = new Rectangle(246, 648, 200, 100);
        appearance.setPageRect(rect).setPageNumber(1);
        signer.setFieldName("sig");

        // Creating the signature
        IExternalSignature pks = new PrivateKeySignature(pk, digestAlgorithm, provider);
        IExternalDigest digest = new BouncyCastleDigest();
        Collection<ICrlClient> crlList = new ArrayList<>();
        crlList.add(new CrlClientOnline(chain));
        signer.signDetached(digest, pks, chain, crlList, ocspClient, tsaClient, estimatedSize, subfilter);
    }

    public void verifySignatures(String path) throws IOException, GeneralSecurityException {
        System.out.println(path);

        PdfReader reader = new PdfReader(path);
        PdfDocument pdfDoc = new PdfDocument(reader);

        SignatureUtil util = new SignatureUtil(pdfDoc);

        List<String> names = util.getSignatureNames();

        for (String name : names) {
            System.out.println("===== " + name + " =====");
            verifySignature(util, name);
        }
        System.out.println();
    }

    public PdfPKCS7 verifySignature(SignatureUtil util, String name) throws GeneralSecurityException, IOException {
        System.out.println("Signature covers whole document: " + util.signatureCoversWholeDocument(name));
        System.out.println("Document revision: " + util.getRevision(name) + " of " + util.getTotalRevisions());
        PdfPKCS7 pkcs7 = util.verifySignature(name);
        System.out.println("Integrity check OK? " + pkcs7.verify());
        return pkcs7;
    }

    public void inspectSignatures(String path) throws IOException, GeneralSecurityException {
        System.out.println(path);
        PdfReader reader = new PdfReader(path);
        PdfDocument pdfDoc = new PdfDocument(reader);

        PdfAcroForm form = PdfAcroForm.getAcroForm(pdfDoc, true);
        Map<String, PdfFormField> fields = form.getFormFields();

        SignatureUtil util = new SignatureUtil(pdfDoc);
        List<String> names = util.getSignatureNames();

        SignaturePermissions perms = null;

        for (String name : names) {
            System.out.println("===== " + name + " =====");
            perms = inspectSignature(fields.get(name), name, perms, util);
        }
        System.out.println();
    }


    public SignaturePermissions inspectSignature(PdfFormField field, String name, SignaturePermissions perms, SignatureUtil util) throws GeneralSecurityException, IOException {
        PdfArray position = field.getWidgets().get(0).getRectangle();


        if (position != null && position.size() > 0) {
            float width = (float) (position.getAsNumber(2).getValue() - position.getAsNumber(0).getValue());
            float height = (float) (position.getAsNumber(3).getValue() - position.getAsNumber(1).getValue());
            if (width == 0 || height == 0) {
                System.out.println("Invisible signature");
            } else {
                PdfPage page = field.getWidgets().get(0).getPage();

                System.out.println(String.format("Field on page %s; llx: %s, lly: %s, urx: %s; ury: %s", page, position.getAsNumber(0).getValue(), position.getAsNumber(1).getValue(),
                        position.getAsNumber(2).getValue(), position.getAsNumber(3).getValue()));
            }
        }

        PdfPKCS7 pkcs7 = verifySignature(util, name);
        System.out.println("Digest algorithm: " + pkcs7.getHashAlgorithm());
        System.out.println("Encryption algorithm: " + pkcs7.getEncryptionAlgorithm());
        System.out.println("Filter subtype: " + pkcs7.getFilterSubtype());
        X509Certificate cert = (X509Certificate) pkcs7.getSigningCertificate();
        System.out.println("Name of the signer: " + CertificateInfo.getSubjectFields(cert).getField("CN"));

        if (pkcs7.getSignName() != null)
            System.out.println("Alternative name of the signer: " + pkcs7.getSignName());
        SimpleDateFormat date_format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SS");
        System.out.println("Signed on: " + date_format.format(pkcs7.getSignDate().getTime()));

        if (pkcs7.getTimeStampDate() != null) {
            System.out.println("TimeStamp: " + date_format.format(pkcs7.getTimeStampDate().getTime()));
            TimeStampToken ts = pkcs7.getTimeStampToken();
            System.out.println("TimeStamp service: " + ts.getTimeStampInfo().getTsa());
            System.out.println("TimeStamp verified? " + pkcs7.verifyTimestampImprint());
        }

        System.out.println("Location: " + pkcs7.getLocation());
        System.out.println("Reason: " + pkcs7.getReason());

        PdfDictionary sigDict = util.getSignatureDictionary(name);
        PdfString contact = sigDict.getAsString(PdfName.ContactInfo);
        if (contact != null)
            System.out.println("Contact info: " + contact);

        perms = new SignaturePermissions(sigDict, perms);
        System.out.println("Signature type: " + (perms.isCertification() ? "certification" : "approval"));
        System.out.println("Filling out fields allowed: " + perms.isFillInAllowed());
        System.out.println("Adding annotations allowed: " + perms.isAnnotationsAllowed());

        for (FieldLock lock : perms.getFieldLocks()) {
            System.out.println("Lock: " + lock.toString());
        }

        return perms;
    }

    public static void main(String[] args) throws IOException, GeneralSecurityException {
        String path = "./src/test/SupplierX.p12";
        String astrasKSpath = "./src/test/ASTRAS_APP.p12";
        char[] pass = "password".toCharArray();

        BouncyCastleProvider provider = new BouncyCastleProvider();
        Security.addProvider(provider);
        KeyStore ks = KeyStore.getInstance("pkcs12", provider.getName());
        ks.load(new FileInputStream(path), pass);

        KeyStore astrasKs = KeyStore.getInstance("pkcs12", provider.getName());
        astrasKs.load(new FileInputStream(astrasKSpath), pass);
        SignWithCertificate app = new SignWithCertificate();

        String alias = "ASTRAS_APP";
        PrivateKey pk = (PrivateKey) astrasKs.getKey(alias, pass);
        Certificate[] chain = astrasKs.getCertificateChain(alias);
        app.sign(SRC, DEST, chain, pk, DigestAlgorithms.SHA256, provider.getName(), PdfSigner.CryptoStandard.CMS, "Test Sign Certificate", "München", null, null, 0);

        alias = "SupplierX";
        pk = (PrivateKey) ks.getKey(alias, pass);
        chain = ks.getCertificateChain(alias);
        app.signAgain(DEST, DEST1, chain, pk, DigestAlgorithms.SHA256, provider.getName(), PdfSigner.CryptoStandard.CMS, "Test Sign", "München", null, null, 0);

        app.verifySignatures(DEST);
        app.verifySignatures(DEST1);
    }
}

我想知道iText如何验证而Adobe没有验证。任何帮助非常感谢(生成的文件在这里:https://drive.google.com/drive/folders/1e8gILlclqftqQRcRb7ZurRzqU-6h1oN-)。

0 个答案:

没有答案