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

