/** * Copyright 2006 by Know-Center, 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. * * $Id: BinaryVerificator_1_0_0.java,v 1.3 2006/10/11 08:03:22 wprinz Exp $ */ package at.gv.egiz.pdfas.impl.verificator.binary; import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.log4j.Logger; import at.gv.egiz.pdfas.exceptions.ErrorCode; import at.gv.egiz.pdfas.framework.input.PdfDataSource; import at.gv.egiz.pdfas.framework.verificator.Verificator; import at.gv.egiz.pdfas.impl.input.CompoundPdfDataSourceImpl; import at.gv.egiz.pdfas.impl.input.DelimitedPdfDataSource; import at.gv.egiz.pdfas.impl.vfilter.helper.VerificationFilterBinaryHelper; import at.knowcenter.wag.egov.egiz.PdfASID; import at.knowcenter.wag.egov.egiz.cfg.ConfigLogger; import at.knowcenter.wag.egov.egiz.cfg.SettingsReader; import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException; import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; import at.knowcenter.wag.egov.egiz.framework.SignatorFactory; import at.knowcenter.wag.egov.egiz.framework.VerificationFilter; import at.knowcenter.wag.egov.egiz.pdf.BinaryBlockInfo; import at.knowcenter.wag.egov.egiz.pdf.BinarySignature; import at.knowcenter.wag.egov.egiz.pdf.BinarySignatureHolder; import at.knowcenter.wag.egov.egiz.pdf.Placeholder; import at.knowcenter.wag.egov.egiz.pdf.ReplaceInfo; import at.knowcenter.wag.egov.egiz.pdf.StringInfo; import at.knowcenter.wag.egov.egiz.sig.SignatureObject; import at.knowcenter.wag.egov.egiz.sig.SignatureTypes; import at.knowcenter.wag.exactparser.parsing.IndirectObjectReference; import at.knowcenter.wag.exactparser.parsing.PDFUtils; import at.knowcenter.wag.exactparser.parsing.results.ArrayParseResult; import at.knowcenter.wag.exactparser.parsing.results.DictionaryParseResult; import at.knowcenter.wag.exactparser.parsing.results.FooterParseResult; import at.knowcenter.wag.exactparser.parsing.results.IndirectObjectReferenceParseResult; import at.knowcenter.wag.exactparser.parsing.results.LiteralStringParseResult; import at.knowcenter.wag.exactparser.parsing.results.NameParseResult; import at.knowcenter.wag.exactparser.parsing.results.NumberParseResult; import at.knowcenter.wag.exactparser.parsing.results.ObjectParseResult; import at.knowcenter.wag.exactparser.parsing.results.ParseResult; /** * The BinaryVerificator parses the EGIT Dictionary and extracts the signature * holder from it. * * @author wprinz */ public class BinaryVerificator_1_0_0 implements Verificator { /** * The Pdf-AS ID of this Verificator. */ public static final PdfASID MY_ID = new PdfASID(SignatorFactory.VENDOR, SignatorFactory.TYPE_BINARY, SignatorFactory.VERSION_1_0_0); /** * Use this to override the MY_ID field. * * @return Returns the Id of this Verificator. */ protected PdfASID getMyId() { return MY_ID; } /** * The /ODS key in the EGIZ Dict. */ public static final byte[] EGIZ_ODS_NAME = new byte[] { 'O', 'D', 'S' }; /** * The /ID key in the EGIZ Dict. */ public static final byte[] EGIZ_KZ_NAME = VerificationFilter.EGIZ_KZ_NAME; /** * The /ByteRange key in the EGIZ Dict. */ public static final byte[] EGIZ_BYTE_RANGE_NAME = new byte[] { 'B', 'y', 't', 'e', 'R', 'a', 'n', 'g', 'e' }; /** * The /replaces key in the EGIZ Dict. */ public static final byte[] EGIZ_REPLACES_NAME = new byte[] { 'r', 'e', 'p', 'l', 'a', 'c', 'e', 's' }; /** * The /encodings key in the EGIZ Dict. */ public static final byte[] EGIZ_ENCODINGS_NAME = new byte[] { 'e', 'n', 'c', 'o', 'd', 'i', 'n', 'g', 's' }; /** * The /Cert key in the EGIZ Dict. */ public static final byte[] EGIZ_CERT_NAME = new byte[] { 'C', 'e', 'r', 't' }; /** * The /TimeStamp key in the EGIZ Dict. */ public static final byte[] EGIZ_TIMESTAMP_NAME = new byte[] { 'T', 'i', 'm', 'e', 'S', 't', 'a', 'm', 'p' }; /** * The logger definition. */ private static final Logger logger_ = ConfigLogger.getLogger(BinaryVerificator_1_0_0.class); /** * Default constructor. */ public BinaryVerificator_1_0_0() { // Default constructor. } /** * @see at.gv.egiz.pdfas.framework.verificator.Verificator#parseBlock(at.gv.egiz.pdfas.framework.input.PdfDataSource, * byte[], * at.knowcenter.wag.exactparser.parsing.results.FooterParseResult, int) */ public List parseBlock(PdfDataSource pdfDataSource, byte [] pdf, FooterParseResult block, int start_of_whole_block) throws PresentableException { // PERF: BinaryVerificator needs byte array. int egiz_index = PDFUtils.indexOfName(pdf, block.tpr.dpr.names, VerificationFilter.EGIZ_DICT_NAME); if (egiz_index < 0) { throw new PDFDocumentException(ErrorCode.COULDNT_VERIFY, "egiz_index = " + egiz_index); } IndirectObjectReferenceParseResult egiz_dict_iorpr = (IndirectObjectReferenceParseResult) block.tpr.dpr.values.get(egiz_index); IndirectObjectReference ior = egiz_dict_iorpr.ior; final int egiz_dict_offset = PDFUtils.getObjectOffsetFromXRefByIndirectObjectReference(block.xpr, ior); ObjectParseResult obj = PDFUtils.parseObject(pdf, egiz_dict_offset); DictionaryParseResult egiz_dict = (DictionaryParseResult) obj.object; NumberParseResult ods_npr = (NumberParseResult) getRequiredValueOfKey(pdf, egiz_dict, EGIZ_ODS_NAME); ArrayParseResult kz_apr = (ArrayParseResult) getRequiredValueOfKey(pdf, egiz_dict, EGIZ_KZ_NAME); PdfASID kz = null; // String kz_string = VerificationFilter.restoreKZ(pdf, kz_apr); String kz_string = VerificationFilterBinaryHelper.restoreKZ(pdf, kz_apr); // dferbas hack baik test // TODO baik hack kz_string = "urn:pdfsigfilter:bka.gv.at:binaer:v1.0.0"; kz = new PdfASID(kz_string); if (!kz_string.equals(getMyId().toString())) { logger_.warn("Warning: Kennzeichnung not recognized:" + kz_string); } ArrayParseResult byte_ranges_apr = (ArrayParseResult) getRequiredValueOfKey(pdf, egiz_dict, EGIZ_BYTE_RANGE_NAME); ArrayParseResult replaces_apr = (ArrayParseResult) getRequiredValueOfKey(pdf, egiz_dict, EGIZ_REPLACES_NAME); ArrayParseResult encodings_apr = (ArrayParseResult) getRequiredValueOfKey(pdf, egiz_dict, EGIZ_ENCODINGS_NAME); ArrayParseResult cert_apr = (ArrayParseResult) getValueOfKey(pdf, egiz_dict, EGIZ_CERT_NAME); byte[] cert = null; if (cert_apr != null && !cert_apr.elements.isEmpty()) { LiteralStringParseResult lspr = (LiteralStringParseResult) cert_apr.elements.get(0); int str_length = lspr.content_end_index - lspr.content_start_index; byte[] encoded = new byte[str_length]; System.arraycopy(pdf, lspr.content_start_index, encoded, 0, encoded.length); cert = Placeholder.unescapePDFString(encoded); } //timestamp ArrayParseResult timestamp_apr = (ArrayParseResult) getValueOfKey(pdf, egiz_dict, EGIZ_TIMESTAMP_NAME); byte[] timestamp = null; if (timestamp_apr != null) { logger_.debug("found /TimeStamp in egiz dict. Extracting..."); if (timestamp_apr != null && !timestamp_apr.elements.isEmpty()) { LiteralStringParseResult lspr = (LiteralStringParseResult) timestamp_apr.elements.get(0); int str_length = lspr.content_end_index - lspr.content_start_index; byte[] encoded = new byte[str_length]; System.arraycopy(pdf, lspr.content_start_index, encoded, 0, encoded.length); timestamp = Placeholder.unescapePDFString(encoded); } } int num_byte_ranges = byte_ranges_apr.elements.size() / 2; List byte_ranges = new ArrayList(); for (int i = 0; i < num_byte_ranges; i++) { NumberParseResult start_npr = (NumberParseResult) byte_ranges_apr.elements.get(2 * i); NumberParseResult length_npr = (NumberParseResult) byte_ranges_apr.elements.get(2 * i + 1); StringInfo si = new StringInfo(); si.string_start = start_npr.number; si.string_length = length_npr.number; byte_ranges.add(si); } StringInfo sis[] = new StringInfo[num_byte_ranges - 1]; for (int i = 0; i < num_byte_ranges - 1; i++) { StringInfo prev = (StringInfo) byte_ranges.get(i); StringInfo next = (StringInfo) byte_ranges.get(i + 1); StringInfo hole = new StringInfo(); hole.string_start = prev.string_start + prev.string_length; hole.string_length = next.string_start - hole.string_start; sis[i] = hole; } int n = replaces_apr.elements.size(); byte[][] brevs = new byte[n][]; for (int i = 0; i < n; i++) { NameParseResult lspr = (NameParseResult) replaces_apr.elements.get(i); byte[] brev = new byte[3]; System.arraycopy(pdf, lspr.name_start_index, brev, 0, brev.length); brevs[i] = brev; // SignatureTypes.convertBrevToType(brev); } n = encodings_apr.elements.size(); byte[][] encodings = new byte[n][]; for (int i = 0; i < n; i++) { NameParseResult lspr = (NameParseResult) encodings_apr.elements.get(i); byte[] enc = new byte[3]; System.arraycopy(pdf, lspr.name_start_index, enc, 0, enc.length); encodings[i] = enc; } BinaryBlockInfo bbi = new BinaryBlockInfo(); bbi.replaces = BinarySignature.reconstructReplaces(pdf, brevs, sis, encodings); bbi.signed_size = ods_npr.number; // BinaryBlockInfo bbi = BinarySignature.retrieveEgizDictInformation(pdf, // ior.object_number, ior.generation_number, egiz_dict_offset); // byte[] original_pdf = BinarySignature.restoreEgizDictInformation(pdf, // bbi); byte[] signed_pdf = BinarySignature.prepareDataToSign(pdf, byte_ranges); // String signed_text = // BinarySignature.retrieveSignableTextFromData(signed_pdf, // signed_pdf.length); // has been moved into the BinarySignatureHolder SignatureObject signature_object = new SignatureObject(); String default_type = SettingsReader.getInstance().getValueFromKey(SignatureTypes.DEFAULT_TYPE); signature_object.setSigType(default_type); signature_object.initByType(); signature_object.setKZ(kz); if (timestamp != null) { String ts = new String(trimZeroBytes(timestamp)); signature_object.setTimeStamp(ts); if (logger_.isDebugEnabled()) { logger_.debug("extracted timestamp " + ts); } } if (cert != null) { try { // ByteArrayInputStream bais = new ByteArrayInputStream(cert); // CertificateFactory cf = CertificateFactory.getInstance("X.509"); // X509Certificate certificate = (X509Certificate) // cf.generateCertificate(bais); // trim zero bytes. - the base 64 cert must not have zero bytes. byte[] b64 = trimZeroBytes(cert); signature_object.storeNewCertificateInLocalStore(b64); } catch (Exception e) { logger_.error(e.getMessage(), e); } } Iterator rit = bbi.replaces.iterator(); while (rit.hasNext()) { ReplaceInfo ri = (ReplaceInfo) rit.next(); String type = SignatureTypes.convertBrevToType(ri.brev); if (type == null) { throw new PresentableException(ErrorCode.UNSUPPORTED_REPLACES_NAME, "Unsupported /replaces name."); } // signature_object.setSigValue(ri.type, ri.value); if (type.equals(SignatureTypes.SIG_DATE)) { signature_object.setSignationDate(ri.value); continue; } if (type.equals(SignatureTypes.SIG_ISSUER)) { signature_object.setSignationIssuer(ri.value); continue; } if (type.equals(SignatureTypes.SIG_VALUE)) { signature_object.setSignationValue(ri.value); continue; } if (type.equals(SignatureTypes.SIG_NUMBER)) { signature_object.setSignationSerialNumber(ri.value); continue; } if (type.equals(SignatureTypes.SIG_ID)) { signature_object.setSignationIDs(ri.value); continue; } if (type.equals(SignatureTypes.SIG_ALG)) { signature_object.setSigAlg(ri.value); continue; } } int iu_length = signed_pdf.length - start_of_whole_block; byte [] iu_block = new byte [iu_length]; System.arraycopy(signed_pdf, start_of_whole_block, iu_block, 0, iu_length); DelimitedPdfDataSource dpds = new DelimitedPdfDataSource(pdfDataSource, start_of_whole_block); PdfDataSource ds = new CompoundPdfDataSourceImpl(dpds, iu_block); //PdfDataSource dsByteArray = new ByteArrayPdfDataSourceImpl(signed_pdf, signed_pdf.length); BinarySignatureHolder signature_holder = new BinarySignatureHolder(ds, signature_object); List holders = new ArrayList(); holders.add(signature_holder); return holders; } private byte[] trimZeroBytes(byte[] arr) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); for (int i = 0; i < arr.length; i++) { if (arr[i] != 0) { baos.write(arr[i]); } } byte[] b64 = baos.toByteArray(); return b64; } /** * Retrieves the value of the key from the dictionary. * * @param pdf * The PDF. * @param egiz_dict * The dictionary. * @param name * The name of the key. * @return Returns the value of the key. An exception is thrown if the key * doesn't exist. * @throws PDFDocumentException * Thrown, if the key doesn't exist in the dictionary. */ protected ParseResult getRequiredValueOfKey(byte[] pdf, DictionaryParseResult egiz_dict, byte[] name) throws PDFDocumentException { final int index = PDFUtils.indexOfName(pdf, egiz_dict.names, name); checkIndex(index); ParseResult value = (ParseResult) egiz_dict.values.get(index); return value; } /** * Throws an excaption, if the index is lower than 0. * * @param name_index * The index. * @throws PDFDocumentException * Thrown, if the index is lower than 0. */ protected void checkIndex(int name_index) throws PDFDocumentException { if (name_index < 0) { throw new PDFDocumentException(ErrorCode.COULDNT_VERIFY, "The name wasn't found in the egiz dict."); } } /** * Retrieves the value of the key from the dictionary. * * @param pdf * The PDF. * @param egiz_dict * The dictionary. * @param name * The name of the key. * @return Returns the key's value, or null if the dictionary didn't contain * that key. */ protected ParseResult getValueOfKey(byte[] pdf, DictionaryParseResult egiz_dict, byte[] name) { final int index = PDFUtils.indexOfName(pdf, egiz_dict.names, name); if (index < 0) { return null; } ParseResult value = (ParseResult) egiz_dict.values.get(index); return value; } }