Insert hidden digest in pdf using iText library

2019-01-18 17:33发布

I search a method for insert a digest (byte array or String) into PDF file using iText library (Java). I create the digest from a String with this method:

private String crypt(double x, ByteArrayOutputStream baos) throws UnsupportedEncodingException, NoSuchAlgorithmException{
    MessageDigest md = MessageDigest.getInstance("SHA-256");
    md.update(String.valueOf(x).getBytes("UTF-8"));
    md.update(String.valueOf(baos).getBytes("UTF-8"));
    byte[] digest = md.digest();

    StringBuffer sb = new StringBuffer();
    for(byte d:digest){
        sb.append(Integer.toHexString(0xFF & d));
    }
    return sb.toString();
}

The digest should be not seen in PDF, but it must be exctracted for comparison.

1条回答
【Aperson】
2楼-- · 2019-01-18 17:53

Such private data can be stored in PieceInfo dictionaries:

A page-piece dictionary (PDF 1.3) may be used to hold private conforming product data. The data may be associated with a page or form XObject by means of the optional PieceInfo entry in the page object (see Table 30) or form dictionary (see Table 95). Beginning with PDF 1.4, private data may also be associated with the PDF document by means of the PieceInfo entry in the document catalogue (see Table 28).

(section 14.5 of ISO 32000-1)

In your case the PieceInfo in the document catalogue seem most apropos.

Using iText you can store data there and retrieve them back again like this using the DocumentPieceInfo helper class below:

Storing document PieceInfo data

PdfName appName = new PdfName("MYAPP");
PdfName dataName = new PdfName("Hash");

DocumentPieceInfo dpi = new DocumentPieceInfo();

PdfReader reader = new PdfReader(...);
dpi.addPieceInfo(reader, appName, dataName, new PdfString(data));

PdfStamper stamper = new PdfStamper(reader, ...);
stamper.close();

Retrieving document PieceInfo data

PdfName appName = new PdfName("MYAPP");
PdfName dataName = new PdfName("Hash");

DocumentPieceInfo dpi = new DocumentPieceInfo();

PdfReader reader = new PdfReader("target/test-outputs/test-with-piece-info.pdf");
PdfObject myData = dpi.getPieceInfo(reader, appName, dataName);

The DocumentPieceInfo helper class

public class DocumentPieceInfo
{
    static PdfName PIECE_INFO = new PdfName("PieceInfo");
    static PdfName LAST_MODIFIED = new PdfName("LastModified");
    static PdfName PRIVATE = new PdfName("Private");

    void addPieceInfo(PdfReader reader, PdfName app, PdfName name, PdfObject value)
    {
        PdfDictionary catalog = reader.getCatalog();
        PdfDictionary pieceInfo = catalog.getAsDict(PIECE_INFO);
        if (pieceInfo == null)
        {
            pieceInfo = new PdfDictionary();
            catalog.put(PIECE_INFO, pieceInfo);
        }

        PdfDictionary appData = pieceInfo.getAsDict(app);
        if (appData == null)
        {
            appData = new PdfDictionary();
            pieceInfo.put(app, appData);
        }

        PdfDictionary privateData = appData.getAsDict(PRIVATE);
        if (privateData == null)
        {
            privateData = new PdfDictionary();
            appData.put(PRIVATE, privateData);
        }

        appData.put(LAST_MODIFIED, new PdfDate());
        privateData.put(name, value);
    }

    PdfObject getPieceInfo(PdfReader reader, PdfName app, PdfName name)
    {
        PdfDictionary catalog = reader.getCatalog();

        PdfDictionary pieceInfo = catalog.getAsDict(PIECE_INFO);
        if (pieceInfo == null)
            return null;

        PdfDictionary appData = pieceInfo.getAsDict(app);
        if (appData == null)
            return null;

        PdfDictionary privateData = appData.getAsDict(PRIVATE);
        if (privateData == null)
            return null;

        return privateData.get(name);
    }
}

This class assumes the Private value to be a dictionary in which in turn the private data are stored. It may be anything, though. To process the private data generated by other programs, you may need some variation.

查看更多
登录 后发表回答