/******************************************************************************* * Copyright 2014 by E-Government Innovation Center EGIZ, Graz, Austria * PDF-AS has been contracted by the E-Government Innovation Center EGIZ, a * joint initiative of the Federal Chancellery Austria and Graz University of * Technology. * * Licensed under the EUPL, Version 1.1 or - as soon they will be approved by * the European Commission - subsequent versions of the EUPL (the "Licence"); * You may not use this work except in compliance with the Licence. * You may obtain a copy of the Licence at: * http://www.osor.eu/eupl/ * * Unless required by applicable law or agreed to in writing, software * distributed under the Licence is distributed on an "AS IS" basis, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the Licence for the specific language governing permissions and * limitations under the Licence. * * This product combines work with different licenses. See the "NOTICE" text * file for details on the various modules and licenses. * The "NOTICE" text file is part of the distribution. Any derivative works * that you distribute must include a readable copy of the "NOTICE" text file. ******************************************************************************/ package at.gv.egiz.pdfas.common.utils; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.pdmodel.encryption.AccessPermission; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import at.gv.egiz.pdfas.common.exceptions.PDFIOException; import at.gv.egiz.pdfas.common.exceptions.PdfAsValidationException; public class PDFUtils { private static final Logger logger = LoggerFactory .getLogger(PDFUtils.class); private static final byte[] signature_pattern = new byte[] { (byte) 0x0A, (byte) 0x2F, (byte) 0x43, (byte) 0x6F, // ./Co (byte) 0x6E, (byte) 0x74, (byte) 0x65, (byte) 0x6E, // nten (byte) 0x74, (byte) 0x73, (byte) 0x20, (byte) 0x0A, // ts . (byte) 0x2F, (byte) 0x42, (byte) 0x79, (byte) 0x74, // /Byt (byte) 0x65, (byte) 0x52, (byte) 0x61, (byte) 0x6E, // eRan (byte) 0x67, (byte) 0x65, (byte) 0x20, (byte) 0x5B, // ge [ }; private static final byte range_seperation = (byte) 0x20; private static final byte range_end = (byte) 0x5D; public static int[] buildExcludeRange(int[] byteRange) throws PDFIOException { if(byteRange.length % 2 != 0) { throw new PDFIOException("error.pdf.io.09"); } int[] exclude_range = new int[byteRange.length-2]; for(int i = 0; i < byteRange.length; i = i + 2) { int offset = byteRange[i]; int size = byteRange[i+1]; if(i + 2 < byteRange.length) { exclude_range[i] = offset + size; // exclude start exclude_range[i+1] = byteRange[i+2] - 1; // exclude end } } return exclude_range; } public static byte[] blackOutSignature(byte[] signatureData, int[] byteRange) throws PDFIOException { if(byteRange.length % 2 != 0) { throw new PDFIOException("error.pdf.io.09"); } int lastOffset = byteRange[byteRange.length - 2]; int lastSize = byteRange[byteRange.length - 1]; int dataSize = lastOffset + lastSize; byte[] data = new byte[dataSize]; int currentdataOff = 0; Arrays.fill(data, (byte)0x0); for(int i = 0; i < byteRange.length; i = i + 2) { int offset = byteRange[i]; int size = byteRange[i+1]; for(int j = 0; j < size; j++) { data[offset + j] = signatureData[currentdataOff]; currentdataOff++; } } return data; } private static int extractASCIIInteger(byte[] data, int offset) { int nextsepp = nextSeperator(data, offset); if (nextsepp < offset) { return -1; } String asciiString = new String(data, offset, nextsepp - offset); logger.debug("Extracted " + asciiString); return Integer.parseInt(asciiString); } private static int nextSeperator(byte[] data, int offset) { for (int i = offset; i < data.length; i++) { if (data[i] == range_seperation) { return i; } else if (data[i] == range_end) { return i; } } return -2; } public static int[] extractSignatureByteRange(byte[] rawPdfData) { int i = 0; for (i = rawPdfData.length - 1; i >= 0; i--) { if (rawPdfData[i] == signature_pattern[0] && i + signature_pattern.length < rawPdfData.length) { boolean match = true; for (int j = 0; j < signature_pattern.length; j++) { if (rawPdfData[i + j] != signature_pattern[j]) { match = false; break; } } if (match) { int offset = i + signature_pattern.length; List byteRange = new ArrayList(); while (offset > 0) { byteRange.add(extractASCIIInteger(rawPdfData, offset)); offset = nextSeperator(rawPdfData, offset); if (rawPdfData[offset] == range_end) { break; } offset++; } int[] range = new int[byteRange.size()]; for (int j = 0; j < byteRange.size(); j++) { range[j] = byteRange.get(j); } return range; } } } return null; } public static void checkPDFPermissions(PDDocument doc) throws PdfAsValidationException { AccessPermission accessPermission = doc.getCurrentAccessPermission(); if (doc.isEncrypted()) { throw new PdfAsValidationException("error.pdf.sig.12", null); } if (!accessPermission.isOwnerPermission()) { throw new PdfAsValidationException("error.pdf.sig.12", null); } } }