Hi Stefan,
I read your request, but haven't much time at the moment. I will check it as soon as possible and try to figure out how to solve your problem (hopefully without interface changes). I think I have already a suitable idea. But give me some time to evaluate your code.

Best regards
Thomas


Am 23.05.2013 14:57, schrieb Stefan Santesson:
Hi again,

I did not receive any reactions on this. Perhaps it was not explained
enough, or was of not interest.
Just checking. I would be grateful for a reply of any kind.

Stefan Santesson

3xA Security AB
Scheelevägen 17, 223 70, Lund
http://AAA-sec.com
[email protected]
+46-767 861337




On 5/17/13 1:20 AM, "Stefan Santesson" <[email protected]> wrote:

Hi,

I have developed a prototype server based signing service for the Swedish
National eID infrastructure.
I'll skip the details, but I recently switched to PDFBox for the PDF
signing
process and it works great. However, I had to modify the COSWriter class
to
get this working.

I'm writing to check whether you would consider adding the functionality I
need to future version of PDFBox.

The problem is the the signature service is just producing the signature,
it
is not trusted to handle the PDF document.
The government service having the PDF document signed is using PDFBox in
a 2
step process.

1) To produce the SignedAttributes DER Object of the CMS signature to be
created. This is the part that is hashed and signed by the signature
service.

2) After receiving the signature and signature certs from the signature service, completing the PDF signature by delivering the complete PKCS#7
object to PDFBox.

There are probably a more pure way to handle this, but Since PDFBox allows me to create a signature interface that produces the SignedData. I found
it
to be the easiest way to run the signature process 2 times.
1st pass using dummy key and dummy certs. This only to obtain the
SignedAttributes.
2nd pass by delivering a SignedData object that include the Signature
value
and certs produced by the signature service.

Now in order to do this, I have to control the random seed added by the COSWriter, or else the signature created by the signature service will not
match the hash in the SignedAttributes produced in the second pass.

My modification is provided below.
I simply provided an extra input parameter to the write function where I
can
provide the long seed

I then added a backwards compatible write function where the long seed is
current time.

By providing the same seed to pass 1 and pass 2, I can get the externally
created signature to match the SignedAttributes produced in the first
pass.
Modifications/additions are highlighted in bold.

/Stefan Sanesson

   /**
    * This will write the pdf document.
    *
    * @param doc The document to write.
    *
* @throws COSVisitorException If an error occurs while generating the
data.
    */
   public void write(PDDocument doc) throws COSVisitorException {
       write(doc, System.currentTimeMillis());
   }

   /**
    * This will write the pdf document.
    *
    * @param doc The document to write.
    * @param idTime The time seed used to generate the id
    *
* @throws COSVisitorException If an error occurs while generating the
data.
    */
   public void write(PDDocument doc, long idTime) throws
COSVisitorException {
       document = doc;
       if (incrementalUpdate) {
           prepareIncrement(doc);
       }

       // if the document says we should remove encryption, then we
shouldn't encrypt
       if (doc.isAllSecurityToBeRemoved()) {
           this.willEncrypt = false;
           // also need to get rid of the "Encrypt" in the trailer so
readers
           // don't try to decrypt a document which is not encrypted
           COSDocument cosDoc = doc.getDocument();
           COSDictionary trailer = cosDoc.getTrailer();
           trailer.removeItem(COSName.ENCRYPT);
       } else {
           SecurityHandler securityHandler =
document.getSecurityHandler();
           if (securityHandler != null) {
               try {

securityHandler.prepareDocumentForEncryption(document);
                   this.willEncrypt = true;
               } catch (IOException e) {
                   throw new COSVisitorException(e);
               } catch (CryptographyException e) {
                   throw new COSVisitorException(e);
               }
           } else {
               this.willEncrypt = false;
           }
       }

       COSDocument cosDoc = document.getDocument();
       COSDictionary trailer = cosDoc.getTrailer();
       COSArray idArray = (COSArray)
trailer.getDictionaryObject(COSName.ID);
       if (idArray == null || incrementalUpdate) {
           try {

//algorithm says to use time/path/size/values in doc to
generate
//the id. We don't have path or size, so do the best we
can
               MessageDigest md = MessageDigest.getInstance("MD5");
md.update(Long.toString(idTime).getBytes("ISO-8859-1"));
               COSDictionary info = (COSDictionary)
trailer.getDictionaryObject(COSName.INFO);
               if (info != null) {
                   Iterator<COSBase> values =
info.getValues().iterator();
                   while (values.hasNext()) {

md.update(values.next().toString().getBytes("ISO-8859-1"));
                   }
               }
               idArray = new COSArray();
               COSString id = new COSString(md.digest());
               idArray.add(id);
               idArray.add(id);
               trailer.setItem(COSName.ID, idArray);
           } catch (NoSuchAlgorithmException e) {
               throw new COSVisitorException(e);
           } catch (UnsupportedEncodingException e) {
               throw new COSVisitorException(e);
           }
       }
       cosDoc.accept(this);
   }


Reply via email to