1

I'm migrating a project from iText 5 to iText 2/or DSS (https://ec.europa.eu/cefdigital/wiki/display/CEFDIGITAL/eSignature.)

Due to the difference between iText versions (DSS use iText 2.17) i can't get a proper signed hash.

Here the code migrated from itex 5 :

            // We get the self-signed certificate from the client
            CertificateFactory factory = CertificateFactory.getInstance("X.509");
            Certificate[] chain = new Certificate[1];
            chain[0] = factory.generateCertificate(new ByteArrayInputStream(decoded));

            // we create a reader and a stamper
            PdfReader reader = new PdfReader(hash.getInputFile());
            try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {

                PdfStamper stamper = PdfStamper.createSignature(reader,
                                                                byteArrayOutputStream,
                                                                '\0',
                                                                null,
                                                                true);

                // HERE WE HAD SOME DIFFERENCE                 
                PdfSignatureAppearance sap = stamper.getSignatureAppearance();

                sap.setVisibleSignature(new Rectangle(36, 748, 36, 748), 1, signField); //invisible

                // iText 5: 
                //sap.setCertificate(chain[0]);

                // iText 2.1.7: 
                sap.setCrypto(null,chain,null,PdfSignatureAppearance.SELF_SIGNED);


                PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKLITE, PdfName.ADBE_PKCS7_DETACHED);
                dic.setReason(sap.getReason());
                dic.setLocation(sap.getLocation());
                dic.setContact(sap.getContact());

                dic.setDate(new PdfDate(sap.getSignDate()));
                sap.setCryptoDictionary(dic);
                HashMap<PdfName,Integer> exc = new HashMap<>();
                exc.put(PdfName.CONTENTS, 8192 * 2 + 2);
                sap.preClose(exc);



                // iText 5: 
                /* 
                ExternalDigest externalDigest = hashAlgorithm ->
                        DigestAlgorithms.getMessageDigest(hashAlgorithm, null);

                ExternalDigest externalDigest = new ExternalDigest() {
                    public MessageDigest getMessageDigest(String hashAlgorithm)
                            throws GeneralSecurityException {
                        return DigestAlgorithms.getMessageDigest(hashAlgorithm, null);
                    }
                };

                PdfPKCS7 sgn = new PdfPKCS7(null,
                        chain,
                        "SHA256",
                        null,
                        externalDigest,
                        false);
                */
                // iText 2.1.7: 
                PdfPKCS7 sgn = new PdfPKCS7(null,chain,null, "SHA256",null,false);

                // WARNING SAP.getRange different response content!!!
                InputStream data = sap.getRangeStream();


                // iText 5:
                //byte[] hashArray = DigestAlgorithms.digest(data, externalDigest.getMessageDigest("SHA256"));

                //iText 2
                MessageDigest md = MessageDigest.getInstance("SHA-256");
                byte[] hashArray = md.digest(IOUtils.toByteArray(data));


                byte[] ocsp = null;

                // iText 5:
                //byte[] sh = sgn.getAuthenticatedAttributeBytes(hashArray,
                //      null,
                //      null,
                //      MakeSignature.CryptoStandard.CMS);

                // iText 2:
                Calendar cal = Calendar.getInstance();
                byte[] sh = sgn.getAuthenticatedAttributeBytes(hashArray,cal,null);

                InputStream shInputStream = new ByteArrayInputStream(sh);

                // iText 5:
                //byte[] signedAttributesHash = DigestAlgorithms.digest(shInputStream,externalDigest.getMessageDigest("SHA256"));

                // iText 2:
                byte[] signedAttributesHash = md.digest(IOUtils.toByteArray(shInputStream));                


                hash.setOcsp(ocsp);                
                hash.setSgn(sgn);
                hash.setFilehash(hashArray);
                hash.setSap(sap);
                hash.setBaos(byteArrayOutputStream);

                hash.setSignedAttributesHashB64(new String(
                    org.bouncycastle.util.encoders.Base64.encode(signedAttributesHash),
                    Charsets.UTF_8)
                );

The problems starts around sap.getRangeStream();, the output are different. The following variables sh and signedAttributesHash had wrong content.

Did someone manage to do this? Any help would be much appreciated.

Alberto M
  • 1,458
  • 1
  • 16
  • 39
  • 1
    If you really have to use an iText before version 5 (more exactly before 5.3-ish as that's when the iText signing API was overhauled) and you have the option to use it via SD DSS, you should switch to using the SD DSS mechanisms for signing and using iText 2.1.7 / OpenPDF only as the PDF engine underneath. At least if you are expected to use current signing profiles (PAdES) or current signing algorithms. – mkl May 16 '19 at 16:04
  • i hope i can implements the "hash-signedhash" solution that i must keep ! So, sign a document only with public certificate and signed attribute hash i start study the documentation: http://dss.nowina.lu/doc/dss-documentation.html#_pades_signature_pdf thanks – Fabrizio Barone May 17 '19 at 07:10
  • as usual i can't find examples, now i try to find some example about "pdfbox externalsigningsupport = true" (https://pdfbox.apache.org/docs/2.0.12/javadocs/org/apache/pdfbox/pdmodel/interactive/digitalsignature/ExternalSigningSupport.html) i hope this can work for me here the request like mine: (https://issues.apache.org/jira/browse/PDFBOX-3552) – Fabrizio Barone May 17 '19 at 08:11
  • @mkl can u tell me more about : "SD DSS mechanisms for signing and using iText 2.1.7" - SD DSS what i understud is the old name, so my actual problem now is figure out how is the old way for signing a pdf. With a pdf debugger i get understaind more about signing, maybe I can insert the Acroform by low level api and twek someting to sign because i can't find anything on the old way to sign a pdf. I'm lucky 'cause our pdf's are all converted in the 1.4 version. Maybe there is a good point to start. – Fabrizio Barone May 20 '19 at 07:30
  • The other old problem is that i find example that need the PK to sign: `signatureParameters = new PAdESSignatureParameters(); signatureParameters.setSigningCertificate(getSigningCert()); signatureParameters.setCertificateChain(getCertificateChain()); signatureParameters.setSignatureLevel(SignatureLevel.PAdES_BASELINE_B); signatureParameters.setLocation("Luxembourg"); signatureParameters.setReason("DSS testing"); signatureParameters.setContactInfo("Jira");` but i don't have it :/ – Fabrizio Barone May 20 '19 at 07:42

1 Answers1

0

Temorary approach with pdfbox.

I can't figure out how to insert the public certificate inside the empty signature :/ like as iText ! I don't know if is possible.

public abstract class testtwostep
{

private static final String ID_PKCS7_DATA = "1.2.840.113549.1.7.1";
private static final String ID_PKCS7_SIGNED_DATA = "1.2.840.113549.1.7.2";
private static final String ID_MD5 = "1.2.840.113549.2.5";
private static final String ID_MD2 = "1.2.840.113549.2.2";
private static final String ID_SHA1 = "1.3.14.3.2.26";
private static final String ID_RSA = "1.2.840.113549.1.1.1";
private static final String ID_DSA = "1.2.840.10040.4.1";
private static final String ID_CONTENT_TYPE = "1.2.840.113549.1.9.3";
private static final String ID_MESSAGE_DIGEST = "1.2.840.113549.1.9.4";
private static final String ID_SIGNING_TIME = "1.2.840.113549.1.9.5";
private static final String ID_MD2RSA = "1.2.840.113549.1.1.2";
private static final String ID_MD5RSA = "1.2.840.113549.1.1.4";
private static final String ID_SHA1RSA = "1.2.840.113549.1.1.5";


public byte[] getAuthenticatedAttributeBytes(byte secondDigest[],Calendar signingTime) throws IOException {

    ASN1EncodableVector attribute = new ASN1EncodableVector();
    ASN1EncodableVector v = new ASN1EncodableVector();
    v.add(new DERObjectIdentifier(ID_CONTENT_TYPE));
    v.add(new DERSet(new DERObjectIdentifier(ID_PKCS7_DATA)));
    attribute.add(new DERSequence(v));
    v = new ASN1EncodableVector();
    v.add(new DERObjectIdentifier(ID_SIGNING_TIME));
    v.add(new DERSet(new DERUTCTime(signingTime.getTime())));
    attribute.add(new DERSequence(v));
    v = new ASN1EncodableVector();
    v.add(new DERObjectIdentifier(ID_MESSAGE_DIGEST));
    v.add(new DERSet(new DEROctetString(secondDigest)));
    attribute.add(new DERSequence(v));
    ByteArrayOutputStream bOut = new ByteArrayOutputStream();
    ASN1OutputStream dout = new ASN1OutputStream(bOut);
    dout.writeObject(new DERSet(attribute));
    dout.close();
    return bOut.toByteArray();
}

public byte[] getHash() throws IOException, NoSuchAlgorithmException, CertificateException {
    String documentFile = "/home/gigio2k/dev/swap/tmp.pdf";
    String documentFileSigned = "/home/gigio2k/dev/swap/tmp_pdfbox.pdf";
    String certStr = "MII....GsNw==";
    byte[] certByte = org.apache.commons.codec.binary.Base64.decodeBase64(certStr.getBytes());

    PDDocument document = PDDocument.load(new File(documentFile));

    Calendar date = Calendar.getInstance();
    long SeedTS = date.getTimeInMillis();
    document.setDocumentId(SeedTS);


    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(date);


    SignatureOptions opt = new SignatureOptions();
    opt.setPreferredSignatureSize(8192);

    document.addSignature(signature,opt);

    ExternalSigningSupport externalSigningSupport = document.saveIncrementalForExternalSigning(null);

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


   //byte[] sh = sgn.getAuthenticatedAttributeBytes(hashArray,null,null,MakeSignature.CryptoStandard.CMS);

        byte[] sh = getAuthenticatedAttributeBytes(digest,date);
        InputStream shInputStream = new ByteArrayInputStream(sh);
        //byte[] signedAttributesHash = DigestAlgorithms.digest(shInputStream, externalDigest.getMessageDigest("SHA256"));
        byte[] signedAttributesHash = md.digest(sh);



        System.out.println("--------------------");
        System.out.println(new String(org.bouncycastle.util.encoders.Base64.encode(signedAttributesHash),Charsets.UTF_8));
    System.out.println(date.getTimeInMillis());
    System.out.println("--------------------");
    System.out.println("Enter b64 signed:");
    System.out.println("--------------------");

    Scanner in = new Scanner(System.in);
    String signedHashB64 = in.nextLine();
    byte[] encodedSignature = org.apache.commons.codec.binary.Base64.decodeBase64(signedHashB64.getBytes());

    PDDocument document2 = PDDocument.load(new File(documentFile));

    Calendar date2 = date;
    document2.setDocumentId(SeedTS);

    PDSignature signature2 = new PDSignature();
    signature2.setFilter(PDSignature.FILTER_ADOBE_PPKLITE);
    signature2.setSubFilter(PDSignature.SUBFILTER_ADBE_PKCS7_DETACHED);
    //signature2.setName("Example User");
    signature2.setLocation("Los Angeles, CA");
    signature2.setReason("Testing");
    signature2.setSignDate(date2);

    SignatureOptions opt2 = new SignatureOptions();
    opt2.setPreferredSignatureSize(8192);

    document2.addSignature(signature2,opt2);

    File file = new File(documentFileSigned);
    FileOutputStream fos = new FileOutputStream(file);
    ExternalSigningSupport externalSigning = document2.saveIncrementalForExternalSigning(fos);
    externalSigning.setSignature(encodedSignature);

    System.out.println("--------------------");
    System.out.println("saved to: " + documentFileSigned);
    System.out.println("--------------------");
    return digest;
}

public static void main(String[] args) throws IOException, GeneralSecurityException
{
    testtwostep t2s = new testtwostep();
    t2s.getHash();
}
}

Please note: i didn't edit the first question because i'll try more way, at the end i edit the original question

  • There are examples about how to sign in PDFBox, see e.g. CreateSignature.java in the source code download. Try to follow these closely and read the method javadocs, e.g. the one of `saveIncrementalForExternalSigning`. From the code above, you don't sign at all. – Tilman Hausherr May 20 '19 at 07:54
  • yes sorry for the lack of information, i previous sign a pdf in 2 step with itext 5 with only the public certificate and an external service that sign the hash or for be more precise sign the `getAuthenticatedAttributeBytes` and save in session the PdfPKCS7 and the PdfSignatureAppearance. Working code is here: https://stackoverflow.com/questions/55901977/insert-a-signedhash-into-pdf-for-external-signing-process-workingsample now i try to do the same with "free / open source version" – Fabrizio Barone May 20 '19 at 08:02
  • But i can't find an example that work with only a public certificate and a signed hash :( – Fabrizio Barone May 20 '19 at 08:02
  • Would this help? https://stackoverflow.com/questions/10424968/add-signed-authenticated-attributes-to-cms-signature-using-bouncycastle I think it is related to using `CMSSignedDataGenerator.addSigners()`. – Tilman Hausherr May 20 '19 at 08:14
  • Thanks, i'll examine the answers and comment and maybe i try with bouncycastle and create Ans.1 DER code manually. i keep this question updated. – Fabrizio Barone May 20 '19 at 08:27