From 535a04fa05f739ec16dd81666e3b0f82dfbd442d Mon Sep 17 00:00:00 2001 From: tknall Date: Wed, 9 Jan 2013 15:41:29 +0000 Subject: pdf-as-lib maven project files moved to pdf-as-lib git-svn-id: https://joinup.ec.europa.eu/svn/pdf-as/pdf-as/trunk@926 7b5415b0-85f9-ee4d-85bd-d5d0c3b42d1c --- .../impl/signator/binary/BinarySignator_1_0_0.java | 598 +++++++++++++++++++++ 1 file changed, 598 insertions(+) create mode 100644 pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_0_0.java (limited to 'pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_0_0.java') diff --git a/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_0_0.java b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_0_0.java new file mode 100644 index 0000000..7f18f0a --- /dev/null +++ b/pdf-as-lib/src/main/java/at/gv/egiz/pdfas/impl/signator/binary/BinarySignator_1_0_0.java @@ -0,0 +1,598 @@ +/** + * 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: BinarySignator_1_0_0.java,v 1.1 2006/08/25 17:07:35 wprinz Exp $ + */ +package at.gv.egiz.pdfas.impl.signator.binary; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import at.gv.egiz.pdfas.api.timestamp.TimeStamper; +import at.gv.egiz.pdfas.exceptions.ErrorCode; +import at.gv.egiz.pdfas.exceptions.framework.SignatorException; +import at.gv.egiz.pdfas.framework.input.DataSource; +import at.gv.egiz.pdfas.framework.input.PdfDataSource; +import at.gv.egiz.pdfas.framework.output.DataSink; +import at.gv.egiz.pdfas.framework.signator.Signator; +import at.gv.egiz.pdfas.framework.signator.SignatorInformation; +import at.gv.egiz.pdfas.impl.input.CompoundPdfDataSourceImpl; +import at.gv.egiz.pdfas.impl.signator.IncrementalUpdateHelper; +import at.gv.egiz.pdfas.utils.OgnlUtil; +import at.knowcenter.wag.egov.egiz.PdfAS; +import at.knowcenter.wag.egov.egiz.PdfASID; +import at.knowcenter.wag.egov.egiz.cfg.OverridePropertyHolder; +import at.knowcenter.wag.egov.egiz.cfg.SettingsReader; +import at.knowcenter.wag.egov.egiz.exceptions.NormalizeException; +import at.knowcenter.wag.egov.egiz.exceptions.PDFDocumentException; +import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; +import at.knowcenter.wag.egov.egiz.exceptions.SignatureException; +import at.knowcenter.wag.egov.egiz.framework.SignatorFactory; +import at.knowcenter.wag.egov.egiz.pdf.BinarySignature; +import at.knowcenter.wag.egov.egiz.pdf.IncrementalUpdateInformation; +import at.knowcenter.wag.egov.egiz.pdf.PositioningInstruction; +import at.knowcenter.wag.egov.egiz.pdf.ReplaceInfo; +import at.knowcenter.wag.egov.egiz.pdf.StringInfo; +import at.knowcenter.wag.egov.egiz.pdf.TablePos; +import at.knowcenter.wag.egov.egiz.sig.SignatureData; +import at.knowcenter.wag.egov.egiz.sig.SignatureDataImpl; +import at.knowcenter.wag.egov.egiz.sig.SignatureFieldDefinition; +import at.knowcenter.wag.egov.egiz.sig.SignatureObject; +import at.knowcenter.wag.egov.egiz.sig.SignatureTypes; +import at.knowcenter.wag.egov.egiz.sig.signatureobject.SignatureObjectHelper; +import at.knowcenter.wag.egov.egiz.tools.Normalizer; +import at.knowcenter.wag.exactparser.ByteArrayUtils; + +import com.lowagie.text.pdf.PdfPTable; + +/** + * Signs the document binary. + * + *

+ * In prepareSign, an Incremental Update is created that contains the Signature + * block and the egiz dictionary. For formatting the layout, variable values are + * filled with placeholders. After the layout has been fixed, all variable + * fields (all holes in the byte ranges) are replaced with 0. This document is + * then base64 encoded and signed. + *

+ *

+ * In finishSign, the variable fields (values, /Cert) are replaced with the + * values according to the encoding. + *

+ * + * @author wprinz + */ +public class BinarySignator_1_0_0 implements Signator { + // 04.11.2010 changed by exthex - fillReplacesWithValue no longer removes + // multiple newlines from values + + private static Log log = LogFactory.getLog(BinarySignator_1_0_0.class); + + /** + * Settings key for baik enables signatures + */ + public static final String SIG_BAIK_ENABLED = "SIG_BAIK_ENABLED"; + + /** + * The Pdf-AS ID of this Signator. + */ + public static final PdfASID MY_ID = new PdfASID(SignatorFactory.VENDOR, + SignatorFactory.TYPE_BINARY, SignatorFactory.VERSION_1_0_0); + + private Normalizer normalizer; + + /** + * @see at.knowcenter.wag.egov.egiz.framework.Signator#getMyId() + */ + public PdfASID getMyId() { + return MY_ID; + } + + /** + * Default constructor. + */ + public BinarySignator_1_0_0() { + try { + this.normalizer = new Normalizer(); + } catch (NormalizeException e) { + String msg = "Normalizer can not be initialized"; + throw new RuntimeException(msg, new SignatureException(400, msg, e)); + } + } + + /** + * @see at.gv.egiz.pdfas.framework.signator.Signator#prepareSign(PdfDataSource, + * String, TablePos, TimeStamper) + */ + public SignatorInformation prepareSign(PdfDataSource pdfDataSource, + String profile, TablePos pos, TimeStamper timeStamper) + throws SignatorException { + try { + // dferbas: has to be true everytime + boolean has_SIG_ID = true; + + String baikStr = SettingsReader.getInstance().getSetting( + "sig_obj." + profile + ".key." + SIG_BAIK_ENABLED, + "default." + SIG_BAIK_ENABLED, "false"); + boolean baikEnabled = "true".equalsIgnoreCase(baikStr); + + if (baikEnabled) { + log.debug("BAIK enabled signature"); + } + + SignatureObject signature_object = PdfAS + .createSignatureObjectFromType(profile); + signature_object.fillValues( + (char) BinarySignature.LAYOUT_PLACEHOLDER, has_SIG_ID, + baikEnabled); + + signature_object.setKZ(getMyId()); + + PdfPTable pdf_table = PdfAS + .createPdfPTableFromSignatureObject(signature_object); + + PositioningInstruction pi = PdfAS.determineTablePositioning(pos, + profile, pdfDataSource, pdf_table); + + List all_field_definitions = signature_object + .getSignatureTypeDefinition().getFieldDefinitions(); + List variable_field_definitions = new ArrayList(); + for (int i = 0; i < all_field_definitions.size(); i++) { + SignatureFieldDefinition sfd = (SignatureFieldDefinition) all_field_definitions + .get(i); + if (sfd.placeholder_length > 0) { + if (sfd.field_name.equals(SignatureTypes.SIG_ID) + && has_SIG_ID == false) { + continue; + } + + if (sfd.field_name.equals(SignatureTypes.SIG_ALG) + && baikEnabled == false) { + continue; + } + + variable_field_definitions.add(sfd); + } + } + + List all_invisible_field_definitions = signature_object + .getSignatureTypeDefinition() + .getInvisibleFieldDefinitions(); + List invisible_field_definitions = new ArrayList(); + boolean isKZInvisible = false; + String invKZString = null; + + for (int i = 0; i < all_invisible_field_definitions.size(); i++) { + SignatureFieldDefinition sfd = (SignatureFieldDefinition) all_invisible_field_definitions + .get(i); + if (sfd.field_name.equals(SignatureTypes.SIG_KZ)) { + isKZInvisible = true; + invKZString = signature_object.getKZ().toString(); + continue; + } + if (sfd.field_name.equals(SignatureTypes.SIG_ID) + && has_SIG_ID == false) { + continue; + } + + if (sfd.field_name.equals(SignatureTypes.SIG_ALG) + && baikEnabled == false) { + continue; + } + invisible_field_definitions.add(sfd); + } + + // check if signature block is invisible, and if so and if also + // signature block is positioned + // on a new page, prevent pdf-as to do that, because why should make + // a new page just for an invisible block + // added by rpiazzi + if (signature_object.getSignatureTypeDefinition() + .getInvisibleFieldDefinitions().size() == SignatureTypes.REQUIRED_SIG_KEYS.length) { + if (pi.isMakeNewPage()) { + int pageNumber = pi.getPage(); + pi = new PositioningInstruction(false, pageNumber - 1, 0, 0); + } + } + // end added + + IncrementalUpdateInformation iui = IncrementalUpdateHelper + .writeIncrementalUpdate(pdfDataSource, pdf_table, profile, + pi, variable_field_definitions, + all_field_definitions, invisible_field_definitions, + invKZString, timeStamper, null, signature_object); + + iui.invisible_field_definitions = invisible_field_definitions; + + iui.invisibleKZString = invKZString; + + String temp_string = iui.temp_ir_number + + " " + iui.temp_ir_generation + " obj"; //$NON-NLS-1$//$NON-NLS-2$ + byte[] temp_bytes = ArrayUtils.add( + temp_string.getBytes("US-ASCII"), 0, (byte) 0x0A); + int temp_start = ByteArrayUtils.lastIndexOf(iui.signed_pdf, + temp_bytes); + byte[] stream_bytes = new byte[] { '>', '>', 's', 't', 'r', 'e', + 'a', 'm', 0x0A }; + int stream_start = ByteArrayUtils.indexOf(iui.signed_pdf, + temp_start, stream_bytes); + iui.content_stream_start = stream_start + stream_bytes.length; + + // update the stream indices + Iterator it = iui.replaces.iterator(); + while (it.hasNext()) { + ReplaceInfo ri = (ReplaceInfo) it.next(); + + Iterator sit = ri.replaces.iterator(); + while (sit.hasNext()) { + StringInfo si = (StringInfo) sit.next(); + si.string_start += iui.content_stream_start; + } + } + // update KZ list indices: + if (!isKZInvisible) { + it = iui.kz_list.iterator(); + while (it.hasNext()) { + StringInfo si = (StringInfo) it.next(); + si.string_start += iui.content_stream_start; + } + } + + BinarySignature.markByteRanges(iui); + + // byte [] old_signed_pdf = iui.signed_pdf; + iui.signed_pdf = BinarySignature.prepareDataToSign(iui.signed_pdf, + iui.byte_ranges); + + BinarySignatorInformation bsi = compressIUI(iui); + return bsi; + + } catch (UnsupportedEncodingException e) { + throw new SignatorException(201, e); + } catch (PDFDocumentException e) { + throw new SignatorException(e.getErrorCode(), e); + } catch (PresentableException e) { + throw new SignatorException(201, e); + } + } + + /** + * @see at.gv.egiz.pdfas.framework.signator.Signator#finishSign(at.gv.egiz.pdfas.framework.signator.SignatorInformation, + * at.gv.egiz.pdfas.framework.output.DataSink) + */ + public void finishSign(SignatorInformation signatorInformation, + DataSink dataSink) throws SignatorException { + try { + IncrementalUpdateInformation iui = uncompressIUI((BinarySignatorInformation) signatorInformation); + + // PERF: need to keep the whole pdf in mem for processing + + // PdfAS.prefixID(iui.signed_signature_object, PdfAS.BINARY_ID); + fillReplacesWithValues(iui); + + // This is needed so that certificates are stored + try { + iui.signed_signature_object.kz = getMyId().toString(); + SignatureObjectHelper + .convertSignSignatureObjectToSignatureObject( + iui.signed_signature_object, iui.signProfile); + } catch (PresentableException e) { + throw new SignatorException(e); + } + + BinarySignature.replaceCertificate(iui); + BinarySignature.replaceTimestamp(iui); + BinarySignature.replacePlaceholders(iui); + // dferbas: alternative sign attrib creation + // PdfReader reader = new PdfReader(iui.signed_pdf); + // + // OutputStream os = + // dataSink.createOutputStream(PdfAS.PDF_MIME_TYPE); + // + // try { + // PdfStamper stamper = PdfStamper.createSignature(reader, os, '\0', + // null, true); + // + // BinarySignature.createAdobeSigAttrib(stamper, + // signatorInformation, signatorInformation.getActualTablePos()); + // + // } catch (DocumentException e) { + // log.error("pdf error", e); + // throw new SignatorException(ErrorCode.CANNOT_WRITE_PDF, e); + // } + + OutputStream os = dataSink.createOutputStream(PdfAS.PDF_MIME_TYPE); + os.write(iui.signed_pdf); + os.close(); + } catch (PDFDocumentException e) { + throw new SignatorException(e.getErrorCode(), e); + } catch (IOException e) { + throw new SignatorException(ErrorCode.DOCUMENT_CANNOT_BE_READ, e); + } + } + + /** + * Reads the signature values from the signed signature object and fills the + * corresponding value in the Replaces array. + * + * @param iui + * The IncrementalUpdateInformation. + */ + protected void fillReplacesWithValues(final IncrementalUpdateInformation iui) { + try { + Iterator it = iui.replaces.iterator(); + HashMap ognlCtx = new HashMap(); + ognlCtx.put("iui", iui); + ognlCtx.put("sso", iui.signed_signature_object); + ognlCtx.put("issuer", iui.signed_signature_object.getIssuerDNMap()); + ognlCtx.put("subject", + iui.signed_signature_object.getSubjectDNMap()); + OgnlUtil ognl = new OgnlUtil(ognlCtx); + + OverridePropertyHolder.setOgnlUtil(ognl); + while (it.hasNext()) { + ReplaceInfo ri = (ReplaceInfo) it.next(); + String overrideVal = OverridePropertyHolder + .getProperty(ri.sfd.field_name); + if (overrideVal != null) { + ri.sfd.value = overrideVal; + ri.value = overrideVal; + } else if (ognl.containsExpression(ri.sfd.value)) { // dferbas + // evaluate expression + String res = ognl.compileMessage(ri.sfd.value); + ri.value = this.normalizer.normalize(res, true); + // Workaround added by rpiazzi + // a-trust wrongly encodes since July 2011, therefore some + // special characters (e.g. Umlaute) have + // to replaced by their right character + /*if ((ri.value.contains("ü") + || ri.value.contains("ä") + || ri.value.contains("ö") + || ri.value.contains("á") + || ri.value.contains("ß") + || ri.value.contains("Ö") + || ri.value.contains("à") + || ri.value.contains("é") + || ri.value.contains("ú") || ri.value + .contains("ç")) + && (ri.sfd.field_name + .equals(SignatureTypes.SIG_SUBJECT))) { + if (ri.value.contains("ü")) { + ri.sfd.value = ri.sfd.value.replace("ü", "�"); + ri.value = ri.value.replace("ü", "�"); + } + if (ri.value.contains("ä")) { + ri.sfd.value = ri.sfd.value.replace("ä", "�"); + ri.value = ri.value.replace("ä", "�"); + } + if (ri.value.contains("ö")) { + ri.sfd.value = ri.sfd.value.replace("ö", "�"); + ri.value = ri.value.replace("ö", "�"); + } + if (ri.value.contains("á")) { + ri.sfd.value = ri.sfd.value.replace("á", "�"); + ri.value = ri.value.replace("á", "�"); + } + if (ri.value.contains("ß")) { + ri.sfd.value = ri.sfd.value.replace("ß", "�"); + ri.value = ri.value.replace("ß", "�"); + } + if (ri.value.contains("Ö")) { + ri.sfd.value = ri.sfd.value.replace("Ö", "�"); + ri.value = ri.value.replace("Ö", "�"); + } + if (ri.value.contains("à")) { + ri.sfd.value = ri.sfd.value.replace("à", "�"); + ri.value = ri.value.replace("à", "�"); + } + if (ri.value.contains("é")) { + ri.sfd.value = ri.sfd.value.replace("é", "�"); + ri.value = ri.value.replace("é", "�"); + } + if (ri.value.contains("ú")) { + ri.sfd.value = ri.sfd.value.replace("ú", "�"); + ri.value = ri.value.replace("ú", "�"); + } + if (ri.value.contains("ç")) { + ri.sfd.value = ri.sfd.value.replace("ç", "�"); + ri.value = ri.value.replace("ç", "�"); + } + }*/ + FixHandyAnsiEncoding(ri); + // end added + } else if (overrideVal == null) { + // If SUBJECT is not overridden and and also isn't an + // expression + // check whether a set value for subject exists. + // In this case take the value from the config file. + // Added by rpiazzi to make a static signator possible + // without having + // to override it any time + if (ri.sfd.field_name.equals(SignatureTypes.SIG_SUBJECT)) { + if (ri.sfd.value.length() != 0) { + ri.value = ri.sfd.value; + } else { + ri.value = iui.signed_signature_object + .retrieveStringValue(ri.sfd.field_name); + } + } else { + ri.value = iui.signed_signature_object + .retrieveStringValue(ri.sfd.field_name); + } + } + } + } finally { + OverridePropertyHolder.removeOgnlUtil(); + } + } + + private void FixHandyAnsiEncoding(ReplaceInfo ri) { + Pattern p = Pattern.compile("&#([0-9]+);"); + Matcher m = p.matcher(ri.value); + + int value = -1; + + while (m.find()) { + value = Integer.parseInt(m.group(1)); + + if (value > 0 && value < 256) { + + log.debug("Replacing Encoding &#" + m.group(1) + ";"); + + byte[] buffer = new byte[1]; + + buffer[0] = (byte) value; + + ByteBuffer bb = ByteBuffer.wrap(buffer); + + CharBuffer cb = Charset.forName("CP1252").decode(bb); + + String str = cb.toString(); + + ri.value = ri.value.replace("&#" + m.group(1) + ";", str); + } + } + } + + /** + * Forms the SignatureData to be used for signing. + * + * @param iui + * The IncrementalUpdateInformation. + * @return Returns the SignatureData to be used for signing. + */ + protected SignatureData formSignatureData(IncrementalUpdateInformation iui) { + // String document_text = + // BinarySignature.retrieveSignableTextFromData(iui.signed_pdf, + // iui.signed_pdf.length); // signed_pdf.length); + // + // byte[] data; + // try + // { + // data = document_text.getBytes("UTF-8"); //$NON-NLS-1$ + // } + // catch (UnsupportedEncodingException e) + // { + // throw new RuntimeException("Very strange: UTF-8 character encoding + // not + // supported.", e); //$NON-NLS-1$ + // } + DataSource ds = new CompoundPdfDataSourceImpl(iui.original_document, + iui.sign_iui_block); + SignatureData signature_data = new SignatureDataImpl(ds, + PdfAS.PDF_MIME_TYPE); + + return signature_data; + } + + protected BinarySignatorInformation compressIUI( + IncrementalUpdateInformation iui) { + iui.sign_iui_block = new byte[iui.signed_pdf.length + - iui.original_document.getLength()]; + System.arraycopy(iui.signed_pdf, iui.original_document.getLength(), + iui.sign_iui_block, 0, iui.sign_iui_block.length); + + iui.signature_data = formSignatureData(iui); + + // remove the signed pdf from memory + iui.signed_pdf = null; + + BinarySignatorInformation bsi = new BinarySignatorInformation(); + bsi.originalDocument = iui.original_document; + bsi.incrementalUpdateBlock = iui.sign_iui_block; + bsi.signatureData = iui.signature_data; + bsi.replaces = iui.replaces; + bsi.cert_start = iui.cert_start; + bsi.cert_length = iui.cert_length; + bsi.enc_start = iui.enc_start; + bsi.enc_length = iui.enc_length; + bsi.atp = iui.actualTablePos; + bsi.signProfile = iui.signProfile; + bsi.timestamp_length = iui.timestamp_length; + bsi.timestamp_start = iui.timestamp_start; + + return bsi; + } + + protected IncrementalUpdateInformation uncompressIUI( + BinarySignatorInformation bsi) { + IncrementalUpdateInformation iui = new IncrementalUpdateInformation(); + + iui.original_document = bsi.originalDocument; + iui.sign_iui_block = bsi.incrementalUpdateBlock; + iui.signature_data = bsi.signatureData; + iui.replaces = bsi.replaces; + iui.cert_start = bsi.cert_start; + iui.cert_length = bsi.cert_length; + iui.enc_start = bsi.enc_start; + iui.enc_length = bsi.enc_length; + iui.actualTablePos = bsi.atp; + iui.signProfile = bsi.signProfile; + iui.timestamp_length = bsi.timestamp_length; + iui.timestamp_start = bsi.timestamp_start; + + iui.signed_signature_object = bsi.signSignatureObject; + + restoreSignedPdf(iui); + + return iui; + } + + protected void restoreSignedPdf(IncrementalUpdateInformation iui) { + iui.signed_pdf = new byte[iui.original_document.getLength() + + iui.sign_iui_block.length]; + + try { + InputStream is = iui.original_document.createInputStream(); + is.read(iui.signed_pdf, 0, iui.original_document.getLength()); + is.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + System.arraycopy(iui.sign_iui_block, 0, iui.signed_pdf, + iui.original_document.getLength(), iui.sign_iui_block.length); + } + + public String getEncoding() { + // not used for binary signature + return "utf-8"; + + } + +} -- cgit v1.2.3