Come Firmare Digitalmente un Dinamicamente Documento PDF Creato Utilizzando PDFBox?

Perdonatemi! Io sono povero in java.
Si prega di Correggere me, ovunque io mi sbagli e migliorare, ovunque io sono povero!

Sto cercando di firmare digitalmente un dinamicamente pdf creato utilizzando PDFBox con il seguente programma:

Attività in Programma:

(i) la Creazione di Template PDF

(ii) l’Aggiornamento ByteRange, xref, startxref

(iii) la Costruzione di Documento Originale per la Creazione di Firme

(iv) la Creazione di Bifamiliare Avvolto Firma Digitale

(v) la Costruzione di PDF Firmato Digitalmente Documento concatenando Originale Doc, non Parte, Staccato di Firma e PDF Originale Parte – II

Osservazioni:

(i) pdfFileOutputStream.scrivere(documentOutputStream.toByteArray()); createsTemplate Documento PDF con Firma Visibile.

(ii) Si Crea un Po ‘ di PDF Documento Firmato, ma ha degli errori (a) invalid token e b), diversi errori del parser
(ora corretto sotto l’abile guida di MKL!)

si Prega di suggerire a me la seguente:

(i) Come aggiungere il Testo della Firma la Firma Visibile sul layer2.

Grazie in Anticipo!

    package digitalsignature;

    import java.awt.geom.AffineTransform;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.security.Signature;
    import java.util.ArrayList;
    import org.bouncycastle.cert.X509CertificateHolder;
    import org.bouncycastle.cert.jcajce.JcaCertStore;
    import org.bouncycastle.cms.CMSProcessableByteArray;
    import org.bouncycastle.cms.CMSTypedData;
    import org.bouncycastle.cms.SignerInfoGenerator;
    import org.bouncycastle.cms.SignerInfoGeneratorBuilder;
    import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
    import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
    import org.bouncycastle.util.Store;

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.security.KeyStore;
    import java.security.PrivateKey;
    import java.security.cert.CertStore;
    import java.security.cert.Certificate;
    import java.security.cert.CollectionCertStoreParameters;
    import java.security.cert.X509Certificate;
    import java.text.DecimalFormat;
    import java.text.SimpleDateFormat;
    import java.util.Arrays;
    import java.util.Calendar;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.List;

    import java.util.Map;
    import org.apache.pdfbox.cos.COSArray;
    import org.apache.pdfbox.cos.COSDictionary;
    import org.apache.pdfbox.cos.COSName;
    import org.apache.pdfbox.pdmodel.PDDocument;
    import org.apache.pdfbox.pdmodel.PDPage;
    import org.apache.pdfbox.pdmodel.PDResources;
    import org.apache.pdfbox.pdmodel.common.PDRectangle;
    import org.apache.pdfbox.pdmodel.common.PDStream;
    import org.apache.pdfbox.pdmodel.edit.PDPageContentStream;
    import org.apache.pdfbox.pdmodel.font.PDFont;
    import org.apache.pdfbox.pdmodel.font.PDType1Font;
    import org.apache.pdfbox.pdmodel.graphics.xobject.PDJpeg;
    import org.apache.pdfbox.pdmodel.graphics.xobject.PDXObjectForm;
    import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceDictionary;
    import org.apache.pdfbox.pdmodel.interactive.annotation.PDAppearanceStream;
    import org.apache.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;
    import org.apache.pdfbox.pdmodel.interactive.digitalsignature.SignatureOptions;
    import org.apache.pdfbox.pdmodel.interactive.form.PDAcroForm;
    import org.apache.pdfbox.pdmodel.interactive.form.PDField;
    import org.apache.pdfbox.pdmodel.interactive.form.PDSignatureField;
    import org.bouncycastle.cms.CMSSignedData;
    import org.bouncycastle.cms.CMSSignedDataGenerator;
    import org.bouncycastle.cms.CMSSignedGenerator;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;


    public class AffixSignature {
        String path = "D:\\reports\\";
        String onlyFileName = "";
        String pdfExtension = ".pdf";
        String pdfFileName = "";
        String pdfFilePath = "";
        String signedPdfFileName = "";
        String signedPdfFilePath = "";
        String ownerPassword = "";
        String tempSignedPdfFileName = "";
        String tempSignedPdfFilePath = "";
        String userPassword = "";
        String storePath = "resources/my.p12";
        String entryAlias = "signerCert";
        String keyStorePassword = "password";
        ByteArrayOutputStream documentOutputStream = null;
        private Certificate[] certChain;
        private static BouncyCastleProvider BC = new BouncyCastleProvider();
        int offsetContentStart = 0;
        int offsetContentEnd = 0;
        int secondPartLength = 0;
        int offsetStartxrefs = 0;
        String contentString = "";
        OutputStream signedPdfFileOutputStream;
        OutputStream pdfFileOutputStream;

        public AffixSignature() {
        try {
            SimpleDateFormat timeFormat = new SimpleDateFormat("hh_mm_ss");

            onlyFileName = "Report_" + timeFormat.format(new Date());
            pdfFileName = onlyFileName + ".pdf";
            pdfFilePath = path + pdfFileName;
            File pdfFile = new File(pdfFilePath);
            pdfFileOutputStream = new FileOutputStream(pdfFile);

            signedPdfFileName = "Signed_" + onlyFileName + ".pdf";
            signedPdfFilePath = path + signedPdfFileName;
            File signedPdfFile = new File(signedPdfFilePath);
            signedPdfFileOutputStream = new FileOutputStream(signedPdfFile);

            String tempFileName = "Temp_Report_" + timeFormat.format(new Date());
            String tempPdfFileName = tempFileName + ".pdf";
            String tempPdfFilePath = path + tempPdfFileName;
            File tempPdfFile = new File(tempPdfFilePath);
            OutputStream tempSignedPdfFileOutputStream = new FileOutputStream(tempPdfFile);

            PDDocument document = new PDDocument();
            PDDocumentCatalog catalog = document.getDocumentCatalog();
            PDPage page = new PDPage(PDPage.PAGE_SIZE_A4);
            PDPageContentStream contentStream = new PDPageContentStream(document, page);


            PDFont font = PDType1Font.HELVETICA;
            Map<String, PDFont> fonts = new HashMap<String, PDFont>();
            fonts = new HashMap<String, PDFont>();
            fonts.put("F1", font);

//           contentStream.setFont(font, 12);
            contentStream.setFont(font, 12);
            contentStream.beginText();
            contentStream.moveTextPositionByAmount(100, 700);
            contentStream.drawString("DIGITAL SIGNATURE TEST");
            contentStream.endText();
            contentStream.close();
            document.addPage(page);

//To Affix Visible Digital Signature
            PDAcroForm acroForm = new PDAcroForm(document);
            catalog.setAcroForm(acroForm);

            PDSignatureField sf = new PDSignatureField(acroForm);

            PDSignature pdSignature = new PDSignature();
            page.getAnnotations().add(sf.getWidget());
            pdSignature.setName("sign");
            pdSignature.setByteRange(new int[]{0, 0, 0, 0});
            pdSignature.setContents(new byte[4 * 1024]);
            pdSignature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
            pdSignature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
            pdSignature.setName("NAME");
            pdSignature.setLocation("LOCATION");
            pdSignature.setReason("SECURITY");
            pdSignature.setSignDate(Calendar.getInstance());
            List<PDField> acroFormFields = acroForm.getFields();

            sf.setSignature(pdSignature);
            sf.getWidget().setPage(page);

            COSDictionary acroFormDict = acroForm.getDictionary();
            acroFormDict.setDirect(true);
            acroFormDict.setInt(COSName.SIG_FLAGS, 3);
            acroFormFields.add(sf);

            PDRectangle frmRect = new PDRectangle();
//           float[] frmRectParams = {lowerLeftX,lowerLeftY,upperRightX,upperRight};
//           float[] frmRectLowerLeftUpperRightCoordinates = {5f, page.getMediaBox().getHeight() - 50f, 100f, page.getMediaBox().getHeight() - 5f};
            float[] frmRectLowerLeftUpperRightCoordinates = {5f, 5f, 205f, 55f};
            frmRect.setUpperRightX(frmRectLowerLeftUpperRightCoordinates[2]);
            frmRect.setUpperRightY(frmRectLowerLeftUpperRightCoordinates[3]);
            frmRect.setLowerLeftX(frmRectLowerLeftUpperRightCoordinates[0]);
            frmRect.setLowerLeftY(frmRectLowerLeftUpperRightCoordinates[1]);

            sf.getWidget().setRectangle(frmRect);

            COSArray procSetArr = new COSArray();
            procSetArr.add(COSName.getPDFName("PDF"));
            procSetArr.add(COSName.getPDFName("Text"));
            procSetArr.add(COSName.getPDFName("ImageB"));
            procSetArr.add(COSName.getPDFName("ImageC"));
            procSetArr.add(COSName.getPDFName("ImageI"));

            String signImageFilePath = "resources/sign.JPG";
            File signImageFile = new File(signImageFilePath);
            InputStream signImageStream = new FileInputStream(signImageFile);
            PDJpeg img = new PDJpeg(document, signImageStream);

            PDResources holderFormResources = new PDResources();
            PDStream holderFormStream = new PDStream(document);
            PDXObjectForm holderForm = new PDXObjectForm(holderFormStream);
            holderForm.setResources(holderFormResources);
            holderForm.setBBox(frmRect);
            holderForm.setFormType(1);

            PDAppearanceDictionary appearance = new PDAppearanceDictionary();
            appearance.getCOSObject().setDirect(true);
            PDAppearanceStream appearanceStream = new PDAppearanceStream(holderForm.getCOSStream());
            appearance.setNormalAppearance(appearanceStream);
            sf.getWidget().setAppearance(appearance);
            acroFormDict.setItem(COSName.DR, holderFormResources.getCOSDictionary());

            PDResources innerFormResources = new PDResources();
            PDStream innerFormStream = new PDStream(document);
            PDXObjectForm innerForm = new PDXObjectForm(innerFormStream);
            innerForm.setResources(innerFormResources);
            innerForm.setBBox(frmRect);
            innerForm.setFormType(1);

            String innerFormName = holderFormResources.addXObject(innerForm, "FRM");

            PDResources imageFormResources = new PDResources();
            PDStream imageFormStream = new PDStream(document);
            PDXObjectForm imageForm = new PDXObjectForm(imageFormStream);
            imageForm.setResources(imageFormResources);
            byte[] AffineTransformParams = {1, 0, 0, 1, 0, 0};
            AffineTransform affineTransform = new AffineTransform(AffineTransformParams[0], AffineTransformParams[1], AffineTransformParams[2], AffineTransformParams[3], AffineTransformParams[4], AffineTransformParams[5]);
            imageForm.setMatrix(affineTransform);
            imageForm.setBBox(frmRect);
            imageForm.setFormType(1);

            String imageFormName = innerFormResources.addXObject(imageForm, "n");
            String imageName = imageFormResources.addXObject(img, "img");

            innerForm.getResources().getCOSDictionary().setItem(COSName.PROC_SET, procSetArr);
            page.getCOSDictionary().setItem(COSName.PROC_SET, procSetArr);
            innerFormResources.getCOSDictionary().setItem(COSName.PROC_SET, procSetArr);
            imageFormResources.getCOSDictionary().setItem(COSName.PROC_SET, procSetArr);
            holderFormResources.getCOSDictionary().setItem(COSName.PROC_SET, procSetArr);

            String holderFormComment = "q 1 0 0 1 0 0 cm /" + innerFormName + " Do Q \n";
            String innerFormComment = "q 1 0 0 1 0 0 cm /" + imageFormName + " Do Q\n";
            String imgFormComment = "q " + 100 + " 0 0 50 0 0 cm /" + imageName + " Do Q\n";

            appendRawCommands(holderFormStream.createOutputStream(), holderFormComment);
            appendRawCommands(innerFormStream.createOutputStream(), innerFormComment);
            appendRawCommands(imageFormStream.createOutputStream(), imgFormComment);

            documentOutputStream = new ByteArrayOutputStream();
            document.save(documentOutputStream);
            document.close();
            tempSignedPdfFileOutputStream.write(documentOutputStream.toByteArray());
            generateSignedPdf();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void appendRawCommands(OutputStream os, String commands) throws IOException {
        os.write(commands.getBytes("ISO-8859-1"));
        os.close();
    }

    public void generateSignedPdf() {
        try {
            //Find the Initial Byte Range Offsets
            String docString = new String(documentOutputStream.toByteArray(), "ISO-8859-1");
            offsetContentStart = (documentOutputStream.toString().indexOf("Contents <") + 10 - 1);
            offsetContentEnd = (documentOutputStream.toString().indexOf("000000>") + 7);
            secondPartLength = (documentOutputStream.size() - documentOutputStream.toString().indexOf("000000>") - 7);
            //Calculate the Updated ByteRange
            String initByteRange = "";
            if (docString.indexOf("/ByteRange [0 1000000000 1000000000 1000000000]") > 0) {
                initByteRange = "/ByteRange [0 1000000000 1000000000 1000000000]";
            } else if (docString.indexOf("/ByteRange [0 0 0 0]") > 0) {
                initByteRange = "/ByteRange [0 0 0 0]";
            } else {
                System.out.println("No /ByteRange Token is Found!");
                System.exit(1);
            }

            String interimByteRange = "/ByteRange [0 " + offsetContentStart + " " + offsetContentEnd + " " + secondPartLength + "]";
            int byteRangeLengthDifference = interimByteRange.length() - initByteRange.length();
            offsetContentStart = offsetContentStart + byteRangeLengthDifference;
            offsetContentEnd = offsetContentEnd + byteRangeLengthDifference;
            String finalByteRange = "/ByteRange [0 " + offsetContentStart + " " + offsetContentEnd + " " + secondPartLength + "]";
            byteRangeLengthDifference += interimByteRange.length() - finalByteRange.length();
            //Replace the ByteRange
            docString = docString.replace(initByteRange, finalByteRange);

            //Update xref Table
            int xrefOffset = docString.indexOf("xref");
            int startObjOffset = docString.indexOf("0000000000 65535 f") + "0000000000 65535 f".length() + 1;
            int trailerOffset = docString.indexOf("trailer") - 2;
            String initialXrefTable = docString.substring(startObjOffset, trailerOffset);
            int signObjectOffset = docString.indexOf("/Type /Sig") - 3;
            String updatedXrefTable = "";
            while (initialXrefTable.indexOf("n") > 0) {
                String currObjectRefEntry = initialXrefTable.substring(0, initialXrefTable.indexOf("n") + 1);
                String currObjectRef = currObjectRefEntry.substring(0, currObjectRefEntry.indexOf(" 00000 n"));
                int currObjectOffset = Integer.parseInt(currObjectRef.trim().replaceFirst("^0+(?!$)", ""));
                if ((currObjectOffset + byteRangeLengthDifference) > signObjectOffset) {
                    currObjectOffset += byteRangeLengthDifference;
                    int currObjectOffsetDigitsCount = Integer.toString(currObjectOffset).length();
                    currObjectRefEntry = currObjectRefEntry.replace(currObjectRefEntry.substring(currObjectRef.length() - currObjectOffsetDigitsCount, currObjectRef.length()), Integer.toString(currObjectOffset));
                    updatedXrefTable += currObjectRefEntry;
                } else {
                    updatedXrefTable += currObjectRefEntry;
                }
                initialXrefTable = initialXrefTable.substring(initialXrefTable.indexOf("n") + 1);
            }
            //Replace with Updated xref Table
            docString = docString.substring(0, startObjOffset).concat(updatedXrefTable).concat(docString.substring(trailerOffset));

            //Update startxref
            int startxrefOffset = docString.indexOf("startxref");
            //Replace with Updated startxref
            docString = docString.substring(0, startxrefOffset).concat("startxref\n".concat(Integer.toString(xrefOffset))).concat("\n%%EOF\n");

            //Construct Original Document For Signature by Removing Temporary Enveloped Detached Signed Content(000...000)
            contentString = docString.substring(offsetContentStart + 1, offsetContentEnd - 1);
            String docFirstPart = docString.substring(0, offsetContentStart);
            String docSecondPart = docString.substring(offsetContentEnd);
            String docForSign = docFirstPart.concat(docSecondPart);

            //Generate Signature
            pdfFileOutputStream.write(docForSign.getBytes("ISO-8859-1"));
            File keyStorefile = new File(storePath);
            InputStream keyStoreInputStream = new FileInputStream(keyStorefile);
            KeyStore keyStore = KeyStore.getInstance("PKCS12");
            keyStore.load(keyStoreInputStream, keyStorePassword.toCharArray());
            certChain = keyStore.getCertificateChain(entryAlias);
            PrivateKey privateKey = (PrivateKey) keyStore.getKey(entryAlias, keyStorePassword.toCharArray());
            List<Certificate> certList = new ArrayList<Certificate>();
            certList = Arrays.asList(certChain);
            Store store = new JcaCertStore(certList);
//           String algorithm="SHA1WithRSA";
//           String algorithm="SHA2WithRSA";
            String algorithm = "MD5WithRSA";
            //String algorithm = "DSA";

            //Updated Sign Method
            CMSTypedData msg = new CMSProcessableByteArray(docForSign.getBytes("ISO-8859-1"));
            CMSSignedDataGenerator generator = new CMSSignedDataGenerator();
            /* Build the SignerInfo generator builder, that will build the generator... that will generate the SignerInformation... */
            SignerInfoGeneratorBuilder signerInfoBuilder = new SignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BC).build());
            //JcaContentSignerBuilder contentSigner = new JcaContentSignerBuilder("SHA2withRSA");
            JcaContentSignerBuilder contentSigner = new JcaContentSignerBuilder(algorithm);
            contentSigner.setProvider(BC);
            SignerInfoGenerator signerInfoGenerator = signerInfoBuilder.build(contentSigner.build(privateKey), new X509CertificateHolder(certList.get(0).getEncoded()));
            generator.addSignerInfoGenerator(signerInfoGenerator);
            generator.addCertificates(store);
            CMSSignedData signedData = generator.generate(msg, false);
            String apHexEnvelopedData = org.apache.commons.codec.binary.Hex.encodeHexString(signedData.getEncoded()).toUpperCase();
            //Construct Content Tag Data
            contentString = apHexEnvelopedData.concat(contentString).substring(0, contentString.length());
            contentString = "<".concat(contentString).concat(">");
            //Construct Signed Document
            String signedDoc = docFirstPart.concat(contentString).concat(docSecondPart);
            //Write Signed Document to File
            signedPdfFileOutputStream.write(signedDoc.getBytes("ISO-8859-1"));
            signedPdfFileOutputStream.close();
            signedDoc = null;
        } catch (Exception e) {
            throw new RuntimeException("Error While Generating Signed Data", e);
        }
    }

    public static void main(String[] args) {
        AffixSignature affixSignature = new AffixSignature();
    }
}

Sotto l’abile guida di MKL, ora il codice aggiornato segni il documento appena creato. Grazie a MKL!

  • Crea un PDF del Documento Firmato, ma ha degli errori – si Prega di condividere questo documento. C’è troppa codice di dire qualcosa senza spendere una considerevole quantità di tempo, ma vedendo un output di esempio che possono dare un’idea immediata di cosa cercare.
  • Che è stato chiesto, ho subito la scansione del codice e dubito PDFBox sarà felice la vostra appendRawCommands(XXXFormStream.createOutputStream(), YYY) chiamate (creazione flussi di output per la forma stessa più di una volta può essere un problema, e il passaggio avanti e indietro tra le forme); inoltre, non sembra essere uno spazio tra i più stringhe si tenta di scrivere lo stesso flusso dando origine sconosciuta Qq operatori. Inoltre il appendRawCommands utilizza UTF-8 che è completamente non-PDF.
  • Il generateSignedDocument probabilmente fa un sacco di danni, in quanto presuppone può lavorare con i file Pdf come se fossero file di testo. Che, in generale, non è il caso. E l’effettiva manipolazione aspetto molto discutibile, troppo.
  • Si prega di condividere il PDF come binario scaricare da qualche parte (ad es. pubblica di dropbox azione); ci sono alcuni dettagli che semplicemente perdersi pubblicando i Pdf come testo. Che stato detto che la struttura sembra abbastanza sensato, solo lo stream 8 0 riferimenti a se stessa nella sua XObject risorse che potrebbe fare qualsiasi visualizzatore di pdf tramite. E sembra che tu abbia modificato più di quanto si voleva, non ci sono oggetti di 11 a 0 e 12 0 nel tuo intervento.
  • Se si confronta il byte di entrambi i documenti, troverete che ci sono molti cambiamenti indesiderati, a prima vista soprattutto la sostituzione di alcuni byte da punti di domanda. Questo molto probabilmente è dovuto a realizzare il byte[] un String per operare sul documento e, infine, cambiando di nuovo in una byte[] di nuovo. Un rapido test dimostra che (nel caso del latino codifica) questa routine cambia il byte 0x81, 0x8d, 0x8f, 0x90, e 0x9d in 0x3f (cioè un punto di domanda). Così, dovrete lavorare con byte e byte[] operazioni invece di String operazioni di qui.
  • Ok, ho aggiunto la risposta, prima ho visto questo cambiamento. Sì, “ISO-8859-1” non sembra rompere qualsiasi specifico byte come predefinito, ma ho ancora il dubbio è una buona idea. Il commento per quanto riguarda gli errori nella firma contenitore e la sua costruzione sono ancora validi.
  • Si utilizza ancora generator.generate(msg, true); perché usare true qui?
  • Ok, ora è false. Che cosa è la corrente di uscita? Adobe Reader accettare ora? Se non, si prega di aggiornare il tuo esempio PDF
  • Ho aggiunto due sezioni per la mia risposta qui sotto. Il problema più importante per te in questo momento più probabile è il calcolo del valore Hash problema.
  • Come aggiungere un Testo per la Firma in Firma Visibile sul layer2. – in sostanza, è necessario registrare un tipo di carattere (ad esempio, denominata F0) in imageFormResources e aggiungere qualcosa come " BT /F0 12 Tf 3 3 Td (Text) Tj ET " per imgFormComment.



One Reply
  1. 13

    Mentre inizialmente questi suggerimenti sono stati presentati come i commenti alla domanda originale, ora il merito di formulare una risposta:

    Problemi con il codice di

    Mentre c’è troppo codice per esaminare e risolvere senza spendere una considerevole quantità di tempo, e mentre l’originale assenza di un campione PDF è stato un ostacolo, una rapida scansione del codice, ha rivelato alcuni problemi:

    • Il appendRawCommands(XXXFormStream.createOutputStream(), YYY) chiamate molto probabile causare problemi con PDFBox: la creazione di flussi di output per la forma stessa più di una volta può essere un problema, e anche il passaggio avanti e indietro tra le forme.

    • Inoltre, non sembra essere uno spazio tra i più stringhe scritto nello stesso torrente che dà luogo sconosciuto Qq operatori. Inoltre il appendRawCommands metodo utilizza UTF-8 che è estraneo PDF.

    • Il generateSignedDocument probabilmente fa un sacco di danni, in quanto presuppone può lavorare con i file Pdf come se fossero file di testo. Che, in generale, non è il caso.

    Risultato PDF problemi di

    Il risultato di esempio PDF, infine, fornito dall’OP permette di individuare alcune effettivamente realizzato problemi:

    • Confronto dei byte di entrambi i documenti (Report_08_05_23.pdf e Signed_Report_08_05_23.pdf), si trova che ci sono molti cambiamenti indesiderati, a prima vista soprattutto la sostituzione di alcuni byte da punti di domanda. Questo è dovuto all’utilizzo di ByteArrayOutputStream.toString() per operare sul documento e, infine, cambiando di nuovo in una byte[].

      E. g. cfr. il Javadoc di ByteArrayOutputStream.toString()

      * <p> This method always replaces malformed-input and unmappable-character
      * sequences with the default replacement string for the platform's
      * default character set. The {@linkplain java.nio.charset.CharsetDecoder}
      * class should be used when more control over the decoding process is
      * required.

      Determinati valori di byte non rappresentare i caratteri in della piattaforma set di caratteri predefinito e quindi si trasformano in Unicode Sostituzione del Carattere e nella trasformazione finale in un byte[] diventare 0x3f (codice ASCII per il punto di domanda). Questo cambiamento uccide compressa streaming dei contenuti, sia di contenuti e flussi di flussi di immagini.

      Per risolvere questo problema, si deve lavorare con byte e byte[] operazioni invece di String operazioni di qui.

    • Il flusso 8 0 riferimenti a se stessa nella sua XObject risorse che potrebbe fare qualsiasi visualizzatore di pdf vomitare. Si prega di astenersi da tale circolarità.

    Firma Contenitore problemi

    La firma non verificare. Così, anche rivedendo.

    • Ispezionare la firma del contenitore si può vedere che è sbagliato: nonostante la firma in adbe.pkcs7.staccato, la firma contenitore incorpora dati. Guardando il codice, la ragione diventa chiaro:

      CMSSignedData sigData = generator.generate(msg, true);

      Il true parametro chiede BC per incorporare la msg dati.

    • Aver iniziato a guardare la firma del codice, un altro problema diventa visibile: Il msg dati di cui sopra non sono semplicemente un digest, che già sono una firma:

      Signature signature = Signature.getInstance(algorithm, BC);
      signature.initSign(privateKey);
      signature.update(docForSign.getBytes());
      CMSTypedData msg = new CMSProcessableByteArray(signature.sign());

    che è sbagliato il seguito, SignerInfoGenerator viene utilizzato per creare la firma.

    Edit: Dopo i problemi citati prima sono stati risolti o almeno lavorato intorno, la firma non è ancora accettata da Adobe Reader. Così, un altro sguardo al codice e:

    Di calcolo del valore Hash problema

    OP costruisce questo ByteRange valore

    String finalByteRange = "/ByteRange [0 " + offsetContentStart + " " + offsetContentEnd + " " + secondPartLength + "]";

    e poi imposta

    String docFirstPart = docString.substring(0, offsetContentStart + 1);
    String docSecondPart = docString.substring(offsetContentEnd - 1);

    Il + 1 e - 1 sono destinati a fare di queste parti di documento di includere anche il < e > che avvolge il byte di firma. Ma l’OP utilizza anche queste stringhe di costruire il sottoscritto dati:

    String docForSign = docFirstPart.concat(docSecondPart);

    Questo è sbagliato, firmato byte non contengono il < e >. Pertanto, il valore di hash più tardi calcolato, inoltre, è sbagliato e Adobe Reader ha buone ragioni per ritenere che il documento è stato manipolato.

    Che è stato detto, ci sono anche altri problemi legati a venire una volta ogni tanto:

    Offset e lunghezza problemi di aggiornamento

    OP inserisce l’intervallo di byte per essere come questo:

    String interimByteRange = "/ByteRange [0 " + offsetContentStart + " " + offsetContentEnd + " " + secondPartLength + "]";
    int byteRangeLengthDifference = interimByteRange.length() - initByteRange.length();
    offsetContentStart = offsetContentStart + byteRangeLengthDifference;
    offsetContentEnd = offsetContentEnd + byteRangeLengthDifference;
    String finalByteRange = "/ByteRange [0 " + offsetContentStart + " " + offsetContentEnd + " " + secondPartLength + "]";
    byteRangeLengthDifference += interimByteRange.length() - finalByteRange.length();
    //Replace the ByteRange
    docString = docString.replace(initByteRange, finalByteRange);

    Ogni uno un po offsetContentStart o offsetContentEnd sarà leggermente al di sotto del 10^n e leggermente sopra in seguito. La linea

    byteRangeLengthDifference += interimByteRange.length() - finalByteRange.length();

    cerca di fare questo, ma finalByteRange (che alla fine è inserito il documento) contiene ancora non corretti valori.

    In un modo simile la rappresentazione del xref avviare inserito come questo

    docString = docString.substring(0, startxrefOffset).concat("startxref\n".concat(Integer.toString(xrefOffset))).concat("\n%%EOF\n");

    possono essere anche più di prima, che rende l’intervallo di byte (calcolata in precedenza) non coprono l’intero documento.

    Inoltre la ricerca di correttori di rilevanti gli oggetti in formato PDF utilizzando le ricerche di testo del documento

    offsetContentStart = (documentOutputStream.toString().indexOf("Contents <") + 10 - 1);
    offsetContentEnd = (documentOutputStream.toString().indexOf("000000>") + 7);
    ...
    int xrefOffset = docString.indexOf("xref");
    ...
    int startxrefOffset = docString.indexOf("startxref");

    mancherà di documenti generici. E. g. se ci sono già precedenti firme nel documento, molto probabilmente sbagliato indici saranno identificati come questo.

Lascia un commento