-1

I'm having problems signing pdf's with pdfbox. The idea is the same as used with itext (Java IText7 PDF Sign Problem - Document has been altered or corrupted since it was signed). Get bytearray from a pdf with empty signature, send byte array to an external entity that returns a hash-signature, and embed that hash in pdf with empty signature.

PDF error: Invalid signature There are errors in formatting or in the information contained in this signature.

enter image description here

The java code (small resume) that i use are:

            //GET External signing content  
        PDDocument doc = PDDocument.load(inputPDF);

        PDSignature signature = new PDSignature();
        signature.setFilter(PDSignature.FILTER_ADOBE_PPKLITE); // default filter
        signature.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
        
        signature.setReason(sigReason);
        signature.setLocation(sigLocation);
        
        
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
        Calendar cal = Calendar.getInstance();
        cal.setTime(sdf.parse(sdf.format(forcedDate)));
        signature.setSignDate(cal);

        
        SignatureOptions signatureOptions = new SignatureOptions();
        signatureOptions.setPreferredSignatureSize(SignatureOptions.DEFAULT_SIGNATURE_SIZE * 8);

        doc.addSignature(signature,signatureOptions);

        ExternalSigningSupport externalSigningSupport = doc.saveIncrementalForExternalSigning(null);
        byte[] content = IOUtils.toByteArray(externalSigningSupport.getContent());
        
        MessageDigest md = MessageDigest.getInstance("SHA256", new BouncyCastleProvider());
        hashtosign = md.digest(content); // this is sent to client

        return hashtosign;
    

    // CALL AMA(entity - external client webservice) TO GET SIGNATURE-HASH (signatureHash)
       String hashToSendAMA = SafePdfHelper.getHashtoSign(hashtosign);
       String signatureHash_B64 = SafeAmaHelper.getAssinat(token,hashToSendAMA,"tst_nunommc",credentialID).getSignatures().get(0);
    // SIGNATURE-HASH 
       byte[] signatureHash = Base64.getDecoder().decode(String.valueOf(signatureHash_B64.toCharArray()));
    


    //InsertHash SIGNATURE-HASH in PDF  
    
    #code
    
    ExternalSigningSupport externalSigningSupport = doc.saveIncrementalForExternalSigning(fosSigned);
    externalSigningSupport.setSignature(signatureHash);

    doc.save("C:/INTEGRACOES/Ama/omitf/tst_signed.pdf");
    return "Signed";

Base64 format of the signature (AMA) is: PMPk04d/sj5OxRZpZfGSJlrQthryaWjmj6tNs3a7g3CgOB02c/a9omVaZD1Upl87XI/FvVMBMoKlGcm7MRO+ENgKApr9O1/joKN5dnucm11OXL8rxov/EAV8cQOLqCVTNvsDDylDy1L7LdYSBCDzvqCKmR8OF7955wyZJeTDiCcYDm3gGN9IDQ82fwMKPuiIt7e1ToZdm7qzLBLTr38K2eM784NUNCFMI152QdtZIxQHl03qXk6IrNoWxW0axh9YcJmbBWlkUo75wm6BjuJzkPOYgaT4/CWTyjuCKtYg748gQQmh89xbBswmtZxw7NRVRXl4DtWE9ceRClKZx0JrIawzkWNhJt42+u62ntNs/Z4a/LRNg8rfmru2C9mhqZ5h6jfIJzk9bX219csIekM46pXofEizAnnMTjDZLWYedHFWojJGybdA76fVlBQx+9nTA7xD/gqDY9NUKW0bjfF9j+diHxNZklZ+0+RzUMF9l3Pq9Hlg2InaZM08yUyhdSYl

Can anyone help please?

-sorry for the bad formatting post

NunoMMC
  • 31
  • 5
  • Please share an example PDF signed by your code. Often a quick analysis of the PDF makes clear where to look in the code or in the environment. – mkl Oct 22 '21 at 12:17
  • I can't add attachments here, i sent it to you by e-mail (filename tst_nunommc_signed_fos.pdf). thanks @mkl – NunoMMC Oct 22 '21 at 13:31
  • Ok, after a first glance at your files: The _signature_ you have to embed is not the array of naked signature bytes but instead a full fledged CMS signature container wrapping and detailing the signature bytes. In your iText 7 based solution you use the iText `PdfPKCS7` class for that. PDFBox does not have own classes for that, you can use BouncyCastle classes, though. – mkl Oct 24 '21 at 14:00
  • An example of BouncyCastle use to create a CMS signature container can be found in `org.apache.pdfbox.examples.signature.CreateSignatureBase.sign(InputStream)`. – mkl Oct 24 '21 at 15:02

1 Answers1

0

I already found a solution. The key was to create a CMS signature container with BouncyCastle.

public static byte[] createSignature(InputStream content, Certificate [] chain, String fileName) throws Exception {
    //BufferedInputStream bis = new BufferedInputStream(content);

    List<Certificate> certList = new ArrayList<>();

    for (Certificate cert: chain){
        certList.add(cert);
    }

    Store certs = new JcaCertStore(certList);

    CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
    gen.addCertificates(certs);

    MessageDigest digest = MessageDigest.getInstance("SHA-256");
    byte [] hashBytes = digest.digest(content.readAllBytes());

    String hashToSign = SafePdfHelper.getHashtoSign(hashBytes);
    String signatureHash_B64 = SafeAmaHelper.getAssinat(token,hashToSign,fileName,credentialID).getSignatures().get(0); //GET SIGNHASH WITH A EXTERNAL ENTITY
    byte[] signedHash = Base64.getDecoder().decode(String.valueOf(signatureHash_B64.toCharArray()));

    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");
        }
    };

    JcaSignerInfoGeneratorBuilder sigb = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build());
    sigb.setDirectSignature(true);

    for (Certificate cert: chain){
        //Certificate x = cert;
        org.bouncycastle.asn1.x509.Certificate certx509 = org.bouncycastle.asn1.x509.Certificate.getInstance(ASN1Primitive.fromByteArray(cert.getEncoded()));
        gen.addSignerInfoGenerator(sigb.build(nonSigner, new X509CertificateHolder(certx509)));
    }

    CMSTypedData msg = new CMSProcessableInputStream(new ByteArrayInputStream("not used".getBytes()));

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

    return signature;
}

thanks @mkl

NunoMMC
  • 31
  • 5
  • Indeed, you need a custom `ContentSigner`. Beware, though, only in the special case of a very simple CMS signature container the document hash is signed directly. If you ever need to generate less simple ones (e.g. for PAdES baseline signatures), your AMA call must be moved into your `ContentSigner.getSignature` implementation and sign the data in the `ByteArrayOutputStream` you return in `ContentSigner.getOutputStream`. – mkl Nov 10 '21 at 10:21
  • This is a weird answer considering that getOutputStream() returns an empty stream. The person at https://stackoverflow.com/questions/76969040/java-apache-pdfbox-issue-with-generating-the-hash-for-external-signing-or-with used that answer and is now stuck. – Tilman Hausherr Aug 25 '23 at 07:40