GREAT!

I've passed the info on to our DigSig team to investigate the issues you've 
raised.

Leonard

-----Original Message-----
From: frleong [mailto:frleon...@yahoo.com]
Sent: Saturday, November 14, 2009 3:59 AM
To: itext-questions@lists.sourceforge.net
Subject: [iText-questions] Document Validation Support in iText (Source code 
attached)


Hi,

After some trial-and-error, I think I've succeeded adding DSS functionality
to iText. Document Verification Information is a new feature in Adobe 9.1 or
later - for those who may have interest, you may right-click on a signature
in Adobe Acrobat 9.1 or later to add "Document Verification Information" so
that we can perform Long Term Verification even when the OCSP server is
unavailable. I am posting here the code to do so.

Comments or suggestions are welcome.

Francisco

Note 1: I'm using Java 1.5 (didn't have patience to code in JDK1.4)
Note 2: I've found bugs in Adobe Reader 9.2. As long as the signature is
performed using PKCS#7 detached mode, it considers the signature as having
revocation information whether it is present or not and has misleading
information in the "Revocation" tab. It shows as using embedded revocation
info, when in fact, it is doing online checking.


-- Add the following method to PdfStamper.java ---
public void addDocumentSecurityStore(PdfDocumentSecurityStore store)
                        throws IOException {

                PdfIndirectReference dssRef = stamper.getPdfIndirectReference();
                PdfDictionary dss = new PdfDictionary();
                dss.put(PdfName.TYPE, new PdfName("DSS"));
                PdfDictionary catalog = stamper.reader.getCatalog();
                catalog.put(new PdfName("DSS"), dssRef);

                HashMap<Integer, PdfIndirectReference> certMap = new 
HashMap<Integer,
PdfIndirectReference>();
                for (Integer certId : store.getCertificates().keySet()) {
                        certMap.put(certId, stamper.getPdfIndirectReference());
                }

                PdfArray certArray = new PdfArray();
                for (Integer certId : store.getCertificates().keySet()) {
                        PdfStream stream = new PdfStream(store.getCertificates()
                                        .get(certId));
                        stamper.addToBody(stream, certMap.get(certId));
                        certArray.add(certMap.get(certId));
                        stamper.markUsed(stream);
                }
                if (certArray.size() > 0)
                        dss.put(new PdfName("Certs"), certArray);

                HashMap<Integer, PdfIndirectReference> ocspMap = new 
HashMap<Integer,
PdfIndirectReference>();
                for (Integer ocspId : store.getOcsps().keySet()) {
                        ocspMap.put(ocspId, stamper.getPdfIndirectReference());
                }

                PdfArray ocspArray = new PdfArray();
                for (Integer ocspId : store.getOcsps().keySet()) {
                        PdfStream stream = new 
PdfStream(store.getOcsps().get(ocspId));
                        stamper.addToBody(stream, ocspMap.get(ocspId));
                        ocspArray.add(ocspMap.get(ocspId));
                        stamper.markUsed(stream);
                }
                if (ocspArray.size() > 0)
                        dss.put(new PdfName("OCSPs"), ocspArray);

                HashMap<Integer, PdfIndirectReference> crlMap = new 
HashMap<Integer,
PdfIndirectReference>();
                for (Integer crlId : store.getCrls().keySet()) {
                        crlMap.put(crlId, stamper.getPdfIndirectReference());
                }

                PdfArray crlArray = new PdfArray();
                for (Integer crlId : store.getCrls().keySet()) {
                        PdfStream stream = new 
PdfStream(store.getCrls().get(crlId));
                        stamper.addToBody(stream, crlMap.get(crlId));
                        crlArray.add(crlMap.get(crlId));
                        stamper.markUsed(stream);
                }
                if (crlArray.size() > 0)
                        dss.put(new PdfName("CRLs"), crlArray);

                if (store.getSignatures().size() > 0) {
                        PdfIndirectReference vriRef = 
stamper.getPdfIndirectReference();
                        PdfDictionary vri = new PdfDictionary();
                        for (String key : store.getSignatures().keySet()) {
                                PdfDictionary vriEntry = new PdfDictionary();
                                vriEntry.put(PdfName.TYPE, new PdfName("VRI"));
                                ValidationInformation v = 
store.getSignatures().get(key);

                                int[] certId = v.getCertId();
                                if (certId != null) {
                                        PdfArray signatureCertArray = new 
PdfArray();
                                        for (int i = 0; i < certId.length; i++) 
{
                                                
signatureCertArray.add(certMap.get(new Integer(
                                                                certId[i])));
                                        }
                                        vriEntry.put(new PdfName("Cert"), 
signatureCertArray);
                                }

                                int[] ocspId = v.getOcspId();
                                if (ocspId != null) {
                                        PdfArray signatureOcspArray = new 
PdfArray();
                                        for (int i = 0; i < ocspId.length; i++) 
{
                                                
signatureOcspArray.add(ocspMap.get(new Integer(
                                                                ocspId[i])));
                                        }
                                        vriEntry.put(new PdfName("OCSP"), 
signatureOcspArray);
                                }

                                int[] crlId = v.getCrlId();
                                if (crlId != null) {
                                        PdfArray signatureCrlArray = new 
PdfArray();
                                        for (int i = 0; i < crlId.length; i++) {
                                                signatureCrlArray
                                                                
.add(crlMap.get(new Integer(crlId[i])));
                                        }
                                        vriEntry.put(new PdfName("CRL"), 
signatureCrlArray);
                                }
                                if (v.getDate() != null)
                                        vriEntry.put(new PdfName("TU"), new 
PdfDate(v.getDate()));
                                vri.put(new PdfName(key), vriEntry);
                        }
                        stamper.addToBody(vri, vriRef);
                        stamper.markUsed(vri);

                        dss.put(new PdfName("VRI"), vriRef);
                }

                stamper.addToBody(dss, dssRef);
                stamper.markUsed(catalog);
        }

--- New Class PdfDocumentSecurityStore ---
package com.lowagie.text.pdf;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;

import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1OutputStream;
import org.bouncycastle.asn1.DEREnumerated;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;

public class PdfDocumentSecurityStore {

        static public class ValidationInformation {
                private byte[] digest;
                private int[] ocspId;
                private int[] crlId;
                private int[] certId;
                private Calendar date;

                public String getKey() {
                        StringBuffer buf = new StringBuffer();
                        for (int i = 0; i < digest.length; i++) {
                                int b = digest[i] & 0xff;
                                if (b >= 0 && b <= 0x0f)
                                        buf.append("0");
                                
buf.append(Integer.toHexString(b).toUpperCase());
                        }
                        return buf.toString();
                }

                public ValidationInformation(byte[] val, int[] certId, int[] 
ocspId,
                                int[] crlId, Calendar date) {
                        try {
                                MessageDigest dg = 
MessageDigest.getInstance("SHA1");
                                digest = dg.digest(val);
                                this.ocspId = ocspId;
                                this.crlId = crlId;
                                this.certId = certId;
                                this.date = date;
                        } catch (Exception e) {

                        }
                }

                public byte[] getDigest() {
                        return digest;
                }

                public int[] getOcspId() {
                        return ocspId;
                }

                public int[] getCrlId() {
                        return crlId;
                }

                public int[] getCertId() {
                        return certId;
                }

                public Calendar getDate() {
                        return date;
                }

        }

        private HashMap<String, ValidationInformation> signatures = new
HashMap<String, ValidationInformation>();
        private HashMap<Integer, byte[]> certificates = new HashMap<Integer,
byte[]>();
        private HashMap<Integer, byte[]> ocsps = new HashMap<Integer, byte[]>();
        private HashMap<Integer, byte[]> crls = new HashMap<Integer, byte[]>();

        public HashMap<String, ValidationInformation> getSignatures() {
                return signatures;
        }

        public HashMap<Integer, byte[]> getCertificates() {
                return certificates;
        }

        public HashMap<Integer, byte[]> getOcsps() {
                return ocsps;
        }

        public HashMap<Integer, byte[]> getCrls() {
                return crls;
        }

        public void registerSignature(byte[] pkcs7, int[] certId, int[] ocspId,
                        int[] crlId, Calendar date) {
                ValidationInformation val = new ValidationInformation(pkcs7, 
certId,
                                ocspId, crlId, date);
                signatures.put(val.getKey(), val);
        }

        public void registerSignature(byte[] pkcs7, int[] certId, int[] ocspId,
                        int[] crlId) {
                registerSignature(pkcs7, certId, ocspId, crlId, new 
GregorianCalendar());
        }

        public void registerSignature(byte[] pkcs7) {
                registerSignature(pkcs7, null, null, null, new 
GregorianCalendar());
        }

        synchronized public int registerCertificate(byte[] cert) {
                int nextId = certificates.size() + 1;
                certificates.put(new Integer(nextId), cert);
                return nextId;
        }

        synchronized public int registerOcspResp(byte[] ocsp) {
                int nextId = ocsps.size() + 1;
                ocsps.put(new Integer(nextId), ocsp);
                return nextId;
        }

        synchronized public int registerOcspBasicResp(byte[] basicResp)
                        throws IOException {
                // ocspBytes is Basic OCSP Response Bytes
                DEROctetString doctet = new DEROctetString(basicResp);

                /*
                 * ResponseBytes ::= SEQUENCE { responseType OBJECT IDENTIFIER, 
response
                 * OCTET STRING }
                 */
                ASN1EncodableVector v2 = new ASN1EncodableVector();
                v2.add(OCSPObjectIdentifiers.id_pkix_ocsp_basic);
                v2.add(doctet);

                DEREnumerated den = new DEREnumerated(0); // Status = 0
                ASN1EncodableVector v3 = new ASN1EncodableVector();
                v3.add(den);
                v3.add(new DERTaggedObject(true, 0, new DERSequence(v2)));

                // OCSPResponse ::= SEQUENCE {
                // responseStatus OCSPResponseStatus,
                // responseBytes [0] EXPLICIT ResponseBytes OPTIONAL }

                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ASN1OutputStream as = new ASN1OutputStream(bos);
                as.writeObject(new DERSequence(v3));
                as.close();
                byte[] ocspResponse = bos.toByteArray();
                int ocspId = registerOcspResp(ocspResponse);
                registerSignature(ocspResponse);
                bos.close();
                return ocspId;
        }

        synchronized public int registerCrl(byte[] crl) {
                int nextId = crls.size() + 1;
                crls.put(new Integer(nextId), crl);
                return nextId;
        }

}
--- Sample Source code ---
package com.esigntrust.pdf.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.Security;
import java.security.Signature;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
import java.util.Calendar;
import java.util.HashMap;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.lowagie.text.DocumentException;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.OcspClientBouncyCastle;
import com.lowagie.text.pdf.PdfDate;
import com.lowagie.text.pdf.PdfDictionary;
import com.lowagie.text.pdf.PdfDocumentSecurityStore;
import com.lowagie.text.pdf.PdfName;
import com.lowagie.text.pdf.PdfPKCS7;
import com.lowagie.text.pdf.PdfReader;
import com.lowagie.text.pdf.PdfSigGenericPKCS;
import com.lowagie.text.pdf.PdfSignatureAppearance;
import com.lowagie.text.pdf.PdfStamper;
import com.lowagie.text.pdf.PdfString;

public class OCSPRevocationSigningSample {

        private final static boolean embedOcspInSignature = false;

        private static final String HASH_ALGORITHM = embedOcspInSignature ?
"SHA256" : "SHA1";

        static class MyPdfPKCS extends PdfSigGenericPKCS {
                /**
                 * The constructor for the default provider.
                 */
                public MyPdfPKCS() {
                        super(PdfName.ADOBE_PPKMS,
                                        (embedOcspInSignature) ? 
PdfName.ADBE_PKCS7_DETACHED
                                                        : 
PdfName.ADBE_PKCS7_SHA1);
                        hashAlgorithm = HASH_ALGORITHM;
                }

                /**
                 * The constructor for an explicit provider.
                 *
                 * @param provider
                 *            the crypto provider
                 */
                public MyPdfPKCS(String provider) {
                        this();
                        this.provider = provider;
                }
        }

        private String filename;

        public OCSPRevocationSigningSample(String filename) {
                this.filename = filename;
        }

        KeyStore keyStore;

        private void loadKeyStore() throws Exception {
                keyStore = KeyStore.getInstance("JKS");
                InputStream is = getClass().getResourceAsStream(
                                "ltv1.jks");
                keyStore.load(is, "password".toCharArray());
        }

        private Certificate[] getCertChain() throws KeyStoreException {
                return new Certificate[] { keyStore.getCertificate("ltv1"),
                                keyStore.getCertificate("ca") };

        }

        private byte[] ocspBytes;
        private byte[] paddedSigBytes;

        private byte[] getEncodedPKCS7Bytes(byte[] hash, String hashAlgorithm)
                        throws Exception {
                Certificate[] certChain = getCertChain();
                PrivateKey privateKey = (PrivateKey) keyStore.getKey("ltv1", 
"password"
                                .toCharArray());

                PdfPKCS7 sgn = new PdfPKCS7(privateKey, certChain, null, 
hashAlgorithm,
                                null, !embedOcspInSignature);
                String url = PdfPKCS7.getOCSPURL((X509Certificate) 
certChain[0]);
                if (certChain.length >= 2)
                        if (url != null && url.length() > 0) {
                                ocspBytes = new OcspClientBouncyCastle(
                                                (X509Certificate) certChain[0],
                                                (X509Certificate) certChain[1], 
url).getEncoded();
                        }

                if (embedOcspInSignature) {
                        Calendar cal = Calendar.getInstance();
                        byte sh[] = sgn
                                        .getAuthenticatedAttributeBytes(hash, 
cal, ocspBytes);
                        sgn.update(sh, 0, sh.length);
                        return sgn.getEncodedPKCS7(hash, cal, null, ocspBytes);
                } else {
                        Signature sign = Signature.getInstance(hashAlgorithm + 
"withRSA");
                        sign.initSign(privateKey);
                        sign.update(hash);
                        sgn.setExternalDigest(sign.sign(), hash, "RSA");
                        return sgn.getEncodedPKCS7();
                }

        }

        public void run() throws Exception {
                File inputFile = new File(filename);
                File tempFile = File.createTempFile("tempPdf", ".pdf");
                File outputFile = new File(inputFile.getParentFile(), "signed-"
                                + inputFile.getName());
                FileInputStream f = new FileInputStream(filename);
                PdfReader reader = new PdfReader(f);
                FileOutputStream fos = new FileOutputStream(outputFile);
                try {
                        loadKeyStore();
                        PdfStamper stp = PdfStamper.createSignature(reader, 
fos, '\0',
                                        tempFile, true);
                        PdfSignatureAppearance sap = 
stp.getSignatureAppearance();
                        Certificate[] certChain = getCertChain();
                        PrivateKey key = (PrivateKey) keyStore.getKey("ltv1", 
"12345678"
                                        .toCharArray());

                        sap.setExternalDigest(new byte[128], new byte[32], 
"RSA");
                        sap.setCrypto(key, certChain, null,
                                        PdfSignatureAppearance.WINCER_SIGNED);
                        sap.setVisibleSignature(new Rectangle(100, 700, 300, 
200), 1,
                                        "Signature");
                        PdfSigGenericPKCS dic = new MyPdfPKCS();
                        dic.setReason(sap.getReason());
                        dic.setLocation(sap.getLocation());
                        dic.setContact(sap.getContact());
                        dic.setDate(new PdfDate(sap.getSignDate()));
                        dic.setSignInfo(key, certChain, null);
                        sap.setCryptoDictionary(dic);
                        HashMap<PdfName, Integer> exc = new HashMap<PdfName, 
Integer>();
                        int estimatedContentLength = 8192;
                        exc.put(PdfName.CONTENTS, new Integer(
                                        estimatedContentLength * 2 + 2));
                        sap.preClose(exc);

                        byte[] hash = getPdfHash(sap);

                        byte[] encodedBytes = getEncodedPKCS7Bytes(hash, 
HASH_ALGORITHM);

                        saveSignature(sap, estimatedContentLength, 
encodedBytes);
                        reader.close();
                        fos.close();

                        File outputFile2 = new File(inputFile.getParentFile(), 
"signed-with-DSS-"
                                        + inputFile.getName());
                        reader = new PdfReader(outputFile.getAbsolutePath());
                        fos = new FileOutputStream(outputFile2);
                        PdfStamper stamper = new PdfStamper(reader, fos, '\0', 
true);
                        PdfDocumentSecurityStore dss = new 
PdfDocumentSecurityStore();
                        int cert1 = 
dss.registerCertificate(certChain[0].getEncoded());
                        int cert2 = 
dss.registerCertificate(certChain[1].getEncoded());

                        int ocspId = -1;
                        if (ocspBytes != null) {
                                dss.registerOcspBasicResp(ocspBytes);
                        }

                        dss.registerSignature(paddedSigBytes, new int[] { 
cert1, cert2 },
                                        ocspId != -1 ? new int[] { ocspId } : 
null, null);
                        stamper.addDocumentSecurityStore(dss);
                        stamper.close();

                        System.out.println("Sign successful");
                } finally {
                        reader.close();
                }
        }

        private void saveSignature(PdfSignatureAppearance sap, int 
estimatedLength,
                        byte[] encodedBytes) throws IOException, 
DocumentException {
                paddedSigBytes = new byte[estimatedLength];
                System.arraycopy(encodedBytes, 0, paddedSigBytes, 0,
                                encodedBytes.length);

                PdfDictionary dic2 = new PdfDictionary();
                dic2.put(PdfName.CONTENTS, new PdfString(paddedSigBytes)
                                .setHexWriting(true));
                sap.close(dic2);
        }

        private byte[] getPdfHash(PdfSignatureAppearance sap)
                        throws NoSuchAlgorithmException, 
NoSuchProviderException,
                        IOException {
                MessageDigest messageDigest = 
MessageDigest.getInstance(HASH_ALGORITHM,
                                "BC");
                byte[] buffer = new byte[8192];
                int n;
                InputStream inp = sap.getRangeStream();
                while ((n = inp.read(buffer)) > 0) {
                        messageDigest.update(buffer, 0, n);
                }
                byte[] hash = messageDigest.digest();
                return hash;
        }

        static public void main(String[] args) throws Exception {
                Security.addProvider(new BouncyCastleProvider());
                new OCSPRevocationSigningSample(args[0]).run();
        }
}


--
View this message in context: 
http://old.nabble.com/Document-Validation-Support-in-iText-%28Source-code-attached%29-tp26348289p26348289.html
Sent from the iText - General mailing list archive at Nabble.com.


------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day
trial. Simplify your report design, integration and deployment - and focus on
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
_______________________________________________
iText-questions mailing list
iText-questions@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/itext-questions

Buy the iText book: http://www.1t3xt.com/docs/book.php
Check the site with examples before you ask questions: 
http://www.1t3xt.info/examples/
You can also search the keywords list: http://1t3xt.info/tutorials/keywords/

------------------------------------------------------------------------------
Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day 
trial. Simplify your report design, integration and deployment - and focus on 
what you do best, core application coding. Discover what's new with
Crystal Reports now.  http://p.sf.net/sfu/bobj-july
_______________________________________________
iText-questions mailing list
iText-questions@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/itext-questions

Buy the iText book: http://www.1t3xt.com/docs/book.php
Check the site with examples before you ask questions: 
http://www.1t3xt.info/examples/
You can also search the keywords list: http://1t3xt.info/tutorials/keywords/

Reply via email to