Hi, Its a great guideline to include the TimeStamp while signing a pdf. But when i am opening on different version of Adobe REader, its gving different result. In Adobe v 8, its working fine but in Adobe v 7 its giving i guess bad request. so, is there any Acrofield which governs the usage of PDF and makes it compatible for all Adobe reader OR this implementation is applicable to only Adobe v 8 and later version?
I have one more question, i am able to timestamp the signature successfully. But only when i am creating a new PDF file. If i am using the same PDF file name as a source and destination to make it as a certified, it gives ClassCastException, ArrayIndexOutOfBoundsException as follows depending upon the usage of sap.preClose(exc) method. When i am adding the contents using the following approach then it is giving ClassCastException as follows: Integer inte = new Integer(contentEst * 2 + 2); HashMap exc = new HashMap(); exc.put(PdfName.CONTENTS, new PdfString(inte.toString().getBytes()).setHexWriting(true)); sap.preClose(exc); java.lang.ClassCastException at com.lowagie.text.pdf.PdfSignatureAppearance.preClose(Unknown Source) at LunaSigner.signPDF(LunaSigner.java:307) at LunaSigner.main(LunaSigner.java:100) When i am adding the contents using the following approach then it is giving IllegalArgumentException as follows: HashMap exc = new HashMap(); exc.put(PdfName.CONTENTS, inte); sap.preClose(exc); java.lang.IllegalArgumentException at java.nio.Buffer.position(Buffer.java:218) at com.lowagie.text.pdf.MappedRandomAccessFile.seek(Unknown Source) at com.lowagie.text.pdf.RandomAccessFileOrArray.seek(Unknown Source) at com.lowagie.text.pdf.PdfReader.getStreamBytesRaw(Unknown Source) at com.lowagie.text.pdf.PdfReader.getStreamBytesRaw(Unknown Source) at com.lowagie.text.pdf.PRStream.toPdf(Unknown Source) at com.lowagie.text.pdf.PdfIndirectObject.writeTo(Unknown Source) at com.lowagie.text.pdf.PdfWriter$PdfBody.add(Unknown Source) at com.lowagie.text.pdf.PdfWriter.addToBody(Unknown Source) at com.lowagie.text.pdf.PdfStamperImp.close(Unknown Source) at com.lowagie.text.pdf.PdfSignatureAppearance.preClose(Unknown Source) at LunaSigner.signPDF(LunaSigner.java:307) at LunaSigner.main(LunaSigner.java:100) So, if the source file and destination file is same, then the below mentioned code is bouncing at sap.preClose(exc) step. HashMap exc = new HashMap(); exc.put(PdfName.CONTENTS, inte); sap.preClose(exc); But the same code works fine if i am creating a new pdf and have source and output pdf name different. so, please reply with you comments on the above points. Thanks Gurpreet Singh Martin Brunecky wrote: > > Since iText currently lacks a direct support for time-stamped PDF > signatures > > > (signatures backed by a Time Stamp Authority - TSA), a fair amount of > effort > > > is needed to match the Acrobat and Reader functionality. > > However, it can be done - at the cost of iText source (PdfPKCS7) > augmentation. > > > > The following posting is essentially a re-packaging of the original > posting > by > > > > >>>>>> Aiken Sam, 2006-11-15 <<<<<<<< > > > > Sam deserves all the credits for this functionality. My contribution is > limited > > to refactoring for subclassing, some minor fixes, cleanup and testing. > > > > OBJECTIVE: > > My posting is made out of self-interest: I would like to see the support > for > > time-stamped signing built into iText. Because class PdfPKCS7 was not > meant > > for subclassing, any "outside iText" implementation ends up cloning most > > of that class - even though changes required for time-stamping are small. > > > > My re-packaging of Aiken Sam's work aimed at two main goals: > > - embed the support within iText with minimal changes to the existing code > > - allow subclassing the TSA client for specific RFC 3161 timestamp > providers > > (such as using provider supplied toolkit) and communication protocols. > > > > TESTING: > > I have done testing using several flavors of certificate (PKCS12 based > certs, > > PKCS11 USB token from VeriSign CDS for Adobe). > > I tested using TSA at "http://dse200.ncipher.com/TSS/HttpTspServer" (no > account > > or password needed) and TSA at www.digistamp.com, offering 'test' service > for free. > > Other services used by Aiken Sam in his testing are now off-the-air. > > I also tested with other Bouncy Castle releases (bc*-jdk15-137.jar). > > > > DEPENDENCIES: > > The class TSAClientBouncyCastle adds a new iText build dependency, Bouncy > Castle > > time stamp support: bctsp-jdk14-135.jar (you must add this to > src/ant/compile.xml). > > You can find this jar on sourceforge, or at www.bouncycastle.org. > > > > > > DEMO: > > Included (below) is a 'demo' sample generating an invisible, time-stamped > > signature, with its support classes. All code builds with javac > -source=1.4, > > even though I prefer 1.5. > > > > LIMITATIONS: > > The implementation and demo provided here uses explicit calls to > preClose() > and > > PdfPKCS7.getEncodedPKCS7(). > > It would be possible to 'hide' all of this by adding something like > > sap.setTSAClient(client) together with adding time-stamp handling to > preClose() > > and close() methods -- I considered such changes too intrusive at this > point. > > > > ISSUES: > > The main issue is what exactly Adobe considers a valid timestamp. The RFC > 3161 > > leaves the time stamped "imprint" open to the protocol users (only > asserting > the > > TSA must NOT change it), and hence it is upon the application to define > it. > > When Adobe reader encounters some 'other' time-stamped imprint, it ignores > it. > > I would appreciate any pointers to Adobe specs, as I would like to expand > upon > > the current work - in some cases, my control over the 'imprint' generation > > is limited. > > > > > > > > ===== iText SOURCE CHANGES > ===================================================== > > Source changes comprise of changes to PdfPKCS7 (3 areas), one new > interface > > and one new class ("default" TSA caller implementation). > > > > ===== PdfPKCS7.java > ============================================================ > > The following is a diff output showing changes from iText 2.0.4 source. > Changes > > include one signature change (getEncodedPKCS7), adding > backwards-compatible > ones, > > TSA client calling code, and one new method - > buildUnauthenticatedAttributes(). > > > >>diff -b PdfPKCS7.orig PdfPKCS7.java > > 770c770 > > < return getEncodedPKCS7(null, null); > > --- > >> return getEncodedPKCS7(null, null, null); > > 780a781,793 > >> return getEncodedPKCS7(secondDigest, signingTime, null); > >> } > >> > >> /** > >> * Gets the bytes for the PKCS7SignedData object. Optionally the > authenticatedAttributes > >> * in the signerInfo can also be set, OR a time-stamp-authority >> client > >> * may be provided. > >> * @param secondDigest the digest in the authenticatedAttributes > >> * @param signingTime the signing time in the authenticatedAttributes > >> * @param tsaClient TSAClient - null or an optional time stamp > authority client > >> * @return byte[] the bytes for the PKCS7SignedData object > >> */ > >> public byte[] getEncodedPKCS7(byte secondDigest[], Calendar > signingTime, TSAClient tsaClient) { > > 881a895,907 > >> // When requested, go get and add the timestamp. May throw an > exception. > >> // Added by Martin Brunecky, 07/12/2007 folowing Aiken Sam, > 2006-11-15 > >> // Sam found Adobe expects time-stamped SHA1-1 of the > encrypted digest > >> if (tsaClient != null) { > >> byte[] tsImprint = > MessageDigest.getInstance("SHA-1").digest(digest); > >> byte[] tsToken = tsaClient.getTimeStampToken(this, > tsImprint); > >> if (tsToken != null) { > >> ASN1EncodableVector unauthAttributes = > buildUnauthenticatedAttributes(tsToken); > >> if (unauthAttributes != null) { > >> signerinfo.add(new DERTaggedObject(false, 1, new > DERSet(unauthAttributes))); > >> } > >> } > >> } > > 922a949,976 > >> /** > >> * Added by Aiken Sam, 2006-11-15, modifed by Martin Brunecky > 07/12/2007 > >> * to start with the timeStampToken (signedData >> 1.2.840.113549.1.7.2). > >> * Token is the TSA response without response status, which is >> usually > >> * handled by the (vendor supplied) TSA request/response interface). > >> * @param timeStampToken byte[] - time stamp token, DER encoded > signedData > >> * @return ASN1EncodableVector > >> * @throws IOException > >> */ > >> private ASN1EncodableVector buildUnauthenticatedAttributes(byte[] > timeStampToken) throws IOException { > >> if (timeStampToken == null) > >> return null; > >> > >> // @todo: move this together with the rest of the defintions > >> String ID_TIME_STAMP_TOKEN = "1.2.840.113549.1.9.16.2.14"; // RFC > 3161 id-aa-timeStampToken > >> > >> ASN1InputStream tempstream = new ASN1InputStream(new > ByteArrayInputStream(timeStampToken)); > >> ASN1EncodableVector unauthAttributes = new >> ASN1EncodableVector(); > >> > >> ASN1EncodableVector v = new ASN1EncodableVector(); > >> v.add(new DERObjectIdentifier(ID_TIME_STAMP_TOKEN)); // > id-aa-timeStampToken > >> ASN1Sequence seq = (ASN1Sequence) tempstream.readObject(); > >> v.add(new DERSet(seq)); > >> > >> unauthAttributes.add(new DERSequence(v)); > >> return unauthAttributes; > >> } > >> > > > > ===== TSAClient.java > =u========================================================= > > package com.lowagie.text.pdf; > > > > /** > > * Time Stamp Authority client (caller) interface. > > * <p> > > * Interface used by the PdfPKCS7 digital signature builder to call > > * Time Stamp Authority providing RFC 3161 compliant time stamp token. > > * @author Martin Brunecky, 07/17/2007 > > */ > > public interface TSAClient { > > /** > > * Get the time stamp token size estimate. > > * Implementation must return value large enough to accomodate the > entire token > > * returned by getTimeStampToken() _prior_ to actual > getTimeStampToken() > call. > > */ > > public int getTokenSizeEstimate(); > > > > /** > > * Get RFC 3161 timeStampToken. > > * Method may return null indicating that timestamp should be skipped. > > * @param caller PdfPKCS7 - calling PdfPKCS7 instance (in case caller > needs it) > > * @param imprint byte[] - data imprint to be time-stamped > > * @return byte[] - encoded, TSA signed data of the timeStampToken > > * @throws Exception - TSA request failed > > */ > > public byte[] getTimeStampToken(PdfPKCS7 caller, byte[] imprint) > throws > Exception; > > > > } > > > > > > ===== TSAClientBouncyCastle.java > =============================================== > > package com.lowagie.text.pdf; > > > > import java.io.*; > > import java.math.*; > > import java.net.*; > > > > > > import org.bouncycastle.asn1.cmp.*; > > import org.bouncycastle.asn1.x509.*; > > import org.bouncycastle.tsp.*; > > > > /** > > * Time Stamp Authority Client interface implementation using Bouncy > Castle > > * org.bouncycastle.tsp package. > > * <p> > > * Created by Aiken Sam, 2006-11-15, refactored by Martin Brunecky, > 07/15/2007 > > * for ease of subclassing. > > * </p> > > */ > > public class TSAClientBouncyCastle implements TSAClient { > > protected String tsaURL; > > protected String tsaUsername; > > protected String tsaPassword; > > protected int tokSzEstimate; > > > > public TSAClientBouncyCastle(String url) { > > this(url, null, null, 4096); > > } > > > > public TSAClientBouncyCastle(String url, String username, String > password) { > > this(url, username, password, 4096); > > } > > > > /** > > * Constructor. > > * Note the token size estimate is updated by each call, as the token > > * size is not likely to change (as long as we call the same TSA using > > * the same imprint length). > > * @param url String - Time Stamp Authority URL (i.e. > "http://tsatest1.digistamp.com/TSA") > > * @param username String - user(account) name > > * @param password String - password > > * @param tokSzEstimate int - estimated size of received time stamp > token (DER encoded) > > */ > > public TSAClientBouncyCastle(String url, String username, String > password, int tokSzEstimate) { > > this.tsaURL = url; > > this.tsaUsername = username; > > this.tsaPassword = password; > > this.tokSzEstimate = tokSzEstimate; > > } > > > > /** > > * Get the token size estimate. > > * Returned value reflects the result of the last succesfull call, > padded > > * @return int > > */ > > public int getTokenSizeEstimate() { > > return tokSzEstimate; > > } > > > > public byte[] getTimeStampToken(PdfPKCS7 caller, byte[] imprint) > throws > Exception { > > return getTimeStampToken(imprint); > > } > > > > /** > > * Get timestamp token - Bouncy Castle request encoding / decoding > layer > > */ > > protected byte[] getTimeStampToken(byte[] imprint) throws Exception { > > byte[] respBytes = null; > > try { > > // Setup the time stamp request > > TimeStampRequestGenerator tsqGenerator = new > TimeStampRequestGenerator(); > > tsqGenerator.setCertReq(true); > > // tsqGenerator.setReqPolicy("1.3.6.1.4.1.601.10.3.1"); > > BigInteger nonce = > BigInteger.valueOf(System.currentTimeMillis()); > > TimeStampRequest request = > tsqGenerator.generate(X509ObjectIdentifiers.id_SHA1.getId() , imprint, > nonce); > > byte[] requestBytes = request.getEncoded(); > > > > // Call the communications layer > > respBytes = getTSAResponse(requestBytes); > > > > // Handle the TSA response > > TimeStampResponse response = new TimeStampResponse(respBytes); > > > > // validate communication level attributes (RFC 3161 PKIStatus) > > response.validate(request); > > PKIFailureInfo failure = response.getFailInfo(); > > int value = (failure == null) ? 0 : failure.intValue(); > > if (value != 0) { > > // @todo: Translate value of 15 error codes defined by > PKIFailureInfo to string > > throw new Exception("Invalid TSA '" + tsaURL + "' response, > code " + value); > > } > > // @todo: validate the time stap certificate chain (if we want > > // assure we do not sign using an invalid timestamp). > > > > // extract just the time stamp token (removes communication > status info) > > TimeStampToken tsToken = response.getTimeStampToken(); > > if (tsToken == null) { > > throw new Exception("TSA '" + tsaURL + "' failed to return > time stamp token"); > > } > > TimeStampTokenInfo info = tsToken.getTimeStampInfo(); // to > view > details > > byte[] encoded = tsToken.getEncoded(); > > long stop = System.currentTimeMillis(); > > > > // Update our token size estimate for the next call (padded to > be > safe) > > this.tokSzEstimate = encoded.length + 32; > > return encoded; > > } > > catch (Exception e) { > > throw e; > > } > > catch (Throwable t) { > > throw new Exception("Failed to get TSA response from '" + > tsaURL > +"'", t); > > } > > } > > > > /** > > * Get timestamp token - communications layer > > * @return - byte[] - TSA response, raw bytes (RFC 3161 encoded) > > */ > > protected byte[] getTSAResponse(byte[] requestBytes) throws Exception { > > // Setup the TSA connection > > URL url = new URL(tsaURL); > > URLConnection tsaConnection = (URLConnection) > url.openConnection(); > > > > tsaConnection.setDoInput(true); > > tsaConnection.setDoOutput(true); > > tsaConnection.setUseCaches(false); > > tsaConnection.setRequestProperty("Content-Type", > "application/timestamp-query"); > > //tsaConnection.setRequestProperty("Content-Transfer-Encoding", > "base64"); > > tsaConnection.setRequestProperty("Content-Transfer-Encoding", > "binary"); > > > > if ((tsaUsername != null) && !tsaUsername.equals("") ) { > > String userPassword = tsaUsername + ":" + tsaPassword; > > tsaConnection.setRequestProperty("Authorization", "Basic " + > > new String(new > sun.misc.BASE64Encoder().encode(userPassword.getBytes()))); > > }; > > OutputStream out = tsaConnection.getOutputStream(); > > out.write(requestBytes); > > out.close(); > > > > // Get TSA response as a byte array > > InputStream inp = tsaConnection.getInputStream(); > > ByteArrayOutputStream baos = new ByteArrayOutputStream(); > > byte[] buffer = new byte[1024]; > > int bytesRead = 0; > > while ((bytesRead = inp.read(buffer, 0, buffer.length)) >= 0) { > > baos.write(buffer, 0, bytesRead); > > } > > byte[] respBytes = baos.toByteArray(); > > > > String encoding = tsaConnection.getContentEncoding(); > > if (encoding != null && encoding.equalsIgnoreCase("base64")) { > > sun.misc.BASE64Decoder dec = new sun.misc.BASE64Decoder(); > > respBytes = dec.decodeBuffer(new String(respBytes)); > > } > > return respBytes; > > } > > > > } > > > > > > ===== D E M O > ================================================================== > > To use the following demo, you must have a PKCS12 certificate, such as > VeriSign > > digital mail ID (or refer to my PKCS11 posting for PKCS11, JCS cert > support), > > and you need access to some Time Stamp Authority (TSA) service, such as an > > account with www.digistamp.com (free test service available). > > > > Depending upon certicate(s) used, you may have to adjust trusted > certificates > > in your Adobe Acrobat or reader. > > > > ===== PdfSignerDemo.java > ======================================================= > > > > package demo; > > > > import java.io.*; > > import java.util.*; > > > > import com.lowagie.text.*; > > import com.lowagie.text.pdf.*; > > > > /** > > * Demo using iText to digitally sign PDF document with a valid time-stamp > > * Demo dependecies: > > * SignerKeystore - interface providing signing certificate access > > * SignerKeystorePKCS12 - implemnation importing PKCS12 (.pfx) certificate > > */ > > public class PdfSignerDemo { > > private SignerKeystore sks; > > private TSAClient def; > > > > public PdfSignerDemo(SignerKeystore sks, TSAClient tsc) throws > Exception > { > > this.sks = sks; > > this.def = tsc; > > } > > > > public void signPDF(String srcFile, String dstFile) throws Exception { > > signPDF(srcFile, dstFile, def); > > } > > > > public void signPDF(String srcFile, String dstFile, TSAClient tsc) { > > try { > > // Prepare for PDF file handling (copy, stamp) > > PdfReader reader = new PdfReader(srcFile); > > FileOutputStream fout = new FileOutputStream(dstFile); > > PdfStamper stp = PdfStamper.createSignature(reader, fout, > '\0'); > > PdfSignatureAppearance sap = stp.getSignatureAppearance(); > > setAppearance(sap); > > > > // Configure PDF signature dictionary (PdfName.ADOBE_PPKLITE > works too) > > PdfSignature dic = new PdfSignature(PdfName.ADOBE_PPKMS, > > PdfName.ADBE_PKCS7_SHA1); > > // dic.setName (null); // optional - PdfPKCS7 uses the > certificate CN > > dic.setReason(sap.getReason()); > > dic.setLocation(sap.getLocation()); > > dic.setContact(sap.getContact()); > > dic.setDate(new PdfDate(sap.getSignDate())); // time-stamp > will > over-rule this > > sap.setCryptoDictionary(dic); > > > > // Estimate signature size, creating a 'fake' one using fake > data > > // (SHA1 length does not depend upon the data length) > > byte[] estSignature = genPKCS7Signature(new > ByteArrayInputStream("fake".getBytes()), null); > > int contentEst = estSignature.length + > > ((tsc == null) ? 0 : > tsc.getTokenSizeEstimate()); > > > > // Preallocate excluded byte-range for the signature content > (hex encoded) > > HashMap exc = new HashMap(); > > exc.put(PdfName.CONTENTS, new Integer(contentEst * 2 + 2)); > > sap.preClose(exc); > > > > // Get the true data signature, including a true time stamp > token > > byte[] encodedSig = genPKCS7Signature(sap.getRangeStream(), > tsc); > > if (contentEst + 2 < encodedSig.length) { > > throw new Exception("Timestamp size estimate " + > contentEst > + > > " is too low for actual " + > > encodedSig.length); > > } > > > > // Copy signature into a zero-filled array, padding it up to > estimate > > byte[] paddedSig = new byte[contentEst]; > > System.arraycopy(encodedSig, 0, paddedSig, 0, > encodedSig.length); > > > > // Finally, load zero-padded signature into the signature > field > /Content > > PdfDictionary dic2 = new PdfDictionary(); > > dic2.put(PdfName.CONTENTS, new > PdfString(paddedSig).setHexWriting(true)); > > sap.close(dic2); // closes sap.close() > > } catch (Throwable t) { > > System.out.println("Signing failed" + t); > > t.printStackTrace(); > > } > > } > > > > > > /** > > * Setup signature appearance. Override to define specifics. > > * @param sap PdfSignatureAppearance > > */ > > protected void setAppearance(PdfSignatureAppearance sap) { > > // Make this an invisible signature > > sap.setVisibleSignature(new Rectangle(0, 0, 0, 0), 1, > "Signature"); > // empty makes field invisible > > } > > > > > > /** > > * Generate the PKCS7 encoded signature > > * @param data InputStream - data to digest > > * @param doTimestamp boolean - true to include time-stamp > > * @return byte[] > > * @throws Exception > > */ > > protected byte[] genPKCS7Signature(InputStream data, TSAClient tsc) > throws Exception { > > // assume sub-filter is adobe.pkcs7.sha1 > > PdfPKCS7 sgn = new PdfPKCS7(sks.getPrivateKey(), sks.getChain(), > null, > > "SHA1", sks.getProvider().getName(), > true); > > byte[] buff = new byte[2048]; > > int len = 0; > > while ((len = data.read(buff)) > 0) { > > sgn.update(buff, 0, len); > > } > > return sgn.getEncodedPKCS7(null, null, tsc); > > } > > > > // Configuration > > // MY digital certificate (PKCS#12 - get an e-mail digital ID from > VeriSign) > > private static final String CERT_PATH = "mycert.pfx"; > > private static final String CERT_PASSW = "mypassword"; > > > > // MY TSA account (go to www.digistamp.com and create one - test is > free) > > private static final String TSA_URL = > "http://tsatest1.digistamp.com/TSA" ; > > private static final String TSA_ACCNT = "99999"; > > private static final String TSA_PASSW = "pwdpwd"; > > > > public static void main(String[] args) throws Exception { > > if (args.length < 1) { > > System.out.println("Usage: PdfSignerDemo file {file} > {file}..."); > > System.exit(1); > > } > > SignerKeystore sks = new SignerKeystorePKCS12(new > FileInputStream(CERT_PATH), CERT_PASSW); > > TSAClient tsc = new TSAClientBouncyCastle(TSA_URL, TSA_ACCNT, > TSA_PASSW); > > > > PdfSignerDemo demo = new PdfSignerDemo(sks, tsc); > > for (int i=0; i<args.length; i++) { > > int iDot = args[i].lastIndexOf('.'); > > demo.signPDF(args[i], args[i].substring(0, iDot) + "_signed" + > args[i].substring(iDot)); > > } > > } > > } > > > > ===== SignerKeystore.java > ====================================================== > > package demo; > > > > import java.security.Provider; > > import java.security.PrivateKey; > > import java.security.cert.Certificate; > > > > public interface SignerKeystore { > > public PrivateKey getPrivateKey() ; > > public Certificate[] getChain() ; > > public Provider getProvider(); > > } > > > > > > ===== SignerKeystorePKCS12.java > ================================================ > > package demo; > > > > import java.io.*; > > import java.security.*; > > import java.security.cert.Certificate; > > > > /** > > * SignerKeystore implementation using PKCS#12 file (.pfx etc) > > */ > > public class SignerKeystorePKCS12 implements SignerKeystore { > > private static Provider prov = null; > > private KeyStore ks; > > private String alias; > > private String pwd; > > > > private PrivateKey key; > > private Certificate[] chain; > > > > public SignerKeystorePKCS12(InputStream inp, String passw) throws > Exception { > > // This should be done once only for the provider... > > if (prov == null) { > > prov = new > org.bouncycastle.jce.provider.BouncyCastleProvider(); > > Security.addProvider(prov); > > } > > > > this.ks = KeyStore.getInstance("pkcs12", prov); > > this.pwd = passw; > > this.ks.load(inp, pwd.toCharArray()); > > this.alias = (String)ks.aliases().nextElement(); > > this.key = (PrivateKey)ks.getKey(alias, pwd.toCharArray()); > > this.chain = ks.getCertificateChain(alias); > > } > > > > public PrivateKey getPrivateKey() { > > return key; > > } > > > > public Certificate[] getChain() { > > return chain; > > } > > > > public Provider getProvider() { > > return ks.getProvider(); > > } > > } > > > > ===== E N D > =================================================================== > > > > > > > > > > > > Martin Brunecky > Software Architect > > RecordFusion > > > <mailto:[EMAIL PROTECTED]> [EMAIL PROTECTED] > AIM: mbrunecky > > > tel: > fax: > > > <http://www.plaxo.com/click_to_call?src=jj_signature&To=303-865-8847+x+225&E > [EMAIL PROTECTED]> 303-865-8847 x 225 > 303-865-8846 > > > > > > <https://www.plaxo.com/add_me?u=38655967504&v0=2505222&k0=891308811> Add > me > to your address book... > > <http://www.plaxo.com/signature> Want a signature like this? > > > > > > ------------------------------------------------------------------------- > This SF.net email is sponsored by: Splunk Inc. > Still grepping through log files to find problems? Stop. > Now Search log events and configuration files using AJAX and a browser. > Download your FREE copy of Splunk now >> http://get.splunk.com/ > _______________________________________________ > iText-questions mailing list > iText-questions@lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/itext-questions > Buy the iText book: http://itext.ugent.be/itext-in-action/ > > -- View this message in context: http://www.nabble.com/PDF-Digital-signature-with-timestamp---using-Time-Stamp-Authority---example-tf4147508.html#a12416847 Sent from the iText - General mailing list archive at Nabble.com. ------------------------------------------------------------------------- This SF.net email is sponsored by: Splunk Inc. Still grepping through log files to find problems? Stop. Now Search log events and configuration files using AJAX and a browser. Download your FREE copy of Splunk now >> http://get.splunk.com/ _______________________________________________ iText-questions mailing list iText-questions@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/itext-questions Buy the iText book: http://itext.ugent.be/itext-in-action/