TomRoush / PdfBox-Android

The Apache PdfBox project ported to work on Android
Apache License 2.0
1k stars 259 forks source link

External signatures #108

Closed joaofortunato91present closed 6 years ago

joaofortunato91present commented 7 years ago

Hello! I am trying to make a digital signature on PDFs and ive been doing some tests with pdfbox-android. First ive tryed to add a external signature on desktop environment with success using the :

ExternalSigningSupport externalSigning = document.saveIncrementalForExternalSigning(fos); byte[] cmsSignature = Files.readAllBytes(pathToSignedHash); externalSigning.setSignature(cmsSignature);

So this is where the problem comes now, i dont have the required classes on the android pdfbox and i cant seem to find a way around it. Any way to make an external signature work in this actual version of android pdfbox or in any soon release?

By the way great work so far :)

TomRoush commented 7 years ago

This class was introduced in v2.0.3 and this library is currently up to date with v1.8.9, so it will be some time before this library will be caught up. I haven't had a chance to take a look at the code for ExternalSigningSupport yet, but I"ll see what it does and if it can easily be replicated easily.

TomRoush commented 7 years ago

I believe I've added the functionality you need if that's the only part that's giving you trouble. I've attached an aar with the necessary changes. pdfbox-android-1.8.9.1.aar.zip

joaofortunato91present commented 7 years ago

Hello Tom! First of all , thanks for the quick response ! I´ve tryed the .aar you uploaded but i have a problem with the implementation. It seems that the methods saveIncrementalForExternalSigning(OutputStream output) and addSignature(PDSignature sigObject) from the PDDocument class do not exist to complete an external signature successfully.

TomRoush commented 7 years ago

I must have missed PDDocument, Try this one. pdfbox-android-1.8.9.1.aar.zip

joaofortunato91present commented 7 years ago

Hey Tom, I´ve been around the code for a while and i keep getting an java.lang.ArrayIndexOutOfBoundsException on the COSWriter.doWriteSignature method that the saveIncrementalForExternalSigning(fos) uses. I´ve tryed many things and i think i have nothing wrong with my code.

The code sample i am using is the following:

`package com.example.joao.pdfboxtests2;

import android.os.Environment; import android.support.v7.app.AppCompatActivity; import android.os.Bundle;

import com.tom_roush.pdfbox.pdmodel.PDDocument; import com.tom_roush.pdfbox.pdmodel.interactive.digitalsignature.ExternalSigningSupport; import com.tom_roush.pdfbox.pdmodel.interactive.digitalsignature.PDSignature;

import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.Calendar;

public class MainActivity extends AppCompatActivity { final String signedHashLoc = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/hashSignature-2.sign"; final String pdfToSignLoc = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/pdftosign.pdf"; final String pdfSignedLoc = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/pdfsignedAndroid.pdf"; //PDDocument document; FileInputStream docToSignIS, signedHashIS; FileOutputStream docSignedOS; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);

    try {
        docToSignIS =new FileInputStream(pdfToSignLoc);
        signedHashIS =new FileInputStream(signedHashLoc);
        docSignedOS =new FileOutputStream(pdfSignedLoc);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }

    //criar o signature dictionary
    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");
    //data da assinatura
    signature.setSignDate(Calendar.getInstance());
    System.out.println("Sign externally...");
    try {
        PDDocument document = PDDocument.load(docToSignIS);
        document.addSignature(signature);
        ExternalSigningSupport externalSigning =document.saveIncrementalForExternalSigning(docSignedOS);
        externalSigning.setSignature(getSignedDigest());
        document.save(docSignedOS);
        document.close();
        docSignedOS.close();
        docToSignIS.close();
        signedHashIS.close();
    } catch (IOException e) {
        e.printStackTrace();
    }

}
public byte[] getSignedDigest(){
    File file = new File(signedHashLoc);
    int size = (int) file.length();
    byte[] bytes = new byte[size];
    try {
        BufferedInputStream buf = new BufferedInputStream(signedHashIS);
        buf.read(bytes, 0, bytes.length);
        buf.close();
    } catch (FileNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    return bytes;
}

}`

The error it gives is the following: image In the line: this.incrementPart[(int)(this.byteRangeOffset + (long)dataToSign - inLength)] = byteRangeBytes[dataToSign];

Meanwhile i´ve tryed to use the following code provided in the samples setup() and happens exactly the same, the arrayIndexOutOfBounds. // Enable Android-style asset loading (highly recommended) PDFBoxResourceLoader.init(getApplicationContext()); // Find the root of the external storage. root = android.os.Environment.getExternalStorageDirectory(); assetManager = getAssets(); PDDocument document = PDDocument.load(assetManager.open("pdftosign.pdf")); Thanks in advance

crockercaria commented 7 years ago

I too get the same error 👍

.. java.lang.RuntimeException: ... Caused by: java.lang.ArrayIndexOutOfBoundsException: length=20732; index=767721 at com.tom_roush.pdfbox.pdfwriter.COSWriter.doWriteSignature(COSWriter.java:722)
at com.tom_roush.pdfbox.pdfwriter.COSWriter.visitFromDocument(COSWriter.java:1114)

public ExternalSigningSupport saveIncrementalForExternalSigning(OutputStream output) throws IOException { if(this.pdfSource == null) { throw new IllegalStateException("document was not loaded from a file or a stream"); } else { COSWriter writer = new COSWriter(output, this.pdfSource); writer.write(this); ***** this.signingSupport = new SigningSupport(writer); return this.signingSupport; } }

The error is here ** in the write(this)

public void write(PDDocument doc) throws IOException { this.write(doc, (SignatureInterface)null); } is the null correct ?

TomRoush commented 7 years ago

Seems like this is more difficult than I hoped. Would it be possible for either of you to share a test signature file that fails that I could use for testing?

joaofortunato91present commented 7 years ago

Hey Tom, Can i have an email to send you the files im using for testing? Some of them are kind of private :)

TomRoush commented 7 years ago

Never mind, I managed to get the error again with my signature.

joaofortunato91present commented 7 years ago

Hello Tom, I have been analyzing the error and i believe i have found the solution by comparing code between the original PDFBox (https://github.com/apache/pdfbox/tree/2bf48eed0223118c22b19c53cff20a2c5ba5fb95) and PDFBox-Android, since i had an external signature solution working on the original one. By comparing what was added to the original one i have found that by switching the code long inLength = incrementalInput.available(); for long inLength = incrementalInput.length(); at the COSWriter.java class it seems to work fine.

TomRoush commented 7 years ago

That's great. I can't check right now, but if everything works as it should, feel free to make a pull request.

mohitatray commented 4 years ago

Sorry if this is a nub question but I do not find ExternalSigningSupport class when I add this line in build.gradle dependencies - implementation 'com.tom_roush:pdfbox-android:1.8.10.1'

This class is not available - import com.tom_roush.pdfbox.pdmodel.interactive.digitalsignature.ExternalSigningSupport;

Why? Does this library still depends on PDFBox below v2.0.3? And can you tell me how to follow these steps to sign PDF using pdfbox-android? I only need this library to digitally sign PDFs. I can generate PDF by PdfDocument class in android.