package at.knowcenter.wag.egov.egiz.pdf; import java.util.HashMap; import java.util.Iterator; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import at.gv.egiz.pdfas.exceptions.ErrorCode; import at.gv.egiz.pdfas.framework.signator.SignatorInformation; import at.gv.egiz.pdfas.utils.OgnlUtil; import at.knowcenter.wag.egov.egiz.cfg.SettingsReader; import at.knowcenter.wag.egov.egiz.exceptions.PresentableException; import at.knowcenter.wag.egov.egiz.exceptions.SettingsException; import at.knowcenter.wag.egov.egiz.sig.SignatureObject; import com.lowagie.text.Rectangle; import com.lowagie.text.pdf.AcroFields; import com.lowagie.text.pdf.PdfDictionary; import com.lowagie.text.pdf.PdfFormField; import com.lowagie.text.pdf.PdfName; import com.lowagie.text.pdf.PdfNumber; import com.lowagie.text.pdf.PdfObject; import com.lowagie.text.pdf.PdfSignature; import com.lowagie.text.pdf.PdfSignatureAppearance; import com.lowagie.text.pdf.PdfStamper; import com.lowagie.text.pdf.PdfString; /** * Helper class for creating adobe signature attributes. * * @author dferbas * */ public class AdobeSignatureHelper { private static final String ADOBE_SIGN_FIELDNAME_KEY = "adobeSignFieldValue"; private static final String ADOBE_SIGN_REASONNAME_KEY = "adobeSignReasonValue"; private static final String ADOBE_SIG_ENABLED_KEY = "adobeSignEnabled"; private static Logger logger = Logger.getLogger(AdobeSignatureHelper.class); public static final String ADOBE_SIG_FILTER = "Adobe.PDF-AS"; public static final String ADOBE_SIG_TEXT_KEY = "adobeSignText"; private static final String ADOBE_VERIFY_URL_KEY = "verifyURL"; /** * Writes Adobe-pdf signature entry with itext * * @param stamper * @param si * @param so * @param atp * @throws PresentableException */ public static void createAdobeSignatureField(PdfStamper stamper, SignatorInformation si, SignatureObject so, ActualTablePos atp, StructContentHelper structHelper) throws PresentableException { try { logger.debug("Creating adobe signature field."); PdfSignatureAppearance sap = stamper.getSignatureAppearance(); String profileId = so.getSignatureTypeDefinition().getType(); String fieldName = getAdobeFieldName(profileId); // find field num /* int nexSigNum = 1; String finalFieldName = fieldName + " #" + nexSigNum; while (stamper.getAcroFields().getField(finalFieldName) != null) { nexSigNum++; finalFieldName = fieldName + " #" + nexSigNum; } */ AcroFields af = stamper.getAcroFields(); Iterator signatureNamesIt = af.getSignatureNames().iterator(); PdfName referenceFilterName = new PdfName(ADOBE_SIG_FILTER); int nextSigNum = 1; while (signatureNamesIt.hasNext()) { PdfDictionary dictionary = (PdfDictionary) af.getSignatureDictionary((String) signatureNamesIt.next()); PdfObject filterName = dictionary.get(PdfName.FILTER); if (filterName != null && filterName.isName()) { PdfName name = (PdfName) filterName; if (referenceFilterName.equals(name)) { nextSigNum++; } } } String finalFieldName = fieldName + " #" + nextSigNum; sap.setCrypto(null, null, null, null); // supress overlay text for visible signatures sap.setLayer2Text(""); sap.setLayer4Text(""); // the following line marks the sig block as adobe sig // sap.setVisibleSignature(createRectangleFromTablePos(iui.actualTablePos), // iui.actualTablePos.page, "PDF-AS-Signatur"); sap.setVisibleSignature(new Rectangle(0, 0, 0, 0), atp.page, finalFieldName); String subfilter = "unknown"; if (so != null && so.getKZ() != null) { subfilter = so.getKZ().toString(); } else if (si != null) { subfilter = si.getSignSignatureObject().kz; } PdfSignature sig = new PdfSignature(new PdfName(ADOBE_SIG_FILTER), new PdfName(subfilter)); // the following fields are not shown by the reader, because its is no // Standard filter // sig.setLocation("location is not visible"); // sig.setReason("reason is not visible"); // contact field is used to embed signature verification url for adobe handler String verifyURL = getVerifyUrl(profileId); if (!StringUtils.isEmpty(verifyURL)) { sig.setContact(getVerifyUrl(profileId)); } else { logger.debug("No verify URL set -> verify URL is not embedded."); } // sig.setDate(new PdfDate()); String reason = getAdobeReasonName(profileId); if (!StringUtils.isEmpty(reason)) { sig.setReason(reason); } /* disabled in order to align adobe signature appearance for textual signatures with binary signatures if (si != null) { XMLGregorianCalendar c = DatatypeFactory.newInstance().newXMLGregorianCalendar( si.getSignSignatureObject().date); sig.setDate(new PdfDate(c.toGregorianCalendar())); } */ sig.setName(getAdobeSignText(profileId, si)); sap.setCryptoDictionary(sig); sap.setCertificationLevel(PdfSignatureAppearance.NOT_CERTIFIED); // content element is mandatory but empty HashMap exc = new HashMap(); exc.put(PdfName.CONTENTS, new Integer(2)); PdfNumber parentNum = structHelper.buildAdobeSigStructParent(); if (parentNum != null) { PdfFormField sigField = PdfFormField.createSignature(stamper.getWriter()); sap.setSigFormField(sigField); sigField.put(PdfName.STRUCTPARENT, parentNum); structHelper.buildAdobeSigStruct(sigField, finalFieldName); } sap.preClose(exc); // *2+2 PdfDictionary dic = new PdfDictionary(); dic.put(PdfName.CONTENTS, new PdfString((String) null).setHexWriting(true)); sap.close(dic); } catch (Exception ex) { logger.error("error", ex); throw new PresentableException(ErrorCode.CANNOT_WRITE_PDF, "Error creating adobe signature attribute", ex); } } /** * Returns if adobe signature is enabled for the passed signature profile. * Config key: {@value #ADOBE_SIG_ENABLED_KEY} * @param sigProfile * @return */ public static boolean isAdobeSignatureFieldEnabled(String sigProfile) { return "true".equalsIgnoreCase( getDefaultableConfigProperty(sigProfile, ADOBE_SIG_ENABLED_KEY, "false")); } protected static Rectangle createRectangleFromTablePos(ActualTablePos pos) { return new Rectangle(pos.x, pos.y, pos.x + pos.width, pos.y - pos.height); } private static String getAdobeFieldName(String sigProfile) { return getDefaultableConfigProperty(sigProfile, ADOBE_SIGN_FIELDNAME_KEY, "PDF-AS Signatur"); } private static String getAdobeReasonName(String sigProfile) { return getDefaultableConfigProperty(sigProfile, ADOBE_SIGN_REASONNAME_KEY, "Informationen zur Prüfung finden Sie unter http://www.signaturpruefung.gv.at"); } private static String getVerifyUrl(String sigProfile) { return getDefaultableConfigProperty(sigProfile, ADOBE_VERIFY_URL_KEY, "http://www.signaturpruefung.gv.at"); } public static String getDefaultableConfigProperty(String sigProfile, String propName, String defaultValue) { String confVal; try { confVal = SettingsReader.getInstance().getSetting( "sig_obj." + sigProfile + "." + propName, "default." + propName, defaultValue); } catch (SettingsException e) { logger.warn("error reading " + propName + " from config. Using default: " + defaultValue, e); return defaultValue; } return confVal; } /** * Evaluate name for adobe signature field. Get from config. Evaluate ognl if * ok. * * @param sigProfile * @param si * @return */ private static String getAdobeSignText(String sigProfile, SignatorInformation si) { String defaultName = "PDF-AS"; try { logger.debug("reading adobe sig name for profile: " + sigProfile); String propKey = ADOBE_SIG_TEXT_KEY + ".textual"; if (si == null) { propKey = propKey.replaceAll("textual", "binary"); } String adobeStr = getDefaultableConfigProperty(sigProfile, propKey, defaultName); HashMap ognlCtx = new HashMap(); OgnlUtil ognl = new OgnlUtil(ognlCtx); if (ognl.containsExpression(adobeStr)) { if (si == null) { logger .error(ADOBE_SIG_TEXT_KEY + " ognl expressions not allowed for binary signatures (SignatorInformation not available)"); return defaultName; } ognlCtx.put("si", si); ognlCtx.put("sso", si.getSignSignatureObject()); String res = ognl.compileMessage(adobeStr); return res; } else { return adobeStr; } } catch (Exception ex) { logger.warn("error creating adobe sign text, using default '" + defaultName + "'", ex); return defaultName; } } }